在合约中调用 uniswap swapExactTokensForTokens 失败

Posted

技术标签:

【中文标题】在合约中调用 uniswap swapExactTokensForTokens 失败【英文标题】:Failed to call uniswap swapExactTokensForTokens in a contract 【发布时间】:2021-11-18 08:52:24 【问题描述】:
contract TestCall 
    ...

    function call(address payable _to, uint256 _value, bytes calldata _data) external onlyOwner payable returns (bytes memory) 
        require(_to != address(0));
        (bool _success, bytes memory _result) = _to.callvalue: _value(_data);
        require(_success);
        return _result;
    

    ...

我在 goerli 测试网中部署了上述合约。 并使用下面的js代码调用TestCall.call函数将token2换成token1。

import  BigNumber, Contract, providers, Wallet, utils  from "ethers";
const provider = new providers.StaticJsonRpcProvider(ETHEREUM_RPC_URL);
const wallet = new Wallet(PRIVATE_KEY, provider); // The account which deployed the contract
const signingAddr = await wallet.getAddress();
const uniswapInterface = new Contract(UNISWAP_ROUTERV2_ADDRESS, UNISWAP_ROUTERV2_ABI, wallet);
const testCallContract = new Contract(TESTCALL_CONTRACT_ADDRESS, TESTCALL_ABI, wallet);
const nonce = await wallet.getTransactionCount();
const gasPriceGwei = "100"; // in GWEI
const gasPriceWei = utils.parseUnits(gasPriceGwei, "gwei");

const amountIn = BigNumber.from(1000000000000);
let amountOut = await uniswapInterface.getAmountsOut(amountIn, [token2Addr, token1Addr]);
amountOut = amountOut[1];
const amountOutMin = amountOut.mul(0.998 * 1000).div(1000);
const params = 
  "amountIn": amountIn,
  "amountOutMin": amountOutMin,
  "path": [token2Addr, token1Addr],
  "to": signingAddr,
  "deadline": Date.now() + 1000*60*10,
;
console.log("params", params)

/// This can run successfully.
// const tx = await uniswapInterface.swapExactTokensForTokens(...Object.values(params), 
//   "value": BigNumber.from(0),
//   "gasLimit": utils.hexlify(1000000),
//   "gasPrice": gasPriceWei,
//   "nonce": nonce
// );
// const receipt = await tx.wait()
// console.log(receipt)

const tx = await uniswapInterface.populateTransaction.swapExactTokensForTokens(...Object.values(params));
console.log(tx);
if (tx === undefined || tx.data === undefined) throw new Error("ERROR")

const transaction = await testCallContract.call(UNISWAP_ROUTERV2_ADDRESS, BigNumber.from(0), tx.data, 
  "value": BigNumber.from(0),
  "gasLimit": utils.hexlify(1000000),
  "gasPrice": gasPriceWei,
  "nonce": nonce,
);
console.log("transaction", JSON.stringify(transaction))
const receipt = await transaction.wait()
console.log(receipt)

在错误消息中找不到有用的信息:

Error encountered during contract execution [Reverted]

使用swapExactETHForTokens成功运行:

const amountInEther = '0.01';
const amountIn = utils.parseEther(amountInEther);
let amountOut = await uniswapInterface.getAmountsOut(amountIn, [WETH_ADDRESS, token2Addr]);
console.log(amountOut);
amountOut = amountOut[1];
const amountOutMin = amountOut.mul(0.998 * 1000).div(1000);
const params = 
  "amountOutMin": amountOutMin,
  "path": [WETH_ADDRESS, token2Addr],
  "to": signingAddr,
  "deadline": Date.now() + 1000*60*10,
;
const tx1 = await uniswapInterface.populateTransaction.swapExactETHForTokens(...Object.values(params), "value": amountIn);
console.log(tx1);
if (tx1 === undefined || tx1.data === undefined) throw new Error("ERROR");

const transaction = await bundleExecutorContract.call(UNISWAP_ROUTERV2_ADDRESS, BigNumber.from(amountIn), tx1.data, 
  "value": amountIn,
  "gasLimit": utils.hexlify(1000000),
  "gasPrice": gasPriceWei,
  "nonce": nonce,
);
console.log("transaction", JSON.stringify(transaction))
const receipt = await transaction.wait()
console.log(receipt)

有没有办法调试部署在测试网中的solidity代码? 我搜索并找到了安全帽,但不知道在这种情况下如何使用它。

【问题讨论】:

【参考方案1】:

我找到了解决方法:

在合约代码中,为 UNISWAP_ROUTERV2_ADDRESS 批准足够的 token2。并发送足够的 token2 到 TESTCALL_CONTRACT_ADDRESS。

由于swapExactTokensForTokens是不可支付的,在js代码中设置值为0。

"value": BigNumber.from(0),

【讨论】:

以上是关于在合约中调用 uniswap swapExactTokensForTokens 失败的主要内容,如果未能解决你的问题,请参考以下文章

Uniswap v3 详解:创建交易对/提供流动性

如何在代币合约中区分在 uniswap 上买卖 erc-20 代币

使用 web3.py 和 infura.io 监听 Uniswap 合约事件

如何使 uniswap v2 去中心化交易所支持以太坊私链

Uniswap的pair合约创建事件

如何使用 web3 js 在 uniswap 上交换令牌