The safer way to send funds from a contract

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Million Ether Homepage course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Million Ether Homepage, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Million Ether Homepage
  • [00:00 - 00:04] If you send funds to a normal address, it won't fail. It will just accept your funds.

    [00:05 - 00:14] But, in Ethereum, your contract can send funds to another contract. And when you send funds to a contract, that contract can run Logic 2.

    [00:15 - 00:24] And you don't always know whether it's going to a normal account or to a contract account. Sometimes the contract receiving your funds will run Logic that fails, and you need to be prepared for that.

    [00:25 - 00:32] Ignoring it can lead to really subtle bugs. In fact, sometimes the contract receiving your funds is malicious, and designed to deliberately fail.

    [00:33 - 00:40] We'll talk through an attack scenario like this in the future bank robber video . But for now, know that when you send funds, it might fail, and you need to deal with that.

    [00:41 - 00:43] Now, let's withdraw these funds. Let's start with two rules.

    [00:44 - 00:49] One, you have to withdraw all of your funds. That is, there's no partial withdrawals right now.

    [00:50 - 00:55] And two, an address can only withdraw their own funds. Here is a buggy and wrong way to do this.

    [00:56 - 01:09] So to the message sender, we'll send the value that's in balances of the message sender, and then we'll set their balances to zero. What's wrong with this code?

    [01:10 - 01:21] Well, the problem is that we're sending the funds, and then clearing the balance, without checking to see if the send succeeded. One failure scenario here is that the send fails because the receiving contract runs out of gas.

    [01:22 - 01:29] Then we set their balance to zero, but the ether wasn't transferred. In this case, we'd unfairly clear their balance, but keep their ether in the bank.

    [01:30 - 01:39] A worst scenario is possible, where all of the funds in the contract could be drained by an attacker. Because of a feature called Re-entrancy, I'll walk through an example of that in the bank robber video too.

    [01:40 - 01:50] Instead, what we should do is clear the balance, send the value, and then check to make sure the send succeeded. If it failed, we need to restore their old balance.

    [01:51 - 01:55] Let me show you three ways we could do this. First, we're going to pull the current balance into the variable "balance".

    [01:56 - 02:08] Next, we'll set the balance of this account to zero before sending any funds. This ensures that if the withdraw function is called again before we're done, the sender won't have any funds left over.

    [02:09 - 02:16] Next, we'll use .send() to send the balance and check the return value. If the return value is false, then we'll call the code in that block.

    [02:17 - 02:23] Here, we're manually resetting the balance back to what it was. This is a somewhat manual way of handling the error.

    [02:24 - 02:31] You would use this case when you want a little more control over how to handle the failure case. The second way we might write this starts out like the first.

    [02:32 - 02:40] Only in this case, if .send() fails, we're calling revert. revert() will revert all of the state changes that happened during this call.

    [02:41 - 02:55] This is different from the previous case in that the previous case handled the error by manually resetting the balance. But in this case, when we use revert() if our function was larger and had made other state changes, revert() would undo all of those as well.

    [02:56 - 03:02] revert() is a great idea to use when you want to say, this whole thing needs to fail. revert() all the changes so far.

    [03:03 - 03:12] And the third way is syntactic sugar over the second way. Here we'll use .transfer() which, if you recall, is effectively the same thing as calling .send() and revert() if it failed.

    [03:13 - 03:22] The main point is be careful when you send funds. Check to make sure they succeed and have a plan to deal with when sending the payment fails.