All Developer News

v4.0.0

SDK/CLI

v4.0 changes how programs call the SDK and adds a preflight check for execute batches, timelocked AccessManager operations and an address registry. It applies to all code written for 3.x, including scripts and agents that read CLI or MCP output. Most 3.x code needs changes, listed in the migration guide.

New

1. Preflight checks

Before: nothing in the SDK checked whether an execute batch would go through before signing.

Now: preflight_execute and fusion vault preflight check an execute batch the way its sender would send it, before signing. They check permission, registered fuses and balance fuses, then simulate the batch and report the revert reason and gas.

Usage: fusion vault preflight from the CLI, preflight_execute in Python, or the MCP tool vault_preflight. Each reports the outcome of the batch before anything is signed.

2. Timelocked AccessManager operations

Before: an account with an execution delay could not use .send(ctx). The SDK had no calls to schedule, execute or cancel an operation.

Now: AccessManager builds schedule, execute and cancel calls. AccessManager.read_call_plan(ctx, call, caller=...) says whether to send now, schedule or wait, and returns the call to send next.

Usage: read_call_plan returns a CallPlan whose kind is immediate, schedule, waiting, ready or forbidden. Its next_call is the call to send. The plan covers the AccessManager’s checks. The target contract’s own checks are outside it.

3. Address registry

Before: fuse, factory and manager addresses lived only in ipor-abi. Scripts, examples and agents hardcoded them.

Now: fetch_address_registry(chain_id) reads the ipor-abi registry and looks up IPOR Fusion addresses by name. The CLI adds fusion address lookup, fusion address names and fusion address list, with matching MCP tools.

Usage: fetch_address_registry(chain_id).address(name) returns the address registered under name.

4. Named contract reverts

Before: revert data of the vault, its managers and fuses read as a bare selector, for example custom error 0x068ca9d8. Only the crosschain errors were registered.

Now: the SDK registers every error declared in the IPOR Fusion contracts. The same revert reads by name, for example AccessManagedUnauthorized(0x...).

Impact: code and alerts that match on a bare selector see the error name in its place.

5. Exact decimal conversion

Before: every script rolled its own conversion between decimal text and raw integers, often through floats.

Now: parse_amount and format_amount convert between decimal text and raw on-chain integers with integer math only. parse_shares and format_shares do the same for vault shares.

Usage: parse_amount("1.5", Decimals(6)) returns Amount(1_500_000). format_amount is its inverse. A float argument raises TypeError. Nothing is rounded: more fractional digits than the token’s decimals raise ValueError.

6. Supported chains and Python versions

Now: the vault tooling supports 11 chains, now including Ink and Robinhood. The SDK runs on Python 3.11 to 3.13.

Usage: --chain-id ink and --chain-id robinhood select the new chains in the vault commands.

Breaking changes

1. Context-free wrappers

Before: wrappers were built with a context, as in PlasmaVault(ctx, vault). The wrapper stored it and copied it into every Call.

Now: wrappers take only an address: PlasmaVault(vault). Execution and chain reads take the context: .call(ctx), .send(ctx), vault.read_balance_fuses(ctx).

Applies to: code that builds a wrapper with a context, calls .call(), .send(), .build_transaction() or .estimate_gas() without one, or calls a 3.x read method such as get_balance_fuses(). The wrapper and .call() lines raise TypeError only when they run.

→ Migration guide: Context-free wrappers

2. Renamed and removed names

Before: 3.x had IporFusionMarkets.RWA and MORPHO_BLUE_ADDRESS, and exported the cross-chain and HyperCore names from the top-level ipor_fusion package.

Now: RWA is EXTERNAL_STATE, and MORPHO_BLUE_ADDRESS is morpho_blue_address(chain_id). Cross-chain and HyperCore names import only from ipor_fusion.crosschain and ipor_fusion.hypercore.

Applies to: code that uses IporFusionMarkets.RWA or MORPHO_BLUE_ADDRESS, or imports cross-chain and HyperCore names from ipor_fusion. Removed imports fail when the module loads.

→ Migration guide: Moved, removed and reshaped

3. One vault snapshot

Before: block_number only selected the block reported in VaultData. The reads ran wherever ctx pointed.

Now: fetch_vault_data(ctx, vault) reads everything at one block. Validation and fusion vault info are built from it.

Applies to: code that calls fetch_vault_data with a PlasmaVault wrapper or a chain_id argument, and code that assigns ctx.default_block.

