利用 0x Swap API 通过智能订单路由实现高效代币交换

本文档介绍了如何通过 Quicknode 集成 0x Swap API,实现高效的代币交换。通过智能订单路由,可以从超过150个流动性来源聚合流动性,从而为代币交易找到最佳价格。文章详细说明了如何查询 API 获取价格,管理代币授权,生成签名交易报价,并使用 Viem 执行交换,同时提供了完整的代码示例和逐步指导。

概述

流动性碎片化是 DeFi 中最大的挑战之一。对于开发者来说,找到代币交易的最佳价格通常意味着需要单独查询多个去中心化交易所 (DEX),这既低效又难以维护。

通过集成 0x Swap API 通过 Quicknode,你可以通过访问智能订单路由来解决这个问题,该路由聚合了多个支持链上超过 150 个来源的流动性。在本指南中,你将学习如何将这种强大的路由逻辑直接集成到你的交易机器人或 dApp 中,使你能够获取指示性价格、管理代币授权并以尽可能最佳的价格执行交易。

我们将使用 Base Mainnet 上的代币互换来演示此集成,但相同的概念适用于所有受支持的链。

你将做什么

  • 查询 0x Swap API 以获取代币对的指示性定价
  • 分析 API 响应以处理代币津贴和余额检查
  • 生成签名交易报价
  • 使用 Viem 执行交换

你将需要什么

  • 启用了 0x Swap API 插件的 Quicknode 端点
  • 已安装 Node.js (建议 v20+)
  • 已安装 tsx
  • 代码编辑器(例如,VS Code)。
  • 用于测试交易的已充值钱包(以及其私钥)(确保你拥有用于 Gas 的本地 Gas 代币和你希望出售的代币)。

为什么使用 0x Swap API?

在编写代码之前,了解为什么许多 DeFi 团队依赖 0x Swap API 会很有帮助。它提供的不仅仅是价格发现。它提供了一组以开发者为中心的功能,可以简化跨多个链的报价、模拟、路由和执行。

  • 多链支持:该 API 支持主要的 EVM 链,包括 Ethereum、Base、Polygon、BNB Smart Chain、Avalanche、Arbitrum、Optimism 等。你的集成在所有链上都以相同的方式工作,这有助于减少代码库中的碎片化。
  • 广泛的流动性覆盖:路由引擎聚合了来自 150 多个来源的流动性。这包括 Uniswap、Curve、Balancer、Aerodrome、PancakeSwap、Trader Joe 等去中心化交易所,以及专业的做市商和 RFQ 提供商。
  • 内置“起飞前”检查 (issues):API 不会在链上导致你的交易失败并浪费 Gas,而是运行模拟以预先在响应的 issues 字段中警告你有关阻止程序的信息。主要检查包括:
    • issues.balance:返回用户出售代币的实际和预期余额。
    • issues.allowance:返回指定消费者的用户代币的实际授权。
  • 智能订单路由:API 不仅仅为你提供价格;它为你提供 route。你可以看到你的交易是如何拆分的,以最大限度地减少滑点。
  • 准备好盈利:如果你正在构建钱包或仪表板,你可以轻松添加自己的费用。该 API 支持收取 联盟费用和交易盈余
  • Gas 优化:路由逻辑在寻找最佳路径时会考虑 Gas 成本。响应提供精确的 gasgasPrice 估算,确保你的交易很可能快速执行而不会Overpaying。

支持的方法

Quicknode 插件在你的端点下公开 0x Swap API,你将在本指南中使用的两个核心方法是:

GET /swap/allowance-holder/price

使用它来获取交易的指示性价格。你传递诸如 chainIdsellTokenbuyTokensellAmount 等参数,API 将返回:

  • 预期输出金额 (buyAmount) 和考虑滑点的最小输出 (minBuyAmount)
  • 路线详细信息和 Gas 预估
  • 可能阻止执行的 issues
  • 费用细分(协议、联盟、Gas 等)

此端点非常适合构建报价预览、在用户键入金额时更新 UI 或运行“假设”检查,而无需提交交易。

GET /swap/allowance-holder/quote

当你准备好执行交换时使用此选项。它返回一个确定的报价,以及 transaction 下的完全构建的交易负载(包括 todatavalue 和 Gas 参数)。你可以将该有效负载直接传递到你的钱包客户端或签名流程中,以将交易发送到链上。

总之,这两种方法使你可以将用户体验分为具有 /price预览步骤和具有 /quote执行步骤,同时保持你的集成在链上简单且一致。

项目设置

步骤 1:初始化项目

首先,你需要设置一个简单的 Node.js 环境并安装必要的依赖项。我们将使用 viem,这是一种轻量级且类型安全的 Ethereum 接口。

  1. 使用你首选的包管理器启动一个新的 Node.js 项目
mkdir quicknode-0x-swap-api
cd quicknode-0x-swap-api
npm init -y
  1. 安装所需的软件包:
npm install viem dotenv
npm install --save-dev @types/node

步骤 2:配置环境变量

  1. 在你的根目录中创建一个 .env 文件以存储你的凭据。
touch .env
  1. 将你的 Quicknode 端点 URL(插件的特定路径扩展)和私钥添加到 .env 文件。

注意:0x Swap API 通过 Quicknode HTTP 端点 URL 上的特定路径扩展 (addon/1117) 访问。

## .env
## QuickNode endpoint URL (不带插件路径)
QUICKNODE_HTTP_URL=https://your-base-endpoint.quiknode.pro/your-key

## 0x Swap API 插件路径
ADD_ON_PATH=addon/1117

## 你的钱包私钥(请保密!)
PRIVATE_KEY=0x...your-private-key-here

将0x Swap API与Viem结合使用

步骤 1:核心导入和设置

在你的项目根目录中创建一个名为 swap.ts 的新文件。

touch swap.ts

然后,使用必要的导入和配置启动你的 swap.ts 文件。

本节导入用于区块链交互的 viem 库函数,设置环境变量,并创建读取区块链状态和发送交易所需的钱包和区块链客户端连接。

