Skip to main content

PAXG Swap Integration

This guide walks through integrating the PAXG swap REST API into your application.
Currently only available via REST API.

Prerequisites

  • A Paxos Labs API key
  • An Ethereum mainnet RPC endpoint
  • A wallet library (viem, ethers, wagmi, etc.)

Supported Token Pairs

PAXG must be either the offerAsset (sell) or wantAsset (buy) in every swap request. Supported stablecoins for the other side of the trade:
TokenAddress
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
USDT0xdAC17F958D2ee523a2206206994597C13D831ec7
PYUSD0x6c3ea9036406852006290770BEdFcAbA0e23A0e8
USDG0xe343167631d89B6Ffc58B88d6b7fB0228795491D

Getting a Swap Quote

Request a quote by calling the /v1/paxg/swapQuotes endpoint:
const response = await fetch(
  'https://api.paxoslabs.com/v1/paxg/swapQuotes?' + new URLSearchParams({
    chainId: '1',
    offerAsset: '0x45804880De22913dAFE09f4980848ECE6EcbAf78', // PAXG
    wantAsset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',  // USDC
    offerAmount: '1000000000000000000', // 1 PAXG in wei
    userAddress: '0x...', // User's wallet address
  }),
  {
    headers: {
      'x-api-key': 'pxl_<public_id>_<secret>',
    },
  }
);

const quote = await response.json();

Response Structure

The quote response includes:
interface SwapQuoteResponse {
  transaction: {
    to: string;      // Contract to send the transaction to
    data: string;    // Encoded swap calldata
    value: string;   // ETH value (usually "0" for token swaps)
    gas: string;     // Estimated gas limit
    gasPrice: string; // Suggested gas price
  };
  buyAmount: string;        // Expected output amount
  minBuyAmount: string;     // Minimum output (with slippage)
  allowanceTarget: string;  // Address to approve tokens to
  currentAllowance?: string; // User's current allowance (if any)
}

Handling Token Approvals

Before executing the swap, ensure the user has approved sufficient tokens:
import { erc20Abi } from 'viem';

// Check if approval is needed
const needsApproval = BigInt(quote.currentAllowance ?? '0') < BigInt(offerAmount);

if (needsApproval) {
  // Request approval
  const approveTx = await walletClient.writeContract({
    address: offerAsset,
    abi: erc20Abi,
    functionName: 'approve',
    args: [quote.allowanceTarget, offerAmount],
  });

  // Wait for approval confirmation
  await publicClient.waitForTransactionReceipt({ hash: approveTx });
}

Executing the Swap

Once approved, execute the swap using the provided calldata:
const swapTx = await walletClient.sendTransaction({
  to: quote.transaction.to,
  data: quote.transaction.data,
  value: BigInt(quote.transaction.value),
  gas: BigInt(quote.transaction.gas),
});

const receipt = await publicClient.waitForTransactionReceipt({ hash: swapTx });

Using Permit2 Authorization

For gasless approvals, use the permit2 auth method:
const response = await fetch(
  'https://api.paxoslabs.com/v1/paxg/swapQuotes?' + new URLSearchParams({
    chainId: '1',
    offerAsset: '0x45804880De22913dAFE09f4980848ECE6EcbAf78',
    wantAsset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
    offerAmount: '1000000000000000000',
    userAddress: '0x...',
    authMethod: 'permit2', // Use Permit2
  }),
  {
    headers: {
      'x-api-key': 'pxl_<public_id>_<secret>',
    },
  }
);

const quote = await response.json();

// The response includes EIP-712 typed data for signing
if (quote.permit2) {
  const signature = await walletClient.signTypedData({
    domain: quote.permit2.eip712.domain,
    types: quote.permit2.eip712.types,
    primaryType: quote.permit2.eip712.primaryType,
    message: quote.permit2.eip712.message,
  });

  // Include signature when executing the swap
  // (implementation depends on your setup)
}

Error Handling

The API returns standard HTTP error codes:
StatusDescription
400Invalid parameters or insufficient liquidity
503DEX aggregator temporarily unavailable
Example error response:
{
  "error": {
    "code": 400,
    "message": "No liquidity available for this swap. Try a smaller amount or different token pair.",
    "status": "INVALID_ARGUMENT"
  }
}
Always validate the minBuyAmount before executing to protect against slippage. Consider showing users the expected output and allowing them to set slippage tolerance.