2

When I try to make a deposit, Ethereum is transferred but the Wrapped ether is not received. Here is my code

interface IWETH is IERC20 {
  receive() external payable;

  function deposit() external payable;

  function withdraw(uint256 wad) external;
}

contract Swap {
  address payable private constant  WETH =
    0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

  function wrapEther() external payable {
    uint256 ETHAmount = msg.value;

    //create WETH from ETH
    if (msg.value != 0) {
      IWETH(WETH).deposit{ value: ETHAmount }();
    }
    require(
      IWETH(WETH).balanceOf(address(this)) >= ETHAmount,
      "Ethereum not deposited"
    );
  }

  function unwrapEther(uint256 Amount) external {
    address payable sender = msg.sender;

    if (Amount != 0) {
      IWETH(WETH).withdraw(Amount);
      sender.transfer(address(this).balance);
    }
  }

}

hard-hat config

import "@nomicfoundation/hardhat-toolbox";
import "hardhat-contract-sizer";
const config = {
  defaultNetwork: "ganache",
  solidity: "0.7.6",
  paths: {
    artifacts: "./src/artifects",
  },
  networks: {
    ganache: {
      allowUnlimitedContractSize: true,
      url: "http://127.0.0.1:8545",
    },
  },
  settings: {
    optimizer: {
      enabled: true,
      runs: 1,
    },
  },
};

export default config;

here is the error after unwrapEther function is being called

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error.
{
"message": "VM Exception while processing transaction: revert",
"code": -32000,
"data": {
"stack": "c: VM Exception while processing transaction: revert\n at Function.c.fromResults
9
  • Just to be sure we're on the same page: In the code above, the WETH should end up in the Swap contract and not in the caller's wallet. Sep 28, 2022 at 17:18
  • Hi, Actually it should end up in the caller's wallet. @AhmedIhsanTawfeeq
    – Yaya
    Sep 28, 2022 at 17:32
  • How are you deploying that contract? On mainnet? On a local fork? Or on a testnet?
    – Foxxxey
    Sep 28, 2022 at 17:38
  • @YahyaParvar if you want it to end up in the caller's wallet, you need to add another step to transfer the WETH from the swap contract to the owner's wallet. This would invalidate the "require" statement you have at the end of "wrapEther()". Sep 28, 2022 at 17:39
  • On a local fork @Foxxxey
    – Yaya
    Sep 28, 2022 at 17:53

3 Answers 3

5
+150

I changed the implementation of wrapEther() as follows:

  1. Add a step to transfer the minted WETH from the Swap contract to the caller's wallet.
  2. Fix the require statement, since the original one had a bug when the caller already had a non-zero balance of WETH.

In a real-life scenario, you should not be using that require statement since it adds cost. Instead, you would test your contracts using a test framework to ensure the code provides the correct behavior.

function wrapEther() external payable {
    uint256 balanceBefore = IWETH(WETH).balanceOf(msg.sender);
    uint256 ETHAmount = msg.value;

    //create WETH from ETH
    if (ETHAmount != 0) {
      IWETH(WETH).deposit{ value: ETHAmount }();
      IWETH(WETH).transfer(msg.sender, ETHAmount);
    }
    require(
      IWETH(WETH).balanceOf(msg.sender) - balanceBefore == ETHAmount,
      "Ethereum not deposited"
    );
  }

As for unwrapEther(...), you need to transfer the required WETH from the caller's wallet to the Swap contract before calling withdraw(...) as follows:

//Extremely important!!!!
receive() external payable {}

function unwrapEther(uint256 Amount) external {
    address payable sender = msg.sender;

    if (Amount != 0) {
      IWETH(WETH).transferFrom(msg.sender, address(this), Amount);
      IWETH(WETH).withdraw(Amount);
      sender.transfer(address(this).balance);
    }
  }

But before calling unwrapEther(...), the caller's wallet needs to approve the Swap contract to spend Amount WETH tokens on its behalf. You can go to the WETH contract page via Etherscan, connect the caller wallet via Metamask, and then call approve by providing the Swap contract address in guy field and Amount in wad field.

10
  • @YahyaParvar I've updated my answer to include unwrapEther Sep 29, 2022 at 16:43
  • It gets reverted even though I approved to send the WETH from ERC20 approve function
    – Yaya
    Sep 29, 2022 at 17:27
  • @YahyaParvar could you send me the link to the failed transaction? Sep 29, 2022 at 18:44
  • Also, make sure that Amount is less than or equal to the caller's WETH balance. Sep 29, 2022 at 18:46
  • The transaction is happening on a local fork of mainnet therefore I don't think we can access the link since it's not available on etherscan or something similar. But I added the error message to the updated question :)
    – Yaya
    Sep 29, 2022 at 19:34
1

I tried to reproduce your contract, it's giving the same thing, the total balance doesn't change after swap. I'm still trying, but have you tried using another contract? I tested this one, and it works pretty good:

pragma solidity ^0.4.18;

contract WETHSwap {
    string public name = "Wrapped Ether";
    string public symbol = "WETH";
    uint8 public decimals = 18;

    event Approval(address indexed src, address indexed guy, uint256 wad);
    event Transfer(address indexed src, address indexed dst, uint256 wad);
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    function() public payable {
        deposit();
    }

    function deposit() public payable {
        balanceOf[msg.sender] += msg.value;
        Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 wad) public {
        require(balanceOf[msg.sender] >= wad);
        balanceOf[msg.sender] -= wad;
        msg.sender.transfer(wad);
        Withdrawal(msg.sender, wad);
    }

    function totalSupply() public view returns (uint256) {
        return this.balance;
    }

    function approve(address guy, uint256 wad) public returns (bool) {
        allowance[msg.sender][guy] = wad;
        Approval(msg.sender, guy, wad);
        return true;
    }

    function transfer(address dst, uint256 wad) public returns (bool) {
        return transferFrom(msg.sender, dst, wad);
    }

    function transferFrom(
        address src,
        address dst,
        uint256 wad
    ) public returns (bool) {
        require(balanceOf[src] >= wad);

        if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) {
            require(allowance[src][msg.sender] >= wad);
            allowance[src][msg.sender] -= wad;
        }

        balanceOf[src] -= wad;
        balanceOf[dst] += wad;

        Transfer(src, dst, wad);

        return true;
    }
}

