Skip to main content

IPOR Fusion Python SDK

IPOR Fusion Python SDK

ipor_fusion is the official Python SDK for IPOR Fusion Plasma Vaults — typed abstractions for DeFi protocol interactions on EVM chains through a fuse adapter pattern.

Maintained by IPOR Labs AG.

Documentation · SDK docs · llms.txt for AI agents · Hosted MCP server · Example bot · Contracts

Workflow CI CD Release
Social Chat on Discord X (formerly Twitter) URL IPOR Official Broadcast
Code PyPI version GitHub License Python Version Linter: Ruff DeepWiki

Quickstart

Install

pip install ipor-fusion

Connect and execute

from ipor_fusion import Web3Context, PlasmaVault, AaveV3SupplyFuse
from web3 import Web3

# 1. Create a Web3 context with your provider and private key
ctx = Web3Context.from_url(
    url="https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY",
    private_key="0x...",
)

# 2. Wrap the PlasmaVault contract
vault = PlasmaVault(ctx, Web3.to_checksum_address("0xVAULT_ADDRESS"))

# 3. Build a fuse action (e.g. supply USDC to Aave V3)
fuse = AaveV3SupplyFuse(Web3.to_checksum_address("0xFUSE_ADDRESS"))
action = fuse.supply(
    asset=Web3.to_checksum_address("0xUSDC_ADDRESS"),
    amount=1_000_000,  # 1 USDC (6 decimals)
)

# 4. Execute on-chain (execute() returns a Call; .send() signs and submits it)
receipt = vault.execute([action]).send()

Fuse, factory and manager addresses per chain are published in ipor-abi (mainnet/mainnet-<chain>-fusion/addresses.json). A fuse must also be registered on the vault; vault.get_fuses().call() lists the registered ones.

Amounts are raw on-chain integers (Amount, Shares in ipor_fusion.types); the SDK never scales by decimals.

Read, send, or simulate

Every wrapper method returns a Call instead of executing. The same Call powers all three modes:

from ipor_fusion import VaultSimulator

total = vault.total_assets().call()          # eth_call -> Amount
receipt = vault.execute([action]).send()     # signed tx -> TxReceipt
payload = vault.execute([action]).calldata   # raw bytes for an external signer

# Simulate first via eth_simulateV1 (no local node); alpha is the account allowed to call execute()
sim = VaultSimulator(ctx.web3, vault=vault.address, alpha=Web3.to_checksum_address("0xALPHA"))
sim.observe("before", vault.total_assets())
sim.execute([action])
sim.observe("after", vault.total_assets())
result = sim.run()
if result.all_success:
    print(result.get("after") - result.get("before"))

CLI Quickstart

The SDK ships with a fusion CLI for inspecting and managing Plasma Vaults from the terminal.

You need an RPC provider URL — get a free key at Alchemy or Infura.

# Install with CLI extras (pipx keeps dependencies isolated)
pipx install 'ipor-fusion[cli]'

# Configure an RPC provider (auto-detects chain ID)
fusion config set-provider https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY

# Inspect a vault (auto-saves to config on first use)
fusion vault info 0xB8a451107A9f87FDe481D4D686247D6e43Ed715e --chain-id ethereum

# List saved vaults
fusion vault list

# Inspect a Morpho Blue market or MetaMorpho vault
fusion market morpho-blue 0xMARKET_ID --chain ethereum
fusion market meta-morpho 0xVAULT_ADDRESS --chain ethereum

MCP Server

Two ways to reach IPOR Fusion from an MCP-compatible AI assistant (Claude Code, Cursor, Windsurf, etc.).

Hosted server (no install)

https://mcp.ipor.io/mcp is public, unauthenticated and read-only — inspect any Fusion vault without your own RPC key.

claude mcp add --transport http ipor-fusion https://mcp.ipor.io/mcp
{
  "mcpServers": {
    "ipor-fusion": {
      "type": "http",
      "url": "https://mcp.ipor.io/mcp"
    }
  }
}

