使用Uniswap V2 SDK在Base Layer2执行Swap时合约回滚报错求助
Uniswap V2 SDK在Base Layer2执行Swap时合约执行Revert问题排查
在Node.js环境中使用Uniswap V2 SDK执行Swap操作时,触发合约执行回滚错误:Warning! Error encountered during contract execution [execution reverted] (Base Layer2),怀疑问题出在数据编码或交易配置上。
初始代码
async function createPair() { const pairAddress = Pair.getAddress(TOKEN, WETH9[TOKEN.chainId]); const pairContract = new ethers.Contract(pairAddress, UniswapV2PoolAbi, provider); const reserves = await pairContract['getReserves'](); const [reserve0, reserve1] = reserves; const reserve0Parsed = BigInt(reserve0.toString()).toString(); const reserve1Parsed = BigInt(reserve1.toString()).toString(); const tokens = [TOKEN, WETH9[TOKEN.chainId]]; const [token0, token1] = tokens[0].sortsBefore(tokens[1]) ? tokens : [tokens[1], tokens[0]]; const pair = new Pair( CurrencyAmount.fromRawAmount(token0, reserve0Parsed), CurrencyAmount.fromRawAmount(token1, reserve1Parsed) ); return pair; } async function swap() { const amountIn = ethers.parseEther('0.00001').toString(); const slippageTolerance = new Percent('50', '10000'); const to = WALLET_ADDRESS; const signer = new ethers.Wallet(WALLET_SECRET); const account = signer.connect(provider); const command = '0x08'; const deadline = 2 * 10 ** 10; const fromEoa = true; const uniswap = new ethers.Contract( swapRouterAddress, [ 'function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable returns (bytes[] memory outputs)', ], account ); const pair = await createPair(); const route = new Route([pair], WETH9[TOKEN.chainId], TOKEN); const trade = new Trade( route, CurrencyAmount.fromRawAmount(WETH9[TOKEN.chainId], amountIn), TradeType.EXACT_INPUT ); const amountOutMin = trade.minimumAmountOut(slippageTolerance).toExact(); const amoutOut = ethers.parseEther(amountOutMin); const pathData = ethers.solidityPacked( ['address', 'address'], [WETH9[ChainId.BASE].address, TOKEN.address] ); const v2Calldata = ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'uint256', 'uint256', 'bytes', 'bool'], [to, amountIn, amoutOut, pathData, fromEoa] ); const tx = await uniswap.execute(command, [v2Calldata], deadline, { value: amountIn, gasPrice: ethers.parseUnits('0.15', 'gwei'), gasLimit: 100000, chainId: ChainId.BASE, from: WALLET_ADDRESS, }); console.log(`Transaction hash: ${tx.hash}`); const receipt = await tx.wait(); console.log(`Transaction was mined in block ${receipt.blockNumber}`); }
代码逻辑概述
- 导入Uniswap V2 SDK组件与ethers.js
- 通过QuickNode HTTP端点配置provider
- 定义钱包地址与密钥
- 声明swapRouterAddress与目标TOKEN
- 实现swap函数,包含交易参数设置、合约实例创建、交易细节计算、数据编码及execute方法调用
更新后代码(仍报错)
async function swap() { const swapRouterAddress = '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad'; const TOKEN = new Token(ChainId.BASE, '0xBC45647eA894030a4E9801Ec03479739FA2485F0', 18); const amountIn = ethers.parseEther('0.00001').toString(); const to = WALLET_ADDRESS; const signer = new ethers.Wallet(WALLET_SECRET); const account = signer.connect(provider); const command = '0x0b08'; const deadline = 2 * 10 ** 10; const fromEoa = false; const FEE = 500; const uniswap = new ethers.Contract( swapRouterAddress, [ 'function execute(bytes calldata commands, bytes[] calldata inputs) external payable returns (bytes[] memory outputs)', ], account ); const pathData = ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'address'], [WETH9[ChainId.BASE].address, TOKEN.address] ); const wrap_calldata = ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'uint256'], [swapRouterAddress, amountIn] ); const v2Calldata = ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'uint256', 'uint256', 'bytes', 'bool'], [to, amountIn, 0, pathData, fromEoa] ); const tx = await uniswap.execute(command, [wrap_calldata, v2Calldata], { value: amountIn, gasPrice: ethers.parseUnits('0.15', 'gwei'), gasLimit: 150000, chainId: ChainId.BASE, from: WALLET_ADDRESS, }); console.log(`Transaction hash: ${tx.hash}`); const receipt = await tx.wait(); console.log(`Transaction was mined in block ${receipt.blockNumber}`); }
错误核心原因
- 合约ABI与调用参数不匹配:更新后代码移除了
execute函数的deadline参数,但Uniswap V2 Router的execute签名要求该参数,导致调用格式错误触发revert。 - 路径编码错误:Uniswap V2的path需要用
solidityPacked直接拼接地址,而非ABI编码数组,更新后代码的路径编码方式不符合合约要求。 - 命令逻辑冗余且参数错误:手动添加
0x0b(WRAP_ETH)命令,但参数编码不符合合约规范,且Router的0x08命令可自动处理ETH转WETH,无需额外操作。 - amountOutMin设置为0:违反Uniswap滑点保护逻辑,部分合约会拒绝此类参数。
- Gas Limit不足:Base链上Swap操作需要更高的Gas上限,手动设置的150000可能不足以覆盖执行成本。
修复后的代码
async function createPair() { const pairAddress = Pair.getAddress(TOKEN, WETH9[TOKEN.chainId]); const pairContract = new ethers.Contract(pairAddress, UniswapV2PoolAbi, provider); const reserves = await pairContract.getReserves(); const [reserve0, reserve1] = reserves; const tokens = [TOKEN, WETH9[TOKEN.chainId]]; const [token0, token1] = tokens[0].sortsBefore(tokens[1]) ? tokens : [tokens[1], tokens[0]]; const pair = new Pair( CurrencyAmount.fromRawAmount(token0, reserve0.toString()), CurrencyAmount.fromRawAmount(token1, reserve1.toString()) ); return pair; } async function swap() { const swapRouterAddress = '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad'; const TOKEN = new Token(ChainId.BASE, '0xBC45647eA894030a4E9801Ec03479739FA2485F0', 18); const amountIn = ethers.parseEther('0.00001'); const slippageTolerance = new Percent('50', '10000'); const to = WALLET_ADDRESS; const signer = new ethers.Wallet(WALLET_SECRET); const account = signer.connect(provider); const command = '0x08'; // 设置合理的过期时间:当前时间+30分钟 const deadline = Math.floor(Date.now() / 1000) + 1800; const fromEoa = true; const uniswap = new ethers.Contract( swapRouterAddress, [ 'function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable returns (bytes[] memory outputs)', ], account ); // 计算交易路径与最小输出量 const pair = await createPair(); const route = new Route([pair], WETH9[ChainId.BASE], TOKEN); const trade = new Trade( route, CurrencyAmount.fromRawAmount(WETH9[ChainId.BASE], amountIn.toString()), TradeType.EXACT_INPUT ); const amountOutMin = trade.minimumAmountOut(slippageTolerance).quotient; // 正确编码路径:用solidityPacked拼接地址 const pathData = ethers.solidityPacked( ['address', 'address'], [WETH9[ChainId.BASE].address, TOKEN.address] ); // 编码V2_EXACT_INPUT参数 const v2Calldata = ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'uint256', 'uint256', 'bytes', 'bool'], [to, amountIn, amountOutMin, pathData, fromEoa] ); // 发送交易,设置足够的Gas上限 const tx = await uniswap.execute(command, [v2Calldata], deadline, { value: amountIn, gasPrice: ethers.parseUnits('0.15', 'gwei'), gasLimit: ethers.toBigInt(300000), chainId: ChainId.BASE, from: WALLET_ADDRESS, }); console.log(`Transaction hash: ${tx.hash}`); const receipt = await tx.wait(); console.log(`Transaction was mined in block ${receipt.blockNumber}`); }
核心修复点
- 恢复
execute函数的完整签名,添加deadline参数并设置合理值 - 用
ethers.solidityPacked正确编码交易路径 - 恢复通过Trade对象计算
amountOutMin,保留滑点保护 - 简化命令逻辑,直接使用
0x08命令由Router自动处理ETH转WETH - 提高Gas上限至300000,确保覆盖执行成本
内容的提问来源于stack exchange,提问作者Ghassane Aboughazaouat
相关产品推荐
相关产品推荐

