Swap ETH to DAI using ISwapRouter from Uniswap

Viewed 1275

I am trying to get started with Uniswap V3. As an example, I took the most basic use case: Given X amount of ETH do a swap to DAI. Unfortunately, I am not being able to make it work.

There is already a very similar question (with no answer) but slightly different as the code doesn't look like mine.

I am using Hardhat to fork the main-net and then I connect Remix to localhost:8545

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

Hardhat config down below:

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

As you can notice (full contract at the very bottom), the contract offers 3 payable functions:

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

All of them fail, even getEstimatedETHforDAI, which is fairly simple and readonly (almost). There is no given reason, so I am blind. When I execute the function from Remix, I just get a generic error "Returned error: Error: Transaction reverted without a reason string". When I look at hardhat console, I see this error:

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:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1582:23)
      at HardhatNode.mineBlock (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:435:16)
      at EthModule._sendTransactionAndReturnHash (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1494:18)
      at HardhatNetworkProvider._sendWithLogging (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:129:22)
      at HardhatNetworkProvider.request (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:106:18)

Looks like the contract is invalid, however I can in EtherScan the Quoter and the Router

Any idea? I really appreciate it.

Here is the full contract

// 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 {}
}
0 Answers
Related