import {
  createWalletClient,
  createPublicClient,
  http,
  parseUnits,
  formatUnits,
  maxUint256,
  erc20Abi,
  Address,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import dotenv from "dotenv";

// Load environment variables
dotenv.config();

// ============================================
// 1. CONFIGURATION & SETUP
// ============================================

// Environment variables validation
const QUICKNODE_HTTP_URL = process.env.QUICKNODE_HTTP_URL;
const ADD_ON_PATH = process.env.ADD_ON_PATH;
const PRIVATE_KEY = process.env.PRIVATE_KEY as Address;

if (!QUICKNODE_HTTP_URL || !ADD_ON_PATH || !PRIVATE_KEY) {
  throw new Error(
    "Missing required environment variables: QUICKNODE_HTTP_URL, ADD_ON_PATH, and PRIVATE_KEY"
  );
}

// Base RPC URL for standard Ethereum RPC calls
const BASE_RPC_URL = QUICKNODE_HTTP_URL;

// Full URL with addon path for 0x Swap API calls
const SWAP_API_URL = `${QUICKNODE_HTTP_URL}/${ADD_ON_PATH}`;

// Token addresses for Base
const TOKENS = {
  NATIVE: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // Native ETH on Base
  WETH: "0x4200000000000000000000000000000000000006", // Wrapped ETH on Base
  USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
  USDe: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34", // USDe on Base
  WBTC: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c", // WBTC on Base
} as const;

// Chain configuration
const CHAIN_ID = "8453"; // Base chain ID

// Initialize account from private key
const account = privateKeyToAccount(PRIVATE_KEY);

// Initialize Viem clients
const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(BASE_RPC_URL),
});

const publicClient = createPublicClient({
  chain: base,
  transport: http(BASE_RPC_URL),
});

步骤 2:类型定义

为 API 响应定义 TypeScript 接口以确保类型安全。

这些类型定义为 API 响应提供了结构,有助于在编译时捕获错误并提供更好的 IDE 自动完成支持。它们定义了我们将从 0x API 接收的数据的确切形状。查看 0x Swap API 文档 以了解更多详细信息。

// ============================================
// 2. TYPE DEFINITIONS
// ============================================

interface SwapPriceResponse {
  allowanceTarget: Address;
  buyAmount: string;
  buyToken: Address;
  sellAmount: string;
  sellToken: Address;
  gas: string;
  gasPrice: string;
  issues: {
    allowance?: {
      actual: string;
      spender: Address;
    };
    balance?: {
      token: Address;
      actual: string;
      expected: string;
    };
    simulationIncomplete?: boolean;
  };
  liquidityAvailable: boolean;
  route: any;
  fees: any;
  minBuyAmount?: string;
}

interface SwapQuoteResponse extends SwapPriceResponse {
  transaction: {
    to: Address;
    data: string;
    gas: string;
    gasPrice: string;
    value: string;
  };
}

步骤 3:价格获取

实现从 0x API 获取指示性价格的函数。

此函数查询 0x API 以获取当前交换价格,而无需创建实际订单。它用于向用户显示预期的交换结果并在提交交易之前检查潜在问题。

此步骤对于验证用户是否有足够的余额、代币批准是否足够以及市场是否有足够的流动性至关重要。

了解 API 响应

API 响应包括多个重要字段,以帮助你有效地管理交换过程:

  • issues:立即检查此对象。如果 issues.balance.actual 低于你的销售金额,你可以提示用户在尝试交换之前充值。

  • liquidityAvailable:一个布尔值,确认你的交易是否有足够的市场深度。

  • route:一个显示交易如何拆分的数组。你可以使用它在你的 UI 中显示“智能路线”可视化。

// ============================================
// 3. PRICE FETCHING
// ============================================

/**
 * 从 0x API 获取指示性价格
 * 这是一个只读端点,用于检查价格而不提交
 */
