Skip to main content
[!TIP] Deposit Method Selection: This page explains Standard Vault Deposit, where the user connects their wallet and invokes the smart contract deposit function. If your users are depositing from a Centralized Exchange (CEX) or using a custodial wallet that does not support DApps, refer to the Deposit by Transfer guide.

Deposit

Token deposits can be made on any supported chains (list here) To create a deposit request:
1

Approve spending of the token by the Vault smart contract

Get Vault & USDC contract addresses from the Smart Contract Addresses page. All our smart contracts are verified, meaning you can retrieve the Vault & Token ABIs from the block explorers of any supported mainnets or testnets.
[!IMPORTANT] The addresses and RPC nodes used in the following examples are for Arbitrum Sepolia testnet. Since Orderly is an omnichain protocol, when deploying to production, replace these with the correct contract addresses and RPC endpoints for your target chain.
String usdcAddress = "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d";
String vaultAddress = "0x0EaC556c0C2321BA25b9DC01e4e3c95aD5CDCd2f";

Dotenv dotenv = Dotenv.load();
String pk = dotenv.get("PRIVATE_KEY");
Credentials credentials = Credentials.create(ECKeyPair.create(Hex.decodeHex(pk)));

Web3j web3j = Web3j.build(new HttpService("https://arbitrum-sepolia.publicnode.com"));
DefaultGasProvider gasProvider = new DefaultGasProvider();

NativeUSDC USDC = new NativeUSDC(usdcAddress, web3j, credentials, gasProvider);

BigInteger depositAmount = new BigInteger("10000000");

// approve USDC ERC-20 to be transferred to Vault contract
USDC.approve(vaultAddress, depositAmount).send();
2

Determine necessary deposit fee in WEI

String brokerId = "woofi_pro";
String tokenId = "USDC";
String orderlyAccountId = "0x...";

Vault vault = new Vault(vaultAddress, web3j, credentials, gasProvider);

Keccak.Digest256 brokerHash = new Keccak.Digest256();
byte[] brokerIdBytes = brokerId.getBytes("UTF-8");
brokerHash.update(brokerIdBytes, 0, brokerIdBytes.length);

Keccak.Digest256 tokenHash = new Keccak.Digest256();
byte[] usdcBytes = tokenId.getBytes("UTF-8");
tokenHash.update(usdcBytes, 0, usdcBytes.length);

Vault.VaultDepositFE depositInput = new Vault.VaultDepositFE(
Hex.decodeHex(orderlyAccountId.substring(2)),
brokerHash.digest(),
tokenHash.digest(),
depositAmount);

// get wei deposit fee for `deposit` call
BigInteger depositFee = vault.getDepositFee(credentials.getAddress(), depositInput).send();

3

Call the `deposit` function of the smart contract

Contains the following structured data:
struct DepositData {
  bytes32 accountId;
  bytes32 brokerHash;
  bytes32 tokenHash;
  uint128 tokenAmount;
}
where:
NameTypeRequiredDescription
accountIdbytesYThe account id of the user in bytes.
brokerHashbytesYThe keccak256 hash of the builder id (eg “woofi_dex”)
tokenHashbytesYThe keccak256 hash of the token string (eg “USDC”)
tokenAmountintYAmount of tokens to be deposited
// deposit USDC into Vault contract
vault.deposit(
      depositInput,
      depositFee).send();
Deposits will take some time to reflect depending on the origin chain, to allow for finalizing of the cross-chain transaction involved in the deposit. Once the deposit is credited, the balance will update and the transaction can be seen on the Get asset history API.

Full example

Code generation via ABI file

  • install Web3j CLI
  • web3j generate solidity -a <./path/to/Vault.json> -o <./out-path/to/Vault.java> -p <package-name>
import java.math.BigInteger;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.DefaultGasProvider;
import io.github.cdimascio.dotenv.Dotenv;

public class DepositExample {

      public static void main(String[] args) throws Exception {

            String brokerId = "woofi_pro";
            String tokenId = "USDC";
            String orderlyAccountId = "0x...";

            String usdcAddress = "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d";
            String vaultAddress = "0x0EaC556c0C2321BA25b9DC01e4e3c95aD5CDCd2f";

            Dotenv dotenv = Dotenv.load();
            String pk = dotenv.get("PRIVATE_KEY");
            Credentials credentials = Credentials.create(ECKeyPair.create(Hex.decodeHex(pk)));

            Web3j web3j = Web3j.build(new HttpService("https://arbitrum-sepolia.publicnode.com"));
            DefaultGasProvider gasProvider = new DefaultGasProvider();

            NativeUSDC USDC = new NativeUSDC(usdcAddress, web3j, credentials, gasProvider);

            BigInteger depositAmount = new BigInteger("10000000");

            // approve USDC ERC-20 to be transferred to Vault contract
            USDC.approve(vaultAddress, depositAmount).send();

            Vault vault = new Vault(vaultAddress, web3j, credentials, gasProvider);

            Keccak.Digest256 brokerHash = new Keccak.Digest256();
            byte[] brokerIdBytes = brokerId.getBytes("UTF-8");
            brokerHash.update(brokerIdBytes, 0, brokerIdBytes.length);

            Keccak.Digest256 tokenHash = new Keccak.Digest256();
            byte[] usdcBytes = tokenId.getBytes("UTF-8");
            tokenHash.update(usdcBytes, 0, usdcBytes.length);

            Vault.VaultDepositFE depositInput = new Vault.VaultDepositFE(
                        Hex.decodeHex(orderlyAccountId.substring(2)),
                        brokerHash.digest(),
                        tokenHash.digest(),
                        depositAmount);

            // get wei deposit fee for `deposit` call
            BigInteger depositFee = vault.getDepositFee(credentials.getAddress(), depositInput).send();

            // deposit USDC into Vault contract
            vault.deposit(
                        depositInput,
                        depositFee).send();
      }

}

