For the complete documentation index, see llms.txt. This page is also available as Markdown.

Upgrading 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.

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.

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.

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):

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:

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 gone. The new flow is initiate, wait out the cooldown, then finalize, with each request made of one or more parts that can optionally route through a provider. Note chainId is an int256 in the part struct:

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 (PENDINGPROVIDER_ACCEPTEDCOMPLETED), use getWithdrawableTime(user) for the countdown, and getPendingWithdrawRequests(user, start, size) for the list. Cancellation is requestCancelWithdraw(requestId); express and virtual withdrawals inside the provider blackout window move to CANCEL_REQUESTED and need provider approval rather than cancelling outright.

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:

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.

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. The signer is cleared before your hook runs, so it can't impersonate the user.

Affiliate fees resolve by specificity (per user + symbol, then per user, then per symbol, then your affiliate-wide default), 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

Last updated