Tools: vaults_list, vault_info, vault_oracle_mapping, fusion_addresses_list, fusion_address_names, fusion_address_lookup, market_morpho_blue, market_meta_morpho.

Local server (from the SDK)

The SDK ships a fusion-mcp server that exposes the CLI over MCP, against your own RPC providers and local config.

# Install with MCP extras (pipx keeps dependencies isolated)
pipx install 'ipor-fusion[mcp]'

Add to your MCP client configuration (e.g. .mcp.json):

{
  "mcpServers": {
    "ipor-fusion": {
      "command": "fusion-mcp",
      "type": "stdio"
    }
  }
}

Available tools:

Tool Description
server_info Server name, running version and changelog entries
config_show Show current configuration (providers, vaults, API key status)
config_set_provider Set RPC provider URL for a chain (auto-detects chain ID)
config_set_etherscan_key Set Etherscan API key (enables contract name resolution)
vault_info Full on-chain vault state — assets, fuses, balances, fees, lending health, reconciliation
vault_role_accounts Accounts holding each AccessManager role on a vault
vault_oracle_mapping Price-oracle sources per asset for a vault
vault_list List all saved vaults
vault_add Save a vault to the local config (auto-fetches on-chain name)
vault_remove Remove a vault from the local config
market_morpho_blue Morpho Blue market parameters and state
market_meta_morpho MetaMorpho V1 or Morpho Vault V2 allocations and caps (Morpho API)

Configure providers and vaults via fusion config or the MCP config tools first.

Besides the tools, fusion-mcp serves the guide from ipor_fusion.guide as the resources fusion://glossary, fusion://architecture, fusion://invariants and fusion://quickstart (the executed deploy-and-operate walk), and the prompts quickstart, deploy_vault, analyze_vault, trace_oracle_pricing and explain_fuse (slash commands in clients that support MCP prompts).

Agent skills

skills/ipor-deploy-vault/SKILL.md teaches a coding agent the full clone → roles → market → access posture → deposit → execute walk, with the invariants and the revert selectors, before it writes vault code. Its body is fusion://invariants and fusion://quickstart from ipor_fusion.guide verbatim (a test keeps them identical), so the skill and the MCP resources are one text with two delivery paths. It follows the Agent Skills format and versions with the SDK. One install per machine:

# Claude Code — plugin with the skill and the hosted MCP server
/plugin marketplace add IPOR-Labs/ipor-fusion.py
/plugin install ipor-fusion@ipor-fusion

# Codex CLI, Gemini CLI, Cursor — the skill; add the MCP server as shown above
npx skills add IPOR-Labs/ipor-fusion.py -g -a codex      # or gemini-cli, cursor

Common errors

Reverts on the deploy-and-configure path, keyed by selector so a failed transaction is greppable. This table and the full invariants ship in the wheel as ipor_fusion.guide, which fusion-mcp serves as fusion://invariants.

Selector Revert What happened Fix
0x8745fbfd DaoFeePackagesArrayEmpty() clone() was sent to IporFusionFactoryImpl Send it to IporFusionFactoryProxy for that chain
0x9996b315 AddressEmptyCode(address) execute() touched a market with no balance fuse add_balance_fuse(market_id, balance_fuse) before the first execute on that market
0x4bf4de4e HandlerNotFound() A protocol called back into the vault during execute() (a flash loan) and no handler is registered for its address and selector update_callback_handler(handler, sender, selector) before the first execute that triggers the callback
0x068ca9d8 AccessManagedUnauthorized(address) The caller lacks the role the function requires: FUSE_MANAGER for add_fuses, grant_market_substrates, add_balance_fuse, update_callback_handler; ALPHA for execute; WHITELIST for deposit and mint on a private vault AccessManager.grant_role(role, account, 0) from the role's admin (OWNER grants ATOMIST, ATOMIST grants the rest); for a reverting deposit, whitelist the depositor or convert the vault to public
ValueError: Private key required for sending transactions SDK, before any transaction .send() on a Web3Context without a key Web3Context(w3, chain_id, signer=..., private_key=...) or Web3Context.from_url(url, private_key=...)

