orderly-deposit-withdraw

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Orderly Network: Deposit & Withdraw

Orderly Network:代币存提

This skill covers depositing and withdrawing assets on Orderly Network, including token approvals, vault interactions, cross-chain operations, and internal transfers.
本技能介绍了如何在Orderly Network上进行资产存提操作,包括代币授权、金库交互、跨链操作以及内部转账。

When to Use

适用场景

  • Building deposit/withdrawal interfaces
  • Managing collateral
  • Implementing cross-chain transfers
  • Handling token approvals
  • Internal transfers between accounts
  • 构建存提界面
  • 管理抵押品
  • 实现跨链转账
  • 处理代币授权
  • 账户间内部转账

Prerequisites

前置条件

  • Connected Web3 wallet (EVM or Solana)
  • Tokens on supported chain
  • Understanding of ERC20 approve pattern
  • Ed25519 key pair for API operations
  • 已连接的Web3钱包(EVM或Solana)
  • 支持链上的代币
  • 了解ERC20授权机制
  • 用于API操作的Ed25519密钥对

Supported Chains & Tokens

支持的链与代币

ChainChain IDUSDCUSDT
Arbitrum42161
Optimism10
Base8453
Ethereum1
Mantle5000-
Solana900900900-
Note: Fetch supported chains dynamically using
GET /v1/public/chain_info?broker_id={your_broker_id}
链名称链IDUSDCUSDT
Arbitrum42161
Optimism10
Base8453
Ethereum1
Mantle5000-
Solana900900900-
注意:可通过
GET /v1/public/chain_info?broker_id={your_broker_id}
动态获取支持的链信息

Deposit Flow

存入流程

1. Fetch chain information
2. Check if token is native (ETH, SOL)
3. Approve token allowance (if not native)
4. Calculate brokerHash and tokenHash
5. Get deposit fee from vault
6. Execute deposit transaction with fee
7. Wait for confirmation
8. Balance updates on Orderly
1. 获取链信息
2. 检查代币是否为原生代币(ETH、SOL)
3. 授权代币额度(非原生代币时)
4. 计算brokerHash和tokenHash
5. 从金库获取存入手续费
6. 执行包含手续费的存入交易
7. 等待交易确认
8. Orderly账户余额更新

React SDK: Complete Deposit Workflow

React SDK:完整存入流程

Step 1: Fetch Chain Information

步骤1:获取链信息

typescript
import { useChain } from "@orderly.network/hooks";

function DepositForm() {
  const { chains, isLoading } = useChain("USDC");

  if (isLoading) return <div>Loading chains...</div>;

  return (
    <select>
      {chains.map(chain => (
        <option key={chain.id} value={chain.id}>
          {chain.name}
        </option>
      ))}
    </select>
  );
}
typescript
import { useChain } from "@orderly.network/hooks";

function DepositForm() {
  const { chains, isLoading } = useChain("USDC");

  if (isLoading) return <div>Loading chains...</div>;

  return (
    <select>
      {chains.map(chain => (
        <option key={chain.id} value={chain.id}>
          {chain.name}
        </option>
      ))}
    </select>
  );
}

Step 2: Get Deposit Metadata

步骤2:获取存入元数据

typescript
import { useDeposit } from '@orderly.network/hooks';

function DepositInfo({ tokenSymbol }: { tokenSymbol: string }) {
  const { symbol, address, decimals, chainId, network } = useDeposit(tokenSymbol);

  if (!address) return <div>Loading deposit info...</div>;

  return (
    <div>
      <h3>Deposit {symbol}</h3>
      <p><strong>Network:</strong> {network}</p>
      <p><strong>Chain ID:</strong> {chainId}</p>
      <p><strong>Contract Address:</strong> {address}</p>
      <p><strong>Decimals:</strong> {decimals}</p>

      <div className="mt-2 text-xs text-gray-500">
        Send only {symbol} to this address on {network}.
      </div>
    </div>
  );
}
typescript
import { useDeposit } from '@orderly.network/hooks';

function DepositInfo({ tokenSymbol }: { tokenSymbol: string }) {
  const { symbol, address, decimals, chainId, network } = useDeposit(tokenSymbol);

  if (!address) return <div>Loading deposit info...</div>;

  return (
    <div>
      <h3>Deposit {symbol}</h3>
      <p><strong>Network:</strong> {network}</p>
      <p><strong>Chain ID:</strong> {chainId}</p>
      <p><strong>Contract Address:</strong> {address}</p>
      <p><strong>Decimals:</strong> {decimals}</p>

      <div className="mt-2 text-xs text-gray-500">
        Send only {symbol} to this address on {network}.
      </div>
    </div>
  );
}

