Hi @nbkeaton,
You have added both of the essential lines of code needed to solve the problem with the withdraw function, and you have correctly adjusted the user’s individual Bank balance…
balance[msg.sender] -= amount;
… before actually transferring the funds out of the contract to the user’s external address…
msg.sender.transfer(amount);
This is to prevent what is known as a re-entrancy attack from occuring after the transfer, but before the user’s individual balance (effectively, their entitlement) is reduced to reflect this operation. You’ll learn more about this type of attack, and how the above order of operations helps to prevent it, in the courses which follow this one.
Your require statement is correctly coded, but it should be placed at the very beginning of your function body, before the user’s individual Bank balance is adjusted. To reduce security risk in smart contracts, the following order of statements should be used…
- Check inputs (require statements)
- Effects (update the contract state)
- External Interactions (e.g.
transfer
method)
Executing the require statement first will also maximise the amount of gas refunded if it fails and revert
is triggered, because if this happens any gas consumed executing code up to and including the require statement will not be refunded.
Your event declaration and corresponding emit statements within the deposit() and withdraw() functions are accurately coded. Both emit statements are in the correct position within their respective function bodies, and they will log the data when these functions are successfully executed.
I can see that your use of the same event declaration to emit events for all 3 functions is a logical attempt to re-use code. However, each function performs a transaction which is different from the others in important ways, and by emitting all events with the name depositDone
, we risk losing this disinction and causing confusion. Furthermore, the third event parameter name depositedTo
incorrectly describes the sender’s address (msg.sender
), which is emitted for this parameter in the transfer() function. The funds are transferred from msg.sender
and “deposited to” the recipient
.
Where the deposit() and withdraw() functions each execute a transaction that only involves 1 user address (the depositer or withdrawer), the transfer() function executes a transaction that involves 2 user addresses (the sender and the recipient). So, it would be useful to include the recipient’s address, as well as the sender’s address, within the data emitted for this particular transaction event.
So, I think we need to have three separate event declarations, each with a name that clearly describes the specific type of transaction it is associated with: (i) ETH transferred from an external address and deposited into the Bank contract, (ii) ETH withdrawn from the Bank contract and transferred to an external address, or (iii) an internal transfer of funds within the Bank contract between two users, which is effectively a reallocation of each user’s share of the Bank contract’s total ETH balance without any change to the actual Bank contract balance itself.
Including a user’s new balance within the event data is a good idea, but for the data emitted by the transfer() function, you need to make it clear whether this is the sender’s new balance or the recipient’s, because this isn’t clear from the parameter name balance
.
Also, in the transfer() function it is probably better to place the emit statement after the assert statement.
Notice that the withdraw function header also includes returns(uint)
This is not mandatory to include, and the function can still operate effectively without returning a value. But as you’ve included it in the function header, you should also include a return statement at the end of your function body. The compiler should have given you a yellow/orange warning for this.
Let me know if anything is unclear, or if you have any questions