Обмен ETH на DAI с помощью ISwapRouter из Uniswap

#solidity #remix #hardhat

Вопрос:

Я пытаюсь начать работу с Uniswap V3. В качестве примера я взял самый простой вариант использования: при X количестве ETH сделайте обмен на DAI. К сожалению, я не в состоянии заставить его работать.

Уже есть очень похожий вопрос (без ответа), но немного другой, так как код не похож на мой.

Я использую жесткую шляпу для раскручивания основной сети, а затем подключаю Remix к localhost:8545

 npx hardhat node --fork https://mainnet.infura.io/v3/{MY_API_KEY}
 

Конфигурация жесткого диска внизу:

 solidity: {
  compilers: [
    {
      version: "0.8.7",
      settings: {
        optimizer: {
          enabled: true,
          runs: 1000,
        }
      }
    }
  ]
}
 

Как вы можете заметить (полный контракт в самом низу), контракт предлагает 3 оплачиваемых функции:

 function convertExactEthToDai() external payable;
function convertEthToExactDai(uint256 daiAmount) external payable;
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256);
 

Все они терпят неудачу, даже оцениваются, что довольно просто и читается (почти). Нет никакой конкретной причины, поэтому я слеп. Когда я выполняю функцию из Remix, я просто получаю общую ошибку "Returned error: Error: Transaction reverted without a reason string" .
Когда я смотрю на консоль hardhat, я вижу эту ошибку:

 eth_sendTransaction
  Contract call:       <UnrecognizedContract>
  Transaction:         0xe17fd5a07ca4a0d5a0f91525a77d21152c41f4dc2a9e59feaac4fec7452ba3a1
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0xc351628eb244ec633d5f21fbd6621e1a683b1181
  Value:               0 ETH
  Gas used:            52351 of 3000000
  Block #13238807:     0x7471674d8b76462230d687644e20970137640a7b2fe005c264a04c2af35d4985

  Error: Transaction reverted without a reason string
      at <UnrecognizedContract>.<unknown> (0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6)
      at <UnrecognizedContract>.<unknown> (0xc351628eb244ec633d5f21fbd6621e1a683b1181)
      at runMicrotasks (<anonymous>)
      at processTicksAndRejections (internal/process/task_queues.js:95:5)
      at HardhatNode._mineBlockWithPendingTxs (D:RepositoriesTheRockfacutherock.blockchain.gettingstartednode_moduleshardhatsrcinternalhardhat-networkprovidernode.ts:1582:23)
      at HardhatNode.mineBlock (D:RepositoriesTheRockfacutherock.blockchain.gettingstartednode_moduleshardhatsrcinternalhardhat-networkprovidernode.ts:435:16)
      at EthModule._sendTransactionAndReturnHash (D:RepositoriesTheRockfacutherock.blockchain.gettingstartednode_moduleshardhatsrcinternalhardhat-networkprovidermoduleseth.ts:1494:18)
      at HardhatNetworkProvider._sendWithLogging (D:RepositoriesTheRockfacutherock.blockchain.gettingstartednode_moduleshardhatsrcinternalhardhat-networkproviderprovider.ts:129:22)
      at HardhatNetworkProvider.request (D:RepositoriesTheRockfacutherock.blockchain.gettingstartednode_moduleshardhatsrcinternalhardhat-networkproviderprovider.ts:106:18)
 

Похоже, контракт недействителен, однако я могу в EtherScan установить квоту и маршрутизатор

Есть идеи? Я действительно ценю это.

Вот полный контракт

 // SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.0;
pragma abicoder v2;

import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol";
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/IQuoter.sol";

interface IUniswapRouter is ISwapRouter {
    function refundETH() external payable;
}

contract Uniswap3 {
  IUniswapRouter public constant uniswapRouter = IUniswapRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
  IQuoter public constant quoter = IQuoter(0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6);
  address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
  address private constant WETH9 = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;

  function convertExactEthToDai() external payable {
    require(msg.value > 0, "Must pass non 0 ETH amount");

    uint256 deadline = block.timestamp   15;
    address tokenIn = WETH9;
    address tokenOut = DAI;
    uint24 fee = 3000;
    address recipient = msg.sender;
    uint256 amountIn = msg.value;
    uint256 amountOutMinimum = 1;
    uint160 sqrtPriceLimitX96 = 0;
    
    ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
        tokenIn,
        tokenOut,
        fee,
        recipient,
        deadline,
        amountIn,
        amountOutMinimum,
        sqrtPriceLimitX96
    );
    
    uniswapRouter.exactInputSingle{ value: msg.value }(params);
    uniswapRouter.refundETH();
    
    // refund leftover ETH to user
    (bool success,) = msg.sender.call{ value: address(this).balance }("");
    require(success, "refund failed");
  }
  
  function convertEthToExactDai(uint256 daiAmount) external payable {
    require(daiAmount > 0, "Must pass non 0 DAI amount");
    require(msg.value > 0, "Must pass non 0 ETH amount");
      
    uint256 deadline = block.timestamp   15; // using 'now' for convenience, for mainnet pass deadline from frontend!
    address tokenIn = WETH9;
    address tokenOut = DAI;
    uint24 fee = 3000;
    address recipient = msg.sender;
    uint256 amountOut = daiAmount;
    uint256 amountInMaximum = msg.value;
    uint160 sqrtPriceLimitX96 = 0;

    ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams(
        tokenIn,
        tokenOut,
        fee,
        recipient,
        deadline,
        amountOut,
        amountInMaximum,
        sqrtPriceLimitX96
    );

    uniswapRouter.exactOutputSingle{ value: msg.value }(params);
    uniswapRouter.refundETH();

    // refund leftover ETH to user
    (bool success,) = msg.sender.call{ value: address(this).balance }("");
    require(success, "refund failed");
  }
  
  // do not used on-chain, gas inefficient!
  function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256) {
    address tokenIn = WETH9;
    address tokenOut = DAI;
    uint24 fee = 10000;
    uint160 sqrtPriceLimitX96 = 0;

    return quoter.quoteExactOutputSingle(
        tokenIn,
        tokenOut,
        fee,
        daiAmount,
        sqrtPriceLimitX96
    );
  }
  
  // important to receive ETH
  receive() payable external {}
}
 

Комментарии:

1. Я тоже сталкиваюсь с той же проблемой… в моем случае методы, доступные только для чтения, работают, но payable методы продолжают отказывать без всякой причины.