Skip to content

Withdraw Collateral (WebSocket)

Withdraw collateral from your Synthetix trading account back to your wallet through the WebSocket connection. Withdrawals are subject to approval, margin requirements, and risk checks.

Endpoint

ws.send() wss://api.synthetix.io/v1/ws/trade

Request

Request Format

{
  "id": "withdraw-1",
  "method": "post",
  "params": {
    "action": "withdrawCollateral",
    "subAccountId": "1867542890123456789",
    "symbol": "USDC",
    "amount": "1000.0",
    "destination": "0x742d35Cc6634C0532925a3b8D371d1c62a39b6e2",
    "nonce": 1704067200000,
    "expiresAfter": 1704067300,
    "signature": {
      "v": 28,
      "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
    }
  }
}

Parameters

Request Parameters

ParameterTypeRequiredDescription
idstringYesClient-generated unique request identifier
methodstringYesMust be "post"
paramsobjectYesContains the withdrawal request payload with all fields flattened

Params Object Fields

ParameterTypeRequiredDescription
actionstringYesMust be "withdrawCollateral"
subAccountIdstringYesSubaccount identifier
symbolstringYesCollateral asset symbol to withdraw (e.g., "USDC", "ETH")
amountstringYesAmount to withdraw as decimal string (e.g., "1000.0")
destinationstringYesDestination wallet address (0x-prefixed Ethereum address)
nonceintegerYesPositive integer, incrementing nonce
expiresAfterintegerNoOptional expiration timestamp in seconds (0 = no expiration)
signatureobjectYesEIP-712 signature

Important: Withdrawals must be signed by the master account owner. Delegate addresses cannot initiate withdrawals.

EIP-712 Type Definition

const WithdrawCollateralTypes = {
  WithdrawCollateral: [
    { name: "subAccountId", type: "uint256" },
    { name: "symbol", type: "string" },
    { name: "amount", type: "string" },
    { name: "destination", type: "address" },
    { name: "nonce", type: "uint256" },
    { name: "expiresAfter", type: "uint256" }
  ]
}

Response Format

Success Response

{
  "id": "withdraw-1",
  "status": 200,
  "result": {
    "requestId": "withdraw-req-123",
    "symbol": "USDC",
    "amount": "1000.0",
    "destination": "0x742d35Cc6634C0532925a3b8D371d1c62a39b6e2"
  }
}
Response Fields:
FieldTypeDescription
requestIdstringUnique withdrawal request identifier
symbolstringAsset symbol being withdrawn
amountstringAmount being withdrawn
destinationstringDestination wallet address

Error Response

{
  "id": "withdraw-1",
  "status": 400,
  "result": null,
  "error": {
    "code": 400,
    "message": "Insufficient withdrawable balance"
  }
}

Implementation Example

import { ethers } from 'ethers';
 
async function withdrawCollateral(ws, signer, subAccountId, symbol, amount, destination) {
  const nonce = Date.now();
  const expiresAfter = Math.floor(Date.now() / 1000) + 300; // 5 minutes
 
  // EIP-712 signature using WithdrawCollateral type
  const domain = {
    name: "Synthetix",
    version: "1",
    chainId: 1,
    verifyingContract: "0x0000000000000000000000000000000000000000"
  };
 
  const types = {
    WithdrawCollateral: [
      { name: "subAccountId", type: "uint256" },
      { name: "symbol", type: "string" },
      { name: "amount", type: "string" },
      { name: "destination", type: "address" },
      { name: "nonce", type: "uint256" },
      { name: "expiresAfter", type: "uint256" }
    ]
  };
 
  const message = {
    subAccountId: BigInt(subAccountId),
    symbol,
    amount,
    destination,
    nonce: BigInt(nonce),
    expiresAfter: BigInt(expiresAfter)
  };
 
  const sig = await signer.signTypedData(domain, types, message);
  const signature = ethers.Signature.from(sig);
 
  // Send request
  ws.send(JSON.stringify({
    id: `withdraw-${Date.now()}`,
    method: "post",
    params: {
      action: "withdrawCollateral",
      subAccountId,
      symbol,
      amount,
      destination,
      nonce,
      expiresAfter,
      signature: { v: signature.v, r: signature.r, s: signature.s }
    }
  }));
}
 
// Usage: Withdraw 1000 USDC to your wallet
await withdrawCollateral(
  ws,
  signer,
  "1867542890123456789",
  "USDC",
  "1000.0",
  "0x742d35Cc6634C0532925a3b8D371d1c62a39b6e2"
);

Validation Rules

  • nonce must be a positive integer, incrementing and unique per request
  • symbol must be a valid collateral symbol (e.g., "USDC", "ETH")
  • amount must be a positive number as string
  • destination must be a valid Ethereum address (0x-prefixed)
  • Account must have sufficient withdrawable balance of the specified asset
  • Withdrawal must not violate margin requirements
  • Must be signed by master account owner (no delegate support)

Error Handling

Common Errors

Error CodeMessageDescription
400Insufficient withdrawable balanceAccount lacks required funds for specified asset
400Margin requirement violationWithdrawal would violate margin rules
400Invalid asset symbolAsset type not supported as collateral
400Account health check failedWithdrawal would make account unhealthy
401Delegate signature not allowedMust be signed by account owner
400Minimum withdrawal amountAmount below minimum threshold

Signing

All trading methods are signed using EIP-712. Each successful trading request will contain:

  • A piece of structured data that includes the sender address
  • A signature of the hash of that structured data, signed by the sender

For detailed information on EIP-712 signing, see EIP-712 Signing.

Nonce Management

The nonce system prevents replay attacks and ensures order uniqueness:

  • Use any positive integer as nonce
  • Each nonce must be greater than the previous one (incrementing)
  • Date.now() is a convenient option, not a requirement
  • If nonce conflicts occur, increment by 1 and retry

:::note SubAccountAction Exception SubAccountAction endpoints (getPositions, getOpenOrders, getOrdersHistory, getTrades, getFundingPayments, getSubAccount, getSubAccounts, getDelegatedSigners, getBalanceUpdates) do not require a nonce. Only the signature and optional expiresAfter parameters are needed. :::

Error CodeDescriptionRetryable
UNAUTHORIZEDEIP-712 signature validation failedNo
VALIDATION_ERRORRequest validation failedNo
MISSING_REQUIRED_FIELDRequired field is missingNo
INVALID_FORMATField format is invalidNo
INVALID_VALUEInvalid parameter valueNo
RATE_LIMIT_EXCEEDEDToo many requests in time windowYes
INSUFFICIENT_MARGINNot enough margin for tradeNo
ORDER_NOT_FOUNDOrder does not existNo
OPERATION_TIMEOUTOperation timed outYes

Next Steps