Skip to Content
Symmio Trading-SDK — the SDK surface for builders on Arbitrum
ReactWithdraw hooks

Withdraw hooks

Withdraw request lifecycle — initiate, cancel, finalize, plus reads for the pending queue.

Reach for useWithdraw first: it is the high-level entry point that takes just { account, amount, receiver } (amount in the collateral token’s decimals) and does the rest — it reads the subaccount’s isolation, picks the right withdraw path, builds the withdraw part, and scales the deallocate amount — so the UI never branches on where the collateral sits or scales anything. useInitiateWithdraw and the deallocate-and-initiate variant remain available for callers that already know the isolation and want to drive one specific path, or need custom (multi-part / cross-chain) withdraw parts.

Import

import { // Reads useLastWithdrawRequestId, usePendingWithdrawRequests, useWithdrawRequest, useWithdrawableTime, // Writes useWithdraw, // high-level entry point useInitiateWithdraw, useSimulateInitiateWithdraw, useRequestCancelWithdraw, useSimulateRequestCancelWithdraw, useFinalizeWithdrawRequest, useSimulateFinalizeWithdrawRequest, } from "@symmio/trading-react";

useWithdraw — high-level withdraw

Pass the subaccount account to the hook, then mutate with a minimal input: { amount, receiver }, with amount in the collateral token’s decimals (e.g. 6 for USDC). You do not pass isolationType, build parts, or scale anything — the hook reads the subaccount’s SubAccountIsolationType via useSubAccount (deduped by react-query with any other useSubAccount on the same key, e.g. one driving a balance display), builds the classic same-chain withdraw part, scales the deallocate amount, and dispatches on the isolation, which determines which balance the funds are in and therefore which underlying action runs:

isolationTypeWhere the funds sitWhat runs
CUSTOM (cross-margin)allocated (margin)deallocateAndInitiateWithdraw — deallocate and initiate in one atomic tx
MARKET / MARKET_DIRECTION (VA)availableinitiateWithdraw — initiate only

A CUSTOM subaccount holds its collateral in the allocated balance, so it must first be deallocated into the available balance; that path batches both legs into a single transaction. Account-layer balances are 1e18-scaled regardless of the collateral token’s decimals, so the hook scales your collateral-decimals amount to the 18-decimal amount the deallocate leg needs. The VA modes already hold their funds in the available balance, so they only initiate the request.

import { useWithdraw } from "@symmio/trading-react"; const { mutate } = useWithdraw({ account }); // amount in the collateral token's decimals (6-dec USDC → 1 USDC): mutate({ amount: 1_000000n, receiver });

On success it invalidates the subaccount’s balance reads (balanceInfo, balanceOf) plus its pending-requests and withdrawable-time reads. On the CUSTOM path the SDK fetches a fresh Muon uPnL signature for the deallocate leg unless you pass one as upnlSig. Backed by the core withdrawAuto action; reach for useInitiateWithdraw / the deallocate-and-initiate variant when you already know the isolation or need custom withdraw parts.

Lifecycle overview

  1. Initiate — useInitiateWithdraw submits a withdraw request. Enters the delayed queue.
  2. Wait — useWithdrawableTime tells the UI when the request becomes claimable.
  3. Finalize — useFinalizeWithdrawRequest claims the funds after the delay.
  4. Cancel (optional) — useRequestCancelWithdraw cancels a pending request.

Each write has a paired useSimulate* — see Simulate then write.

Reads

useLastWithdrawRequestId

Id of the most recent request for an account.

const lastId = useLastWithdrawRequestId({ account });

usePendingWithdrawRequests

Every queued (not yet claimable) request for an account.

const pending = usePendingWithdrawRequests({ account });

useWithdrawRequest

Details for one specific request.

const request = useWithdrawRequest({ account, requestId });

useWithdrawableTime

Timestamp when a queued request becomes claimable. Bind to a countdown / claim button.

const { withdrawableAt, isReady } = useWithdrawableTime({ requestId });

Writes

Every write on this page is relayable: pass gasless: true in the mutation variables to sign it as an InstantLayer operation and let the relayer pay the gas. See Gasless hooks. A relayed write still pays an operational fee from the Core balance — budget it first, as Gasless withdrawals describes.

useInitiateWithdraw / useSimulateInitiateWithdraw

Submit a withdraw request directly, without the isolation dispatch. This is the initiate-only leg — the right call for a MARKET / MARKET_DIRECTION (VA) subaccount whose funds are already in the available balance, or when you drive the deallocateAndInitiateWithdraw variant yourself for a CUSTOM subaccount. Prefer useWithdraw when you don’t want to branch on isolation.

const simulation = useSimulateInitiateWithdraw({ account, amount }); const mutation = useInitiateWithdraw();

useRequestCancelWithdraw / useSimulateRequestCancelWithdraw

Cancel a pending request.

const mutation = useRequestCancelWithdraw(); mutation.mutate({ account, requestId });

useFinalizeWithdrawRequest / useSimulateFinalizeWithdrawRequest

Claim funds after the delay expires.

const mutation = useFinalizeWithdrawRequest(); mutation.mutate({ account, requestId });

Gasless withdrawals

Relaying a withdrawal removes the gas, not the cost. The GaslessLayer charges an operational fee after the operation executes, from the Core balance of the account that pays — for a plain withdrawal, the sub-account itself. The withdrawal and its fee draw on the same balance, so budget both, in Core units:

required Core balance = withdrawal amount in Core units + operational fee in Core units

The withdrawal amount is in the collateral token’s decimals, while the Core balance and the fee are 18-decimal. Convert the amount with collateralToCore18 before you compare. A MARKET / MARKET_DIRECTION sub-account withdraws from its available balance:

import { collateralToCore18 } from "@symmio/trading-core"; import { useAccountBalanceOf, useSymmioChainId, useSymmioConfig, useWithdraw } from "@symmio/trading-react"; const config = useSymmioConfig(); const chainId = useSymmioChainId(); const { collateralDecimals } = config.getChainConfig(chainId).addresses; const balance18 = useAccountBalanceOf({ account }).data; // 18-decimal Core units const required18 = collateralToCore18(amount, collateralDecimals) + fee18; const affordable = balance18 !== undefined && balance18 >= required18; const { mutate } = useWithdraw({ account }); mutate({ amount, receiver, gasless: true }); // amount stays in collateral decimals

A CUSTOM sub-account holds its funds in the allocated balance and deallocates the amount first, so budget the same total against its allocatedBalance from useAccountBalanceInfo. The fee is drawn from the free balance first, then from the allocated balance.

fee18 is the operation’s quoted fee — totalFee18 from useGaslessFeeQuote for the batch you relay. The quote is a preview, so keep a margin, and reserve the budgets of any other relayed operations still pending. Neither the quote nor the transparent mode’s fee pre-flight checks that the payer can afford the fee. A shortfall is refused before acceptance with SIMULATION_REVERTED and the Core revert OperationalFee: Insufficient balance, which approving more allowance does not fix. Read current fees rather than hard-coding them; they are deployment configuration.

A relayed withdrawal that succeeds is initiated, not received. A succeeded relay, and the mutation that resolves with it, means the withdraw request exists on-chain and its amount has left the Core balance. The funds reach the receiver only after the cooldown and a separate finalize. Show “withdrawal initiated” until then, and follow the request with useWithdrawableTime and usePendingWithdrawRequests.

Finalizing and cancelling cost a fee too when relayed. Each is its own relayed operation, charged in Core units to the request’s sub-account unless the daily free quota covers it:

  • A relayed finalize adds nothing to the Core balance, so the fee must still be there when it runs. Withdrawing the whole balance leaves nothing to pay it with. Keep the fee back, or finalize from the wallet (gasless: false), which costs native gas but no operational fee.
  • A relayed cancel of a pending request refunds the amount before the fee is collected, so the refund can cover the fee. A cancel that only asks the request’s withdraw provider to cancel refunds nothing yet, so it needs the reserve as well.
  • SYMMIO Contract — the underlying createClassicWithdrawPart primitive.
  • InstantLayer — the delegated instant-withdraw variant.
  • Errors — reverts surface as SymmioRequestError kind: "revert".
Last updated on