clone() grants the owner only Roles.OWNER_ROLE (1). OWNER grants Roles.ATOMIST_ROLE (100), which administers Roles.FUSE_MANAGER_ROLE (300, configuration), Roles.ALPHA_ROLE (200, execute) and Roles.WHITELIST_ROLE (800, deposit on a private vault).

Configuration order on a fresh vault: add_fuses → grant_market_substrates → add_balance_fuse → execute; all three configuration steps are mandatory.

A flash-loan fuse also needs update_callback_handler(handler, sender, selector) before its first execute: Morpho Blue calls back onMorphoFlashLoan(uint256,bytes), the Euler EVC onEulerFlashLoan(bytes), and an unregistered callback reverts HandlerNotFound().

The full clone → configure → deposit → execute sequence is exercised in tests/test_simulate_vault_from_scratch_base.py.

Custom errors of the crosschain contracts decode by name: importing ipor_fusion.crosschain registers their signatures (CROSSCHAIN_ERROR_SIGNATURES), so a simulated or sent call that reverts reads NavSettledStale(8453) in SimulationResult.revert_reason and TransactionError instead of a bare selector. register_custom_errors([...]) adds your own, and SimulationResult.raise_for_failure() raises SimulationError naming the first failed call.

Vault construction examples

Runnable, canonical examples for building and configuring a vault from scratch live in examples. They preview and simulate through eth_simulateV1 — nothing is ever signed or broadcast.

  • Simple Aave V3 supply vault — start here for vault creation, role bootstrap, and configuring a single supported market.
  • Advanced Euler V2 credit-market vault — continue here for composing multiple functional fuses, typed Euler substrates and sub-accounts, and an ordered collateral → borrow → repay → unwind lifecycle.
  • External-state margin leg — a vault whose capital sits off-chain: typed market-50 substrates, a dual-custodian propose/confirm marking the NAV, the scheduled-withdrawal prep an alpha performs, and a mismatched confirmation being rejected.
  • Crosschain CCIP lifecycle — a vault whose capital works on another chain: supply USDC from an Arbitrum hub to a HyperEVM spoke over CCIP, attest the remote balance with two keys, deposit and redeem on the spoke, recall and claim, driven through CrosschainLane and relayed by CrosschainSimulator.
export BASE_PROVIDER_URL="https://base-mainnet.g.alchemy.com/v2/YOUR_KEY"
uv run python examples/simple_aave_v3_supply_base.py
uv run python examples/advanced_euler_v2_credit_market_base.py
uv run python examples/external_state_margin_leg_base.py
uv run python examples/crosschain_ccip_usdc_arbitrum_hyperevm.py  # needs ARBITRUM_PROVIDER_URL and HYPEREVM_PROVIDER_URL

No RPC key? BASE_PROVIDER_URL=https://mainnet.base.org (Base's public RPC) runs the examples out of the box — it supports eth_simulateV1 and serves their pinned blocks. It is rate-limited, so use a dedicated archive node for repeated or CI runs.

Architecture

The SDK uses a fuse adapter pattern:

  • Fuses encode protocol-specific calls into FuseAction objects (pure calldata, no state)
  • PlasmaVault batches and executes FuseAction sequences on-chain via execute(); a batch is atomic
  • Web3Context manages provider connections, signing, and transaction dispatch
  • Call is the lazy result of every wrapper method: .call(), .send(), .calldata, or feed it to VaultSimulator
Fuse.method()  -->  FuseAction  -->  PlasmaVault.execute([actions])  -->  Call  -->  .send() / simulate

Core modules (ipor_fusion.core)

