Instant Withdrawal for Frontend Builders

This document outlines the end-to-end user interaction flows, API endpoints, data schemas, and error codes for integrating the Symmio Instant Withdrawal feature in the frontend application. It is intended for UI developers and focuses on the requests, responses, and sequence of steps rather than backend implementation details.

1. Core Concepts

  • Symmio Contract: Symmio protocol.

  • Instant Withdrawal: A bridge contract enabling users to withdraw funds with selectable fee policies and cooldowns.

  • Backend Bot: Off-chain API service handling policy retrieval, validation, scheduling, selecting fee policies, and triggering withdrawals.

  • Frontend: The UI layer where users interact.

  • User: End user interacting via wallet and UI.

2. API Endpoints

All endpoints are prefixed with /v1. Authentication uses JWT via an Authorization: Bearer <token> header, obtained through the SIWE login flow.

2.1. POST /v1/fee-options

Fetch available withdrawal policies (fee & cooldown) for a given account and amount.

  • Request (Query Parameters):

    • account (string; required; EVM address)

    • amount (integer; required; must be > 0; amount in wei)

  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response Schema (200):

interface FeeOptionsResponseSchema {
  count: number;          // number of available options
  options: FeeOption[];
}
interface FeeOption {
  fee: number;           // total fee in wei
  cooldown: number;      // cooldown period in seconds
  user: string;          // checksumed ETH address
  amount: number;        // requested amount in wei
  validTime: number;     // Unix epoch (seconds) when this option expires
}
  • Behavior:

    • Validates JWT and wallet ownership of account.

    • Checks if an existing pending request exists. If yes, returns error code for locked options.

    • Computes policies to determine the fees.

  • Error Codes:

Code
Message
Condition

11

unauthorized

The owner_address from JWT does not match the owner for provided account.

5

Already has pending withdrawal options try in seconds

A pending request already exists.

6

not enough user balance

The user’s on-chain balance is less than amount.

10

requested amount for bridge is very low

The requested amount is less than or equal to OPERATOR_FEE.

2.2. POST /v1/unlock/{account}

Clear any existing lock on fee policies for the given account, allowing immediate re-fetch of options.

  • Request:

    • account (string; required; EVM address)

  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response Schema (200):

interface UnlockAccountResponseSchema {
  message: string;  // "Account unlocked successfully."
}
  • Behavior:

    • Validates JWT and wallet ownership of account.

    • Removes pending fee options.

    • Returns { message: "Account <account> unlocked successfully." }.

  • Error Codes:

Code
Message
Condition

11

unauthorized

The owner_address from JWT does not match the owner for provided account.


2.3. GET /v1/pending-fee-policy/{account}

Retrieve currently locked fee policies for the given account, if any exist.

  • Request:

    • account (string; required; EVM address)

  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response Schema (200):

interface FeeOptionsResponseSchema {
  count: number;          // number of available options
  options: FeeOption[];
}
interface FeeOption {
  fee: number;           // total fee in wei
  cooldown: number;      // cooldown period in seconds
  user: string;          // checksumed ETH address
  amount: number;        // requested amount in wei
  validTime: number;     // Unix epoch (seconds) when this option expires
}
  • Behavior:

    • Validates JWT and wallet ownership of account.

  • Error Codes:

Code
Message
Condition

11

unauthorized

The owner_address from JWT does not match the owner for provided account.


2.4. GET /v1/max-instant-value/{account}

Fetch the maximum amount (in wei) that the user can withdraw instantly at this moment.

  • Request (Path Parameter):

    • account (string; required; EVM address)

  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response Schema (200):

interface MaxInstantWithdrawableAmountResponseSchema {
  amount: number; // maximum instant-withdrawable amount in wei
}
  • Behavior:

    • Validates JWT and wallet ownership of account.

    • Checks user balance and risk factor.

    • Returns { amount: <max_instant> }.

  • Error Codes:

Code
Message
Condition

11

unauthorized

The owner_address from JWT does not match the owner for provided account.


2.5. GET /v1/get-select-receiver-message?receiver={address}&bridgeId={bridgeId}

Construct an EIP‑712 typed-data payload that the user can sign to authorize selecting a different recipient (multi-account) for a given bridgeId.

  • Request (Query Parameters):

    • receiver (string; required; checksumed EVM address)

    • bridgeId (integer; required)

  • Request Headers:

    • No JWT required (open endpoint)

  • Response Schema (200):

interface GetSelectReceiverMessageResponse {
  payload: EIP712SelectReceiverMessage;
}
interface EIP712SelectReceiverMessage {
  types: EIP712Types;
  domain: EIP712DomainModel;
  primaryType: "SelectReceiver";
  message: {
    receiver: string; // address
    bridgeId: number;
  };
}
  • Behavior: Returns an on‑chain domain and typed data structure, according to EIP‑712.

2.6. POST /v1/select-fee-policy

Submit a signed fee policy selection for an existing bridge transaction.

  • Request Body Schema (application/json):

interface SelectFeePolicyRequestSchema {
  symmioBridgeId: number;        // bridge transaction ID
  cooldown: number;              // chosen cooldown
  feeAmount: number;             // chosen fee (wei)
  receiver?: SpecifiedReceiver | null; // optional new recipient object
}
interface SpecifiedReceiver {
  address: string;       // checksumed Ethereum address
  signature: string;     // EIP‑712 signature hex
}
  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response Schema (200):

