使用UniswapV2Router调用swapExactTokensForETH遇TRANSFER_FROM_FAILED错误求助
UniswapV2 swapExactTokensForETH 触发 TransferHelper: TRANSFER_FROM_FAILED 错误原因及修复
问题场景
通过自定义合约调用UniswapV2Router的swapExactTokensForETH实现SAND兑换ETH,合约及测试代码如下:
自定义合约代码
function swapForETH( uint amountInMax, uint amountOut, address[] calldata path, address to )external{ IUniswapV2Router02(ROUTER).swapExactTokensForETH( amountInMax, amountOut, path, to, block.timestamp ); }
测试代码
it("should swap exact tokens for ETH", async()=>{ let ownerSandBalance = await SAND_TOKEN.balanceOf(owner.address); console.log("Owner Sand Balance: ", (ownerSandBalance).toString()); let tx = await SAND_TOKEN.connect(owner).approve(ROUTER, ownerSandBalance); await tx.wait(); let approvance = await SAND_TOKEN.allowance(owner.address, ROUTER); console.log("Approval from owner to contract: ", (approvance).toString()); let result = await contract.connect(owner).swapForETH( ownerSandBalance, "100000000000000000", [SAND, WETH], owner.address, ); console.log("", (result[0] / decimals).toString()); console.log("", (result[1] / decimals).toString()); console.log(result); });
执行测试时触发TransferHelper: TRANSFER_FROM_FAILED错误,owner的SAND余额为18576609770834064884363。
错误原因
授权对象完全错误
你给Uniswap Router授权了代币额度,但实际调用流程中,Uniswap Router会从你的自定义合约地址转移SAND代币(因为调用Router的是你的合约,msg.sender为合约地址),而非直接从owner地址转移。因此owner需要授权的是你的自定义合约,而不是Router。合约未处理代币转移逻辑
你的自定义合约在调用Router前,既没有将owner的SAND转移到合约地址,也没有获取owner的授权来转移代币,导致Router执行transferFrom时,无法从合约地址转出SAND(合约无余额且无授权),最终触发转移失败错误。
修复方案
1. 修正测试代码中的授权对象
将授权目标从Router改为你的自定义合约地址:
// 替换原授权代码 let tx = await SAND_TOKEN.connect(owner).approve(contract.address, ownerSandBalance); await tx.wait(); // 验证授权(可选) let approvance = await SAND_TOKEN.allowance(owner.address, contract.address); console.log("Approval from owner to contract: ", (approvance).toString());
2. 完善自定义合约的代币处理逻辑
在调用Router前,先将owner的SAND转移到合约,并给Router授权合约内的代币额度,同时添加返回值以便测试获取兑换结果:
function swapForETH( uint amountInMax, uint amountOut, address[] calldata path, address to )external returns (uint[] memory amounts){ // 从owner地址转移指定数量SAND到合约 IERC20(path[0]).transferFrom(msg.sender, address(this), amountInMax); // 授权Router转移合约内的SAND IERC20(path[0]).approve(ROUTER, amountInMax); // 执行兑换并返回结果 amounts = IUniswapV2Router02(ROUTER).swapExactTokensForETH( amountInMax, amountOut, path, to, block.timestamp ); }
补充说明
swapExactTokensForETH的返回值是兑换前后的代币数量数组([amountIn, amountOut]),因此合约需要添加returns声明,测试代码才能正确获取结果。- 若不想将代币转入合约,也可以使用Uniswap的
swapExactTokensForETHSupportingFeeOnTransferTokens函数(如果代币支持手续费),但常规场景下,上述方案最直接可靠。
内容的提问来源于stack exchange,提问作者Matteo Gigli
相关产品推荐
相关产品推荐

