You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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。

错误原因

  1. 授权对象完全错误
    你给Uniswap Router授权了代币额度,但实际调用流程中,Uniswap Router会从你的自定义合约地址转移SAND代币(因为调用Router的是你的合约,msg.sender为合约地址),而非直接从owner地址转移。因此owner需要授权的是你的自定义合约,而不是Router。

  2. 合约未处理代币转移逻辑
    你的自定义合约在调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 16:20:45