It wasn't me who did it, I found it in some library in github. Hope this helps

4
  • Well this is just another WETH contract. I believe OP wants to interact with the actual WETH contract deployed at 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, not make its own version
    – Foxxxey
    Sep 28, 2022 at 17:28
  • That is correct. @Foxxxey
    – Yaya
    Sep 28, 2022 at 17:33
  • Thanks for your effort. But actually I wanted to call the actual WETH contract and not writing one on my own :)
    – Yaya
    Sep 28, 2022 at 17:34
  • Awesome... I was still looking, and I found out something. When you do the swap, the Balance stay in your New Deployed Contract, so you need add one more step to just after swap, your contract transer the WETh amout to the caller.
    – brunovjk
    Sep 28, 2022 at 18:31
1

The chain you're deploying on is an empty chain, not a mainnet fork. The WETH contract doesnt exist on that chain if you don't deploy it. Read https://hardhat.org/hardhat-network/docs/guides/forking-other-networks

EDIT : Here's the fixed code :

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {

  function deposit() external payable;

  function withdraw(uint256 wad) external;
}

contract Swap {
  address private constant  WETH =
    0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

  function wrapEther() external payable {
    uint256 ETHAmount = msg.value;

    //create WETH from ETH
    if (msg.value != 0) {
      IWETH(WETH).deposit{ value: ETHAmount }();
    }
    require(
      IWETH(WETH).balanceOf(address(this)) >= ETHAmount,
      "Ethereum not deposited"
    );
    // transfer will do, you don't need to use transferFrom, use it only when the tokens you want to transfer arent held by the contract (like in unwrapEther())  
    IWETH(WETH).transfer(msg.sender, IWETH(WETH).balanceOf(address(this)));
  }
  
  // To receive ETH from the WETH's withdraw function (it won't work without it) 
  receive() external payable {}
  
  
  function unwrapEther(uint256 Amount) external {
    address payable sender = payable(msg.sender);
    if (Amount != 0) {
      // Taking tokens from a wallet require allowance, look up https://eips.ethereum.org/EIPS/eip-20#methods, especially the paragraphs on transferFrom() and approve()
      require(IWETH(WETH).allowance(msg.sender, address(this)) >= Amount, "insufficient allowance");
      IWETH(WETH).transferFrom(msg.sender, address(this), Amount);
      IWETH(WETH).withdraw(Amount);
      sender.transfer(address(this).balance);
    }
  }
}
6
  • I'm running npx ganache-cli -f https://mainnet.infura.io/v3/f758ee8390c4409b969064ae326b082d. Other contracts exist on the network. I think problem is with something else what do you think?
    – Yaya
    Sep 28, 2022 at 18:12
  • Oh, wierd. Well it should work then, as far as i can see. Does the transaction completes, or does it revert when you call wrapEther?
    – Foxxxey
    Sep 28, 2022 at 18:12
  • It does get completed. But the wrapped ether is not in caller's wallet lol
    – Yaya
    Sep 28, 2022 at 18:13
  • Oh, well yeah that's normal, it's in the contract. I'll edit my answer with the fixed code, just give me a min :)
    – Foxxxey
    Sep 28, 2022 at 18:14
  • Don't you think we should transfer the WETH to sender's waller via calling transferFrom(address(this), msg.sender) ?
    – Yaya
    Sep 28, 2022 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.