→ Migration guide: Vault reads and validation

4. Typed batches and simulations

Before: sim.observe("label", call) returned the simulator, and values were read with result.get("label"). Mixed-type multicall reads went through a cast list.

Now: batch.add(call) and VaultSimulator.observe(call) return typed handles read after run(). SimulationResult.get is removed.

Applies to: code that calls sim.observe("label", call) or reads SimulationResult.get, SimulationResult.observations or SimulatedCallResult.decoded.

→ Migration guide: Typed batches and simulation observations

5. SDK exceptions

Before: some SDK exceptions did not derive from IporFusionError, among them MorphoApiError, ExternalStateEventDecodeError and VerifierResultUnavailable. Some raises used builtin classes, for example IndexError from read_owner when no account held OWNER_ROLE.

Now: every SDK exception derives from IporFusionError and keeps its builtin base. A chain, provider or API answer the SDK cannot use raises an IporFusionError subclass.

Applies to: exception handlers that list except IporFusionError before a more specific handler. That handler now catches these errors first. It also applies to code that catches IndexError from read_owner, which now raises OwnerNotFoundError.

→ Migration guide: Errors

6. CLI and MCP JSON output

Before: the CLI’s vault validate --json and the MCP vault_validate tool returned two different shapes, with separate lists for warnings and criticals. MCP output added null keys for absent optional fields.

Now: both return one document with a findings list, filtered by severity. Absent optional fields are left out.

Applies to: scripts and agents that read vault validate --json or the MCP vault_validate output, and code that reads null keys from MCP output.

→ Migration guide: Behavior changes

7. Market limits

Before: setup_markets_limits typed limits as Amount and documented them as asset amounts. A limit of 1_000_000 for 1 USDC became a near-zero share that blocks the market once limits are activated.

Now: limits are WAD shares of total assets, where 10**18 equals 100%. A limit outside 0..10**18 raises ValueError.

Applies to: code that calls PlasmaVault.setup_markets_limits, and limits set through it with 3.x. PlasmaVault.get_market_limit(market).call(ctx) returns a limit as its Wad share.

→ Migration guide: Behavior changes

8. Morpho amounts

Before: 3.x converted borrow shares by dividing by total_shares + 1 in floating point, and supply shares without the virtual offsets. Large positions lost their low digits. Small or empty markets could be off by orders of magnitude.

Now: share conversion uses Morpho Blue’s exact integer math (SharesMathLib), with borrow rounded up and supply rounded down.

Applies to: code and alerts built on 3.x Morpho figures: MorphoReader.read_position_breakdown, the vault info Morpho rows and the Morpho lending health.

→ Migration guide: Behavior changes

9. Cross-chain and HyperCore deployments (preview)

Before: the SDK read earlier cross-chain deployments and the HyperCore market-55 test deployment, including the pilot and canary deployments.

Now: ipor_fusion.crosschain and ipor_fusion.hypercore follow only the official 2026-10-21 deployment. Pilot and canary deployments are no longer supported.

Applies to: code that uses ipor_fusion.crosschain or ipor_fusion.hypercore with vaults on contracts earlier than the 2026-10-21 release.

→ Migration guide: Crosschain follows the released contracts

→ Migration guide: HyperCore follows the released contracts

10. Typed IDs, addresses and amounts

Before: chain IDs, addresses and amounts were plain integers and strings.

Now: ids, addresses and amounts are nominal types: ChainId, MarketId, PlasmaVaultAddress, Amount and others. Raw input goes through parse_*.

Applies to: type-checked code (pyright, mypy, Pylance) that passes literals or raw values where the SDK expects these types. Untyped code is unchanged: ChainId(1) is 1 at runtime.

→ Migration guide: Nominal types

Fixes

1. Failed reads in validation

Before: 3.x caught every exception in the lending-health readers and dropped the position. A provider outage could leave no lending finding and an ok verdict.

Now: a position whose reads fail is listed in VaultLendingHealth.unreadable and reported as a lending_health warning. A failed lending-health read or unpriceable-token check is reported as an incomplete_read warning.

Impact: validation results flag reads that did not complete.


Upgrade with pip install -U ipor-fusion. Migration guide: MIGRATION_4.md. Full changelog: v4.0.0 on GitHub.

These notes describe changes to the IPOR Fusion SDK. They are not operational or financial advice. Operators are responsible for testing changes against their own vaults and configuration before use.