> 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/building-a-new-frontend.md).

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

```python
# 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, you can create several at once
).transact({"from": user})
```

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

```python
# Deposit into the SubAccount balance
account_layer.functions.depositForAccount(sub_account, amount).transact({"from": user})

# Or deposit and allocate in a single call
account_layer.functions.depositAndAllocateForAccount(sub_account, amount).transact({"from": user})
```

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

```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 a VA that already exists, use `addMargin` with its address:

```python
margin_facet.functions.addMargin(virtual_account, amount).transact({"from": user})
```

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:

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

```python
import time
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"

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

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"]

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

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

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.


---

# 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/building-a-new-frontend.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.
