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

UniswapV2 getAmountsOut执行无原因回退问题求助

UniswapV2 Router getAmountsOut调用无原因回退排查

问题背景

在Fantom网络开发UniswapV2 LP代币价格聚合器,测试Swap操作(模拟闪电贷场景)时,调用IUniswapV2Router02的getAmountsOut函数出现无原因回退,无法定位问题。

合约代码

function convertEthToToken(
        address tokenAddress,
        uint tokenAmount,
        uint deadline
    ) public payable {
        address[] memory path = new address[](2);
        path[0] = IUniswapV2Router02(UNISWAP_V2_ROUTER).WETH();
        path[1] = tokenAddress;

        ERC20(tokenAddress).approve(UNISWAP_V2_ROUTER, tokenAmount);
        ERC20(IUniswapV2Router02(UNISWAP_V2_ROUTER).WETH()).approve(
            UNISWAP_V2_ROUTER,
            tokenAmount
        );
        IUniswapV2Router02(UNISWAP_V2_ROUTER).swapETHForExactTokens{
            value: msg.value
        }(tokenAmount, path, address(this), deadline);

        // refund leftover ETH to user
        msg.sender.call{value: address(this).balance}("");
    }

function swap(
        address _tokenIn,
        address _tokenOut,
        uint256 _amountIn,
        address _to,
        uint256 _deadline
    ) public payable {
        // transfer the amount in tokens from msg.sender to this contract
        convertEthToToken(_tokenIn, _amountIn, _deadline);

        //by calling IERC20 approve you allow the uniswap contract to spend the tokens in this contract
        IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);

        address[] memory path;
        path = new address[](2);
        path[0] = _tokenIn;
        path[1] = _tokenOut;

        // @here occurs this error -> "Error: Transaction reverted without a reason string"!!!
        uint256[] memory amountsExpected = IUniswapV2Router02(UNISWAP_V2_ROUTER)
            .getAmountsOut(_amountIn, path);

        uint256[] memory amountsReceived = IUniswapV2Router02(UNISWAP_V2_ROUTER)
            .swapExactTokensForTokens(
                amountsExpected[0],
                (amountsExpected[1] * 990) / 1000, // accepting a slippage of 1%
                path,
                _to,
                _deadline
            );
        console.log("swap finished. ", amountsReceived[0]);
    }

测试代码

it("swapping", async () => {
    const latestBlock = await ethers.provider.getBlockNumber();
    const timestamp = (await ethers.provider.getBlock(latestBlock)).timestamp;
    await priceOracle
      .connect(owner)
      .swap(FTM, WBTC, 100000, owner.address, timestamp + 1000, {
        value: 1000000,
      });
    await priceOracle.getSafePrice(FTM_BTC_LP);
  });

排查方向与修复建议

1. 路径合法性检查

UniswapV2的getAmountsOut要求路径内每一对代币都存在对应的Pair合约。直接在测试中单独调用Router的getAmountsOut,传入相同的_amountIn和[_tokenIn, _tokenOut]路径,确认Fantom链上该代币对是否有流动性池,无对应Pair会直接回退。

2. convertEthToToken逻辑修正

  • 多余授权:swapETHForExactTokens是用ETH兑换代币,无需提前授权目标代币给Router,移除ERC20(tokenAddress).approve和WETH的授权代码。
  • swapETHForExactTokens风险:该函数要求用ETH兑换指定数量的代币,若msg.value不足以覆盖所需ETH,函数会直接回退,导致后续getAmountsOut根本未执行。建议改用swapExactETHForTokens(按输入ETH量兑换代币),避免固定输出量的限制。
  • 退款容错:原退款逻辑msg.sender.call{value: address(this).balance}("")若遇到sender为无receive/fallback的合约,会导致整个交易回退。添加try/catch处理:
    (bool success,) = msg.sender.call{value: address(this).balance}("");
    if(!success) {
        // 可将ETH暂存合约或做其他处理,避免交易失败
    }
    

3. 代币路径参数错误

Fantom链上UniswapV2 Router的WETH()对应WFTM地址,测试中传入的FTM是原生代币地址,UniswapV2路径不支持直接用原生代币作为路径项,需将_tokenIn替换为WFTM地址,或在swap函数中判断原生代币,单独处理ETH类兑换逻辑。

4. 前置状态校验

在调用getAmountsOut前,添加断言确认合约已持有足够的_tokenIn,避免convertEthToToken兑换失败后无代币可用的情况:

require(IERC20(_tokenIn).balanceOf(address(this)) >= _amountIn, "Insufficient token balance");

内容的提问来源于stack exchange,提问作者master talent

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:01:14