Skip to content

Commit 9008db3

Browse files
Merge pull request #128 from IntersectMBO/feat/provider-full-utxo-resolution-scripts-datums
feat/provider full utxo resolution scripts datums
2 parents 4cea537 + d42ca5f commit 9008db3

19 files changed

Lines changed: 748 additions & 241 deletions

.changeset/sour-bats-grin.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"@evolution-sdk/evolution": patch
3+
---
4+
5+
### Provider Improvements: Full UTxO Resolution with Scripts and Datums
6+
7+
**Blockfrost Provider:**
8+
- Added pagination support for `getUtxos` and `getUtxosWithUnit` (handles addresses with >100 UTxOs)
9+
- Full UTxO resolution now fetches reference scripts and resolves datum hashes
10+
- Updated `BlockfrostDelegation` schema to match actual `/accounts/{stake_address}` endpoint response
11+
- Added `BlockfrostAssetAddress` and `BlockfrostTxUtxos` schemas for proper endpoint handling
12+
- Improved `evaluateTx` to always use the more reliable `/utils/txs/evaluate/utxos` JSON endpoint
13+
- Added `EvaluationFailure` handling in evaluation response schema
14+
- Fixed delegation transformation to use `withdrawable_amount` for rewards
15+
- Added Conway era governance parameters (`drep_deposit`, `gov_action_deposit`) to protocol params
16+
17+
**Kupmios Provider:**
18+
- Removed unnecessary double CBOR encoding for Plutus scripts (Kupo returns properly encoded scripts)
19+
20+
**PoolKeyHash:**
21+
- Added `FromBech32` schema for parsing pool IDs in bech32 format (pool1...)
22+
- Added `fromBech32` and `toBech32` helper functions
23+
24+
**Transaction Builder:**
25+
- Added `passAdditionalUtxos` option to control UTxO passing to provider evaluators (default: false to avoid OverlappingAdditionalUtxo errors)
26+
- Added `scriptDataFormat` option to choose between Conway-era array format and Babbage-era map format for redeemers
27+
- Fixed cost model detection to check reference scripts (not just witness set scripts) for Plutus version detection