interface SelectFeePolicyResponseSchema {
  bridge_id: number;      // same as symmioBridgeId
  execution_time: number; // Unix epoch (seconds) when `processWithdrawal` will be called
  fee: number;            // fee in wei
}
  • Behavior:

    • Validates JWT and that the current user is either the owner or the user on-chain for symmioBridgeId.

    • Fetches BridgeTransaction from Symmio contract and validate data and status.

    • Calls select_fee_policy_for_bridge on-chain.

    • Returns { bridge_id, fee: feeAmount, execution_time }.

  • Error Codes:

Code
Message
Condition

11

unauthorized

The owner_address from JWT does not match the owner for provided account.

7

invalid bridge transaction

The provided bridgeId is not in RECEIVED status or does not point to the InstantWithdrawal contract.

8

invalid bridge policy option

No pending options exist for this user, or the selected fee/cooldown combination does not match any stored option.

9

You already selected a withdrawal option for bridge or it has executed.

A fee policy has already been selected for this bridge (bridge.bridge_type == instant).

2.7. POST /v1/bridges/{account}/{start}/{size}

Search and paginate historical bridge transactions for a given user account:

  • Request:

    • account (string; required; EVM address)

    • start (integer; required; offset index)

    • size (integer; required; page size)

  • Request Body Schema (application/json):

interface BridgeTransactionRequestModel {
  bridge_state?: "pending" | "executed" | "settled" | "suspend" | null;
  bridge_type?: "instant" | "after_cool_down" | "no_policy" | null;
}
  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response Schema (200):

    interface BridgeTransactionSearchResponseSchema {
      count: number;
      bridges: BridgeTransactionResponseModel[] | null;
    }
    interface BridgeTransactionResponseModel {
      bridge_id: number;
      user_address: string;
      bridge_state: "pending" | "executed" | "settled" | "suspend";
      bridge_type: "instant" | "after_cool_down" | "no_policy";
      bridge_amount: number;        // in wei
      fee_amount: number;           // in wei
      bridge_transaction_time: number; // Unix epoch seconds
      execution_time?: number;      // Unix epoch seconds or null
    }
  • Behavior:

    • Validates JWT and wallet ownership of account.

    • Queries database, ordering by creation time descending.

    • If no records, returns { count: 0, bridges: null }.

    • Otherwise returns { count: <number_of_rows>, bridges: [ ... ] }.

  • Error Codes:

    Code
    Message
    Condition

    11

    unauthorized

    The owner_address from JWT does not match the owner for provided account.

Authentication Endpoints

2.8.1. POST /v1/auth/nonce

Request a one-time nonce for SIWE login:

  • Request Body (application/json):

    interface AddressSchema {
      address: string; // checksumed Ethereum address
    }
  • Response (200):

    { "nonce": "0x1234…abcd" }
  • Behavior:

    • Generates random nonce for the address.

  • Error Codes:

    • ValueError (missing or malformed address)

2.8.2. GET /v1/auth/sign-in-message

Return an EIP‑4361 formatted message for user signature:

  • Request (Query):

    • address (string; required, EVM address)

    • domain (string; required)

    • uri (string; required)

    • statement (string; optional)

    • version (string; optional)

  • Response Schema (200):

    interface SignInMessageResponse {
      message: string;  // full EIP‑4361 text
      params: {
        address: string;
        domain: string;
        uri: string;
        statement?: string;
        version: string;
        chainId: number;
        nonce: string;
        issuedAt: string;
        expiration_time: string;
      };
    }
  • Behavior

    • Constructs a full EIP-4361-formatted message string comprising

    • Returns both the single message string and the underlying data object.

    • The UI shows message in the wallet’s signing modal and keeps the returned data for the subsequent POST /v1/auth/login call.

2.8.3. POST /v1/auth/login

Validate signed SIWE message and issue JWT:

  • Request Body:

    interface LoginRequest {
      message: CustomSiweMessage;   // full EIP‑4361 payload
      signature: string;            // hex signature
    }
    interface CustomSiweMessage {
      domain: string;
      address: string;
      uri: string;
      version: string;
      chainId: number;
      issuedAt: string;
      nonce: string;
      statement?: string;
      expirationTime?: string;
    }
  • Response Schema (200):

    interface Token {
      access_token: string; // JWT
      token_type: string;   // "bearer"
    }
  • Error Codes:

    Code
    Message
    Condition

    14

    expired_nonce

    No nonce found or expired.

    18

    nonce_mismatch

    Provided nonce differs from stored.

    12

    invalid_signature

    Signature verification failed.

2.8.4. GET /v1/auth/me

Retrieve the currently authenticated user address:

  • Request Headers:

    • Authorization: Bearer <JWT>

  • Response (200):

    interface AddressSchema {
      address: string; // checksumed Ethereum address
    }
  • Error Codes:

    Code
    Message
    Condition

    11

    unauthorized

    The owner_address from JWT does not match the owner for provided account.

Bridge Enums Overview

Bridge States:

Value
Meaning

pending

The bridge transaction is recorded but process withdrawal has not been called yet.

executed

A fee policy was selected, and the withdrawal has been executed on-chain.

settled

The funds have been withdrawn from Symmio to the bridge.

suspend

The bridge process is on hold (e.g., due to a suspension from Symmio).

Bridge Types:

Value
Meaning

instant

The user chose an instant-withdrawal policy with custom fee and cooldown.

after_cool_down

The user selected the default policy that requires waiting for Symmio cooldown before withdrawal.

no_policy

No fee policy has been chosen yet; the bridge is awaiting the user to request options and select one.

Last updated