> For the complete documentation index, see [llms.txt](https://docs.ipor.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipor.io/build-on-fusion/developer-guide/deploy-your-first-fusion-vault.md).

# 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

```bash
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.

```python
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](https://github.com/IPOR-Labs/ipor-fusion).
* 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

* Factory proxy addresses for every supported chain: Addresses
* Fuse addresses by name and chain: [`ipor-abi`](https://github.com/IPOR-Labs/ipor-abi), or the hosted MCP server's `fusion_address_lookup`
* The SDK runs this exact walk in one `eth_simulateV1` batch: `tests/test_simulate_vault_from_scratch_base.py` in [`ipor-fusion.py`](https://github.com/IPOR-Labs/ipor-fusion.py)
* A strategy bot that operates an existing vault: [`ipor-fusion-alpha-example`](https://github.com/IPOR-Labs/ipor-fusion-alpha-example)