async function getPrice(
  sellToken: Address,
  buyToken: Address,
  sellAmount: string,
  decimals: number = 18,
  slippageBps: number = 100
): Promise<SwapPriceResponse> {
  const sellAmountWei = parseUnits(sellAmount, decimals).toString();

  const params = new URLSearchParams({
    chainId: CHAIN_ID,
    sellToken,
    buyToken,
    sellAmount: sellAmountWei,
    taker: account.address,
    slippageBps: slippageBps.toString(),
  });

  const headers: HeadersInit = {
    "Content-Type": "application/json",
  };

  const url = `${SWAP_API_URL}/swap/allowance-holder/price?${params.toString()}`;
  console.log(`   Fetching price from: ${url}`);

  const response = await fetch(url, { headers });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Price fetch failed: ${error}`);
  }

  return response.json();
}

步骤 4:余额和批准检查

将这些辅助函数添加到你的脚本中,以标准化你检查余额和授权的方式,自动处理原生 ETH(需要 getBalance)和 ERC20 代币(需要 balanceOfallowance)之间的逻辑差异。

虽然 0x Swap API 已经在 issues 字段中包含起飞前检查,但我们可以使用这些函数来显示余额和检查授权。

// ============================================
// 4. BALANCE CHECKING
// ============================================

/**
 * 检查代币是否为原生 ETH 代币
 */
function isNativeToken(tokenAddress: Address): boolean {
  return tokenAddress.toLowerCase() === TOKENS.NATIVE.toLowerCase();
}

/**
 * 检查特定地址的代币余额
 */
async function checkBalance(
  tokenAddress: Address,
  userAddress: Address
): Promise<bigint> {
  // 对于原生 ETH,使用 getBalance 代替 ERC20 balanceOf
  if (isNativeToken(tokenAddress)) {
    const balance = await publicClient.getBalance({
      address: userAddress,
    });
    return balance;
  }

  const balance = await publicClient.readContract({
    address: tokenAddress as Address,
    abi: erc20Abi,
    functionName: "balanceOf",
    args: [userAddress as Address],
  });

  return balance;
}

/**
 * 获取代币小数位数
 */
async function getTokenDecimals(tokenAddress: Address): Promise<number> {
  // 原生 ETH 始终有 18 个小数位数
  if (isNativeToken(tokenAddress)) {
    return 18;
  }

  const decimals = await publicClient.readContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: "decimals",
  });

  return decimals;
}

/**
 * 检查 spender 的当前授权
 */
async function checkAllowance(
  tokenAddress: Address,
  owner: Address,
  spender: Address
): Promise<bigint> {
  const allowance = await publicClient.readContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: "allowance",
    args: [owner, spender],
  });

  return allowance;
}

步骤 5:问题检测

实现逻辑以检测和处理常见的交换问题。

此函数分析 API 响应中是否存在潜在问题,例如余额不足或缺少代币授权。它提供明确的错误消息,并在需要时自动触发批准过程,使交换过程更加顺畅。API 响应中的 issues 字段用于确定问题的类型以及要采取的相应操作。

// ============================================
// 5. ISSUE DETECTION & HANDLING
// ============================================

/**
 * 分析并处理价格响应中的问题
 */
async function handleIssues(priceData: SwapPriceResponse): Promise<void> {
  console.log("🔍 Analyzing potential issues...");

  // Check for balance issues
  if (priceData.issues.balance) {
    const { token, actual, expected } = priceData.issues.balance;

    // Skip balance check for native ETH as it's handled differently
    if (!isNativeToken(token)) {
      const decimals = await getTokenDecimals(token);
      const actualFormatted = formatUnits(BigInt(actual), decimals);
      const expectedFormatted = formatUnits(BigInt(expected), decimals);

      console.log(`\n⚠️  BALANCE ISSUE DETECTED:`);
      console.log(`   Token: ${token}`);
      console.log(`   Current balance: ${actualFormatted}`);
      console.log(`   Required: ${expectedFormatted}`);
      console.log(
        `   Shortfall: ${formatUnits(
          BigInt(expected) - BigInt(actual),
          decimals
        )}`
      );

      throw new Error(
        `Insufficient balance. Need ${expectedFormatted} but have ${actualFormatted}`
      );
    } else {
      // 对于原生 ETH,仍然进行检查,但使用更简单的格式
      const decimals = 18; // 原生 ETH 有 18 个小数位数
      const actualFormatted = formatUnits(BigInt(actual), decimals);
      const expectedFormatted = formatUnits(BigInt(expected), decimals);

      console.log(`\n⚠️  BALANCE ISSUE DETECTED:`);
      console.log(`   Token: Native ETH`);
      console.log(`   Current balance: ${actualFormatted} ETH`);
      console.log(`   Required: ${expectedFormatted} ETH`);
      console.log(
        `   Shortfall: ${formatUnits(
          BigInt(expected) - BigInt(actual),
          decimals
        )} ETH`
      );

      throw new Error(
        `Insufficient balance. Need ${expectedFormatted} ETH but have ${actualFormatted} ETH`
      );
    }
  }

  // 检查授权问题 - 跳过原生 ETH(不需要批准)
  if (priceData.issues.allowance && !isNativeToken(priceData.sellToken)) {
    const { spender, actual } = priceData.issues.allowance;
    const requiredAmount = BigInt(priceData.sellAmount);
    const currentAllowance = BigInt(actual);

    console.log(`\n⚠️  ALLOWANCE ISSUE DETECTED:`);
    console.log(`   Current allowance: ${currentAllowance.toString()}`);
    console.log(`   Required allowance: ${requiredAmount.toString()}`);
    console.log(`   Spender (AllowanceHolder): ${spender}`);

    // 检查当前授权是否足以进行此交换
    if (currentAllowance >= requiredAmount) {
      console.log(`   ✅ Current allowance is sufficient for this swap`);
    } else {
      console.log(`   Action: Setting approval for exact swap amount...`);
      await setTokenApprovalForAmount(
        priceData.sellToken,
        spender,
        requiredAmount
      );
      console.log(`✅ Token approval completed successfully`);
    }
  } else if (isNativeToken(priceData.sellToken)) {
    console.log("✅ Native ETH selected - no approval needed");
  } else {
    console.log("✅ No issues detected - ready to proceed");
  }

  // 检查模拟问题
  if (priceData.issues.simulationIncomplete) {
    console.log("⚠️  Warning: Simulation incomplete - transaction may fail");
  }
}

步骤 6:代币批准

使用确切金额实现安全的代币批准。

此函数处理交换之前所需的 ERC20 代币批准。它不使用无限批准(安全风险),而是仅批准每次交换所需的确切金额,首先检查现有授权(作为 API 响应后的第二层检查)以避免不必要的交易。

// ============================================
// 6. TOKEN APPROVAL
// ============================================

/**
 * 为交换所需的准确金额设置代币批准
 * 这比无限批准更安全
 */
async function setTokenApprovalForAmount(
  tokenAddress: Address,
  spender: Address,
  amount: bigint
): Promise<void> {
  try {
    // 检查当前授权
    const currentAllowance = await checkAllowance(
      tokenAddress,
      account.address,
      spender
    );

    console.log(`   Current allowance: ${currentAllowance.toString()}`);
    console.log(`   Required amount: ${amount.toString()}`);

    // 仅在当前授权不足时才批准
    if (currentAllowance >= amount) {
      console.log(
        `   ℹ️  Current allowance is already sufficient for this swap`
      );
      return;
    }

    // 计算需要多少额外批准
    // 我们将批准所需的准确金额
    const approvalAmount = amount;

    console.log(`   Approving exact amount: ${approvalAmount.toString()}`);

    // 模拟批准交易
    console.log(`   Simulating approval transaction...`);
    const { request } = await publicClient.simulateContract({
      account,
      address: tokenAddress,
      abi: erc20Abi,
      functionName: "approve",
      args: [spender, approvalAmount],
    });

    // 执行批准
    console.log(`   Sending approval transaction...`);
    const hash = await walletClient.writeContract(request);
    console.log(`   Approval tx hash: ${hash}`);

    // 等待确认
    console.log(`   Waiting for confirmation...`);
    const receipt = await publicClient.waitForTransactionReceipt({ hash });

    if (receipt.status !== "success") {
      throw new Error("Approval transaction failed");
    }

    console.log(`   Approval confirmed in block ${receipt.blockNumber}`);

    // 验证新授权
    const newAllowance = await checkAllowance(
      tokenAddress,
      account.address,
      spender
    );
    console.log(`   New allowance: ${newAllowance.toString()}`);
  } catch (error) {
    console.error("❌ Approval failed:", error);
    throw error;
  }
}

步骤 7:报价获取

0x API 获取确定的、可执行的报价。

price 端点不同,quote 端点返回一个完整的、可立即执行的交易。这表示交易意图,并且通常会获得更好的价格。它包括滑点保护,以确保最小输出金额。

了解 API 响应

响应包括一个 transaction 对象。这是你将传递给你的钱包或 web3 库的内容:

  • transaction.to:交易将与之交互的合约地址(通常是 0x Exchange Proxy 或 AllowanceHolder)。
  • transaction.data:包含交换逻辑的编码十六进制数据。
  • transaction.value:要发送的原生 ETH 金额(以 wei 为单位)。这对于 ERC-20 交换通常为 0,但如果你交换原生 ETH,则不为零。
// ============================================
// 7. QUOTE FETCHING
// ============================================

/**
 * 从 0x API 获取确定的报价
 * 这将返回一个可执行的交易
 */
async function getQuote(
  sellToken: Address,
  buyToken: Address,
  sellAmount: string,
  slippageBps: number,
  decimals: number = 18
): Promise<SwapQuoteResponse> {
  const sellAmountWei = parseUnits(sellAmount, decimals).toString();

  const params = new URLSearchParams({
    chainId: CHAIN_ID,
    sellToken,
    buyToken,
    sellAmount: sellAmountWei,
    taker: account.address,
    slippageBps: slippageBps.toString(),
  });

  const headers: HeadersInit = {
    "Content-Type": "application/json",
  };

  const url = `${SWAP_API_URL}/swap/allowance-holder/quote?${params.toString()}`;
  console.log(`   Requesting firm quote...`);

  const response = await fetch(url, { headers });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Quote fetch failed: ${error}`);
  }

  return response.json();
}

步骤 8:交易执行

将交换交易提交到区块链。

此函数从报价中获取交易数据并将其提交到区块链。它处理代币到代币的交换(值 = 0)和原生 ETH 交换(值 > 0),并记录重要详细信息以提高透明度。