Step 3: Execute Deposit via Contract

步骤3:通过合约执行存入

typescript
import { ethers } from 'ethers';

const ERC20_ABI = [
  'function approve(address spender, uint256 amount) returns (bool)',
  'function allowance(address owner, address spender) view returns (uint256)',
  'function balanceOf(address owner) view returns (uint256)',
];

const VAULT_ABI = [
  'function deposit(tuple(bytes32 accountId, bytes32 brokerHash, bytes32 tokenHash, uint256 tokenAmount) input) external payable',
  'function getDepositFee(address account, tuple(bytes32 accountId, bytes32 brokerHash, bytes32 tokenHash, uint256 tokenAmount) input) view returns (uint256)',
];

async function depositUSDC(
  provider: ethers.BrowserProvider,
  amount: string,
  vaultAddress: string,
  usdcAddress: string,
  brokerId: string,
  orderlyAccountId: string
) {
  const signer = await provider.getSigner();
  const userAddress = await signer.getAddress();

  // 1. Approve token
  const usdc = new ethers.Contract(usdcAddress, ERC20_ABI, signer);
  const amountWei = ethers.parseUnits(amount, 6);
  const allowance = await usdc.allowance(userAddress, vaultAddress);

  if (allowance < amountWei) {
    console.log('Approving USDC...');
    const approveTx = await usdc.approve(vaultAddress, ethers.MaxUint256);
    await approveTx.wait();
    console.log('Approved');
  }

  // 2. Calculate hashes
  const encoder = new TextEncoder();
  const brokerHash = ethers.keccak256(encoder.encode(brokerId));
  const tokenHash = ethers.keccak256(encoder.encode('USDC'));

  // 3. Create deposit input struct
  const depositInput = {
    accountId: orderlyAccountId,
    brokerHash: brokerHash,
    tokenHash: tokenHash,
    tokenAmount: amountWei,
  };

  // 4. Get deposit fee
  const vault = new ethers.Contract(vaultAddress, VAULT_ABI, signer);
  const depositFee = await vault.getDepositFee(userAddress, depositInput);
  console.log('Deposit fee:', ethers.formatEther(depositFee), 'ETH');

  // 5. Execute deposit with fee
  console.log('Depositing...');
  const depositTx = await vault.deposit(depositInput, { value: depositFee });
  const receipt = await depositTx.wait();
  console.log('Deposited:', receipt.hash);

  return receipt;
}
typescript
import { ethers } from 'ethers';

const ERC20_ABI = [
  'function approve(address spender, uint256 amount) returns (bool)',
  'function allowance(address owner, address spender) view returns (uint256)',
  'function balanceOf(address owner) view returns (uint256)',
];

const VAULT_ABI = [
  'function deposit(tuple(bytes32 accountId, bytes32 brokerHash, bytes32 tokenHash, uint256 tokenAmount) input) external payable',
  'function getDepositFee(address account, tuple(bytes32 accountId, bytes32 brokerHash, bytes32 tokenHash, uint256 tokenAmount) input) view returns (uint256)',
];

async function depositUSDC(
  provider: ethers.BrowserProvider,
  amount: string,
  vaultAddress: string,
  usdcAddress: string,
  brokerId: string,
  orderlyAccountId: string
) {
  const signer = await provider.getSigner();
  const userAddress = await signer.getAddress();

  // 1. Approve token
  const usdc = new ethers.Contract(usdcAddress, ERC20_ABI, signer);
  const amountWei = ethers.parseUnits(amount, 6);
  const allowance = await usdc.allowance(userAddress, vaultAddress);

  if (allowance < amountWei) {
    console.log('Approving USDC...');
    const approveTx = await usdc.approve(vaultAddress, ethers.MaxUint256);
    await approveTx.wait();
    console.log('Approved');
  }

  // 2. Calculate hashes
  const encoder = new TextEncoder();
  const brokerHash = ethers.keccak256(encoder.encode(brokerId));
  const tokenHash = ethers.keccak256(encoder.encode('USDC'));

  // 3. Create deposit input struct
  const depositInput = {
    accountId: orderlyAccountId,
    brokerHash: brokerHash,
    tokenHash: tokenHash,
    tokenAmount: amountWei,
  };

  // 4. Get deposit fee
  const vault = new ethers.Contract(vaultAddress, VAULT_ABI, signer);
  const depositFee = await vault.getDepositFee(userAddress, depositInput);
  console.log('Deposit fee:', ethers.formatEther(depositFee), 'ETH');

  // 5. Execute deposit with fee
  console.log('Depositing...');
  const depositTx = await vault.deposit(depositInput, { value: depositFee });
  const receipt = await depositTx.wait();
  console.log('Deposited:', receipt.hash);

  return receipt;
}

