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

Building a New Frontend

This guide walks through building a new exchange frontend on Symmio Core v0.8.5, from registering your frontend to a working trade-and-withdraw flow. It assumes you're starting fresh.

You integrate with Symmio through the AccountLayer, a single shared diamond that manages every user's accounts, handles affiliate onboarding and fee distribution, and gives each trade configurable position isolation. You register as an affiliate and build against the AccountLayer directly, rather than deploying a contract per user or per frontend.

The account model

Funds move through two levels before they reach a trade:

User → deposit → SubAccount → addMargin → VirtualAccount → trade
                 SubAccount ← auto-sweep ← VirtualAccount (when its last position closes)

A SubAccount is a user's account under your frontend. It's bound to your affiliate, a specific Symmio core instance, and an isolation type chosen when it's created. A user can hold several, say one for conservative trades and one for aggressive ones.

A VirtualAccount (VA) sits beneath a SubAccount and holds the margin for a trade. Each VA is an independent trading address with its own balance and positions, so a liquidation in one VA can't touch another. When a VA's last position closes, the AccountLayer sweeps its funds back to the parent SubAccount and recycles the address, so you never manage that lifecycle by hand.

Both SubAccounts and VAs are virtual addresses: deterministic, CREATE2-style addresses with no deployed contract behind them. The AccountLayer tracks all their state centrally.

Isolation types

When a SubAccount is created, its isolation type decides how VAs are created during trading. For most retail UIs, POSITION is the safest default.

Under MARKET, each quote opens a new VA for that market by default; enabling Single VA Mode routes later quotes for the same market into the existing VA instead. You can fix the isolation type per product or expose it as a user choice.

Note there are two related enums. The SubAccount's type uses SubAccountIsolationType (POSITION, MARKET, MARKET_DIRECTION, CUSTOM). When you target a specific VA in the margin calls below, you use VirtualAccountIsolationType (POSITION, MARKET, MARKET_LONG, MARKET_SHORT). MARKET_DIRECTION at the SubAccount level splits into MARKET_LONG and MARKET_SHORT at the VA level.

Step 1: Register as an affiliate

Every frontend trades through an affiliate registration. It's a two-step process:

  1. Request registration with your frontend name, brand color, admin address, fee stakeholder configuration, and the Symmio core instance(s) you'll use.

  2. A protocol APPROVER_ROLE holder approves it. On approval, a fee distributor address is generated and your affiliate is registered on each allowed core.

Once your affiliate is ACTIVE, users can create SubAccounts under it. Until then, account creation fails.

Step 2: Create an account

A new user starts by creating a SubAccount with your chosen isolation type. 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. If your UI lets users pick an isolation type, explain the trade-off briefly: more isolation means more safety per position but a little more overhead per trade.

Step 3: Deposit and add margin

Depositing happens in two moves. First, collateral goes into the SubAccount:

Then, before a trade on a non-CUSTOM SubAccount, the user funds the VA that will hold the position. For a brand-new trade you fund the next VA; 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 a VA that already exists, use addMargin with its address:

Margin can also be pulled back manually with removeMargin(virtual_account, amount, upnl_sig), which requires a UPNL (SingleUpnlSig) signature proving the VA stays solvent.

Step 4: Trade

Trades run through _call() on the AccountLayer. You encode the Symmio core call and pass it through; the AccountLayer routes it according to the SubAccount's isolation type:

On a non-CUSTOM SubAccount, the AccountLayer creates or reuses a VA, routes the quote through it, and records which quoteId belongs to which VA. On a CUSTOM SubAccount, the call executes directly with no VA involved.

Step 5: Virtual account cleanup (automatic)

You don't manage VA lifecycle for non-CUSTOM isolation. The flow:

Creation: a VA is created when the first quote is sent (or reused, under Single VA Mode).

Tracking: the AccountLayer keeps a list of which quoteIds live in which VA.

Cleanup: when a position closes, Symmio core notifies the AccountLayer. If the VA has no remaining quoteIds, its margin is deallocated, funds return to the parent SubAccount, and the VA address is recycled.

Listen to VirtualAccountCreated, VirtualAccountDeleted, and VirtualAccountReused to keep your UI in sync.

Step 6: Withdrawals

Withdrawals are a two-step flow: initiate a request, wait out the cooldown (a configurable withdrawCooldownPeriod, measured from the user's last deallocation), then finalize. A request is made up of one or more parts, each of which can optionally route through an express or virtual provider; for a plain same-chain withdrawal, leave both as the zero address. Note chainId is an int256 in the part struct.

For your UI, getWithdrawableTime(user) gives the countdown to the next finalizable withdrawal, and getPendingWithdrawRequests(user, start, size) lists active requests. Show status as it moves from PENDING to PROVIDER_ACCEPTED to COMPLETED. Cancellation is via requestCancelWithdraw(requestId); express and virtual withdrawals inside the provider blackout window move to CANCEL_REQUESTED and need provider approval rather than cancelling immediately.

Event indexing

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

Funding rates are tracked as weighted averages at the symbol level (pre-multiplied by market price at storage time), so displaying a user's funding debt is a per-symbol lookup rather than a walk over every open position.

Last updated