// ============================================
// 8. TRANSACTION EXECUTION
// ============================================

/**
 * 将交换交易提交到区块链
 */
async function submitTransaction(
  transaction: SwapQuoteResponse["transaction"]
): Promise<Address> {
  console.log("📤 Submitting transaction to the blockchain...");
  console.log(`   To: ${transaction.to}`);
  console.log(`   Gas limit: ${transaction.gas}`);
  console.log(
    `   Gas price: ${formatUnits(BigInt(transaction.gasPrice), 9)} Gwei`
  );
  console.log(`   Value: ${transaction.value} wei`);

  const hash = await walletClient.sendTransaction({
    to: transaction.to,
    data: transaction.data as `0x${string}`,
    gas: BigInt(transaction.gas),
    gasPrice: BigInt(transaction.gasPrice),
    value: transaction.value ? BigInt(transaction.value) : 0n,
  });

  return hash;
}

步骤 9:主要编排

将所有步骤合并到一个完整的交换流程中。

这是编排整个交换过程的主要函数。它以正确的顺序协调所有先前的函数:获取价格、处理问题、获取报价以及执行交换,并在每个步骤中进行详细记录。

// ============================================
// 9. MAIN SWAP ORCHESTRATION
// ============================================

/**
 * 执行完整的代币交换
 */
async function executeSwap(
  sellToken: Address,
  buyToken: Address,
  sellAmount: string,
  slippageBps: number = 100
): Promise<any> {
  console.log("\n" + "=".repeat(60));
  console.log("🔄 STARTING TOKEN SWAP");
  console.log("=".repeat(60));

  const sellDecimals = await getTokenDecimals(sellToken);
  const buyDecimals = await getTokenDecimals(buyToken);

  console.log(`\n📊 Swap Parameters:`);
  console.log(
    `   Sell: ${sellAmount} tokens (${sellToken}...)`
  );
  console.log(`   Buy: ${buyToken}...`);
  console.log(
    `   Slippage: ${slippageBps / 100}% (${slippageBps} bps)`
  );
  console.log(`   User: ${account.address}`);

  try {
    // 步骤 1:获取价格
    console.log("\n" + "-".repeat(60));
    console.log("📈 STEP 1: FETCHING INDICATIVE PRICE");
    console.log("-".repeat(60));
    const priceData = await getPrice(
      sellToken,
      buyToken,
      sellAmount,
      sellDecimals,
      slippageBps
    );

    if (!priceData.liquidityAvailable) {
      throw new Error("❌ Insufficient liquidity for this trade");
    }

    const buyAmountFormatted = formatUnits(
      BigInt(priceData.buyAmount),
      buyDecimals
    );
    const minBuyAmountFormatted = priceData.minBuyAmount
      ? formatUnits(BigInt(priceData.minBuyAmount), buyDecimals)
      : "N/A";

    console.log(`✅ Price fetched successfully:`);
    console.log(`   Expected output: ${buyAmountFormatted}`);
    console.log(`   Minimum output: ${minBuyAmountFormatted}`);
    console.log(`   Estimated gas: ${priceData.gas} units`);

    // 步骤 2:处理问题
    console.log("\n" + "-".repeat(60));
    console.log("🔍 STEP 2: CHECKING FOR ISSUES");
    console.log("-".repeat(60));

    // 处理问题(包括批准)
    await handleIssues(priceData);

    // 步骤 3:获取报价
    console.log("\n" + "-".repeat(60));
    console.log("📋 STEP 3: FETCHING FIRM QUOTE");
    console.log("-".repeat(60));
    const quoteData = await getQuote(
      sellToken,
      buyToken,
      sellAmount,
      slippageBps,
      sellDecimals
    );

    const quoteBuyAmount = formatUnits(
      BigInt(quoteData.buyAmount),
      buyDecimals
    );
    console.log(`✅ Quote received:`);
    console.log(`   Final output: ${quoteBuyAmount}`);
    console.log(`   Transaction to: ${quoteData.transaction.to}`);

    // 步骤 4:执行交换
    console.log("\n" + "-".repeat(60));
    console.log("🚀 STEP 4: EXECUTING SWAP");
    console.log("-".repeat(60));
    const txHash = await submitTransaction(quoteData.transaction);

    console.log(`✅ Transaction submitted!`);
    console.log(`   Hash: ${txHash}`);
    console.log("\n⏳ Waiting for confirmation...");

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

    console.log("\n" + "=".repeat(60));
    if (receipt.status === "success") {
      console.log(`🎉 SWAP SUCCESSFUL!`);
      console.log(`   Block: ${receipt.blockNumber}`);
      console.log(`   Gas used: ${receipt.gasUsed.toString()}`);
    } else {
      console.log(`❌ TRANSACTION FAILED`);
    }
    console.log("=".repeat(60) + "\n");

    return receipt;
  } catch (error) {
    console.error("\n❌ Swap failed:", error);
    throw error;
  }
}

步骤 10:实用程序函数

添加辅助函数以显示余额和运行脚本。

这些实用程序函数提供方便的方法来一次性检查所有代币余额并设置主要执行流程。displayBalances 函数通过显示交换前/后的余额来帮助验证交换是否正常工作。

// ============================================
// 10. UTILITY FUNCTIONS
// ============================================

/**
 * 显示代币余额
 */
async function displayBalances(): Promise<void> {
  console.log("\n💰 Current Balances:");

  for (const [symbol, address] of Object.entries(TOKENS)) {
    const balance = await checkBalance(address, account.address);
    const decimals = await getTokenDecimals(address);
    const formatted = formatUnits(balance, decimals);

    // 使用 ETH 后缀显示原生 ETH
    if (isNativeToken(address)) {
      console.log(`   ${symbol}: ${formatted} ETH`);
    } else {
      console.log(`   ${symbol}: ${formatted}`);
    }
  }
}

步骤 11:主要执行

最后,我们将所有内容合并到主执行函数中。此函数获取价格以验证条件,确保批准到位,获取确定的报价并将交易提交到区块链。

// ============================================
// 11. MAIN EXECUTION
// ============================================

async function main() {
  try {
    console.log("🔑 Wallet Configuration:");
    console.log(`   Address: ${account.address}`);
    console.log(`   Network: Base (Chain ID: ${CHAIN_ID})`);

    // 显示当前余额
    await displayBalances();

    // 示例交换:0.000001 ETH -> USDC,滑点为 1% (100 bps)
    await executeSwap(
      TOKENS.NATIVE, // 出售原生 ETH
      TOKENS.USDC, // 购买 USDC
      "0.000001", // 交换金额
      100 // bps 中 1% 的滑点
    );

    // 显示更新后的余额
    await displayBalances();
  } catch (error) {
    console.error("Error in main:", error);
    process.exit(1);
  }
}

