Contract Architecture Overview

Symmio is an intent-based derivatives protocol that implements the Diamond Standard (EIP-2535) architecture with specialized facets, a Muon oracle system, and multiple security mechanisms including a 12-hour fraud proof window for withdrawals. The protocol coordinates bilateral trading between retail users (PartyA) and professional market makers (PartyB), with all critical operations requiring cryptographically verified off-chain signatures.

Diamond Pattern

The diamond pattern allows a single entry-point contract to delegate calls to specialized facet contracts based on function selectors. Individual facets can be added, replaced, or removed without changing the Diamond's address or disrupting storage.

Storage organization uses isolated layout pattern

The protocol maintains seven distinct storage layouts to prevent collision between facets:

  • DiamondStorage: Contract ownership via ds.contractOwner

  • GlobalAppStorage: Collateral address, fee collector, role mappings, pause states, emergency mode, and balance limits

  • AccountStorage: User balances, allocated balances, locked values (CVA, LF, MM), pending locks, nonces, suspension status, and liquidation details

  • QuoteStorage: All quote structs, pending quote arrays, position counts, and quote ID counter

  • MAStorage: Cooldown durations, liquidator share, PartyB registration status, and liquidation timestamps

  • SymbolStorage: Trading symbol configurations including leverage limits, fees, and funding rate parameters

  • MuonStorage: Oracle application ID, gateway address, TSS public key, and signature validity windows

Facet Overview

Facet
Core Responsibility

deposit(), withdraw(), allocate(), deallocate(), internalTransfer()

Role management, PartyB registration, symbol configuration, pause controls, emergency mode

sendQuote(), requestToCancelQuote(), requestToClosePosition(), force actions

lockQuote(), unlockQuote(), acceptCancelRequest(), openPosition()

fillCloseRequest(), position modifications

Batch operations for multiple quotes

liquidatePartyA(), liquidatePartyB(), liquidatePositionsPartyA(), liquidatePositionsPartyB()

chargeFundingRate() with epoch-based timing

transferToBridge(), processWithdrawal(), suspension mechanisms

Settlement state management, cooldown tracking

Read-only queries for balances, quotes, symbols, liquidation status, configuration

Roles within SYMMIO

The protocol implements eight distinct roles with different permissions:

Role
Key Permissions

DEFAULT_ADMIN_ROLE

Grant/revoke all roles, set collateral, emergency mode, unsuspend addresses, set fee collector and balance limits

PAUSER_ROLE

Pause global operations, liquidation, accounting, PartyA/PartyB actions

UNPAUSER_ROLE

Unpause any paused function

PARTY_B_MANAGER_ROLE

Register and deregister PartyB addresses

SYMBOL_MANAGER_ROLE

Add symbols, configure leverage, trading fees, funding parameters

SETTER_ROLE

Configure cooldowns, liquidator share, force close parameters

MUON_SETTER_ROLE

Update Muon oracle configuration and application IDs

SUSPENDER_ROLE

Suspend addresses and bridge transactions

Quote Lifecycle

The trading process follows a Send → Lock → Open → Close flow with precise function calls at each transition.

Stage 1: Quote creation (PENDING state)

PartyA initiates via sendQuote() with parameters including partyBsWhiteList, symbolId, positionType (LONG=1, SHORT=0), orderType (LIMIT=0, MARKET=1), price, quantity, and margin components:

  • CVA (Credit Valuation Adjustment): Penalty paid by liquidated party to counterparty

  • MM (Maintenance Margin): Backing amount considered in liquidation calculations

  • LF (Liquidation Fee): Reward paid to liquidator

The function requires a SingleUpnlAndPriceSig Muon signature. Funds lock in pendingLockedBalances[partyA].

Stage 2: Quote locking (LOCKED state)

PartyB calls lockQuote(quoteId, upnlSig, increaseNonce), which verifies PartyB's uPnL via LibMuon.verifyPartyBUpnl(), checks validation rules in LibPartyB.checkPartyBValidationToLockQuote(), and updates partyBPendingLockedBalances. PartyB can release via unlockQuote(), reverting to PENDING or EXPIRED.

Stage 3: Position opening (OPENED state)

openPosition(quoteId, filledAmount, openedPrice, upnlSig) supports partial fills—filling 20 of 100 units creates a new quote for the remainder. The function applies trading fees, performs solvency verification via LibSolvency.isSolventAfterOpenPosition(), and requires PairUpnlAndPriceSig attestation.

Stage 4: Position closing

PartyA requests closure via requestToClosePosition(quoteId, closePrice, quantityToClose, orderType, deadline), transitioning to CLOSE_PENDING. PartyB executes via fillCloseRequest(quoteId, filledAmount, closedPrice, upnlSig). LIMIT orders allow multiple partial fills; MARKET orders require full execution. Both check solvency via LibSolvency.isSolventAfterClosePosition().