Withdrawal

[!WARNING] PnL Settlement Requirement: Any realized profit from open or recently closed positions must be settled via the Settle PnL flow before it can be added to your withdrawable_balance. If you attempt to withdraw without settling your PnL, the withdrawal amount may exceed your available balance and fail. See Settle PnL for details.
To withdraw tokens from Orderly:
1

Choose a valid chain to withdraw your funds to

A list of supported chains can be found on our Smart Contract Addresses page.
2

Check if the chain has sufficient liquidity

Since Orderly is an omnichain trading infrastructure, the liquidity of withdrawn assets might need to move across chains. If there is not enough liquidity of the withdrawn asset on the target chain, then an additional cross-chain transaction fee will be deducted from the user’s account.
3

Obtain a withdrawal nonce

Get a nonce from Get Withdrawal Nonce API.
4

Obtain a signature from EIP-712

Sign an EIP-712 message of message type Withdraw:
"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"},
]
Example:
{
  "brokerId": "woofi_pro",
  "chainId": 421614,
  "receiver": "0x036Cb579025d3535a0ADcD929D05481a3189714b",
  "token": "USDC",
  "amount": 1000000,
  "withdrawNonce": 1,
  "timestamp": 1685973017064
}
where:
NameTypeRequiredDescription
brokerIdstringYThe unique identifier of the Builder (also known as Broker ID). A list of valid IDs can be retrieved via the Get list of builders API.
chainIdintYChain ID of the chain that the funds should be withdrawn to (within those that are supported by the Network)
receiverstringYAddress of the receiver, which should be equal to the address of the account.
tokenstringYThe string representation of the token that is being withdrawn (eg “USDC”)
amountstringYAmount of tokens to be withdrawn
withdrawNonceintYValid withdrawal nonce from Get Withdrawal Nonce API
timestamptimestampYcurrent timestamp in UNIX milliseconds
5

Make a withdraw request

Full Example

Unlike key registration which uses the off-chain domain, a withdrawal signature requires the on-chain domain targeting the Orderly Ledger contract. Furthermore, note the type formatting difference:
  • In the EIP-712 cryptography signature message, amount must be represented as a numeric value (uint256/BigInt).
  • In the final REST API JSON payload, the amount field must be serialized as a String.
import { ethers } from "ethers";

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" }
  ]
};

// Target Ledger Contract Address (Arbitrum Sepolia L2 Ledger shown below)
// Mainnet: 0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203
// Testnet: 0x1826B75e2ef249173FC735149AE4B8e9ea10abff
const ON_CHAIN_DOMAIN = {
  name: "Orderly",
  version: "1",
  chainId: 421614,
  verifyingContract: "0x1826B75e2ef249173FC735149AE4B8e9ea10abff"
};

const BASE_URL = "https://testnet-api.orderly.org";

async function requestWithdrawal() {
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!);

  // 1. Fetch withdrawal nonce from private API
  const timestamp = Date.now();
  const signatureKey = await wallet
    .signTypedData
    // ... Assume signature for API auth is already created
    ();
  // (Assuming you obtained withdrawNonce = 1 and orderlyAccountId)
  const withdrawNonce = 1;
  const orderlyAccountId = "0x...";

  // 2. Prepare EIP-712 message (amount as BigInt)
  const withdrawMessage = {
    brokerId: "woofi_dex",
    chainId: 421614,
    receiver: wallet.address,
    token: "USDC",
    amount: 1000000n, // 1 USDC (6 decimals)
    withdrawNonce,
    timestamp
  };

  // 3. Sign EIP-712 typed data
  const signature = await wallet.signTypedData(
    ON_CHAIN_DOMAIN,
    { Withdraw: types.Withdraw },
    withdrawMessage
  );

  // 4. Send request to REST API (amount as String)
  const response = await fetch(`${BASE_URL}/v1/withdraw_request`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "orderly-timestamp": String(timestamp),
      "orderly-account-id": orderlyAccountId,
      "orderly-key": "...",
      "orderly-signature": "..." // Ed25519 API signature
    },
    body: JSON.stringify({
      message: {
        brokerId: withdrawMessage.brokerId,
        chainId: withdrawMessage.chainId,
        receiver: withdrawMessage.receiver,
        token: withdrawMessage.token,
        amount: withdrawMessage.amount.toString(), // MUST be String
        withdrawNonce: withdrawMessage.withdrawNonce,
        timestamp: withdrawMessage.timestamp
      },
      signature,
      userAddress: wallet.address
    })
  });

  const result = await response.json();
  console.log(result);
}