// 如果直接调用则运行
if (require.main === module) {
  main();
}

完整代码

查看下面的完整 swap.ts 文件:

点击展开

import {
  createWalletClient,
  createPublicClient,
  http,
  parseUnits,
  formatUnits,
  maxUint256,
  erc20Abi,
  Address,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import dotenv from "dotenv";

// 加载环境变量
dotenv.config();

// ============================================
// 1. 配置和设置
// ============================================

// 环境变量验证
const QUICKNODE_HTTP_URL = process.env.QUICKNODE_HTTP_URL;
const ADD_ON_PATH = process.env.ADD_ON_PATH;
const PRIVATE_KEY = process.env.PRIVATE_KEY as Address;
``````markdown
if (!QUICKNODE_HTTP_URL || !ADD_ON_PATH || !PRIVATE_KEY) {
  throw new Error(
    "Missing required environment variables: QUICKNODE_HTTP_URL, ADD_ON_PATH, and PRIVATE_KEY"
  );
}

// Base RPC URL for standard Ethereum RPC calls
// 用于标准以太坊 RPC 调用的基础 RPC URL
const BASE_RPC_URL = QUICKNODE_HTTP_URL;

// Full URL with addon path for 0x Swap API calls
// 带有插件路径的完整 URL,用于 0x Swap API 调用
const SWAP_API_URL = `${QUICKNODE_HTTP_URL}/${ADD_ON_PATH}`;

// Token addresses for Base
// Base 链上的 Token 地址
const TOKENS = {
  NATIVE: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // Native ETH on Base
  WETH: "0x4200000000000000000000000000000000000006", // Wrapped ETH on Base
  USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
  USDe: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34", // USDe on Base
  WBTC: "0x0555E30da8f98308EdB960aa94C0Db47230d2B9c", // WBTC on Base
} as const;

// Chain configuration
// 链配置
const CHAIN_ID = "8453"; // Base chain ID

// Initialize account from private key
// 从私钥初始化账户
const account = privateKeyToAccount(PRIVATE_KEY);

// Initialize Viem clients
// 初始化 Viem 客户端
const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(BASE_RPC_URL),
});

const publicClient = createPublicClient({
  chain: base,
  transport: http(BASE_RPC_URL),
});

// ============================================
// 2. TYPE DEFINITIONS
// 2. 类型定义
// ============================================

interface SwapPriceResponse {
  allowanceTarget: Address;
  buyAmount: string;
  buyToken: Address;
  sellAmount: string;
  sellToken: Address;
  gas: string;
  gasPrice: string;
  issues: {
    allowance?: {
      actual: string;
      spender: Address;
    };
    balance?: {
      token: Address;
      actual: string;
      expected: string;
    };
    simulationIncomplete?: boolean;
  };
  liquidityAvailable: boolean;
  route: any;
  fees: any;
  minBuyAmount?: string;
}

interface SwapQuoteResponse extends SwapPriceResponse {
  transaction: {
    to: Address;
    data: string;
    gas: string;
    gasPrice: string;
    value: string;
  };
}

// ============================================
// 3. PRICE FETCHING
// 3. 价格获取
// ============================================

/**
 * Fetch indicative price from 0x API
 * This is a read-only endpoint to check prices without committing
 */