REST API: Get Deposit Info

REST API:获取存入信息

typescript
// Get supported tokens with collateral factors
GET /v1/public/token

// Response
{
  "success": true,
  "data": {
    "rows": [
      {
        "token": "USDC",
        "decimals": 6,
        "address": {
          "42161": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
          "10": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
        },
        "collateral_factor": 1.0
      },
      {
        "token": "USDT",
        "decimals": 6,
        "address": { ... },
        "collateral_factor": 0.95
      }
    ]
  }
}

// Get chain info
GET /v1/public/chain_info?broker_id={broker_id}

// Get vault balance (TVL)
GET /v1/public/vault_balance
typescript
// Get supported tokens with collateral factors
GET /v1/public/token

// Response
{
  "success": true,
  "data": {
    "rows": [
      {
        "token": "USDC",
        "decimals": 6,
        "address": {
          "42161": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
          "10": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
        },
        "collateral_factor": 1.0
      },
      {
        "token": "USDT",
        "decimals": 6,
        "address": { ... },
        "collateral_factor": 0.95
      }
    ]
  }
}

// Get chain info
GET /v1/public/chain_info?broker_id={broker_id}

// Get vault balance (TVL)
GET /v1/public/vault_balance

Understanding Collateral Factors

理解抵押系数

Each token has a collateral factor (0.0 to 1.0) that determines how much of your deposit counts toward trading collateral:
TokenCollateral Factor$1000 Deposit = Collateral Value
USDC1.0 (100%)$1000
USDT0.95 (95%)$950
OtherVariesDepends on risk assessment
Example: If you deposit $1000 USDT with a 0.95 collateral factor, only $950 counts as collateral for margin calculations.
每个代币都有一个抵押系数(0.0到1.0),用于计算存入资产可作为交易抵押品的比例:
代币抵押系数1000美元存入对应的抵押品价值
USDC1.0 (100%)1000美元
USDT0.95 (95%)950美元
其他代币依情况而定取决于风险评估
示例:若你存入1000美元USDT,其抵押系数为0.95,则只有950美元会被计入保证金计算的抵押品价值。

Withdrawal Flow

提取流程

1. Get withdrawal nonce
2. Sign withdrawal message with EIP-712 (EVM) or Ed25519 (Solana)
3. Submit signed request with Ed25519 API auth
4. Wait for processing
5. Receive tokens on chain
1. 获取提取随机数
2. 使用EIP-712(EVM)或Ed25519(Solana)签署提取消息
3. 通过Ed25519 API认证提交已签名的请求
4. 等待处理
5. 链上接收代币

React SDK: useWithdraw Hook

React SDK:useWithdraw Hook

typescript
import { useWithdraw } from '@orderly.network/hooks';

function WithdrawForm() {
  const { withdraw, isLoading } = useWithdraw();

  const [amount, setAmount] = useState('');
  const [destination, setDestination] = useState('');

  const handleWithdraw = async () => {
    try {
      await withdraw({
        symbol: "USDC",
        amount: "50",
        address: "0x123...",
        chainId: 1, // Ethereum Mainnet
        network: "ethereum"
      });
      alert('Withdrawal initiated!');
    } catch (error) {
      console.error('Withdrawal failed:', error);
    }
  };

  return (
    <div className="withdraw-form">
      <input
        type="text"
        placeholder="Amount"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />

      <input
        type="text"
        placeholder="Destination Address"
        value={destination}
        onChange={(e) => setDestination(e.target.value)}
      />

      <button onClick={handleWithdraw} disabled={isLoading}>
        {isLoading ? 'Processing...' : 'Withdraw'}
      </button>
    </div>
  );
}
typescript
import { useWithdraw } from '@orderly.network/hooks';

