Gas & Fees
Synchronicity bills transaction gas in a dedicated SYNC token, separate from the collateral tokens used for trading. Standard transactions pay gas at submission. Reactive callbacks — TWAP child fills, SL/TP triggers, order trigger calls, conditional executions — pre-reserve a gas_limit at submission time so they can run later, and each of those pre-reserved limits is bounded by a per-item cap.
This page describes the new required gas_limit fields and the reactive-queue behavior you need to understand when building transactions — whether you’re using the TypeScript SDK or constructing call payloads directly from the schema. For the deep mechanics (base-fee curve, EIP-1559 update, refund flow, etc.), see the upstream reference at synchronicity/rollup/docs/gas.md.
The SYNC gas token
| Field | Value |
|---|---|
token_id | token_1nyl0e0yweragfsatygt24zmd8jrr2vqtvdfptzjhxkguz2xxx3vs0y07u7 |
symbol | SYNC |
name | SYNC |
decimals | 6 |
SYNC appears in GET /api/account/:accountId/balances alongside collateral tokens. It is debited automatically whenever the account pays gas — there is no separate “gas withdrawal” call.
To send SYNC to another account, use the bank.transfer call (see Transferring SYNC below).
The 2-D gas_limit shape
Synchronicity meters gas in two dimensions: compute and storage. Every gas_limit is a 2-element array of u64:
"gas_limit": [500000, 100000]The first slot is compute gas, the second is storage gas. Both must be specified.
gas_limit is now a required field on the following embedded structs:
SlTp— used byperp_exchange.create_orders(instop_loss/take_profit) andperp_exchange.manage_sl_tp(increate/modify)OrderTriggerCall.dependency_call— used byperp_exchange.create_orders(intrigger_calls) andperp_exchange.set_trigger_calladvanced_orders.create_twap_order— directly on the call messageconditional_execution.add_conditional_execution— in the embeddeddependency_calland on every entry ofon_cancel_calls
Payloads that omit gas_limit on any of these paths will be rejected with a deserialization error. The TypeScript SDK handles the new field automatically once you upgrade; if you’re building call payloads directly from the schema, you need to add it yourself.
max_priority_fee_bips
Each of the structs above also accepts an optional max_priority_fee_bips field. It sets the flush priority of the resulting reactive callback: when the reactive queue has more items than a single cycle can drain, items with higher max_priority_fee_bips are flushed first. Defaults to 0 if omitted. Raise it on a callback you want to win a race against competing reactive work under load — for example, a stop-loss trigger you need to land ahead of other queued callbacks.
The reactive queue
When a reactive callback fires — a TWAP child fill, an SL/TP trigger, an order’s trigger_calls after a fill, a conditional_execution.add_conditional_execution whose condition became true — it normally executes inline. Only under significant load, where the total amount of reactive work triggered between clearings exceeds what can run in a single cycle, does the excess spill into a reactive queue and get drained in max_priority_fee_bips-descending order on the next cycle.
Because any reactive callback could end up running through that deferred path, it must pre-reserve its gas_limit up front, and that gas_limit is bounded by a per-item cap (max_item_gas_limit). If your gas_limit exceeds the cap, the rollup rejects the transaction at submission with an error like:
gas_limit [a, b] exceeds max per-item reactive gas [x, y]Set gas_limit to the smallest value that comfortably covers your callback’s worst case.
Failure handling
When a deferred dispatch fails — insufficient gas at flush time, runtime revert, validation error — the queued call’s on_failure_calls fire for cleanup, and a *Failed event is emitted instead of the normal success event. The cleanup paths to know about:
| Source | Failure cleanup | Failure event |
|---|---|---|
| SL/TP | RemoveSlTpRef removes the stale reference from the position | TriggerFailed |
| TWAP | RemoveOrphanedTwap clears the TWAP from state, marks cancellation_reason: "DispatchFailed" | TwapOrderCancelled |
| Conditional execution | on_cancel_calls fire; the conditional execution is removed | ConditionalExecutionFailed |
Clients reading TWAP order status will start to see cancellation_reason: "DispatchFailed" in addition to the existing UserRequest and OrderCreationFailed reasons.
Gas charges
Your SYNC balance in GET /api/account/:accountId/balances reflects gas charges automatically. Most clients don’t need to track individual gas events beyond that.
Delegated transactions
When a transaction is submitted via sync_accounts.call_with_signature, gas is charged to the inner signer — the account whose signature authorizes the inner call — regardless of who submits the outer envelope. The inner signer must hold a SYNC balance for the delegated call to succeed.
Transferring SYNC
To send SYNC (or any other bank token) to another account, use the bank.transfer call:
{
"bank": {
"transfer": {
"to": "0x<recipient_address>",
"coins": {
"token_id": "token_1nyl0e0yweragfsatygt24zmd8jrr2vqtvdfptzjhxkguz2xxx3vs0y07u7",
"amount": "1000000"
}
}
}
}| Field | Type | Description |
|---|---|---|
to | string | Recipient Ethereum address |
coins.token_id | string | Token ID to transfer (use the SYNC token id to send gas) |
coins.amount | string | Amount in the token’s native decimals |