不能使用 Uniswap V3 SwapRouter 进行多跳交换,SwapRouter.exactInput(params) 抛出 'UNPREDICTABLE_GAS_LIMIT'

Can't use Uniswap V3 SwapRouter for multihop swaps, SwapRouter.exactInput(params) throws 'UNPREDICTABLE_GAS_LIMIT'

我正在尝试使用新的 Uniswap V3 合约实现互换。 我正在使用 Quoter 合同来获取报价,并使用 SwapRouter 来进行交换。 例如,如果我使用直接交换方法(当代币有池时)- -

ethersProvider = new ethers.providers.Web3Provider(web3.currentProvider, 137);

uniSwapQuoter = new ethers.Contract(uniSwapQuoterAddress, QuoterAbi.abi, ethersProvider);

uniSwapRouterV3 = new ethers.Contract(uniSwapRouterAddress, RouterAbi.abi, 
ethersProvider.getSigner());

uniSwapQuoter.callStatic.quoteExactInputSingle(.....) 
uniSwapQuoter.callStatic.quoteExactOutputSingle(.....)
uniSwapRouterV3.exactInputSingle(params)

一切正常,但是当我尝试使用多跳引号和多跳交换失败时


    "reason": "cannot estimate gas; transaction may fail or may require manual gas limit",
    "code": "UNPREDICTABLE_GAS_LIMIT",
    "error": {
        "code": -32000,
        "message": "execution reverted"
    },
    "method": "estimateGas",
    "transaction": {
        "from": "0x532d647481c20f4422A8331339D76b25cA569959",
        "to": "0xE592427A0AEce92De3Edee1F18E0157C05861564",
        "data": "0xc04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000002a6b82b6dd3f38eeb63a35f2f503b9398f02d9bb0000000000000000000000000000000000000000000000000000000861c468000000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000422791bca1f2de4661ed88a30c99a7a9449aa841740005007ceb23fd6bc0add59e62ac25578270cff1b9f619003000c26d47d5c33ac71ac5cf9f776d63ba292a4f7842000000000000000000000000000000000000000000000000000000000000",
        "accessList": null
    }

为了编码参数,我使用了测试中的 uniswap 示例:

function encodePath(tokenAddresses, fees) {
  const FEE_SIZE = 3

  if (path.length != fees.length + 1) {
    throw new Error('path/fee lengths do not match')
  }

  let encoded = '0x'
  for (let i = 0; i < fees.length; i++) {
    // 20 byte encoding of the address
    encoded += path[i].slice(2)
    // 3 byte encoding of the fee
    encoded += fees[i].toString(16).padStart(2 * FEE_SIZE, '0')
  }
  // encode the final token
  encoded += path[path.length - 1].slice(2)

  return encoded.toLowerCase()
}

最后是我为引用所做的示例代码:

    const routeAndFees = await getAddressPath(path);
    const encodedPath = await encodePath(routeAndFees.path, routeAndFees.fees);
    const usdcWithDecimals = parseFloat(usdcAmount) * 1000000
    const tokenDecimals = path[path.length - 1].tokenOut.decimals;

    try {
        const amountOut = await uniSwapQuoter.callStatic.quoteExactInput(encodedPath, usdcWithDecimals.toString());
        console.log("Token amount out:", parseFloat(amountOut) / (10 ** tokenDecimals));
        return {
            tokenOut: parseFloat(amountOut) / (10 ** tokenDecimals),
            usdcIn: parseFloat(usdcAmount)
        };
    } catch (e) {
        console.log(e);
        return e;
    }
}

并交换:

async function multiSwap(path, userAddress, usdcAmount) {
    const usdcWithDecimals = parseFloat(usdcAmount) * 1000000
    const routeAndFees = await getAddressPath(path);
    const encodedPath = await encodePath(routeAndFees.path, routeAndFees.fees);

    const params = {
        path: encodedPath,
        recipient: userAddress,
        deadline: Math.floor(Date.now() / 1000) + 900,
        amountIn: usdcWithDecimals.toString(),
        amountOutMinimum: 0,
    }
    try {
        return  await uniSwapRouterV3.exactInput(params);
    } catch (e) {
        console.log(e);
        return e;
    }
}

路径应该是 [address,fee,address,fee,address],我不确定它的编码,但没有找到任何其他示例。实际上没有找到任何进行 uniswap v3 多跳交换的示例,即使在 UniDocs 中也有交易示例和单池交换...... 有人可以指出我在这里做错了什么吗? 同样的错误出现在引用和交换时:/

我正在 Polygon 主网上进行测试,我可以直接在 uniswap 上进行相同的路径交换,但是当我触发脚本时它失败了...

您应该散列费用值。而不是 0 添加 6。这应该适合你:

 async function encodePath(path, fees, exactInput) {
    const FEE_SIZE = 6
    if (path.length !== fees.length + 1) {
        throw new Error('path/fee lengths do not match')
    }
    if (!exactInput) {
        path = path.reverse();
        fees = fees.reverse();
    }

    let encoded = '0x'
    for (let i = 0; i < fees.length; i++) {
        encoded += path[i].slice(2)
        let fee = web3.utils.toHex(parseFloat(fees[i])).slice(2).toString();
        encoded += fee.padStart(FEE_SIZE, '0');
    }
    encoded += path[path.length - 1].slice(2)
    return encoded    
}