Skip to main content

Swap Integration Guide

This guide covers integrating the Zet Swap API for same-chain and cross-chain token swaps.

Overview

Your App                    Zet API                   LiFi (Router)
   │                           │                          │
   ├── GET /swap/tokens ──────►│                          │
   │◄── Token list ────────────┤                          │
   │                           │                          │
   ├── POST /swap/quote ──────►│── getRoutes() ──────────►│
   │◄── Quote + route info ────┤◄── Best route ───────────┤
   │                           │                          │
   ├── POST /swap/execute ────►│                          │
   │                           │── ERC-20 approve ───► Chain
   │                           │── Execute swap ─────► Chain
   │                           │── Poll status ──────────►│
   │                           │                          │
   │◄── Webhook: swap.completed ◄─────────────────────────┤
   │                           │                          │

Step 1: Get available tokens

Fetch the list of supported tokens, optionally filtered by chain:
const tokens = await fetch('https://api.zet.money/v1/swap/tokens?chain=base', {
  headers: { 'x-api-key': process.env.ZET_API_KEY },
}).then(r => r.json());

// tokens.data = [
//   { symbol: "ETH", name: "Ethereum", decimals: 18, chain: "base", ... },
//   { symbol: "USDC", name: "USD Coin", decimals: 6, chain: "base", ... },
//   { symbol: "CNGN", name: "cNGN", decimals: 6, chain: "base", ... },
//   { symbol: "cbBTC", name: "Coinbase Wrapped BTC", decimals: 8, chain: "base", ... },
// ]

Step 2: Get a swap quote

Same-chain swap

Swap USDC to ETH on Base:
const quote = await fetch('https://api.zet.money/v1/swap/quote', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.ZET_API_KEY,
  },
  body: JSON.stringify({
    fromToken: 'USDC',
    toToken: 'ETH',
    amount: '100',
    fromChain: 'base',
    toChain: 'base',      // Same chain
    slippage: 0.5,         // 0.5% slippage tolerance
  }),
}).then(r => r.json());

Cross-chain swap

Swap USDC on Base to BNB on BSC:
const quote = await fetch('https://api.zet.money/v1/swap/quote', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.ZET_API_KEY,
  },
  body: JSON.stringify({
    fromToken: 'USDC',
    toToken: 'BNB',
    amount: '100',
    fromChain: 'base',
    toChain: 'bsc',       // Cross-chain
  }),
}).then(r => r.json());

Quote response

{
  "success": true,
  "data": {
    "quoteId": "qt_01H8X5abc",
    "fromToken": "USDC",
    "toToken": "ETH",
    "fromChain": "base",
    "toChain": "base",
    "fromAmount": "100",
    "toAmount": "0.0285",
    "rate": "3508.77",
    "isCrossChain": false,
    "fees": {
      "platformFee": "0.013",
      "platformFeeFiat": "20",
      "bridgeFee": "0",
      "totalFee": "0.013"
    },
    "route": {
      "steps": 1,
      "estimatedTime": "30 seconds",
      "providers": ["Uniswap V3"]
    },
    "expiresAt": "2026-03-02T10:05:00Z"
  }
}

Fee structure

Swap TypePlatform FeeBridge Fee
Same-chain20 NGN (in source token)None
Cross-chain50 NGN (in source token)Variable (from bridge)
The platform fee is converted from NGN to the source token at market rate and deducted from the swap.

Step 3: Execute the swap

Custodial (Zet-managed wallet)

const swap = await fetch('https://api.zet.money/v1/swap/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: `swap_${swapId}`,
  }),
}).then(r => r.json());

// For custodial, execution starts immediately
// transactionHash is available right away
console.log(swap.data.transactionHash); // "0xabc..."

Non-custodial (user-managed wallet)

For non-custodial wallets, Zet returns the raw transaction data for the user to sign:
const swap = await fetch('https://api.zet.money/v1/swap/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: `swap_${swapId}`,
  }),
}).then(r => r.json());

// Step 1: User signs ERC-20 approval (if needed)
if (swap.data.approvalData) {
  const approvalTx = await userWallet.sendTransaction({
    to: swap.data.approvalData.to,
    data: swap.data.approvalData.data,
    value: swap.data.approvalData.value,
  });
  await approvalTx.wait();
}

// Step 2: User signs the swap transaction
const swapTx = await userWallet.sendTransaction({
  to: swap.data.transactionData.to,
  data: swap.data.transactionData.data,
  value: swap.data.transactionData.value,
  gasLimit: swap.data.transactionData.gasLimit,
});
await swapTx.wait();

Execute response

{
  "success": true,
  "data": {
    "transactionId": "txn_01H8X5def",
    "reference": "swap_001",
    "status": "pending",
    "transactionHash": "0xabc123...",
    "approvalData": null,
    "transactionData": null
  }
}

Step 4: Monitor completion

app.post('/webhooks/zet', (req, res) => {
  const { event, data } = req.body;

  if (event === 'swap.completed') {
    console.log(`Swap complete: ${data.amount} received`);
    console.log(`TX: ${data.transactionHash}`);
  }

  if (event === 'swap.failed') {
    console.log(`Swap failed: ${data.errorMessage}`);
    // Funds remain in the source wallet on failure
  }

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

Timing expectations

Swap TypeEstimated Time
Same-chain (e.g., USDC → ETH on Base)15-45 seconds
Cross-chain (e.g., USDC Base → BNB BSC)1-5 minutes

Slippage

The default slippage tolerance is 0.3%. You can customize it per quote:
{
  "fromToken": "USDC",
  "toToken": "ETH",
  "amount": "100",
  "slippage": 1.0
}
  • Low slippage (0.1-0.3%): Tighter price protection, may fail in volatile markets
  • Medium slippage (0.5-1.0%): Good balance of protection and reliability
  • High slippage (1-3%): Almost always succeeds, but may get a worse price

Routing

Zet uses LiFi to find the optimal swap route:
  • Same-chain: Routes through the best DEX (Uniswap, SushiSwap, Aerodrome, PancakeSwap, etc.)
  • Cross-chain: Finds the best bridge + DEX combination (Stargate, Across, Hop, etc.)
The route.providers field in the quote tells you which protocols will be used.

Best practices

  1. Show the route details — display estimated time and providers to set user expectations
  2. Default to 0.3% slippage — increase only if swaps are frequently failing
  3. Use same-chain swaps when possible — they’re faster, cheaper, and more reliable
  4. Handle partial fills — in rare cases, the received amount may differ slightly from the quote
  5. Retry on NO_ROUTE_FOUND — liquidity changes; a route may become available with a different amount