// 从 0x API 获取指示性价格
// 这是一个只读端点,用于在不提交的情况下检查价格
async function getPrice(
  sellToken: Address,
  buyToken: Address,
  sellAmount: string,
  decimals: number = 18,
  slippageBps: number = 100
): Promise<SwapPriceResponse> {
  const sellAmountWei = parseUnits(sellAmount, decimals).toString();

  const params = new URLSearchParams({
    chainId: CHAIN_ID,
    sellToken,
    buyToken,
    sellAmount: sellAmountWei,
    taker: account.address,
    slippageBps: slippageBps.toString(),
  });

  const headers: HeadersInit = {
    "Content-Type": "application/json",
  };

  const url = `${SWAP_API_URL}/swap/allowance-holder/price?${params.toString()}`;
  console.log(`   Fetching price from: ${url}`);
  // 从以下地址获取价格:${url}

  const response = await fetch(url, { headers });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Price fetch failed: ${error}`);
    // 价格获取失败:${error}
  }

  return response.json();
}

// ============================================
// 4. BALANCE CHECKING
// 4. 余额检查
// ============================================

/**
 * Check if a token is the native ETH token
 */
// 检查 token 是否为原生 ETH token
function isNativeToken(tokenAddress: Address): boolean {
  return tokenAddress.toLowerCase() === TOKENS.NATIVE.toLowerCase();
}

/**
 * Check token balance for a specific address
 */
// 检查特定地址的 token 余额
async function checkBalance(
  tokenAddress: Address,
  userAddress: Address
): Promise<bigint> {
  // For native ETH, use getBalance instead of ERC20 balanceOf
  // 对于原生 ETH,使用 getBalance 而不是 ERC20 balanceOf
  if (isNativeToken(tokenAddress)) {
    const balance = await publicClient.getBalance({
      address: userAddress,
    });
    return balance;
  }

  const balance = await publicClient.readContract({
    address: tokenAddress as Address,
    abi: erc20Abi,
    functionName: "balanceOf",
    args: [userAddress as Address],
  });

  return balance;
}

/**
 * Get token decimals
 */
// 获取 token 的小数位数
async function getTokenDecimals(tokenAddress: Address): Promise<number> {
  // Native ETH always has 18 decimals
  // 原生 ETH 始终有 18 位小数
  if (isNativeToken(tokenAddress)) {
    return 18;
  }

  const decimals = await publicClient.readContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: "decimals",
  });

  return decimals;
}

/**
 * Check current allowance for a spender
 */
// 检查 spender 当前的授权额度
async function checkAllowance(
  tokenAddress: Address,
  owner: Address,
  spender: Address
): Promise<bigint> {
  const allowance = await publicClient.readContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: "allowance",
    args: [owner, spender],
  });

  return allowance;
}

// ============================================
// 5. ISSUE DETECTION & HANDLING
// 5. 问题检测与处理
// ============================================

/**
 * Analyze and handle issues from price response
 */
// 分析和处理价格响应中的问题
async function handleIssues(priceData: SwapPriceResponse): Promise<void> {
  console.log("🔍 Analyzing potential issues...");
  // 🔍 正在分析潜在问题...

  // Check for balance issues
  // 检查余额问题
  if (priceData.issues.balance) {
    const { token, actual, expected } = priceData.issues.balance;

    // Skip balance check for native ETH as it's handled differently
    // 跳过原生 ETH 的余额检查,因为它以不同的方式处理
    if (!isNativeToken(token)) {
      const decimals = await getTokenDecimals(token);
      const actualFormatted = formatUnits(BigInt(actual), decimals);
      const expectedFormatted = formatUnits(BigInt(expected), decimals);

      console.log(`\n⚠️  BALANCE ISSUE DETECTED:`);
      // \n⚠️ 检测到余额问题:
      console.log(`   Token: ${token}`);
      // Token: ${token}
      console.log(`   Current balance: ${actualFormatted}`);
      // 当前余额:${actualFormatted}
      console.log(`   Required: ${expectedFormatted}`);
      // 需要:${expectedFormatted}
      console.log(
        `   Shortfall: ${formatUnits(
          BigInt(expected) - BigInt(actual),
          decimals
        )}`
      );
      // 短缺:${formatUnits(BigInt(expected) - BigInt(actual), decimals)}

      throw new Error(
        `Insufficient balance. Need ${expectedFormatted} but have ${actualFormatted}`
      );
      // 余额不足。 需要 ${expectedFormatted} 但只有 ${actualFormatted}
    } else {
      // For native ETH, still check but with simpler formatting
      // 对于原生 ETH,仍然检查但使用更简单的格式
      const decimals = 18; // Native ETH has 18 decimals
      const actualFormatted = formatUnits(BigInt(actual), decimals);
      const expectedFormatted = formatUnits(BigInt(expected), decimals);

      console.log(`\n⚠️  BALANCE ISSUE DETECTED:`);
      // \n⚠️ 检测到余额问题:
      console.log(`   Token: Native ETH`);
      // Token: 原生 ETH
      console.log(`   Current balance: ${actualFormatted} ETH`);
      // 当前余额:${actualFormatted} ETH
      console.log(`   Required: ${expectedFormatted} ETH`);
      // 需要:${expectedFormatted} ETH
      console.log(
        `   Shortfall: ${formatUnits(
          BigInt(expected) - BigInt(actual),
          decimals
        )} ETH`
      );
      // 短缺:${formatUnits(BigInt(expected) - BigInt(actual), decimals)} ETH

      throw new Error(
        `Insufficient balance. Need ${expectedFormatted} ETH but have ${actualFormatted} ETH`
      );
      // 余额不足。 需要 ${expectedFormatted} ETH 但只有 ${actualFormatted} ETH
    }
  }

  // Check for allowance issues - skip for native ETH (no approval needed)
  // 检查授权额度问题 - 跳过原生 ETH(不需要批准)
  if (priceData.issues.allowance && !isNativeToken(priceData.sellToken)) {
    const { spender, actual } = priceData.issues.allowance;
    const requiredAmount = BigInt(priceData.sellAmount);
    const currentAllowance = BigInt(actual);

    console.log(`\n⚠️  ALLOWANCE ISSUE DETECTED:`);
    // \n⚠️ 检测到授权额度问题:
    console.log(`   Current allowance: ${currentAllowance.toString()}`);
    // 当前授权额度:${currentAllowance.toString()}
    console.log(`   Required allowance: ${requiredAmount.toString()}`);
    // 需要的授权额度:${requiredAmount.toString()}
    console.log(`   Spender (AllowanceHolder): ${spender}`);
    // Spender (AllowanceHolder): ${spender}

    // Check if current allowance is sufficient for this swap
    // 检查当前授权额度是否足以进行此次交换
    if (currentAllowance >= requiredAmount) {
      console.log(`   ✅ Current allowance is sufficient for this swap`);
      // ✅ 当前授权额度足以进行此次交换
    } else {
      console.log(`   Action: Setting approval for exact swap amount...`);
      // 操作:正在设置精确交换金额的批准...
      await setTokenApprovalForAmount(
        priceData.sellToken,
        spender,
        requiredAmount
      );
      console.log(`✅ Token approval completed successfully`);
      // ✅ Token 批准已成功完成
    }
  } else if (isNativeToken(priceData.sellToken)) {
    console.log("✅ Native ETH selected - no approval needed");
    // ✅ 选择原生 ETH - 不需要批准
  } else {
    console.log("✅ No issues detected - ready to proceed");
    // ✅ 未检测到问题 - 准备继续
  }

  // Check for simulation issues
  // 检查模拟问题
  if (priceData.issues.simulationIncomplete) {
    console.log("⚠️  Warning: Simulation incomplete - transaction may fail");
    // ⚠️ 警告:模拟不完整 - 交易可能失败
  }
}

// ============================================
// 6. TOKEN APPROVAL
// 6. TOKEN 批准
// ============================================

/**
 * Set token approval for exact amount needed for swap
 * This is safer than infinite approvals
 */
// 为交换所需的精确金额设置 token 批准
// 这比无限批准更安全
async function setTokenApprovalForAmount(
  tokenAddress: Address,
  spender: Address,
  amount: bigint
): Promise<void> {
  try {
    // Check current allowance
    // 检查当前授权额度
    const currentAllowance = await checkAllowance(
      tokenAddress,
      account.address,
      spender
    );

    console.log(`   Current allowance: ${currentAllowance.toString()}`);
    // 当前授权额度:${currentAllowance.toString()}
    console.log(`   Required amount: ${amount.toString()}`);
    // 需要的金额:${amount.toString()}

    // Only approve if current allowance is insufficient
    // 仅在当前授权额度不足时批准
    if (currentAllowance >= amount) {
      console.log(
        `   ℹ️  Current allowance is already sufficient for this swap`
      );
      // ℹ️ 当前授权额度已足以进行此次交换
      return;
    }

    // Calculate how much additional approval is needed
    // 计算需要多少额外批准
    // We'll approve for the exact amount needed
    // 我们将批准所需的精确金额
    const approvalAmount = amount;

    console.log(`   Approving exact amount: ${approvalAmount.toString()}`);
    // 正在批准精确金额:${approvalAmount.toString()}

    // Simulate the approval transaction
    // 模拟批准交易
    console.log(`   Simulating approval transaction...`);
    // 正在模拟批准交易...
    const { request } = await publicClient.simulateContract({
      account,
      address: tokenAddress,
      abi: erc20Abi,
      functionName: "approve",
      args: [spender, approvalAmount],
    });

    // Execute the approval
    // 执行批准
    console.log(`   Sending approval transaction...`);
    // 正在发送批准交易...
    const hash = await walletClient.writeContract(request);
    console.log(`   Approval tx hash: ${hash}`);
    // 批准 tx 哈希:${hash}

    // Wait for confirmation
    // 等待确认
    console.log(`   Waiting for confirmation...`);
    // 正在等待确认...
    const receipt = await publicClient.waitForTransactionReceipt({ hash });

    if (receipt.status !== "success") {
      throw new Error("Approval transaction failed");
      // 批准交易失败
    }

    console.log(`   Approval confirmed in block ${receipt.blockNumber}`);
    // 批准已在区块 ${receipt.blockNumber} 中确认

    // Verify the new allowance
    // 验证新的授权额度
    const newAllowance = await checkAllowance(
      tokenAddress,
      account.address,
      spender
    );
    console.log(`   New allowance: ${newAllowance.toString()}`);
    // 新的授权额度:${newAllowance.toString()}
  } catch (error) {
    console.error("❌ Approval failed:", error);
    // ❌ 批准失败:${error}
    throw error;
  }
}

// ============================================
// 7. QUOTE FETCHING
// 7. 报价获取
// ============================================

/**
 * Get a firm quote from 0x API
 * This returns an executable transaction
 */
// 从 0x API 获取确定报价
// 这将返回一个可执行的交易
async function getQuote(
  sellToken: Address,
  buyToken: Address,
  sellAmount: string,
  slippageBps: number,
  decimals: number = 18
): Promise<SwapQuoteResponse> {
  const sellAmountWei = parseUnits(sellAmount, decimals).toString();

  const params = new URLSearchParams({
    chainId: CHAIN_ID,
    sellToken,
    buyToken,
    sellAmount: sellAmountWei,
    taker: account.address,
    slippageBps: slippageBps.toString(),
  });

  const headers: HeadersInit = {
    "Content-Type": "application/json",
  };

  const url = `${SWAP_API_URL}/swap/allowance-holder/quote?${params.toString()}`;
  console.log(`   Requesting firm quote...`);
  // 正在请求确定报价...

  const response = await fetch(url, { headers });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Quote fetch failed: ${error}`);
    // 报价获取失败:${error}
  }

  return response.json();
}