function WithdrawForm() {
  const { withdraw, isLoading } = useWithdraw();

  const [amount, setAmount] = useState('');
  const [destination, setDestination] = useState('');

  const handleWithdraw = async () => {
    try {
      await withdraw({
        symbol: "USDC",
        amount: "50",
        address: "0x123...",
        chainId: 1, // Ethereum Mainnet
        network: "ethereum"
      });
      alert('Withdrawal initiated!');
    } catch (error) {
      console.error('Withdrawal failed:', error);
    }
  };

  return (
    <div className="withdraw-form">
      <input
        type="text"
        placeholder="Amount"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />

      <input
        type="text"
        placeholder="Destination Address"
        value={destination}
        onChange={(e) => setDestination(e.target.value)}
      />

      <button onClick={handleWithdraw} disabled={isLoading}>
        {isLoading ? 'Processing...' : 'Withdraw'}
      </button>
    </div>
  );
}

REST API: Withdrawal

REST API:提取操作

Step 1: Get Withdrawal Nonce

步骤1:获取提取随机数

typescript
GET /v1/withdraw_nonce
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "withdraw_nonce": 123456
  }
}
typescript
GET /v1/withdraw_nonce
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "withdraw_nonce": 123456
  }
}

Step 2: Create EIP-712 Signature (EVM)

步骤2:创建EIP-712签名(EVM)

typescript
// Use ON-CHAIN domain for withdrawals (see EIP-712 Domain Configuration in orderly-api-authentication)
const domain = {
  name: 'Orderly',
  version: '1',
  chainId: 42161,
  verifyingContract: '0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203', // Mainnet Ledger
  // verifyingContract: '0x1826B75e2ef249173FC735149AE4B8e9ea10abff' // Testnet Ledger
};

const types = {
  Withdraw: [
    { name: 'brokerId', type: 'string' },
    { name: 'chainId', type: 'uint256' },
    { name: 'receiver', type: 'address' },
    { name: 'token', type: 'string' },
    { name: 'amount', type: 'uint256' },
    { name: 'withdrawNonce', type: 'uint64' },
    { name: 'timestamp', type: 'uint64' },
  ],
};

const withdrawMessage = {
  brokerId: 'woofi_dex',
  chainId: 42161,
  receiver: '0xTargetAddress',
  token: 'USDC',
  amount: '10000000', // 10 USDC (6 decimals)
  withdrawNonce: withdrawNonceFromStep1,
  timestamp: Date.now(),
};

// Sign with wallet
const signature = await wallet.signTypedData(domain, types, withdrawMessage);
typescript
// Use ON-CHAIN domain for withdrawals (see EIP-712 Domain Configuration in orderly-api-authentication)
const domain = {
  name: 'Orderly',
  version: '1',
  chainId: 42161,
  verifyingContract: '0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203', // Mainnet Ledger
  // verifyingContract: '0x1826B75e2ef249173FC735149AE4B8e9ea10abff' // Testnet Ledger
};

const types = {
  Withdraw: [
    { name: 'brokerId', type: 'string' },
    { name: 'chainId', type: 'uint256' },
    { name: 'receiver', type: 'address' },
    { name: 'token', type: 'string' },
    { name: 'amount', type: 'uint256' },
    { name: 'withdrawNonce', type: 'uint64' },
    { name: 'timestamp', type: 'uint64' },
  ],
};

const withdrawMessage = {
  brokerId: 'woofi_dex',
  chainId: 42161,
  receiver: '0xTargetAddress',
  token: 'USDC',
  amount: '10000000', // 10 USDC (6 decimals)
  withdrawNonce: withdrawNonceFromStep1,
  timestamp: Date.now(),
};

// Sign with wallet
const signature = await wallet.signTypedData(domain, types, withdrawMessage);

Step 3: Submit Withdrawal Request

步骤3:提交提取请求

typescript
POST /v1/withdraw_request
Body: {
  "message": withdrawMessage,
  "signature": "0x..."
}
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "withdraw_id": 123
  },
  "timestamp": 1702989203989
}
typescript
POST /v1/withdraw_request
Body: {
  "message": withdrawMessage,
  "signature": "0x..."
}
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "withdraw_id": 123
  },
  "timestamp": 1702989203989
}

Withdrawal for Solana

Solana提取操作

typescript
import { DefaultSolanaWalletAdapter } from '@orderly.network/default-solana-adapter';
import { signAsync } from '@noble/ed25519';

const walletAdapter = new DefaultSolanaWalletAdapter();
await walletAdapter.active({
  address: solanaAddress,
  provider: {
    signMessage: async (msg: Uint8Array) => {
      return await signAsync(msg, privateKey);
    }
  }
});

