Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/blockfrost-nullable-params.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@evolution-sdk/evolution": patch
---

Blockfrost protocol parameters now accept `null` for the 15 fields the Blockfrost OpenAPI spec marks nullable (`max_val_size`, `cost_models`, `cost_models_raw`, `price_mem`, `price_step`, `max_tx_ex_mem`, `max_tx_ex_steps`, `max_block_ex_mem`, `max_block_ex_steps`, `collateral_percent`, `max_collateral_inputs`, `coins_per_utxo_size`, `min_fee_ref_script_cost_per_byte`, `drep_deposit`, `gov_action_deposit`). Previously one `null` failed the whole `getProtocolParameters` decode; the decoded `null` now reaches the transform, whose existing defaults apply.
39 changes: 22 additions & 17 deletions packages/evolution/src/sdk/provider/internal/Blockfrost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,33 @@ export const BlockfrostProtocolParameters = Schema.Struct({
key_deposit: Schema.BigInt,
min_utxo: Schema.optional(Schema.BigInt),
max_tx_size: Schema.BigInt,
max_val_size: Schema.optional(Schema.BigInt),
// The Blockfrost OpenAPI spec marks the fields below nullable: a `null` must
// decode (to null) instead of failing the whole response; the transform's
// `?? 0n` / `|| 0` defaults then apply.
max_val_size: Schema.optionalWith(Schema.BigInt, { nullable: true }),
utxo_cost_per_word: Schema.optional(Schema.BigInt),
cost_models: Schema.optional(
Schema.Record({ key: Schema.String, value: Schema.Record({ key: Schema.String, value: Schema.BigInt }) })
cost_models: Schema.optionalWith(
Schema.Record({ key: Schema.String, value: Schema.Record({ key: Schema.String, value: Schema.BigInt }) }),
{ nullable: true }
),
cost_models_raw: Schema.optional(
Schema.Record({ key: Schema.String, value: Schema.Array(Schema.BigInt) })
cost_models_raw: Schema.optionalWith(
Schema.Record({ key: Schema.String, value: Schema.Array(Schema.BigInt) }),
{ nullable: true }
),
price_mem: Schema.optional(Schema.NumberFromString),
price_step: Schema.optional(Schema.NumberFromString),
max_tx_ex_mem: Schema.optional(Schema.BigInt),
max_tx_ex_steps: Schema.optional(Schema.BigInt),
max_block_ex_mem: Schema.optional(Schema.BigInt),
max_block_ex_steps: Schema.optional(Schema.BigInt),
price_mem: Schema.optionalWith(Schema.NumberFromString, { nullable: true }),
price_step: Schema.optionalWith(Schema.NumberFromString, { nullable: true }),
max_tx_ex_mem: Schema.optionalWith(Schema.BigInt, { nullable: true }),
max_tx_ex_steps: Schema.optionalWith(Schema.BigInt, { nullable: true }),
max_block_ex_mem: Schema.optionalWith(Schema.BigInt, { nullable: true }),
max_block_ex_steps: Schema.optionalWith(Schema.BigInt, { nullable: true }),
max_block_size: Schema.BigInt,
collateral_percent: Schema.optional(Schema.BigInt),
max_collateral_inputs: Schema.optional(Schema.BigInt),
coins_per_utxo_size: Schema.optional(Schema.BigInt),
min_fee_ref_script_cost_per_byte: Schema.optional(Schema.NumberFromString),
collateral_percent: Schema.optionalWith(Schema.BigInt, { nullable: true }),
max_collateral_inputs: Schema.optionalWith(Schema.BigInt, { nullable: true }),
coins_per_utxo_size: Schema.optionalWith(Schema.BigInt, { nullable: true }),
min_fee_ref_script_cost_per_byte: Schema.optionalWith(Schema.NumberFromString, { nullable: true }),
// Conway era governance parameters
drep_deposit: Schema.optional(Schema.BigInt),
gov_action_deposit: Schema.optional(Schema.BigInt)
drep_deposit: Schema.optionalWith(Schema.BigInt, { nullable: true }),
gov_action_deposit: Schema.optionalWith(Schema.BigInt, { nullable: true })
})

export type BlockfrostProtocolParameters = Schema.Schema.Type<typeof BlockfrostProtocolParameters>
Expand Down
40 changes: 40 additions & 0 deletions packages/evolution/test/provider/Blockfrost.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,46 @@ describe("Blockfrost getProtocolParameters", () => {
})
})

it("accepts null for every nullable protocol parameter and falls back to the defaults", async () => {
// The OpenAPI spec marks these 15 fields nullable; one null must not fail the decode
const payload = JSON.parse(PROTOCOL_PARAMETERS) as Record<string, unknown>
for (const field of [
"max_val_size",
"cost_models",
"cost_models_raw",
"price_mem",
"price_step",
"max_tx_ex_mem",
"max_tx_ex_steps",
"max_block_ex_mem",
"max_block_ex_steps",
"collateral_percent",
"max_collateral_inputs",
"coins_per_utxo_size",
"min_fee_ref_script_cost_per_byte",
"drep_deposit",
"gov_action_deposit"
]) {
payload[field] = null
}
stubRoutes({ "/epochs/latest/parameters": JSON.stringify(payload) })

const params = await Effect.runPromise(BlockfrostEffect.getProtocolParameters(BASE, PROJECT_ID))

expect(params.maxValSize).toBe(0)
expect(params.priceMem).toBe(0)
expect(params.priceStep).toBe(0)
expect(params.maxTxExMem).toBe(0n)
expect(params.maxTxExSteps).toBe(0n)
expect(params.coinsPerUtxoByte).toBe(0n)
expect(params.collateralPercentage).toBe(0)
expect(params.maxCollateralInputs).toBe(0)
expect(params.minFeeRefScriptCostPerByte).toBe(0)
expect(params.drepDeposit).toBe(0n)
expect(params.govActionDeposit).toBe(0n)
expect(params.costModels).toEqual({ PlutusV1: {}, PlutusV2: {}, PlutusV3: {} })
})

it("falls back to the named cost models as numbers", async () => {
stubRoutes({ "/epochs/latest/parameters": PROTOCOL_PARAMETERS.replace(/"cost_models_raw": \{[^}]*\},/, "") })

Expand Down