KalqixGuideKalqiX API

Withdrawals

Follow this guide to complete your withdrawal process.

Overview

Withdrawal is a three-step process that requires authentication and on-chain verification.

Step 1: Initiate Withdrawal

Provide details about which asset you want to withdraw.

Endpoint: POST /v1/withdrawals

Required Headers:

  • x-api-key: Your API key

  • x-api-signature: HMAC signature (see Quick Start Guide)

  • x-api-timestamp: Current timestamp in milliseconds

  • Content-Type: application/json

Request Body:

{
  "asset": "USDC",
  "amount": "100",
  "chain_id": 1,
  "signature": "0x...",
  "timestamp": 1767225600000
}

Field Descriptions:

  • asset (string): Asset symbol (e.g., "USDC", "BTC")

  • amount (string): Amount in formatted decimals

  • chain_id (number): Destination chain ID (1 = Ethereum mainnet, etc.)

  • signature (string): Wallet signature of the withdrawal payload

  • timestamp (number): Current time as Unix timestamp in milliseconds

Message Signing:

Sign a canonicalized JSON payload with sorted keys:

const payload = {
  action: 'WITHDRAW',
  asset: 'USDC',
  amount: '100',
  chain_id: 1,
  timestamp: Date.now()
};

// Sort keys and create canonical JSON
const sortedKeys = Object.keys(payload).sort();
const canonicalPayload = {};
sortedKeys.forEach(key => {
  canonicalPayload[key] = payload[key];
});
const message = JSON.stringify(canonicalPayload);
const signature = await wallet.signMessage(message);

Important:

  • Timestamp must be within 5 minutes of server time

  • All keys must be sorted alphabetically

  • Include explicit action: 'WITHDRAW' in signed payload

Success Response (200):

{
  "_id": "unique-withdrawal-request-id"
}

Step 2: Get Proof

Using the withdrawal ID from Step 1, fetch the withdrawal Merkle Proof. Proofs are submitted on-chain for verification.

Important: Proofs usually take a couple of seconds to generate. Poll this endpoint until proof_ready returns true. We recommend polling every 2-3 seconds.

Endpoint: GET /v1/withdrawals/{id}/claim

Required Headers:

  • x-api-key: Your API key

  • x-api-signature: HMAC signature

  • x-api-timestamp: Current timestamp in milliseconds

Path Parameters:

  • id (string): The withdrawal request ID from Step 1

Response:

{
  "proof_ready": true,
  "amount": "100000000",
  "amount_formatted": "100.0",
  "local_exit_proof": "",
  "global_exit_proof": "",
  "global_claim_index": "42",
  "token_address": "",
  "chain_id": 1,
  "destination_network_id": 2,
  "destination_address": "0x...",
  "bridge_contract_address": "0x..."
}

Field Descriptions:

  • proof_ready (boolean): Whether the proof is ready for claiming

  • amount (string): Amount in base units (wei)

  • amount_formatted (string): Human-readable amount with decimals

  • local_exit_proof (string): Local Merkle proof

  • global_exit_proof (string): Global Merkle proof

  • global_claim_index (string): Index in the global Merkle tree

  • token_address (string): Smart contract address of the token

  • chain_id (number): Blockchain chain ID

  • destination_network_id (number): Destination network ID on Kalqix tree

  • destination_address (string): Your wallet address on destination chain

  • bridge_contract_address (string): Bridge contract address on destination chain

Step 3: Claim Withdrawal

Once your proof is ready, submit it to the on-chain smart contract to complete the withdrawal.

Call the destination network's bridge contract with the proof payload using web3 or ethers.js:

// Submit the claim transaction
const tx = await contract['claimAsset'](
  local_exit_proof,
  global_exit_proof,
  global_claim_index,
  token_address,
  destination_network_id,
  destination_address,
  amount
);

// Wait for confirmation
const receipt = await tx.wait();
console.log('Withdrawal claimed! Transaction hash:', receipt.hash);

Complete Workflow Summary

  1. Initiate → POST /v1/withdrawals → Get _id

  2. Poll → GET /v1/withdrawals/{id}/claim → Wait for proof_ready: true

  3. Claim → Call bridge contract's claimAsset() → Receive funds on destination chain