const withdrawMessage = await walletAdapter.generateWithdrawMessage({
  brokerId: BROKER_ID,
  receiver: solanaAddress,
  token: 'USDC',
  amount: '1000',
  timestamp: Date.now(),
  nonce: withdrawNonceFromStep1,
});

const signature = await walletAdapter.signMessage(withdrawMessage.message);

// Submit withdrawal request
POST /v1/withdraw_request
Body: {
  "message": withdrawMessage.message,
  "signature": signature,
  "userAddress": solanaAddress,
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
  // "verifyingContract": "0x1826B75e2ef249173FC735149AE4B8e9ea10abff"
}
Headers: { Standard Orderly Auth Headers }
typescript
import { DefaultSolanaWalletAdapter } from '@orderly.network/default-solana-adapter';
import { signAsync } from '@noble/ed25519';

const walletAdapter = new DefaultSolanaWalletAdapter();
await walletAdapter.active({
  address: solanaAddress,
  provider: {
    signMessage: async (msg: Uint8Array) => {
      return await signAsync(msg, privateKey);
    }
  }
});

const withdrawMessage = await walletAdapter.generateWithdrawMessage({
  brokerId: BROKER_ID,
  receiver: solanaAddress,
  token: 'USDC',
  amount: '1000',
  timestamp: Date.now(),
  nonce: withdrawNonceFromStep1,
});

const signature = await walletAdapter.signMessage(withdrawMessage.message);

// Submit withdrawal request
POST /v1/withdraw_request
Body: {
  "message": withdrawMessage.message,
  "signature": signature,
  "userAddress": solanaAddress,
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
  // "verifyingContract": "0x1826B75e2ef249173FC735149AE4B8e9ea10abff"
}
Headers: { Standard Orderly Auth Headers }

Cross-Chain Withdrawal

跨链提取

Cross-chain withdrawal is required when the destination chain differs from the chain where funds were deposited.
typescript
POST /v1/withdraw_request
Body: {
  "message": {
    ...withdrawMessage,
    "chainId": 10,
    "allowCrossChainWithdraw": true
  },
  "signature": signature,
  "userAddress": walletAddress,
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "withdraw_id": 123
  },
  "timestamp": 1702989203989
}
Error Code 22: If
allowCrossChainWithdraw
is
false
when a cross-chain withdrawal is needed, the API returns:
json
{
  "success": false,
  "code": 22,
  "message": "Cross-chain withdrawal required for this withdrawal request"
}
当目标链与存入资产的链不同时,需要使用跨链提取。
typescript
POST /v1/withdraw_request
Body: {
  "message": {
    ...withdrawMessage,
    "chainId": 10,
    "allowCrossChainWithdraw": true
  },
  "signature": signature,
  "userAddress": walletAddress,
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "withdraw_id": 123
  },
  "timestamp": 1702989203989
}
错误码22:若跨链提取需要但
allowCrossChainWithdraw
设为
false
,API会返回:
json
{
  "success": false,
  "code": 22,
  "message": "Cross-chain withdrawal required for this withdrawal request"
}

Internal Transfer

内部转账

Transfer funds between Orderly accounts instantly with no gas fees.
在Orderly账户间即时转账,无需Gas费。

React SDK: useInternalTransfer Hook

React SDK:useInternalTransfer Hook

typescript
import { useInternalTransfer } from '@orderly.network/hooks';

function TransferFunds() {
  const [amount, setAmount] = useState('');
  const { transfer, isLoading } = useInternalTransfer();

  const handleTransfer = async () => {
    try {
      await transfer({
        token: 'USDC',
        amount: parseFloat(amount),
      });
      alert('Transfer successful!');
    } catch (e) {
      console.error(e);
    }
  };

  return (
    <div>
      <input
        type="number"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount"
      />
      <button
        onClick={handleTransfer}
        disabled={isLoading || !amount}
      >
        {isLoading ? 'Transferring...' : 'Transfer'}
      </button>
    </div>
  );
}
typescript
import { useInternalTransfer } from '@orderly.network/hooks';

function TransferFunds() {
  const [amount, setAmount] = useState('');
  const { transfer, isLoading } = useInternalTransfer();

  const handleTransfer = async () => {
    try {
      await transfer({
        token: 'USDC',
        amount: parseFloat(amount),
      });
      alert('Transfer successful!');
    } catch (e) {
      console.error(e);
    }
  };

  return (
    <div>
      <input
        type="number"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount"
      />
      <button
        onClick={handleTransfer}
        disabled={isLoading || !amount}
      >
        {isLoading ? 'Transferring...' : 'Transfer'}
      </button>
    </div>
  );
}