Cancellation flows

  • requestToCancelQuote(): PENDINGCANCELED or LOCKEDCANCEL_PENDING

  • acceptCancelRequest(): PartyB accepts cancellation

  • requestToCancelCloseRequest(): CLOSE_PENDINGCANCEL_CLOSE_PENDING

  • acceptCancelCloseRequest(): Returns to OPENED state

Liquidation Process

PartyA liquidation (4-step process)

Trigger condition: Total UPNL < -(allocated_balance - mm - pending_locked_values)

  1. Mark liquidated: liquidatePartyA(partyA, upnlSig) records timestamp

  2. Set prices: setSymbolsPrice(partyA, priceSig) establishes liquidation prices for all position symbols

  3. Clear pending: liquidatePendingPositionsPartyA(partyA) liquidates unlocked quotes

  4. Clear positions: liquidatePositionsPartyA(partyA, quoteIds[]) liquidates specified open positions

The LiquidationDetail struct captures: liquidationId, liquidationType, upnl, totalUnrealizedLoss, deficit, liquidationFee, timestamp, involvedPartyBCounts, partyAAccumulatedUpnl, and disputed flag.

PartyB liquidation (2-step process)

PartyB liquidation operates per-PartyA:

  1. liquidatePartyB(partyB, partyA, upnlSig)

  2. liquidatePositionsPartyB(partyB, partyA, priceSig)

Funding Rate Mechanics

Funding rates are charged through epoch-based windows. chargeFundingRate(partyA, quoteIds[], rates[], upnlSig) applies periodic adjustments to open positions. This simulates the charging of funding rates without costly on-chain settlements each epoch.

Timing mechanics

  • Epoch duration: Symbol-specific fundingRateEpochDuration

  • Window time: fundingRateWindowTime defines valid application period within epoch

  • Latest epoch: latestEpochTimestamp = (block.timestamp / epochDuration) * epochDuration

Validation requirements

Price adjustment formula

SYMMIO Bridge Mechanics

Standard withdrawal flow

The protocol enforces a 12-hour cooldown between deallocation and withdrawal—a fraud proof window allowing watchdogs to detect malicious behaviour. Suspended parties cannot withdraw after this period.

The Bridge feature allows users to bypass the full cooldown period when withdrawing their money. Certain addresses can be designated as "bridges" within the system. The method transferToBridge enables users to quickly transfer the amount they wish to withdraw. The trusted bridge will then send that amount to the user's wallet outside of SYMMIO. After the cooldown period is complete, the bridge can withdraw that amount from SYMMIO. The bridge is responsible for ensuring user compliance with system regulations. If SYMMIO detects any wrongdoing by the user, the bridge transaction will be suspended, and the bridge may become unable to fully withdraw the funds after SYMMIO restores the transaction.

Bridge operations

  • transferToBridge(amount, bridgeAddress): Initiates transfer, emits TransferToBridge event with transaction ID

  • withdrawReceivedBridgeValue(transactionId) / withdrawReceivedBridgeValues(transactionIds[]): Processes bridged withdrawals

  • suspendBridgeTransaction(transactionId): SUSPENDER_ROLE can freeze transactions

  • restoreBridgeTransaction(transactionId, validAmount): DISPUTE_ROLE restores with adjusted amount

Force Actions

Force Actions allow users to take action in situations where counterparties are unresponsive or when market conditions require intervention. This feature includes:

  • Forcing the cancellation of quotes or close requests.

  • Forcing the closure of positions.

  • Settling uPnLs for the hedger and then forcing the closure of a position.

These functions help protect the system from prolonged exposure to risk and ensure that positions can be closed even when normal procedures fail.

Force cancel quote

forceCancelQuote(quoteId) executes when:

  • Quote is in CANCEL_PENDING state

  • block.timestamp > quote.statusModifyTimestamp + maLayout.forceCancelCooldown

Returns trading fee to PartyA and removes from pending quotes.

Force close position

forceClosePosition(quoteId, upnlSig) executes when:

  • Quote is in CLOSE_PENDING state

  • forceCloseCooldown has elapsed

  • Within deadline

  • Order type is LIMIT

  • Price gap condition met via forceCloseGapRatio:

    • Long: upnlSig.price >= requestedClosePrice + (requestedClosePrice * forceCloseGapRatio) / 1e18

    • Short: upnlSig.price <= requestedClosePrice - (requestedClosePrice * forceCloseGapRatio) / 1e18

Emergency close

emergencyClosePosition(quoteId, upnlSig) operates on OPENED or CLOSE_PENDING quotes using current market price with solvency verification.

Last updated