Eight
Eight

Reputation: 3

Error when Interacting with ETH in Uniswap Functions (swapExactETHForTokens, swapExactTokensForETH) using Hardhat

I am facing an issue while trying to interact with Ethereum (ETH) in UniswapV2 functions on Hardhat. Specifically, I encounter errors when using the swapExactETHForTokens and swapExactTokensForETH functions from the UniswapV2Router contract. The rest of the Uniswap functions that interacts between two ERC-20 tokens appear to work fine, but the functions use ETH for its inpur or output (e.g., swapExactETHForTokens, swapExactTokensForETH) are causing errors. For your information, I am using ethers 5.7.2.

Could anyone provide insights into why I am facing this issue and how it can be resolved? I will provide the code and error log below. Any help would be greatly appreciated. Thank you!

Smart contract code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import "./interfaces/IERC20.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/IUniswapV2Router.sol";

contract UniswapV2Helper {

    IUniswapV2Router uniswap;
    
    address public immutable WETH;
    address public owner;

    constructor(address _WETH){
        WETH = _WETH;
        owner = msg.sender;
    }

    address private constant UNISWAP_V2_ROUTER =
        0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
        // sepolia 0xC532a74256D3Db42D0Bf7a0400fEFDbad7694008 

    IUniswapV2Router private constant router =
        IUniswapV2Router(UNISWAP_V2_ROUTER);

// 

    //1. token exact amount in, exact token -> token 
    function swapExactTokensForTokens(address tokenIn, address tokenOut, uint amountIn, uint amountOutMin)
        external 
    {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).approve(UNISWAP_V2_ROUTER, amountIn);
        address[] memory path = new address[](2);
        path[0] = tokenIn; //input token
        path[1] = tokenOut; //output token
        router.swapExactTokensForTokens(
            amountIn,
            amountOutMin,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    //2. token exact amount out, token -> exact token
    function swapTokensForExactTokens(
        address tokenIn,
        address tokenOut,
        uint amountOutDesired,
        uint amountInMax
    ) external {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountInMax);
        IERC20(tokenIn).approve(UNISWAP_V2_ROUTER, amountInMax);
        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = tokenOut;
        router.swapTokensForExactTokens(
            amountOutDesired,
            amountInMax,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
        IERC20(tokenIn).transfer(msg.sender, IERC20(tokenIn).balanceOf(address(this)));
    }

    //3. ETH exact amount in, exact ETH -> token
    function swapExactETHForTokens(address tokenOut, uint amountOutMin)
        external payable
    {
        address[] memory path = new address[](2);
        path[0] = WETH;
        path[1] = tokenOut;
        router.swapExactETHForTokens{value: msg.value}(
            amountOutMin,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    //4. ETH exact amount out, token -> exact ETH
    function swapTokensForExactETH(address tokenIn, uint amountOut, uint amountInMax)
        external
    {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountInMax);
        IERC20(tokenIn).approve(UNISWAP_V2_ROUTER, amountInMax);

        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = WETH;
        router.swapTokensForExactETH(
            amountOut,
            amountInMax,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    // 5. token exact amount in, exact token -> ETH
    function swapExactTokensForETH(address tokenIn, uint amountIn, uint amountOutMin)
        external
    {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).approve(UNISWAP_V2_ROUTER, amountIn);

        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = WETH;
        router.swapExactTokensForETH(
            amountIn,
            amountOutMin,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    // 6. token exact amout out, ETH -> exact token
    function swapETHForExactTokens(address tokenOut, uint amountOut)
        external payable
    {
        address[] memory path = new address[](2);
        path[0] = WETH;
        path[1] = tokenOut;
        router.swapETHForExactTokens{value: msg.value}(
            amountOut,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        address tokenIn, 
        address tokenOut, 
        uint amountIn, 
        uint amountOutMin
        )
        external {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).approve(UNISWAP_V2_ROUTER, amountIn);
        address[] memory path = new address[](2);
        path[0] = tokenIn; //input token
        path[1] = tokenOut; //output token
        router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            amountIn,
            amountOutMin,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        address tokenOut, 
        uint amountOutMin
    ) external payable{
        address[] memory path = new address[](2);
        path[0] = WETH;
        path[1] = tokenOut;
        router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: msg.value}(
            amountOutMin,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    function swapExactTokensForETHSupportingFeeOnTransferTokens(address tokenIn, uint amountIn, uint amountOutMin)
        external
    {
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).approve(UNISWAP_V2_ROUTER, amountIn);

        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = WETH;
        router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            amountIn,
            amountOutMin,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    function sellMyToken(address token, uint amountIn) external {
        
        //need to transfer the token manually
        IERC20(token).approve(UNISWAP_V2_ROUTER, amountIn);

        address[] memory path = new address[](2);
        path[0] = token;
        path[1] = WETH;
        router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            amountIn,
            1,
            path,
            msg.sender,
            block.timestamp + 3 minutes
        );
    }

    function withdrawETH() public {
        require(msg.sender == owner, "You are not the owner");
        
        payable(owner).transfer(address(this).balance);
    }

    function withdrawToken(address tokenAddress) public {
        require(msg.sender == owner, "You are not the owner");

        IERC20(tokenAddress).transfer(owner, address(this).balance);
    }

    receive() external payable {}
    fallback() external payable {}
}

test-swap.js

const {ethers} = require("hardhat")
const {assert, expect} = require("chai")

const WETH9 = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
const DAI   = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
describe("UniswapV2Helper", () => {

  let accounts;
  let contractFactory;
  let contract;
  let weth
  let dai
  let provider

  beforeEach(async ()=>{
    accounts = await ethers.getSigners()
    provider = ethers.provider

    // connect to interface
    weth = await ethers.getContractAt("IWETH", WETH9)
    dai  = await ethers.getContractAt("IERC20", DAI)

    // deploy contract
    contractFactory = await ethers.getContractFactory("UniswapV2Helper")
    contract = await contractFactory.deploy(WETH9)

  })

  it("Should return DAI Token", async () => {
    const amountETHIn = ethers.utils.parseEther("1.0");
    const amountOutMin = 1;
  
    const transactionResponse = await contract.connect(accounts[0]).swapExactETHForTokens(dai, amountOutMin, { value: amountETHIn });
    await transactionResponse.wait(1);
  
    console.log("Transaction Hash: ", transactionResponse.hash);
  
    const finalDAIBalance = await dai.balanceOf(accounts[0].address);
  
    console.log("Final DAI balance: ", amountETHIn);
  });

  it.only("Should return ETH from DAI", async () => {
    const amountDAIIn = ethers.utils.parseEther("1.0");
    const amountOutMin = 1;

    await dai.connect(accounts[0]).approve(contract.address, amountDAIIn)
  
    const transactionResponse = await contract.swapExactTokensForETH(dai, amountDAIIn, amountOutMin);
    await transactionResponse.wait(1);
   
    console.log("Got ETH: ", provider.getBalance(accounts[0]));
  });
})

Error that I got:

UniswapV2Helper

    1) Should return ETH from DAI


  0 passing (3s)
  1 failing

  1) UniswapV2Helper
       Should return ETH from DAI:
     Error: invalid address or ENS name (argument="name", value={"interface":{"fragments":[{"name":"Approval","anonymous":false,"inputs":[{"name":"owner","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"spender","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"value","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"type":"event","_isFragment":true},{"name":"Transfer","anonymous":false,"inputs":[{"name":"from","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"to","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"value","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"type":"event","_isFragment":true},{"type":"function","name":"allowance","constant":true,"inputs":[{"name":"owner","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"spender","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true}],"outputs":[{"name":null,"type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"payable":false,"stateMutability":"view","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},{"type":"function","name":"approve","constant":false,"inputs":[{"name":"spender","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"amount","type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"outputs":[{"name":null,"type":"bool","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"bool","_isParamType":true}],"payable":false,"stateMutability":"nonpayable","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},{"type":"function","name":"balanceOf","constant":true,"inputs":[{"name":"account","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true}],"outputs":[{"name":null,"type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"payable":false,"stateMutability":"view","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},{"type":"function","name":"totalSupply","constant":true,"inputs":[],"outputs":[{"name":null,"type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"payable":false,"stateMutability":"view","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},{"type":"function","name":"transfer","constant":false,"inputs":[{"name":"to","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"amount","type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"outputs":[{"name":null,"type":"bool","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"bool","_isParamType":true}],"payable":false,"stateMutability":"nonpayable","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},{"type":"function","name":"transferFrom","constant":false,"inputs":[{"name":"from","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"to","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"amount","type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"outputs":[{"name":null,"type":"bool","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"bool","_isParamType":true}],"payable":false,"stateMutability":"nonpayable","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true}],"_abiCoder":{"coerceFunc":null},"functions":{"allowance(address,address)":{"type":"function","name":"allowance","constant":true,"inputs":[{"name":"owner","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"spender","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true}],"outputs":[{"name":null,"type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"payable":false,"stateMutability":"view","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},"approve(address,uint256)":{"type":"function","name":"approve","constant":false,"inputs":[{"name":"spender","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"amount","type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"outputs":[{"name":null,"type":"bool","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"bool","_isParamType":true}],"payable":false,"stateMutability":"nonpayable","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},"balanceOf(address)":{"type":"function","name":"balanceOf","constant":true,"inputs":[{"name":"account","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true}],"outputs":[{"name":null,"type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"payable":false,"stateMutability":"view","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},"totalSupply()":{"type":"function","name":"totalSupply","constant":true,"inputs":[],"outputs":[{"name":null,"type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"payable":false,"stateMutability":"view","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},"transfer(address,uint256)":{"type":"function","name":"transfer","constant":false,"inputs":[{"name":"to","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"amount","type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"outputs":[{"name":null,"type":"bool","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"bool","_isParamType":true}],"payable":false,"stateMutability":"nonpayable","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true},"transferFrom(address,address,uint256)":{"type":"function","name":"transferFrom","constant":false,"inputs":[{"name":"from","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"to","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"amount","type":"uint256","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"outputs":[{"name":null,"type":"bool","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"bool","_isParamType":true}],"payable":false,"stateMutability":"nonpayable","gas":{"type":"BigNumber","hex":"0x01ba8140"},"_isFragment":true}},"errors":{},"events":{"Approval(address,address,uint256)":{"name":"Approval","anonymous":false,"inputs":[{"name":"owner","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"spender","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"value","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"type":"event","_isFragment":true},"Transfer(address,address,uint256)":{"name":"Transfer","anonymous":false,"inputs":[{"name":"from","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"to","type":"address","indexed":true,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true},{"name":"value","type":"uint256","indexed":false,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"uint256","_isParamType":true}],"type":"event","_isFragment":true}},"structs":{},"deploy":{"name":null,"type":"constructor","inputs":[],"payable":false,"stateMutability":"nonpayable","gas":null,"_isFragment":true},"_isInterface":true},"provider":"<WrappedHardhatProvider>","signer":"<SignerWithAddress 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266>","callStatic":{},"estimateGas":{},"functions":{},"populateTransaction":{},"filters":{},"_runningEvents":{},"_wrappedEmits":{},"address":"0x6B175474E89094C44Da98b954EedeAC495271d0F","resolvedAddress":{}}, code=INVALID_ARGUMENT, version=contracts/5.7.0)
      at Logger.makeError (node_modules\@ethersproject\logger\src.ts\index.ts:269:28)
      at Logger.throwError (node_modules\@ethersproject\logger\src.ts\index.ts:281:20)
      at Logger.throwArgumentError (node_modules\@ethersproject\logger\src.ts\index.ts:285:21)
      at D:\About Projects\UniswapV2\node_modules\@ethersproject\contracts\src.ts\index.ts:123:16
      at step (node_modules\@ethersproject\contracts\lib\index.js:48:23)
      at Object.next (node_modules\@ethersproject\contracts\lib\index.js:29:53)
      at fulfilled (node_modules\@ethersproject\contracts\lib\index.js:20:58)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at runNextTicks (node:internal/process/task_queues:64:3)
      at listOnTimeout (node:internal/timers:538:9)

Upvotes: 0

Views: 1132

Answers (1)

Ibrahim Khalil
Ibrahim Khalil

Reputation: 26

The error message tells you that the value you are passing is not an address.

const transactionResponse = await contract.swapExactTokensForETH(dai, amountDAIIn, amountOutMin);

The swapExactTokensForETH() function of your contract takes an address data type for its first argument, but you accidentally provided a contract instance of the DAI token instead.

To resolve the error, you need to pass the actual Ethereum address of the DAI token, rather than the contract instance

Upvotes: 0

Related Questions