Hi @Bitborn,
No… neither of these functions receive ether, so they don’t need to be marked payable
. A function should only be marked payable
when it needs to receive ether from an external address to add to the contract address balance e.g. the deposit() function. It might help to think of payable
in a function header as the code that enables msg.value
to be added to the contract address balance, without us having to add any further code for this. In the deposit() function, this happens automatically because we have marked the function as payable
. The line …
balance[msg.sender] += msg.value;
… then adds the same value to the individual user’s balance in the mapping, in order to keep track of their share of the total funds held in the contract.
However, we should not mark the withdraw() function as payable
, because it doesn’t need to receive any ether from an external address and add it to the contract balance. If we make it payable
, then, if the caller sends an ether value to the withdraw() function by mistake (as well as calling it with a withdrawal amount) then this ether would be added to the contract balance and potentially lost to the user: this is because the withdraw() function does not make an equivalent accounting adjustment to the mapping for such a deposit (only for withdrawals). However, the function is much securer if we leave it as non-payable, because then, if the caller makes the same mistake, the function will revert, and the ether won’t be sent.
Effectively, the withdraw() function does the opposite of what the deposit() function does: it deducts ether from the contract balance and sends it to an external address. It does this using the address member transfer
(which is like a method called on a payable address)…
<address payable>.transfer(uint amount);
The separate transfer() function (not the special transfer
method in the withdraw function) shouldn’t be marked payable
either, for the same reasons I’ve already outlined for the withdraw() function. In fact, calling the transfer() function doesn’t involve any ether entering or leaving the Bank contract at all. Instead, it performs an internal transfer of funds (effectively, a reallocation of funds) between two individual users of the Bank contract. The net effect to the contract balance is zero, and the only change is to the individual user balances in the mapping, in order to adjust the amount each of the parties involved in the internal transfer is entitled to withdraw from the contract (their share of the total pooled funds) i.e. the recipient’s entitlement (balance) is increased by the same amount the sender’s entitlement is reduced.
Yes, you are right that we need this line of code. I’ve checked the solution code for the Inheritance Assignment in GitHub, and this line of code is missing in the withdraw() function in the Bank contract. This is an error. Well spotted!
msg.sender.transfer(amount)
transfers ether from the contract address balance to the caller’s external address, but it doesn’t adjust the individual user balances in the mapping. As I’ve explained above, these balances perform an internal accounting role, and record each user’s share of the contract’s total ether balance. So, just as we increase a user’s individual balance when they deposit ether in the contract, we need to do the opposite whenever they make a withdrawal.
If you still have difficulties depositing, withdrawing and internally transferring funds with your deployed Bank contract, you may find this post helpful. It lays out step-by-step instructions of how to properly check that all of your functions are working correctly, and it details what you should see happening and where, if it is.
You can check the Bank contract’s ether balance if you add the following function to your Bank contract (if you don’t already have it) …
function getContractBalance() public view returns(uint) {
return address(this).balance;
}
Let me know if anything is still unclear, or if you have any further questions