Add eth Withdraw Functionality in a Smart Contract and Emit Solidity Events

Share this video with your friends

Social Share Links

Send Tweet
Published 2 years ago
Updated 2 years ago

To reduce the amount of gas used, we will create a withdraw function that lets the owner of the contract withdraw funds that have been added when they want rather than withdraw with every transaction.

Your smart contract is ready to be used, since you have confidence in the code after testing it.

But there are a few things that you can add to improve the behavior and security of it.

Let's start by adding events.

Similar to any other programming language Events in solidity are a way to inform the calling application about the current state of the contract. Like notifying the javascript client about a change made to the contract allowing the client application to react to it.

Events are defined within the contract as a global and are called from inside a function. To declare an event you use the event keyword, followed by an identifier and the parameter list. The values passed to the parameters are then used to log the information that is saved as part of the transaction inside the block.

To send the event you'll use the emit keyword from anywhere inside the contract by passing the name of the event and the corresponding parameters.

For this contract you want to let the client application to know when a tip was successfully sent. To do that you'll create a new event named NewTip like this

event NewTip(address indexed from, string message, string name, uint256 amount);

It will accept an address to identify who sent the tip, the message of the tip and also the name and the amount of eth sent,

And then, it will be emitted from the sendTip function

// send the event
emit NewTip(msg.sender, _message, _name, msg.value);

This event will use the same parameters that the rest of the sendTip operation.

  • msg.sender to get the address of who sent the tip
  • _message the string with the message sent
  • _name the name of the sender
  • msg.value the amount of eth sent.

Instructor: [0:00] There is one more feature that you can add to your smart contract before deploying it. These are events. Events are a way to allow the client of the smart contract to know that something happened inside of it. Events are defined within the contract code and send it out by using the emit keyword. [0:23] Let's create an event called new tip using the event keyword, define the values that the event will have, an address type named from, and a string type to store the message, another string to store the name, and finally an integer to store the amount.

[0:45] Then, let's emit this event when the sendTip function is done, meaning after the update of the array passed to the emit call, the same values that you send to the array.

[0:59] Now, since you updated the contract, you need to test the changes. Let's test that the event is working. Should react to the tip event. This is, again, an asynchronous function.

[1:16] Perform a similar action as before by getting the sender address using the get signers function, define the amount you want to send, trigger the transaction, and wait for the transaction to be done.

[1:32] Now, you can check if the event was emitted from the contract by using the to emit matcher. Passing the contract instance, the name of the event, and the expected content of it. Let's run the test and the test fails because I'm missing await keyword in line 62.

[1:56] Let's add it and run the test again. Now, the tests are passing and you can be confident that the changes didn't break your smart contract. Next step is to add a bit of improvement to the contract code to spend less gas and improve security. Remember, every transaction on Ethereum will cost some gas.

[2:22] The current code of the contract is acting as a bridge to immediately move money from the sender to the contract owner meaning that two transactions for every tip sent are performed, but the contract can store eth and then allow the owner of the contract to withdraw it at any moment, meaning fewer transactions and therefore less gas.

[2:51] To accomplish this, you will need a function to perform the withdrawal. The function can only be called by the owner of the contract. To do this, you can use the require keyword to check that the color of the function represented by the message.sender variable is the same address as the owner of the contract.

[3:15] This pattern is so common that you can easily move this behavior to some utility function. These utilities are known as modifiers. A modifier is a way to change the behavior of a function without actually changing the logic of it. It will use the same logic as the code you just wrote, but also you need to add this underscore here.

[3:44] This underscore is known as wildcard. You can think of it as a placeholder. This placeholder will be replaced with the body of the function that is being modified at compile time. Now, update the withdraw function to use the modifier.

[4:03] Next step is to change the behavior of the sendTip function to store the money received instead of immediately sending over to the owner. For this, just remove these lines, now the function will store any received eth. This is because the function is labeled as payable.

[4:27] Now, let's write the logic to withdraw. First, check the amount stored in the contract, by getting the balance of the contract. Then, by using the require function, check that the balance is more than zero. Now, let's perform the withdrawal by using the call method.

[4:48] Finally, let's create a new event to let the client know that the withdrawal happened, emit the new event at the end of the withdrawal function, and let's update the test again. First, let's update the sendTip test.

[5:10] Now, you need the contract balance by using the ethers.provider.getBalance function on the contract address. Also, remove this, because you'll not use it anymore, and update this line to use the contract address instead of the owner. Let's clean up this unused variables and run the test.

[5:34] Let's write the scenario to check the new withdraw function. Let's retrieve the owner's balance, all the tips, and the contract balance. With tips array, you can calculate the amount of money received to compare it with the contract balance. Check that the sum of the tips is equal to the balance amount.

[6:03] Now, let's connect the owner and perform the withdrawal code. Same as before, let's use the change ether balance matcher to check the changes in the balance. Also, get the current hour of balance to compare it with the previous value. It's important to notice that the change in the contract balance is negative.

[6:28] You need to multiply the value using the [inaudible] function from the Bignumber object with negative one. Now, let's run the test. One more step, let's check that the new withdrawal event was sent. Copy this previous code here, change the name of the event, and the arguments used.

[6:53] The final scenario that you need is to check that no one else than the owner can perform the withdrawal transaction. This test is about checking that the transaction is reverted, if the sender is not the owner. Retrieve another user address and trigger the transaction by using the new address.

[7:17] Let's expect that the transaction is reverted, run the test, and see that all the tests are green. Now, you are confident that the smart contract is doing well.

[7:33] In this lesson, you updated the sendTip function to store the amount received, created a modifier to check that only the owner can perform certain action, and created a withdraw function that checked the contract balance and that the caller is the owner.

[7:56] Then, you wrote test for each of the newer scenarios and learned a new matter named change ether balance that checked that the balance of an address changed in certain amount.