Module Purpose
Web3Context Provider connection, signing, tx dispatch, gas estimation
Call Pre-encoded contract call; .call(), .send(), .calldata, .build_transaction()
PlasmaVault ERC-4626 vault — execute, deposit, withdraw, fuse and market configuration
VaultSimulator Batch execute + reads through eth_simulateV1, multi-block, no local node
FusionFactory Deploy a new vault (clone, clone_supervised)
AccessManager Role-based access control
RewardsManager Claim and vest rewards
WithdrawManager Time-windowed withdrawal requests
FeeManager Deposit, performance, and management fee configuration
FeeAccount Fee escrow account, resolves its FeeManager
PriceOracleMiddleware Asset price feeds
PriceOracleMiddlewareManager Per-vault price-source overrides
ExternalStateExecutor NAV propose/confirm for off-vault capital (market 50)
StargateCrosschainExecutor, CcipCrosschainExecutor Crosschain executor reads and attestation (propose_balance, approve_balance)
StargateCrosschainDispatcher, CcipCrosschainDispatcher Remote dispatcher state (observation, tracked idle, command lane)
StargateCrosschainFactory, CcipCrosschainFactory Executor creation and route/asset configuration reads
CrosschainLane, open_lane One executor/dispatcher pair driven transport-agnostically: supply, recall, command, claim, buckets, attestation (attestation, needs_attestation, staleness_max); enforces_min_received identifies real supply floors (CCIP requires zero); open_lane detects the transport and finds the fuses
discover_deployment, open_lanes Read a vault's crosschain market from the hub alone (executors with transport, factory, fuses, attestation keys and spokes served; remote vaults per spoke) and open every lane; LANES maps a transport kind to its lane class
CrosschainSimulator Multi-chain eth_simulateV1 relay: replays LayerZero and CCIP messages between pinned chains by impersonating the endpoint or router on delivery, no bridge needed
ccip_token_lane Whether a CCIP message lane exists from a chain and whether a token's pool serves it, read on Chainlink's Router, OnRamp, TokenAdminRegistry and pool
erc20_balance_slot, VaultSimulator.with_erc20_balance Fund any address with any ERC-20 in a simulation by a storage override (slot found by probing), no holder to impersonate
ERC20 Token reads and approvals

Supported protocols (ipor_fusion.fuses)

Protocol Fuses
Aave V3 AaveV3SupplyFuse, AaveV3BorrowFuse
Morpho MorphoSupplyFuse, MorphoCollateralFuse, MorphoBorrowFuse, MorphoFlashLoanFuse, MorphoClaimFuse
Euler V2 EulerV2SupplyFuse, EulerV2CollateralFuse, EulerV2ControllerFuse, EulerV2BorrowFuse, EulerV2BatchFuse, EulerV2SwapDeployFuse, EulerV2SwapReconfigureFuse, EulerV2SwapRegistryFuse
Uniswap V3 UniswapV3SwapFuse, UniswapV3NewPositionFuse, UniswapV3ModifyPositionFuse, UniswapV3CollectFuse
Ramses V2 RamsesV2NewPositionFuse, RamsesV2ModifyPositionFuse, RamsesV2CollectFuse, RamsesClaimFuse
Compound V3 CompoundV3SupplyFuse
Gearbox V3 GearboxSupplyFuse, GearboxStakeFuse
ERC-4626 ERC4626SupplyFuse
Fluid Instadapp FluidInstadappSupplyFuse, FluidInstadappStakingFuse
Merkl MerklClaimWrapperFuse
Universal UniversalTokenSwapperFuse
Off-vault capital AsyncActionFuse (market 40), ExternalStateOperationFuse (market 50)
Crosschain StargateCrosschainSupplyFuse, StargateCrosschainCommandFuse, CcipCrosschainSupplyFuse, CcipCrosschainCommandFuse, CrosschainClaimFuse (market 54); transport-agnostic bases CrosschainSupplyFuse / CrosschainCommandFuse with StargateSendParams / CcipSendParams, plus Command, CrosschainSubstrateLib, OptionsBuilder