REST API: Internal Transfer (v2)

REST API:内部转账(v2)

Internal transfers move funds between Orderly accounts instantly with no gas fees.
Allowed scenarios:
  • Main and sub-accounts
  • Accounts within the same wallet group
  • Same address across all brokers
  • Whitelisted accounts
  • Accounts under the same whitelisted broker
内部转账可在Orderly账户间即时转移资金,无需Gas费。
允许的场景
  • 主账户与子账户
  • 同一钱包组内的账户
  • 所有经纪商下的相同地址
  • 白名单账户
  • 同一白名单经纪商下的账户

EVM (EIP-712 Signing)

EVM(EIP-712签名)

typescript
// Step 1: Get transfer nonce (Ed25519 API auth)
GET /v1/transfer_nonce
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "transfer_nonce": "789012"
  }
}

// Step 2: Sign EIP-712 message with wallet (ON-CHAIN domain - see orderly-api-authentication)
const receiverHex = receiverAddress.startsWith('0x')
  ? receiverAddress.slice(2)
  : receiverAddress;
const receiverBytes32 = ethers.zeroPadValue('0x' + receiverHex, 32);

const domain = {
  name: 'Orderly',
  version: '1',
  chainId: 42161,
  verifyingContract: '0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203' // Mainnet Ledger
};

const types = {
  InternalTransfer: [
    { name: 'receiver', type: 'bytes32' },
    { name: 'token', type: 'string' },
    { name: 'amount', type: 'uint256' },
    { name: 'transferNonce', type: 'uint64' }
  ]
};

const message = {
  receiver: receiverBytes32,
  token: 'USDC',
  amount: ethers.parseUnits('100', 6),
  transferNonce: Number(transferNonce)
};

const signature = await wallet.signTypedData(domain, types, message);

