2

This might be a basic question, but still dealing with conceptual stuff about ethereum and solidity.

Original question was: How does solidity send() & transfer() know about contract ether? Editing it to: Why function that does transfer without a mapping fails?

  • If a contract wants to hold ether you create a payable function that receives the msg.value.

  • To withdraw ether you would specify a function with the intended address destination and amount.

However when I test the second statement it only seems to be true if I use a mapping. On the example below not concerned about security, so this code would not apply to real life.

If I do another function that does not a mapping, no error is returned, and although it succeds performing the transaction, no ETHER is trully withdrawn.

To me both are the same, one keeps a track of addresses and the other one does not, however the transfer is the same.

So based on this behavior is where my confusion stems on how a function to withdraw knows it is ether belonging to the contract. To deposit we have "payable" but no equivalent to withdraw.

I know one could do msg.sender.transfer(address(this).balance) where address(this) refers to the contract.

pragma solidity >=0.5.0 <0.6.0;

contract CheckBehavior {

    mapping(address => uint256) _deposits;

    function depo() public payable{

        _deposits[msg.sender]= msg.value;

    }

//not working
    function withdrawA(uint256 _amt) public {

        msg.sender.transfer(_amt);
    }

//not working , I would expect this to work fine as withdrawWithGas()

    function withdrawB(address payable payee, uint256 _amt) public {

        payee.transfer(_amt);
}

//Works. My understanding is that the mapping is a security pattern to avoid re-entrancy, as
this keeps track of addresses and one can condition based on it
    function withdrawWithGas(address payable payee) public {
        uint256 payment = _deposits[payee];

        _deposits[payee] = 0;

        payee.transfer(payment);
    }

}

First image is the transaction of the deposit, money reflects as subtracted fine on remix.

deposit

Second image is transaction of withdrawA, which does transact but does not give me any ether. If I use instead the last one it works.

withdraw

1 Answer 1

3

After testing in Remix everything works just fine. I deployed the code, I called depo with value and tested all the functions. Because I'm not sure if you can see contract's balance in some way directly in Remix I added an extra function to return the current contract balance and called that function after every withdraw function call to verify the results.

A few notes:

  • Your picture shows different code compared to the question's code. It calls function withdraw which doesn't exist in your code.
  • If you first execute any of the other functions to take out Ether the last function will fail as the contract doesn't hold enough Ether anymore.

Here's my experiment with gas costs. You can do the same and you'll see that the numbers match:

  1. Originally wallet has 100 Ethers
  2. Deploy contract costs 280398 gas. Wallet has 99,...719602 Ethers.
  3. Transact with depo: tx costs 41472 gas and I send 500 wei. Wallet has 99,...677630 Ethers.
  4. Transact with withdrawA: tx costs 29217 gas and I request 200 wei. Wallet has 99,...648613 Ethers. Everything matches and I get 200 wei.

Here's the full code I used:

pragma solidity ^0.5.0;

contract CheckBehavior {

    mapping(address => uint256) _deposits;

    function depo() public payable{
        _deposits[msg.sender]= msg.value;
    }

    function checkBalance() public returns (uint) {
        return address(this).balance;
    }

    function withdrawA(uint256 _amt) public {

        msg.sender.transfer(_amt);
    }

    function withdrawB(address payable payee, uint256 _amt) public {

        payee.transfer(_amt);
    }

    function withdrawWithGas(address payable payee) public {
        uint256 payment = _deposits[payee];

        _deposits[payee] = 0;

        payee.transfer(payment);
    }
}
3
  • I added the A to distinguish between the other withdraw function, code is the same. Due to lazy I never created checkbalance, perhaps I would have noticed ether got depleted in the first place. I'm able to validate your second note, so then my question would be: where the ether goes if it is not returning to msg.sender when using withdrawA(), as that function is still not working for me and should be the same as last one right?
    – DRP
    Jan 9, 2020 at 18:36
  • Modified my answer and added instructions for my experiment. According to the experiment all numbers are correct and everything works as it should. Jan 9, 2020 at 20:19
  • I see my mistake now... when doing deposit the dropdown is in ether so 1 is in fact 1000000000000000000, and when doing the withdraw I was typing 1 as this was ether when in fact it was wei. Thanks for your diligence.
    – DRP
    Jan 9, 2020 at 20:29

Your Answer

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

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