// ============================================
// 8. TRANSACTION EXECUTION
// 8. 交易执行
// ============================================

/**
 * Submit the swap transaction to the blockchain
 */
// 将交换交易提交到区块链
async function submitTransaction(
  transaction: SwapQuoteResponse["transaction"]
): Promise<Address> {
  console.log("📤 Submitting transaction to the blockchain...");
  // 📤 正在将交易提交到区块链...
  console.log(`   To: ${transaction.to}`);
  // 至:${transaction.to}
  console.log(`   Gas limit: ${transaction.gas}`);
  // Gas 限制:${transaction.gas}
  console.log(
    `   Gas price: ${formatUnits(BigInt(transaction.gasPrice), 9)} Gwei`
  );
  // Gas 价格:${formatUnits(BigInt(transaction.gasPrice), 9)} Gwei
  console.log(`   Value: ${transaction.value} wei`);
  // 价值:${transaction.value} wei

  const hash = await walletClient.sendTransaction({
    to: transaction.to,
    data: transaction.data as `0x${string}`,
    gas: BigInt(transaction.gas),
    gasPrice: BigInt(transaction.gasPrice),
    value: transaction.value ? BigInt(transaction.value) : 0n,
  });

  return hash;
}

// ============================================
// 9. MAIN SWAP ORCHESTRATION
// 9. 主要交换编排
// ============================================

/**
 * Execute a complete token swap
 */
// 执行完整的 token 交换
async function executeSwap(
  sellToken: Address,
  buyToken: Address,
  sellAmount: string,
  slippageBps: number = 100
): Promise<any> {
  console.log("\n" + "=".repeat(60));
  console.log("🔄 STARTING TOKEN SWAP");
  // 🔄 开始 TOKEN 交换
  console.log("=".repeat(60));

  const sellDecimals = await getTokenDecimals(sellToken);
  const buyDecimals = await getTokenDecimals(buyToken);

  console.log(`\n📊 Swap Parameters:`);
  // \n📊 交换参数:
  console.log(
    `   Sell: ${sellAmount} tokens (${sellToken}...)`
  );
  // 出售:${sellAmount} tokens (${sellToken}...)
  console.log(`   Buy: ${buyToken}...`);
  // 购买:${buyToken}...
  console.log(
    `   Slippage: ${slippageBps / 100}% (${slippageBps} bps)`
  );
  // 滑点:${slippageBps / 100}% (${slippageBps} bps)
  console.log(`   User: ${account.address}`);
  // 用户:${account.address}

  try {
    // STEP 1: Get Price
    // 步骤 1:获取价格
    console.log("\n" + "-".repeat(60));
    console.log("📈 STEP 1: FETCHING INDICATIVE PRICE");
    // 📈 步骤 1:获取指示性价格
    console.log("-".repeat(60));
    const priceData = await getPrice(
      sellToken,
      buyToken,
      sellAmount,
      sellDecimals,
      slippageBps
    );

    if (!priceData.liquidityAvailable) {
      throw new Error("❌ Insufficient liquidity for this trade");
      // ❌ 此交易的流动性不足
    }

    const buyAmountFormatted = formatUnits(
      BigInt(priceData.buyAmount),
      buyDecimals
    );
    const minBuyAmountFormatted = priceData.minBuyAmount
      ? formatUnits(BigInt(priceData.minBuyAmount), buyDecimals)
      : "N/A";

    console.log(`✅ Price fetched successfully:`);
    // ✅ 价格已成功获取:
    console.log(`   Expected output: ${buyAmountFormatted}`);
    // 预期输出:${buyAmountFormatted}
    console.log(`   Minimum output: ${minBuyAmountFormatted}`);
    // 最小输出:${minBuyAmountFormatted}
    console.log(`   Estimated gas: ${priceData.gas} units`);
    // 估计 gas:${priceData.gas} 单位

    // STEP 2: Handle Issues
    // 步骤 2:处理问题
    console.log("\n" + "-".repeat(60));
    console.log("🔍 STEP 2: CHECKING FOR ISSUES");
    // 🔍 步骤 2:检查问题
    console.log("-".repeat(60));

    // Handle issues (including approval)
    // 处理问题(包括批准)
    await handleIssues(priceData);

    // STEP 3: Get Quote
    // 步骤 3:获取报价
    console.log("\n" + "-".repeat(60));
    console.log("📋 STEP 3: FETCHING FIRM QUOTE");
    // 📋 步骤 3:获取确定报价
    console.log("-".repeat(60));
    const quoteData = await getQuote(
      sellToken,
      buyToken,
      sellAmount,
      slippageBps,
      sellDecimals
    );

    const quoteBuyAmount = formatUnits(
      BigInt(quoteData.buyAmount),
      buyDecimals
    );
    console.log(`✅ Quote received:`);
    // ✅ 已收到报价:
    console.log(`   Final output: ${quoteBuyAmount}`);
    // 最终输出:${quoteBuyAmount}
    console.log(`   Transaction to: ${quoteData.transaction.to}`);
    // 交易至:${quoteData.transaction.to}

    // STEP 4: Execute Swap
    // 步骤 4:执行交换
    console.log("\n" + "-".repeat(60));
    console.log("🚀 STEP 4: EXECUTING SWAP");
    // 🚀 步骤 4:执行交换
    console.log("-".repeat(60));
    const txHash = await submitTransaction(quoteData.transaction);

    console.log(`✅ Transaction submitted!`);
    // ✅ 交易已提交!
    console.log(`   Hash: ${txHash}`);
    // 哈希:${txHash}
    console.log("\n⏳ Waiting for confirmation...");
    // 等待确认...

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

    console.log("\n" + "=".repeat(60));
    if (receipt.status === "success") {
      console.log(`🎉 SWAP SUCCESSFUL!`);
      // 🎉 交换成功!
      console.log(`   Block: ${receipt.blockNumber}`);
      // 区块:${receipt.blockNumber}
      console.log(`   Gas used: ${receipt.gasUsed.toString()}`);
      // Gas 使用量:${receipt.gasUsed.toString()}
    } else {
      console.log(`❌ TRANSACTION FAILED`);
      // ❌ 交易失败
    }
    console.log("=".repeat(60) + "\n");

    return receipt;
  } catch (error) {
    console.error("\n❌ Swap failed:", error);
    // ❌ 交换失败:${error}
    throw error;
  }
}

