Reputation: 3
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
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