packages/evolution-devnet/src/Config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ export const DEFAULT_KUPO_CONFIG: Required<KupoConfig> = {
720720
*/
721721
export const DEFAULT_OGMIOS_CONFIG: Required<OgmiosConfig> = {
722722
enabled: true,
723-
image: "cardanosolutions/ogmios:v6.12.0",
723+
image: "cardanosolutions/ogmios:v6.14.0",
724724
port: 1337,
725725
logLevel: "info"
726726
} as const
@@ -734,7 +734,7 @@ export const DEFAULT_OGMIOS_CONFIG: Required<OgmiosConfig> = {
734734
*/
735735
export const DEFAULT_DEVNET_CONFIG: Required<DevNetConfig> = {
736736
clusterName: "devnet",
737-
image: "ghcr.io/intersectmbo/cardano-node:10.4.1",
737+
image: "ghcr.io/intersectmbo/cardano-node:10.5.1",
738738
ports: {
739739
node: 4001,
740740
submit: 8090

packages/evolution-devnet/test/TxBuilder.Scripts.test.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ describe("TxBuilder Script Handling", () => {
179179
const signBuilder = await builder.build({
180180
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
181181
availableUtxos: [fundingUtxo],
182-
protocolParameters: PROTOCOL_PARAMS
182+
protocolParameters: PROTOCOL_PARAMS,
183+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
183184
})
184185

185186
const tx = await signBuilder.toTransaction()
@@ -388,7 +389,8 @@ describe("TxBuilder Script Handling", () => {
388389
const signBuilder = await builder.build({
389390
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
390391
availableUtxos: [multiAssetUtxo],
391-
protocolParameters: PROTOCOL_PARAMS
392+
protocolParameters: PROTOCOL_PARAMS,
393+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
392394
})
393395

394396
const tx = await signBuilder.toTransaction()
@@ -600,7 +602,8 @@ describe("TxBuilder Script Handling", () => {
600602
const signBuilder = await builder.build({
601603
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
602604
availableUtxos: [utxoWithRefScript, utxoWithoutRefScript],
603-
protocolParameters: PROTOCOL_PARAMS
605+
protocolParameters: PROTOCOL_PARAMS,
606+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
604607
})
605608

606609
const tx = await signBuilder.toTransaction()
@@ -674,7 +677,8 @@ describe("TxBuilder Script Handling", () => {
674677
const signBuilder = await builder.build({
675678
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
676679
availableUtxos: [tightFundingUtxo, collateralUtxo],
677-
protocolParameters: PROTOCOL_PARAMS
680+
protocolParameters: PROTOCOL_PARAMS,
681+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
678682
})
679683

680684
const tx = await signBuilder.toTransaction()
@@ -755,7 +759,8 @@ describe("TxBuilder Script Handling", () => {
755759
const signBuilder = await builder.build({
756760
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
757761
availableUtxos: [collateralUtxo],
758-
protocolParameters: PROTOCOL_PARAMS
762+
protocolParameters: PROTOCOL_PARAMS,
763+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
759764
})
760765

761766
const tx = await signBuilder.toTransaction()
@@ -816,7 +821,8 @@ describe("TxBuilder Script Handling", () => {
816821
const signBuilder = await builder.build({
817822
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
818823
availableUtxos: [],
819-
protocolParameters: PROTOCOL_PARAMS
824+
protocolParameters: PROTOCOL_PARAMS,
825+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
820826
})
821827

822828
const tx = await signBuilder.toTransaction()
@@ -863,7 +869,8 @@ describe("TxBuilder Script Handling", () => {
863869
const signBuilder = await builder.build({
864870
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
865871
availableUtxos: [],
866-
protocolParameters: PROTOCOL_PARAMS
872+
protocolParameters: PROTOCOL_PARAMS,
873+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
867874
})
868875

869876
const tx = await signBuilder.toTransaction()
@@ -908,7 +915,8 @@ describe("TxBuilder Script Handling", () => {
908915
const signBuilder = await builder.build({
909916
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
910917
availableUtxos: [],
911-
protocolParameters: PROTOCOL_PARAMS
918+
protocolParameters: PROTOCOL_PARAMS,
919+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
912920
})
913921

914922
const tx = await signBuilder.toTransaction()
@@ -998,7 +1006,8 @@ describe("TxBuilder Script Handling", () => {
9981006
const signBuilder = await builder.build({
9991007
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
10001008
availableUtxos: [],
1001-
protocolParameters: PROTOCOL_PARAMS
1009+
protocolParameters: PROTOCOL_PARAMS,
1010+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
10021011
})
10031012

10041013
const tx = await signBuilder.toTransaction()
@@ -1040,7 +1049,8 @@ describe("TxBuilder Script Handling", () => {
10401049
const signBuilder = await builder.build({
10411050
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
10421051
availableUtxos: [],
1043-
protocolParameters: PROTOCOL_PARAMS
1052+
protocolParameters: PROTOCOL_PARAMS,
1053+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
10441054
})
10451055

10461056
const tx = await signBuilder.toTransaction()
@@ -1120,7 +1130,8 @@ describe("TxBuilder Script Handling", () => {
11201130
const signBuilder = await builder.build({
11211131
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
11221132
availableUtxos: [collateralUtxo1, collateralUtxo2, collateralUtxo3, collateralUtxo4],
1123-
protocolParameters: PROTOCOL_PARAMS
1133+
protocolParameters: PROTOCOL_PARAMS,
1134+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
11241135
})
11251136

11261137
const tx = await signBuilder.toTransaction()
@@ -1195,7 +1206,8 @@ describe("TxBuilder Script Handling", () => {
11951206
const signBuilder = await builder.build({
11961207
changeAddress: CoreAddress.fromBech32(CHANGE_ADDRESS),
11971208
availableUtxos: [collateralUtxo1, collateralUtxo2, collateralUtxo3],
1198-
protocolParameters: PROTOCOL_PARAMS
1209+
protocolParameters: PROTOCOL_PARAMS,
1210+
passAdditionalUtxos: true // Required for synthetic UTxOs not on chain
11991211
})
12001212

12011213
const tx = await signBuilder.toTransaction()

packages/evolution/src/Address.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchem
169169
Eff.gen(function* () {
170170
const result = yield* Eff.try({
171171
try: () => {
172+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
173+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
172174
const decoded = bech32.decode(fromA as any, false)
173175
const bytes = bech32.fromWords(decoded.words)
174176
return new Uint8Array(bytes)

packages/evolution/src/AddressEras.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchem
176176
Eff.gen(function* () {
177177
const result = yield* Eff.try({
178178
try: () => {
179+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
180+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
179181
const decoded = bech32.decode(fromA as any, false)
180182
const bytes = bech32.fromWords(decoded.words)
181183
return new Uint8Array(bytes)

packages/evolution/src/CommitteeColdCredential.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchem
122122
Effect.gen(function* () {
123123
const result = yield* Effect.try({
124124
try: () => {
125+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
126+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
125127
const decoded = bech32.decode(fromA as any, false)
126128
if (decoded.prefix !== "cc_cold") {
127129
throw new Error(`Invalid prefix: expected "cc_cold", got "${decoded.prefix}"`)

packages/evolution/src/CommitteeHotCredential.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchem
122122
Effect.gen(function* () {
123123
const result = yield* Effect.try({
124124
try: () => {
125+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
126+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
125127
const decoded = bech32.decode(fromA as any, false)
126128
if (decoded.prefix !== "cc_hot") {
127129
throw new Error(`Invalid prefix: expected "cc_hot", got "${decoded.prefix}"`)

packages/evolution/src/DRep.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchem
331331
Eff.gen(function* () {
332332
const result = yield* Eff.try({
333333
try: () => {
334+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
335+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
334336
const decoded = bech32.decode(fromA as any, false)
335337
if (decoded.prefix !== "drep") {
336338
throw new Error(`Invalid prefix: expected "drep", got "${decoded.prefix}"`)

packages/evolution/src/PoolKeyHash.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Equal, FastCheck, Hash, Inspectable, Schema } from "effect"
1+
import { bech32 } from "@scure/base"
2+
import { Effect as Eff, Equal, FastCheck, Hash, Inspectable, ParseResult, Schema } from "effect"
23

34
import * as Bytes from "./Bytes.js"
45
import * as Hash28 from "./Hash28.js"
@@ -59,6 +60,38 @@ export const FromHex = Schema.compose(Hash28.BytesFromHex, FromBytes).annotation
5960
identifier: "PoolKeyHash.FromHex"
6061
})
6162

63+
/**
64+
* Schema transformer from bech32 string (pool1...) to PoolKeyHash.
65+
*
66+
* @since 2.0.0
67+
* @category schemas
68+
*/
69+
export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchema(PoolKeyHash), {
70+
strict: true,
71+
encode: (poolKeyHash) =>
72+
Eff.gen(function* () {
73+
const words = bech32.toWords(poolKeyHash.hash)
74+
return bech32.encode("pool", words, false)
75+
}),
76+
decode: (fromA, _, ast) =>
77+
Eff.gen(function* () {
78+
const result = yield* Eff.try({
79+
try: () => {
80+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
81+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
82+
const decoded = bech32.decode(fromA as any, false)
83+
const bytes = bech32.fromWords(decoded.words)
84+
return new Uint8Array(bytes)
85+
},
86+
catch: () => new ParseResult.Type(ast, fromA, `Failed to decode Bech32 pool id: ${fromA}`)
87+
})
88+
return yield* ParseResult.decode(FromBytes)(result)
89+
})
90+
}).annotations({
91+
identifier: "PoolKeyHash.FromBech32",
92+
description: "Transforms Bech32 pool id string to PoolKeyHash"
93+
})
94+
6295
/**
6396
* FastCheck arbitrary for generating random PoolKeyHash instances.
6497
*
@@ -105,3 +138,19 @@ export const toBytes = Schema.encodeSync(FromBytes)
105138
* @category encoding
106139
*/
107140
export const toHex = Schema.encodeSync(FromHex)
141+
142+
/**
143+
* Parse PoolKeyHash from bech32 string (pool1...).
144+
*
145+
* @since 2.0.0
146+
* @category parsing
147+
*/
148+
export const fromBech32 = Schema.decodeSync(FromBech32)
149+
150+
/**
151+
* Encode PoolKeyHash to bech32 string (pool1...).
152+
*
153+
* @since 2.0.0
154+
* @category encoding
155+
*/
156+
export const toBech32 = Schema.encodeSync(FromBech32)

packages/evolution/src/PrivateKey.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ export const FromBech32 = Schema.transformOrFail(Schema.String, Schema.typeSchem
9393
decode: (fromA, _, ast) =>
9494
E.gen(function* () {
9595
const { prefix, words } = yield* ParseResult.try({
96+
// Note: `as any` needed because bech32.decode expects template literal type `${Prefix}1${string}`
97+
// but Schema provides plain string. Consider using decodeToBytes which accepts string.
9698
try: () => bech32.decode(fromA as any, 1023),
9799
catch: (error) => new ParseResult.Type(ast, fromA, `Failed to decode bech32 string: ${(error as Error).message}`)
98100
})

0 commit comments

Comments
 (0)