The mainnet crosschain POC vault uses a keccak-derived market ID, not 54. Use crosschain_market_id("IPOR_FUSION_CROSSCHAIN_USDC_POC_V1") for that deployment; open_lane discovers the vault's actual market grants rather than assuming 54. | HyperCore (Hyperliquid, market 55) | HyperCoreDepositFuse, HyperCoreMarginFuse, HyperCoreSendFuse, HyperCoreOrderFuse, HyperCoreCancelFuse, HyperCoreBuilderFeeFuse, HyperCoreSubstrates |

Readers (ipor_fusion.readers)

Read-only aggregators over positions and health, no fuses involved:

Reader Purpose
MorphoReader, AaveV3Reader, CompoundV3Reader Market params, rates and vault positions per lending protocol
UniswapV3Reader, RamsesV2Reader LP position details
fetch_vault_lending_health Health factor per lending market a vault is in
build_oracle_mapping How the vault prices every configured asset

Supported networks

  • Ethereum mainnet
  • Arbitrum One
  • Base

Development

uv sync --all-extras                                       # Install dependencies
uv run pytest tests/test_fuse_encoding.py -n auto -v       # Unit tests (fast, offline)
uv run pytest -v -s                                        # All tests (needs `.*_PROVIDER_URL` variables in `.env`)
uv run ruff format ./                                      # Format
uv run ruff check ./                                       # Lint
uv run pyright                                             # Type check

Integration tests need provider URLs in .env (eth_simulateV1 at a pinned fork block — no local node):

cp .env.example .env
# Edit .env with ARBITRUM_PROVIDER_URL, ETHEREUM_PROVIDER_URL, BASE_PROVIDER_URL

Contributor and coding-agent instructions (commands, conventions, invariants, domain rules) live in AGENTS.md.

Examples

For full usage patterns, see the example repository: ipor-fusion-alpha-example

Metadata

Release files for ipor-fusion 3.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ipor-fusion 3.8.0
File Size Uploaded
ipor_fusion-3.8.0.tar.gz 832.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ipor-fusion 3.8.0
File Interpreter ABI Platform
ipor_fusion-3.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.2 MB

Release files / ipor_fusion-3.8.0.tar.gz

Download URL ipor_fusion-3.8.0.tar.gz
Size 832.0 kB
Tags Source
SHA-256 checksum
How to use checksums
1db961525f3a1868866d01935cebfbb262f3ff2c69c575236f20477f0a9310ed
BLAKE2b-256 checksum
How to use checksums
1163a709173c094a5daff07919b7edf00e0355f43062aaa43a9a44807168b175
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / ipor_fusion-3.8.0-py3-none-any.whl

Download URL ipor_fusion-3.8.0-py3-none-any.whl
Size 326.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ea56be744c34126c8f43c4f038539c84a27363e6d5609408130f017443b27a01
BLAKE2b-256 checksum
How to use checksums
4e35c2d5de06b58deb83e73810592610a7876d5c2614a18f340c2d3343ee4dac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

3.12.0

2 release files

3.11.2

2 release files

3.11.1

2 release files

3.9.0

2 release files

This release

3.8.0 This release

2 release files

3.7.0

2 release files

3.6.11

2 release files

3.6.10

2 release files

3.6.9

2 release files

3.6.8

2 release files

3.6.7

2 release files

3.6.6

2 release files

3.6.5

2 release files

3.6.4

2 release files

3.6.3

2 release files

3.6.2

2 release files

3.6.1

2 release files

3.6.0

2 release files

3.5.0

2 release files

3.4.0

2 release files

3.3.1

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.0

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

2.2.0

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.0

2 release files

0.23.0

2 release files

0.22.0

2 release files

0.20.0

2 release files

0.19.0

2 release files

0.18.0

2 release files

0.17.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page