// Step 3: Submit to v2 endpoint
POST /v2/internal_transfer
Body: {
  "message": {
    "receiver": "0xReceiverAddress",
    "token": "USDC",
    "amount": "100",
    "transferNonce": "789012",
    "chainId": "42161",
    "chainType": "EVM"
  },
  "signature": "0x...",
  "userAddress": "0xSenderAddress",
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication
typescript
// Step 1: Get transfer nonce (Ed25519 API auth)
GET /v1/transfer_nonce
// Requires Ed25519 API authentication

// Response
{
  "success": true,
  "data": {
    "transfer_nonce": "789012"
  }
}

// Step 2: Sign EIP-712 message with wallet (ON-CHAIN domain - see orderly-api-authentication)
const receiverHex = receiverAddress.startsWith('0x')
  ? receiverAddress.slice(2)
  : receiverAddress;
const receiverBytes32 = ethers.zeroPadValue('0x' + receiverHex, 32);

const domain = {
  name: 'Orderly',
  version: '1',
  chainId: 42161,
  verifyingContract: '0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203' // Mainnet Ledger
};

const types = {
  InternalTransfer: [
    { name: 'receiver', type: 'bytes32' },
    { name: 'token', type: 'string' },
    { name: 'amount', type: 'uint256' },
    { name: 'transferNonce', type: 'uint64' }
  ]
};

const message = {
  receiver: receiverBytes32,
  token: 'USDC',
  amount: ethers.parseUnits('100', 6),
  transferNonce: Number(transferNonce)
};

const signature = await wallet.signTypedData(domain, types, message);

// Step 3: Submit to v2 endpoint
POST /v2/internal_transfer
Body: {
  "message": {
    "receiver": "0xReceiverAddress",
    "token": "USDC",
    "amount": "100",
    "transferNonce": "789012",
    "chainId": "42161",
    "chainType": "EVM"
  },
  "signature": "0x...",
  "userAddress": "0xSenderAddress",
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication

Solana (Adapter Signing)

Solana(适配器签名)

typescript
import { DefaultSolanaWalletAdapter } from '@orderly.network/default-solana-adapter';

// Step 1: Get transfer nonce
GET /v1/transfer_nonce
// Requires Ed25519 API authentication

// Step 2: Generate and sign message with adapter
const walletAdapter = new DefaultSolanaWalletAdapter();
// ... initialize adapter ...

const transferMessage = await walletAdapter.generateTransferMessage({
  receiver: receiverAddress,
  token: 'USDC',
  amount: '100',
  transferNonce: nonceFromStep1,
  chainId: 900900900,
  chainType: 'SOL'
});

const signature = await walletAdapter.signMessage(transferMessage.message);

// Step 3: Submit to v2 endpoint
POST /v2/internal_transfer
Body: {
  "message": {
    "receiver": "SolanaReceiverAddress",
    "token": "USDC",
    "amount": "100",
    "transferNonce": "789012",
    "chainId": "900900900",
    "chainType": "SOL"
  },
  "signature": signature,
  "userAddress": solanaAddress,
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication
Important Notes:
  • Transfer nonce must be obtained before each transfer (via Ed25519 auth)
  • The actual transfer requires wallet signature (EIP-712 for EVM, adapter for Solana)
  • Receiver address must be converted to bytes32 for EVM signing
  • Internal transfers are not available for Delegate Signer accounts
  • v2 endpoint includes optional memo field
typescript
import { DefaultSolanaWalletAdapter } from '@orderly.network/default-solana-adapter';

// Step 1: Get transfer nonce
GET /v1/transfer_nonce
// Requires Ed25519 API authentication

// Step 2: Generate and sign message with adapter
const walletAdapter = new DefaultSolanaWalletAdapter();
// ... initialize adapter ...

const transferMessage = await walletAdapter.generateTransferMessage({
  receiver: receiverAddress,
  token: 'USDC',
  amount: '100',
  transferNonce: nonceFromStep1,
  chainId: 900900900,
  chainType: 'SOL'
});

const signature = await walletAdapter.signMessage(transferMessage.message);

// Step 3: Submit to v2 endpoint
POST /v2/internal_transfer
Body: {
  "message": {
    "receiver": "SolanaReceiverAddress",
    "token": "USDC",
    "amount": "100",
    "transferNonce": "789012",
    "chainId": "900900900",
    "chainType": "SOL"
  },
  "signature": signature,
  "userAddress": solanaAddress,
  "verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication
重要说明
  • 每次转账前必须获取转账随机数(通过Ed25519认证)
  • 实际转账需要钱包签名(EVM使用EIP-712,Solana使用适配器)
  • EVM签名时需将接收方地址转换为bytes32格式
  • 代理签名账户不支持内部转账
  • v2端点支持可选的备注字段

Asset History

资产历史

typescript
// Get deposit/withdrawal history
GET /v1/asset/history?token=USDC&side=DEPOSIT&page=1&size=20
// Requires Ed25519 API authentication

// Query parameters (all optional):
//   token: USDC
//   side: DEPOSIT | WITHDRAW
//   status: NEW | CONFIRM | PROCESSING | COMPLETED | FAILED | PENDING_REBALANCE
//   start_t: timestamp (e.g., 1702989203989)
//   end_t: timestamp (e.g., 1702989203989)
//   page: 1 (default)
//   size: 20 (default)

// Response
{
  "success": true,
  "data": {
    "rows": [
      {
        "id": "tx_123",
        "token": "USDC",
        "side": "DEPOSIT",
        "amount": "1000.00",
        "status": "COMPLETED",
        "tx_hash": "0x...",
        "chain_id": 42161,
        "created_at": 1699123456
      }
    ],
    "total": 50
  }
}
typescript
// Get deposit/withdrawal history
GET /v1/asset/history?token=USDC&side=DEPOSIT&page=1&size=20
// Requires Ed25519 API authentication

// Query parameters (all optional):
//   token: USDC
//   side: DEPOSIT | WITHDRAW
//   status: NEW | CONFIRM | PROCESSING | COMPLETED | FAILED | PENDING_REBALANCE
//   start_t: timestamp (e.g., 1702989203989)
//   end_t: timestamp (e.g., 1702989203989)
//   page: 1 (default)
//   size: 20 (default)

// Response
{
  "success": true,
  "data": {
    "rows": [
      {
        "id": "tx_123",
        "token": "USDC",
        "side": "DEPOSIT",
        "amount": "1000.00",
        "status": "COMPLETED",
        "tx_hash": "0x...",
        "chain_id": 42161,
        "created_at": 1699123456
      }
    ],
    "total": 50
  }
}

Testnet Faucet

测试网水龙头

typescript
// Get testnet USDC (testnet only)
POST /v1/faucet/usdc
Headers: { Standard Orderly Auth Headers }

// EVM Testnet
POST https://testnet-operator-evm.orderly.org/v1/faucet/usdc

// Solana Testnet
POST https://testnet-operator-sol.orderly.org/v1/faucet/usdc

// Each account can use faucet max 5 times
// EVM: 1000 USDC per request
// Solana: 100 USDC per request
typescript
// Get testnet USDC (testnet only)
POST /v1/faucet/usdc
Headers: { Standard Orderly Auth Headers }

// EVM Testnet
POST https://testnet-operator-evm.orderly.org/v1/faucet/usdc

// Solana Testnet
POST https://testnet-operator-sol.orderly.org/v1/faucet/usdc

// Each account can use faucet max 5 times
// EVM: 1000 USDC per request
// Solana: 100 USDC per request

Get Contract Addresses

获取合约地址

typescript
import { getContractAddresses } from '@orderly.network/contracts';

// EVM chains
const addresses = getContractAddresses('arbitrum', 'mainnet');
// {
//   vault: '0x816f722424B49Cf1275cc86DA9840Fbd5a6167e9',
//   usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
//   usdt: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9'
// }

// Or fetch dynamically
GET /v1/public/chain_info?broker_id={broker_id}
typescript
import { getContractAddresses } from '@orderly.network/contracts';

// EVM chains
const addresses = getContractAddresses('arbitrum', 'mainnet');
// {
//   vault: '0x816f722424B49Cf1275cc86DA9840Fbd5a6167e9',
//   usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
//   usdt: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9'
// }

// Or fetch dynamically
GET /v1/public/chain_info?broker_id={broker_id}

Verifying Contract Addresses

验证合约地址

EnvironmentVerifying Contract
Mainnet
0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203
Testnet
0x1826B75e2ef249173FC735149AE4B8e9ea10abff
环境验证合约地址
主网
0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203
测试网
0x1826B75e2ef249173FC735149AE4B8e9ea10abff

Common Issues

常见问题

"Insufficient allowance" error

"Insufficient allowance" 错误

  • Call
    approve()
    before deposit
  • Check if approval transaction confirmed
  • Ensure you're approving the vault address, not token address
  • 存入前调用
    approve()
  • 检查授权交易是否已确认
  • 确保授权的是金库地址而非代币地址

"Insufficient balance" error

"Insufficient balance" 错误

  • Check wallet balance, not just Orderly balance
  • Ensure correct token decimals
  • Account for deposit fee (native token like ETH)
  • 检查钱包余额,而非仅Orderly账户余额
  • 确保代币小数位数正确
  • 考虑存入手续费(如ETH等原生代币)

Withdrawal stuck in "PROCESSING"

提取操作卡在"PROCESSING"状态

  • Check
    /v1/asset/history
    for status updates
  • Contact support if stuck > 24 hours
  • 调用
    /v1/asset/history
    获取状态更新
  • 若超过24小时仍未处理,请联系支持团队

"Cross-chain withdrawal required" error (Code 22)

"Cross-chain withdrawal required" 错误(代码22)

  • Set
    allow_cross_chain_withdrawal: true
    in request body
  • Destination chain differs from deposit chain
  • Expect longer processing time
  • 在请求体中设置
    allow_cross_chain_withdrawal: true
  • 目标链与存入链不同
  • 处理时间会更长

Internal transfer not available

内部转账不可用

  • Not available for Delegate Signer accounts
  • Ensure accounts meet allowed transfer scenarios
  • Check that Ed25519 authentication is properly signed
  • 代理签名账户不支持内部转账
  • 确保账户符合允许的转账场景
  • 检查Ed25519认证是否正确签署

EIP-712 signature mismatch

EIP-712签名不匹配

Common mistakes:
  • withdrawNonce
    type should be
    uint64
    , not
    uint256
  • token
    type should be
    string
    , not
    address
  • Missing
    fee
    field in message
  • Wrong verifying contract address
  • Wrong chain ID in domain
常见错误:
  • withdrawNonce
    类型应为
    uint64
    而非
    uint256
  • token
    类型应为
    string
    而非
    address
  • 消息中缺少
    fee
    字段
  • 验证合约地址错误
  • 域中的链ID错误

Related Skills

相关技能

  • orderly-api-authentication - Ed25519 API authentication and EIP-712 signing
  • orderly-sdk-react-hooks - React SDK hooks reference
  • orderly-onboarding - Account setup and getting started
  • orderly-api-authentication - Ed25519 API认证与EIP-712签名
  • orderly-sdk-react-hooks - React SDK Hook参考
  • orderly-onboarding - 账户设置与入门指南