Skip to main content

Cross-Chain Transfer Guide

This guide covers integrating the Zet Cross-Chain Transfer API to move tokens between different blockchains.

Overview

Cross-chain transfers use the same underlying infrastructure as cross-chain swaps (LiFi aggregation) but are designed specifically for moving tokens from one chain to another, optionally converting to a different token on the destination chain.
Your App                    Zet API                   Bridge Protocol
   │                           │                          │
   ├── GET /transfer/chains ──►│                          │
   │◄── Chain + token list ────┤                          │
   │                           │                          │
   ├── POST /transfer/quote ──►│── Find route ───────────►│
   │◄── Quote + bridge info ───┤◄── Best bridge ──────────┤
   │                           │                          │
   ├── POST /transfer/execute ►│                          │
   │                           │── Approve + bridge ──► Source Chain
   │                           │                    Bridge relay ──► Dest Chain
   │                           │── Poll status ──────────►│
   │                           │                          │
   │◄── Webhook: transfer.completed ◄─────────────────────┤
   │                           │                          │

Supported chains

Source chains (send from)

ChainChain IDTokens
Base8453ETH, USDC, CNGN, cbBTC
BSC56BNB, USDC, USDT, CNGN

Destination chains (send to)

ChainChain ID
Base8453
BSC56
Ethereum1
Polygon137
Arbitrum42161
Avalanche43114
Solana1151111081099710
You can also query supported chains dynamically via GET /transfer/chains.

Step 1: Get a transfer quote

Transfer 100 USDC from Base to Ethereum:
const quote = await fetch('https://api.zet.money/v1/transfer/quote', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.ZET_API_KEY,
  },
  body: JSON.stringify({
    fromChain: 'base',
    toChain: 'ethereum',
    token: 'USDC',
    toToken: 'USDC',              // Same token on destination
    amount: '100',
    destinationAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f5bA16',
  }),
}).then(r => r.json());

Bridge + swap

Transfer USDC from Base and receive ETH on Ethereum:
const quote = await fetch('https://api.zet.money/v1/transfer/quote', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.ZET_API_KEY,
  },
  body: JSON.stringify({
    fromChain: 'base',
    toChain: 'ethereum',
    token: 'USDC',
    toToken: 'ETH',              // Different token on destination
    amount: '100',
    destinationAddress: '0x742d35Cc...',
  }),
}).then(r => r.json());

Quote response

{
  "success": true,
  "data": {
    "quoteId": "qt_01H8X6abc",
    "fromChain": "base",
    "toChain": "ethereum",
    "fromToken": "USDC",
    "toToken": "USDC",
    "fromAmount": "100",
    "toAmount": "99.85",
    "fees": {
      "platformFee": "0.032",
      "platformFeeFiat": "50",
      "bridgeFee": "0.15",
      "totalFee": "0.182"
    },
    "route": {
      "steps": 2,
      "estimatedTime": "2-5 minutes",
      "bridge": "Stargate"
    },
    "expiresAt": "2026-03-02T10:05:00Z"
  }
}

Fee breakdown

FeeAmountDescription
Platform fee50 NGN (in source token)Zet cross-chain fee
Bridge feeVariableSet by the bridge protocol (Stargate, Across, etc.)

Step 2: Execute the transfer

Custodial

const transfer = await fetch('https://api.zet.money/v1/transfer/execute', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.ZET_API_KEY,
  },
  body: JSON.stringify({
    quoteId: quote.data.quoteId,
    walletId: 'wal_01H8X3abc',
    reference: `transfer_${transferId}`,
  }),
}).then(r => r.json());

Non-custodial

const transfer = await fetch('https://api.zet.money/v1/transfer/execute', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.ZET_API_KEY,
  },
  body: JSON.stringify({
    quoteId: quote.data.quoteId,
    sourceAddress: '0x742d35Cc...',
    reference: `transfer_${transferId}`,
  }),
}).then(r => r.json());

// User signs approval + bridge transaction
if (transfer.data.approvalData) {
  await userWallet.sendTransaction(transfer.data.approvalData);
}
await userWallet.sendTransaction(transfer.data.transactionData);

Step 3: Monitor completion

Cross-chain transfers take longer than same-chain operations because they involve bridge relay confirmation on the destination chain.
BridgeEstimated Time
Stargate1-5 minutes
Across2-10 minutes
Hop5-20 minutes
Listen for the transfer.completed webhook:
app.post('/webhooks/zet', (req, res) => {
  const { event, data } = req.body;

  if (event === 'transfer.completed') {
    console.log(`Transfer complete on ${data.chain}`);
    console.log(`TX: ${data.transactionHash}`);
  }

  if (event === 'transfer.failed') {
    // Funds typically remain on the source chain
    console.log(`Transfer failed: ${data.errorMessage}`);
  }

  res.status(200).send('OK');
});

Common patterns

Portfolio rebalancing

Move tokens between chains to rebalance a user’s portfolio:
// Move 50 USDC from Base to BSC
const quote = await getTransferQuote({
  fromChain: 'base',
  toChain: 'bsc',
  token: 'USDC',
  toToken: 'USDC',
  amount: '50',
  destinationAddress: walletAddress, // Same address, different chain
});

Multi-chain withdrawals

Let users withdraw to any supported chain:
// User wants to withdraw 100 USDC to Polygon
const quote = await getTransferQuote({
  fromChain: 'base',
  toChain: 'polygon',
  token: 'USDC',
  toToken: 'USDC',
  amount: '100',
  destinationAddress: userPolygonAddress,
});

Best practices

  1. Show estimated time — cross-chain transfers are slower than same-chain; manage user expectations
  2. Display the bridge protocol — users benefit from knowing which bridge is being used
  3. Handle stuck transactions — bridges can occasionally slow down; implement timeout alerts
  4. Prefer same-chain when possible — if both source and destination are on the same chain, use the Swap API instead
  5. Verify destination address format — ensure the address is valid for the destination chain (especially for Solana)
  6. Show fee breakdown — bridge fees vary significantly; transparency builds trust