// ============================================
// 10. UTILITY FUNCTIONS
// 10. 实用函数
// ============================================

/**
 * Display token balances
 */
// 显示 token 余额
async function displayBalances(): Promise<void> {
  console.log("\n💰 Current Balances:");
  // \n💰 当前余额:

  for (const [symbol, address] of Object.entries(TOKENS)) {
    const balance = await checkBalance(address, account.address);
    const decimals = await getTokenDecimals(address);
    const formatted = formatUnits(balance, decimals);

    // Display native ETH with ETH suffix
    // 使用 ETH 后缀显示原生 ETH
    if (isNativeToken(address)) {
      console.log(`   ${symbol}: ${formatted} ETH`);
      // ${symbol}: ${formatted} ETH
    } else {
      console.log(`   ${symbol}: ${formatted}`);
      // ${symbol}: ${formatted}
    }
  }
}

// ============================================
// 11. MAIN EXECUTION
// 11. 主要执行
// ============================================

async function main() {
  try {
    console.log("🔑 Wallet Configuration:");
    // 🔑 钱包配置:
    console.log(`   Address: ${account.address}`);
    // 地址:${account.address}
    console.log(`   Network: Base (Chain ID: ${CHAIN_ID})`);
    // 网络:Base (Chain ID: ${CHAIN_ID})

    // Display current balances
    // 显示当前余额
    await displayBalances();

    // Example swap: 0.000001 ETH -> USDC with 1% slippage (100 bps)
    // 示例交换:0.000001 ETH -> USDC,滑点 1% (100 bps)
    await executeSwap(
      TOKENS.NATIVE,
      TOKENS.USDC,
      "0.000001", // Swap amount
      // 交换金额
      100 // 1% slippage in bps
      // 1% 滑点,以 bps 为单位
    );

    // Display updated balances
    // 显示更新后的余额
    await displayBalances();
  } catch (error) {
    console.error("Error in main:", error);
    // main 函数中的错误:${error}
    process.exit(1);
  }
}

// Run if called directly
// 如果直接调用则运行
if (require.main === module) {
  main();
}

Running the Script

To run your bot, adjust the swap parameters in the main function and run the script:

运行脚本

要运行你的机器人,请调整 main 函数中的交换参数并运行脚本:

tsx swap.ts

The output should look like this: 输出应如下所示:

🔑 Wallet Configuration:
   Address: 0x0a417DDB75Dc491C90F044Ea725E8329A1592d00
   Network: Base (Chain ID: 8453)

💰 Current Balances:
   NATIVE: 0.007370389645673835 ETH
   WETH: 0
   USDC: 0
   USDe: 0
   WBTC: 0

============================================================
🔄 STARTING TOKEN SWAP
============================================================

📊 Swap Parameters:
   Sell: 0.000001 tokens (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE...)
   Buy: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913...
   Slippage: 1% (100 bps)
   User: 0x0a417DDB75Dc491C90F044Ea725E8329A1592d00

   # ... rest of the output showing each step ...
   # ... 显示每个步骤的其余输出 ...

============================================================
🎉 SWAP SUCCESSFUL!
   Block: 38479262
   Gas used: 483760
============================================================

💰 Current Balances:
   NATIVE: 0.007364615567936407 ETH
   WETH: 0
   USDC: 0.00278
   USDe: 0
   WBTC: 0

Conclusion

You have successfully integrated the 0x Swap API to perform programmatic token swaps. By using this flow, your application can now source the most efficient pricing from over 150 liquidity venues without managing complex routing logic yourself.

结论

你已成功集成 0x Swap API 以执行程序化的 token 交换。通过使用此流程,你的应用程序现在可以从 150 多个流动性场所获取最有效的定价,而无需自己管理复杂的路由逻辑。

Next Steps

  • Explore Advanced Routing: Look into the includedSources parameter to restrict swaps to specific DEXs (e.g., only Uniswap or Curve).

  • Use Slippage Protection: Modify the slippageBps parameter (Basis Points) in your /quote request to protect your trade against market volatility during execution (e.g., set to 200 for 2%).

  • Build a Frontend: Connect this logic to a React frontend using wagmi to allow users to swap tokens directly from your UI.

下一步

  • 探索高级路由:查看 includedSources 参数以将交换限制为特定的 DEX(例如,仅限 Uniswap 或 Curve)。

  • 使用滑点保护:修改 /quote 请求中的 slippageBps 参数(基点)以保护你的交易在执行过程中免受市场波动的影响(例如,设置为 200 表示 2%)。

  • 构建前端:使用 wagmi 将此逻辑连接到 React 前端,以允许用户直接从你的 UI 交换 token。

For more details on available parameters, check the official 0x API documentation. 有关可用参数的更多详细信息,请查看官方 0x API 文档

Subscribe to our newsletter for more articles and guides on Web3 and blockchain. If you have any questions or need further assistance, feel free to join our Discord server or provide feedback using the form below. Stay informed and connected by following us on X (@Quicknode) and our Telegram announcement channel. 订阅我们的 时事通讯,了解更多关于 Web3 和区块链的文章和指南。如果你有任何疑问或需要进一步的帮助,请随时加入我们的 Discord 服务器或使用下面的表格提供反馈。请在 X (@Quicknode) 和我们的 Telegram 公告频道 上关注我们,以保持信息畅通并与我们保持联系。

We ❤️ Feedback!
我们 ❤️ 反馈!

Let us know if you have any feedback or requests for new topics. We'd love to hear from you. 如果你对新主题有任何反馈或要求,请 告诉我们。我们很乐意倾听你的声音。



>- 原文链接: [quicknode.com/guides/mar...](https://www.quicknode.com/guides/marketplace/marketplace-add-ons/efficient-token-swaps-with-smart-order-routing)
>- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。