> For the complete documentation index, see [llms.txt](https://docs.symm.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.symm.io/exchange-builder-documentation/frontend-builder-technical-guidance/migrating-from-multiaccount-0.8.4.md).

# Migrating from MultiAccount (0.8.4)

This guide is for frontends already running on Symmio Core v0.8.4 and moving to v0.8.5. It covers what changed, the paths available to you, and the concrete steps and event updates each one needs. You don't have to move all at once: a backward-compatible wrapper lets you get onto v0.8.5 quickly and migrate fully on your own schedule.

#### What v0.8.5 introduces

The changes that affect frontends fall into six areas:

* **AccountLayer:** one Diamond proxy in place of per-frontend MultiAccounts, with SubAccounts, VirtualAccounts, and four isolation modes.
* **New Withdraw System:** multi-provider, multi-chain withdrawals in place of the single-step flow, with optional express (instant) and virtual (cross-chain) providers.
* **Instant Layer:** EIP-712 signed operations in place of delegateAccess, with templates for atomic multi-step trades.
* **Express Deposits:** affiliate-configurable deposit splitting that builds a liquidity pool for faster withdrawals.
* **Hook System:** on-chain callbacks on position open, close, cancel, and fee events for custom logic without core changes.

You have two paths, plus an optional account-import step.

#### Option A: Migrate to the AccountLayer (recommended)

Your frontend talks to SubAccounts and VirtualAccounts directly and gains the full feature set: position isolation, the affiliate fee system, express deposits, hooks, and the new withdrawal flow. Future protocol upgrades then happen centrally, with no more per-frontend redeployments.

**The account hierarchy**

The AccountLayer adds a two-level structure:

```
User (EOA)
├── SubAccount (1): bound to affiliate + Symmio core + isolation type
│   ├── VirtualAccount (11)
│   └── VirtualAccount (12)
└── SubAccount (2)
    ├── VirtualAccount (21)
    └── VirtualAccount (22)
```

A SubAccount is the user's top-level unit, bound to your affiliate, a Symmio core, and an isolation type. A VirtualAccount (VA) sits beneath it and holds the margin for a trade: an independent address with its own balance and positions, so a liquidation in one VA can't reach another. When a VA's last position closes, its funds sweep back to the parent SubAccount and the address is recycled. Both are virtual (CREATE2-style) addresses with no deployed contract; the AccountLayer holds their state.

**Two isolation enums (don't conflate them)**

There are **two** isolation enums in the AccountLayer, and several functions below take the *VirtualAccount* one rather than the *SubAccount* one:

* `SubAccountIsolationType`: set when a SubAccount is created. `POSITION (0)`, `MARKET (1)`, `MARKET_DIRECTION (2)`, `CUSTOM (3)`.
* `VirtualAccountIsolationType`: used by the margin and predict calls when targeting a specific VA. `POSITION`, `MARKET`, `MARKET_LONG`, `MARKET_SHORT`. `MARKET_DIRECTION` at the SubAccount level splits into `MARKET_LONG` and `MARKET_SHORT` at the VA level.

**Choosing an isolation type**

The SubAccount isolation type, set at creation, governs how VAs are created during trading:

* **POSITION:** one VA per trade. Maximum isolation; a liquidation on one position can't affect another. The safest default for retail UIs.
* **MARKET:** one VA per symbol. By default each quote opens a new VA for that market; enable Single VA Mode to route later quotes for the same market into the existing VA.
* **MARKET\_DIRECTION:** one VA per symbol and direction (BTC longs separate from BTC shorts).
* **CUSTOM:** no automatic VA creation; trades run directly through the SubAccount or through manually created VAs. Closest to old MultiAccount behavior.

**Step 1: Account creation**

Replace your old create-account flow. `createSubAccounts` takes the affiliate address plus an **array of `SubAccountCreationData` structs**. The Symmio core, isolation type, name, metadata, and single-VA flag are *fields of the struct*, not positional arguments.

```python
# Old: MultiAccount
multi_account.functions.addAccount("My Account").transact({"from": user})

# New: AccountLayer, create one or more SubAccounts.
# SubAccountCreationData = (name, metadata, symmioCore, isolationType, singleVAMode)
# isolationType uses SubAccountIsolationType: 0=POSITION, 1=MARKET, 2=MARKET_DIRECTION, 3=CUSTOM
sub_account_data = [(
    "My Account",          # name
    b"",                   # metadata
    symmio_core_address,   # symmioCore
    0,                     # isolationType (POSITION)
    False,                 # singleVAMode
)]

account_layer.functions.createSubAccounts(
    affiliate_address,
    sub_account_data,      # array, create multiple at once if you like
).transact({"from": user})
```

`createSubAccounts` returns the deterministic addresses of the created SubAccounts.

**Step 2: Deposit and add margin**

Funding now has an extra level. Collateral first goes into the SubAccount.

```python
account_layer.functions.depositForAccount(sub_account, amount).transact({"from": user})

# or, deposit and allocate together:
account_layer.functions.depositAndAllocateForAccount(sub_account, amount).transact({"from": user})
```

Then, before a trade on a non-CUSTOM SubAccount, fund the target VA. For a new trade you fund the *next* VA, and the AccountLayer derives which VA that is from the isolation type and symbol, so you don't pass a VA address. The VA isolation type uses `VirtualAccountIsolationType` (`POSITION`, `MARKET`, `MARKET_LONG`, `MARKET_SHORT`):

```python
va_isolation_type = 0  # VirtualAccountIsolationType.POSITION
symbol_id = 1

# Optional: predict the address purely for display/indexing
next_va = view_facet.functions.predictNextVirtualAccountAddress(
    sub_account, va_isolation_type, symbol_id
).call()

# Fund the next VA: pass isolation type + symbolId, NOT a VA address
margin_facet.functions.addMarginToNextVA(
    sub_account, va_isolation_type, symbol_id, amount
).transact({"from": user})
```

To top up an existing VA, use `addMargin(virtual_account, amount)`.

**Step 3: Trade execution**

Trades go through `_call()`, which routes based on isolation type:

```python
send_quote_calldata = symmio_core.encodeABI(fn_name="sendQuoteWithAffiliateAndData", args=[...])
account_layer.functions._call(sub_account, [send_quote_calldata]).transact({"from": user})
```

On a non-CUSTOM SubAccount the AccountLayer creates or reuses a VA, routes the quote, and tracks the quoteId. On CUSTOM it executes directly.

**Step 4: VA cleanup is automatic**

You don't manage VA lifecycle for non-CUSTOM isolation. A VA is created on the first quote, the AccountLayer tracks which quoteIds belong to it, and when a position closes with no quoteIds left the margin is deallocated, funds return to the SubAccount, and the address is recycled. Listen to `VirtualAccountCreated`, `VirtualAccountDeleted`, and `VirtualAccountReused` to keep your UI current.

**Step 5: Withdrawals**

The single-step withdraw is deprecated. It remains callable until the protocol sets the `legacyWithdrawalDeprecated` flag, after which it reverts; new integrations should use the initiate/finalize flow.

```python
import time

ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"

parts = [{
    "id": 1,
    "amount": withdraw_amount,
    "chainId": current_chain_id,        # int256
    "receiver": user_address,           # bytes — 20 bytes for an EVM address
    "virtualProvider": ZERO_ADDRESS,    # no cross-chain
    "expressProvider": ZERO_ADDRESS,    # no instant
}]

# initiateWithdraw(parts, speedUp, data)
tx = withdraw_facet.functions.initiateWithdraw(parts, False, b"").transact({"from": user})
receipt = w3.eth.wait_for_transaction_receipt(tx)

event = withdraw_facet.events.WithdrawInitiated().process_receipt(receipt)[0]
request_id = event["args"]["requestId"]
cooldown_end_time = event["args"]["cooldownEndTime"]  # based on last deallocation, not initiation

while time.time() < cooldown_end_time:
    time.sleep(10)

withdraw_facet.functions.finalizeWithdrawRequest(user_address, request_id).transact({"from": user})
```

To offer instant withdrawals, set a part's `expressProvider` to a registered provider: it fronts the funds immediately and SYMMIO reimburses it after the cooldown. For your UI, show status (`PENDING` → `PROVIDER_ACCEPTED` → `COMPLETED`), use `getWithdrawableTime(user)` for the countdown, and `getPendingWithdrawRequests(user, start, size)` for the list.&#x20;

> Cancellation is `requestCancelWithdraw(requestId)`. A request still in `PENDING` cancels outright. Once a provider has accepted the request (`PROVIDER_ACCEPTED`), behavior splits: an express withdrawal moves to `CANCEL_REQUESTED` and needs provider approval, while a pure-virtual (cross-chain) withdrawal cancels outright, with the provider notified. Pure-virtual withdrawals are additionally subject to a cancel blackout window: they can only be cancelled while more than the configured blackout period remains before `cooldownEndTime`  inside the window the call reverts and the withdrawal proceeds to completion.

#### Option B: Use the AccountManager wrapper (backward-compatible)

When your affiliate registers, an AccountManager proxy is deployed for you automatically. It exposes nearly the same API as the old MultiAccount (`addAccount`, `depositForAccount`, `withdrawFromAccount`, `_call`, `getAccounts`), so in most cases you swap a contract address and update event indexing. Internally it authenticates the caller (by temporarily setting globalSigner on the AccountLayer) and proxies the call through. It creates SubAccounts with CUSTOM isolation, which behaves like the old MultiAccount: no automatic VA creation, trades execute directly through the SubAccount.

**Step 1: Register as an affiliate**

Registration is two steps: you request with your frontend name, brand color, admin address, fee stakeholder configuration, and the Symmio core instance(s) you'll use; then a protocol `APPROVER_ROLE` holder approves it. On approval, an AccountManager proxy is deployed (deterministic CREATE2), a fee distributor address is generated, and your affiliate is registered on each allowed core. Your affiliate is then `ACTIVE` and users can create accounts under it.

**Step 2: Swap the contract address**

Replace the old MultiAccount address with the AccountManager address. The function surface matches:

```python
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(RPC_URL))

# Old: MultiAccount
multi_account = w3.eth.contract(address=OLD_MULTI_ACCOUNT_ADDRESS, abi=multi_account_abi)

# New: AccountManager (same ABI for core functions)
account_manager = w3.eth.contract(address=ACCOUNT_MANAGER_ADDRESS, abi=account_manager_abi)

# These calls work identically:
account_manager.functions.addAccount("My Account").transact({"from": user})
account_manager.functions.depositForAccount(account_address, amount).transact({"from": user})
account_manager.functions.depositAndAllocateForAccount(account_address, amount).transact({"from": user})
account_manager.functions.withdrawFromAccount(account_address, amount).transact({"from": user})
account_manager.functions._call(account_address, [encoded_call_data]).transact({"from": user})
account_manager.functions.getAccounts(user_address, 0, 10).call()
```

**Step 3: Import existing accounts (optional)**

Users with old MultiAccount accounts can bring them across. Imports are **batched** through `importLegacyAccounts` (plural): you pass the legacy contract, the affiliate, the list of Symmio cores, and an array of `LegacyAccountImportData` structs. Each struct is `(account, name, coreIndex)`, where `coreIndex` points into the `symmioCores` array.

```python
# LegacyAccountImportData = (account, name, coreIndex)
accounts_data = [(
    legacy_account_address,   # account
    "My Account",             # name
    0,                        # coreIndex -> symmio_cores[0]
)]

account_layer.functions.importLegacyAccounts(
    legacy_multi_account_contract,  # legacyContract
    affiliate_address,              # affiliate
    [symmio_core_address],          # symmioCores
    accounts_data,                  # LegacyAccountImportData[]
).transact({"from": user})
```

This creates a SubAccount with CUSTOM isolation, preserving the original address and ownership.

#### Instant Layer

The Instant Layer replaces `delegateAccess`. Instead of granting a PartyB broad, indefinite permission, the user signs a specific operation (EIP-712) that the PartyB submits alongside its own. This closes the gap where a quote took a few seconds to reach PartyB through chain events, without the trust problem of open-ended delegation.

Read more about the Instant Layer's implementation.

**Event changes:** see the Instant Layer documentation.

#### Hooks and fees

Two AccountLayer features worth adopting once the core migration is done.

Hooks are on-chain callbacks on every position open, close, cancel, expiry, fee charge, and liquidation settlement: useful for campaign tracking, rebates from `onFeeCharged`, loyalty NFTs, or analytics. After approval, a protocol admin (holding `INTEGRATION_ADMIN_ROLE`) registers your hook with `control_facet.functions.registerHook(affiliate_address, hook_contract)`, and your contract implements `ISymmioHook`. Because `ISymmioHook` is an interface, you must implement **all** of its callbacks: `onOpenPosition`, `onClosePosition`, `onCancelQuote`, `onCloseExpired`, `onFeeCharged`, and `onLiquidationSettled` (leave the ones you don't need as no-ops). Keep them lean: a reverting hook blocks the operation it's attached to, hooks receive all remaining gas, and both your hook and the system-wide hook fire on every event except liquidation settlement: `onLiquidationSettled` carries no quote context to resolve an affiliate, so only the system-wide hook receives it.

Affiliate fees resolve by specificity (per user + symbol, then per user, then per symbol, then your affiliate-wide default, and finally the symbol's default trading fee if no affiliate fee is set), with open and close fees set independently via `setAffiliateFee` and `setAffiliateFeeForUser` on the ControlFacet. Collected fees split among the stakeholders configured at registration, whose shares (including `symmioShare`) must sum to exactly `1e18`.

#### Event changes

Point your indexer at two addresses, the Symmio core diamond and the AccountLayer diamond, and index against the current v0.8.5 event signatures. The essentials for a working UI:

**Account lifecycle (AccountLayer):**

* `SubAccountCreated`, `SubAccountDeleted`
* `VirtualAccountCreated`, `VirtualAccountDeleted`, `VirtualAccountReused`
* `AddMargin`, `RemoveMargin`

**Trading (Symmio core):**

* `SendQuote(address partyA, uint256 quoteId, address[] partyBsWhiteList, address affiliate, bytes paramsData, bytes data)`: quote params are encoded into `paramsData`; decode with `abi.decode(paramsData, (uint256, uint8, uint8, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256))`, which maps to `(symbolId, positionType, orderType, price, marketPrice, quantity, cva, lf, partyAmm, partyBmm, tradingFee, deadline)`.
* `OpenPosition`, `FillCloseRequest` (carries a `closeId`)
* Liquidation events carry a `liquidationId` (a `bytes` value) you can use to correlate multi-step flows.

**Withdrawals (Symmio core):**

* `WithdrawInitiated` (carries `cooldownEndTime`), `WithdrawAccepted`, `WithdrawFinalized`
* `WithdrawCancelRequested`, `WithdrawCancelled`, plus `WithdrawSuspended` / `WithdrawRejected` / `WithdrawSpeedUpAccepted`

**Fees and funding (Symmio core):**

* `TradingFeeCharged`, `TradeVolumeRecorded`
* `SetLongFundingFee`, `SetShortFundingFee`, `UpdateAccumulatedFundingFee`, `ChargeAccumulatedFundingFee`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.symm.io/exchange-builder-documentation/frontend-builder-technical-guidance/migrating-from-multiaccount-0.8.4.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
