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:

  • clone versus clone_supervised: same arguments; the supervised variant is gated by a maintenance role. Use clone.
  • The balance fuse above is the BalanceFuseAaveV3 entry 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, read PlasmaVault(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.substrates and documented in the fuse libraries of the contracts repository (opens in new tab).
  • Give the bot its own key: grant ALPHA_ROLE to the bot's address instead of owner, 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

Last updated