Deploy your first Fusion vault
The executed eleven-transaction SDK walk to clone, configure and fund a Fusion vault from scratch.
The path from nothing to a vault with a live position is eleven transactions, all signed by the account that will own the vault. There is no shortcut in the SDK: every one of them is mandatory. The MCP invariants resource (fusion://invariants) says what reverts when a step is skipped. Copy the walk below and change only the addresses.
Install
pip install ipor-fusion # SDK (Python 3.10+)
curl -L https://foundry.paradigm.xyz | bash && foundryup # anvil, for fork tests
anvil --fork-url <BASE_RPC_URL> --chain-id 8453 # then point the SDK at :8545
The hosted read-only server at https://mcp.ipor.io/mcp and the bundled fusion-mcp server inspect vaults; neither deploys, configures, deposits or executes. Those go through the SDK below.
The walk: clone, roles, market, access posture, deposit, execute
Base, USDC into Aave V3. Executed end to end on a Base fork with this SDK: all eleven transactions land, the vault's idle USDC goes from 0 to the deposit through deposit(), and its aToken balance rises after execute. Swap the four addresses for another chain or market (see Addresses); keep the order.
import os
from eth_account import Account
from web3 import Web3
from ipor_fusion import (
ERC20,
AccessManager,
IporFusionMarkets,
PlasmaVault,
Roles,
Web3Context,
)
from ipor_fusion.core import FusionFactory
from ipor_fusion.fuses import AaveV3SupplyFuse
# A Base RPC, or an anvil fork of Base: anvil --fork-url <BASE_RPC_URL> --chain-id 8453
w3 = Web3(Web3.HTTPProvider("http://localhost:8545"))
OWNER_PRIVATE_KEY = os.environ["OWNER_PRIVATE_KEY"] # .send() signs locally; .call() previews need no key
owner = Account.from_key(OWNER_PRIVATE_KEY).address # owns, configures and operates the vault
ctx = Web3Context(w3, chain_id=8453, signer=owner, private_key=OWNER_PRIVATE_KEY)
FACTORY_PROXY = "0x1455717668fA96534f675856347A973fA907e922" # IporFusionFactoryProxy, Base
USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
SUPPLY_FUSE = "0x26fD6EF391E98C78CfCA27e00c3d15be4D941625" # SupplyFuseAaveV3, Base
BALANCE_FUSE = "0xf53f3EaFfDf67539256365cA7299540A98b60BA9" # BalanceFuseAaveV3, Base
# 1. Deploy. Clone addresses are CREATE2-deterministic: .call() previews them for
# free, .send() with the same arguments creates the vault and its managers.
factory = FusionFactory(ctx, FACTORY_PROXY)
clone = factory.clone(
asset_name="My Vault",
asset_symbol="MV",
underlying_token=USDC,
redemption_delay_seconds=0,
owner=owner,
)
instance = clone.call(ctx)
clone.send(ctx)
vault_address = instance.plasma_vault
access_manager_address = instance.access_manager
# 2. Roles. The clone grants `owner` only OWNER_ROLE. ATOMIST first: it administers the rest.
am = AccessManager(ctx, access_manager_address)
am.grant_role(Roles.ATOMIST_ROLE, owner, 0).send(ctx)
am.grant_role(Roles.FUSE_MANAGER_ROLE, owner, 0).send(ctx)
am.grant_role(Roles.ALPHA_ROLE, owner, 0).send(ctx)
# 3. Market, in this order: fuses -> substrates -> balance fuse.
vault = PlasmaVault(ctx, vault_address)
vault.add_fuses([SUPPLY_FUSE]).send(ctx)
usdc_substrate = bytes(12) + bytes.fromhex(USDC[2:]) # address right-aligned in bytes32
vault.grant_market_substrates(IporFusionMarkets.AAVE_V3, [usdc_substrate]).send(ctx)
vault.add_balance_fuse(IporFusionMarkets.AAVE_V3, BALANCE_FUSE).send(ctx)
# 4. Access posture. A fresh clone is private and deposit() reverts until one of these.
am.grant_role(Roles.WHITELIST_ROLE, owner, 0).send(ctx) # (a) own bot: whitelist the depositor
# vault.convert_to_public_vault().send(ctx) # (b) outside money: ATOMIST-only, one-way
# 5. Fund and run the first strategy step. The depositor must already hold the USDC.
amount = 1_000 * 10**6
ERC20(ctx, USDC).approve(vault_address, amount).send(ctx)
vault.deposit(amount, owner).send(ctx)
vault.execute([AaveV3SupplyFuse(SUPPLY_FUSE).supply(asset=USDC, amount=amount)]).send(ctx)
Notes on the walk:
cloneversusclone_supervised: same arguments; the supervised variant is gated by a maintenance role. Useclone.- The balance fuse above is the
BalanceFuseAaveV3entry of the registry for Base and is verified working on a fresh clone. Vaults already in production may register a different one for the same market; to match them, readPlasmaVault(ctx, address).get_balance_fuses()on such a vault. - Substrate layouts are market-specific. For Aave V3 the substrate is the asset address right-aligned in 32 bytes; other markets use typed layouts, decoded by
ipor_fusion.substratesand documented in the fuse libraries of the contracts repository (opens in new tab). - Give the bot its own key: grant
ALPHA_ROLEto the bot's address instead ofowner, and keep the owner key offline.
Creation is not configuration
Creating a vault deploys the Fusion vault infrastructure; it does not make the vault strategy-ready. See Vault configuration step-by-step for the full configuration surface (price oracle, scheduled withdrawals, fees, and more) beyond the minimal market setup shown above.
Pointers
- Factory proxy addresses for every supported chain: Addresses
- Fuse addresses by name and chain:
ipor-abi(opens in new tab), or the hosted MCP server'sfusion_address_lookup - The SDK runs this exact walk in one
eth_simulateV1batch:tests/test_simulate_vault_from_scratch_base.pyinipor-fusion.py(opens in new tab) - A strategy bot that operates an existing vault:
ipor-fusion-alpha-example(opens in new tab)