Base链用Ethers.js调用Uniswap Universal Router购币报错排查
问题描述
在Base链上使用Node.js + Ethers.js通过Uniswap Universal Router购买新上线代币时,调用execute函数编码路径和输入时触发报错,错误信息如下:
if (types.length !== values.length) { ^ TypeError: Cannot read properties of undefined (reading 'length') at AbiCoder.encode (/home/oriok/DEV/degen/Arbitrum/node_modules/@ethersproject/abi/lib/abi-coder.js:82:37) at Object.<anonymous> (/home/oriok/DEV/degen/Arbitrum/B_Base/testinUni.js:52:24) at Module._compile (node:internal/modules/cjs/loader:1103:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) at node:internal/main/run_main_module:17:47
使用的代码如下:
const {ethers} = require("ethers") const AbiCoder = new ethers.utils.AbiCoder() // const tokenToBuy = '0xe67aB9Efb46d987d2aF816597F6716069A8a7426' //KIF //Router const routerAddress = '0x198EF79F1F515F02dFE9e3115eD9fC07183f02fC' // router const file = fs.readFileSync("./B_Base/ABIUniversal.json", 'utf8') // Calls sushi ABI const routerAbi = JSON.parse(file) const routerContract = new ethers.Contract(routerAddress, routerAbi, signer) // token input const prompt = ps() const tokenToBuy = String(prompt("Enter token name: ")) //wbnb to spend const spend = ethers.utils.parseEther("0.0001") const commands = 0x00 const path = [AbiCoder.encode(WETH), AbiCoder.encode(5000), AbiCoder.encode(tokenToBuy)] // Encode the parameters const inputs = AbiCoder.encode(WALLET_ADDRESS, spend, 0, path, false) const buy = async () => { // .execute execute(bytes commands,bytes[] inputs,uint256 deadline) const buy = await routerContract.execute( commands, inputs, Date.now() + 1000 * 60 * 10) receipt = await buy.wait() transactionHash = receipt.transactionHash console.log(transactionHash) } buy()
此前使用SwapRouterV2无法购买部分代币,希望明确错误原因,同时了解其他支持购买各类代币的路由方案。
错误原因及修复方案
1. AbiCoder.encode 调用格式错误
ethers.utils.AbiCoder.encode要求第一个参数为类型数组,第二个为对应的值数组,你直接传入值而未指定类型,导致types参数为undefined,触发types.length读取错误。
修复路径编码
Uniswap V3的路径格式为tokenIn -> fee -> tokenOut,正确做法是使用solidityPack一次性编码三个元素,而非单独编码后再组合:
// Base链WETH固定地址 const WETH = "0x4200000000000000000000000000000000000006"; // 编码V3路径:地址 + 24位手续费 + 地址 const path = ethers.utils.solidityPack( ["address", "uint24", "address"], [WETH, 5000, tokenToBuy] );
修复execute的inputs编码
commands = 0x00对应V3_SWAP_EXACT_IN命令,该命令的参数类型为(address recipient, uint256 amountIn, uint256 amountOutMinimum, bytes path, bool payerIsUser),需按类型编码为bytes,且inputs参数要求是bytes数组(每个命令对应一个元素):
// 替换原inputs编码逻辑 const inputs = [ AbiCoder.encode( ["address", "uint256", "uint256", "bytes", "bool"], [WALLET_ADDRESS, spend, 0, path, true] // payerIsUser设为true,代表用自身钱包支付ETH ) ];
2. 补充代码缺失依赖与初始化
代码中存在未定义的变量和依赖,需补充:
- 引入
fs模块:const fs = require("fs"); - 引入
prompt-sync并初始化:const ps = require("prompt-sync")(); - 初始化Provider和Signer:
// 使用Base链RPC节点(如Alchemy/Infura) const provider = new ethers.providers.JsonRpcProvider("https://base-mainnet.g.alchemy.com/v2/你的API_KEY"); // 用私钥初始化签名者 const signer = new ethers.Wallet("你的钱包私钥", provider); const WALLET_ADDRESS = signer.address;
3. 修复后的完整代码
const {ethers} = require("ethers"); const fs = require("fs"); const ps = require("prompt-sync")(); const AbiCoder = new ethers.utils.AbiCoder(); const WETH = "0x4200000000000000000000000000000000000006"; // Base链WETH地址 const routerAddress = '0x198EF79F1F515F02dFE9e3115eD9fC07183f02fC'; // Universal Router地址 // 初始化Provider与Signer const provider = new ethers.providers.JsonRpcProvider("https://base-mainnet.g.alchemy.com/v2/你的API_KEY"); const signer = new ethers.Wallet("你的钱包私钥", provider); const WALLET_ADDRESS = signer.address; // 读取Router ABI const file = fs.readFileSync("./B_Base/ABIUniversal.json", 'utf8'); const routerAbi = JSON.parse(file); const routerContract = new ethers.Contract(routerAddress, routerAbi, signer); // 获取目标代币地址(注意输入地址而非代币名称) const tokenToBuy = ps("Enter token address: "); const spend = ethers.utils.parseEther("0.0001"); const commands = "0x00"; // V3_SWAP_EXACT_IN命令 // 编码V3交易路径 const path = ethers.utils.solidityPack( ["address", "uint24", "address"], [WETH, 5000, tokenToBuy] ); // 编码命令输入参数 const inputs = [ AbiCoder.encode( ["address", "uint256", "uint256", "bytes", "bool"], [WALLET_ADDRESS, spend, 0, path, true] ) ]; const buy = async () => { try { // deadline需传入秒级时间戳(Date.now()返回毫秒,需除以1000) const tx = await routerContract.execute( commands, inputs, Math.floor(Date.now() / 1000) + 60 * 10 ); const receipt = await tx.wait(); console.log("交易哈希:", receipt.transactionHash); } catch (err) { console.error("交易失败:", err); } }; buy();
其他可选路由方案
如果Uniswap Universal Router仍有兼容性问题,可尝试以下路由:
- Uniswap SwapRouterV3:专门针对V3流动性池的路由,兼容性比V2更好,支持更多新上线代币
- 1inch Router:聚合多个DEX的路由,自动扫描最优流动性路径,对新代币的支持更友好
- SushiSwap Router:与Uniswap接口兼容,部分Uniswap无流动性的代币可能在SushiSwap存在交易对
- PancakeSwap Router(Base版):若目标代币在Base链的PancakeSwap有流动性,可使用该路由
内容的提问来源于stack exchange,提问作者0xDegen
相关产品推荐
相关产品推荐

