This course provides a deep dive into the Solidity programming language which is used to implement smart contracts on blockchain platforms. This course will give you a practical understanding of Solidity and how to develop your own smart contracts.
In the previous lecture, we were talking about the concept of time and somewhere in this section, we were already talking briefly about exceptions, but in this lecture I really want to highlight what they are, how they are used, and how they are not used and what exactly or how this concept of exceptions works in Solidity because it's slightly different from other programming languages. Now I want to start in this lecture with a very, very simple smart contract and it's pretty much the same as we had in previous lectures. Let me just quickly guide you through the smart contract in order for you to understand what's going on here. I have one constructor and this constructor is called when the smart contract is deployed and is setting an address which is a storage variable called owner to the address that is deploying the smart contract. So, in essence if I'm going to deploy a smart contract from this account, then this address will be stored here. Then I have one modifier that compares the current sending transaction's person the from field to the owner which is stored in this stage variable which was the owner that was set during deployment of the transaction. And we have one function which is setting a Boolean if this smart contract is running or not. So, this is going to be the outcome. So, it's going to set this Boolean as running to true or not, but this can only be done by the person who is the owner of the smart contract. So, this is a very standard architecture, how you can have an admin user in your smart contract. But it goes on. Now we have one function to deposit money and we actually want this function only to work if the smart contract is running. And in previous lectures of this section, we were working simply with if else control mechanisms. The problem here is that if else, if isRunning is true then increase the balance. This doesn't really work here. Let me make this public and let me show you what I mean. So, I'm going to deploy the smart contract and in my current balance there is zero and if I set this running to true now and deposit one ether, then the balance is obviously one ether. And now I set this to false and I again deposit one ether and have a look here at the account balance is 98.99999 and so on. So, I deposit one ether, now it's 97.9999 and so on. So, the ether was deposited, but it wasn't credited to my mapping here because of my if(isRunning). I still have only one ether in my mapping, but the ether is actually deposited to my smart contract. So, my smart contract has now two either even though my balance mapping just didn't record this either. So, I would need to have another way of just completely interrupting the transaction and stopping the execution, and this is done with exceptions. In this case we are going to use require for this and this will stop the execution of a smart contract, stop the execution of the transaction, and revert everything from that stage on. And they're actually free commands that you can use in Solidity in order to stop a transaction. The one is require, the other one is assert and the third one is revert. Require and assert both evaluate an expression and if that evaluates to false then they will stop a transaction. Let me give you an example. So, instead of if(isRunning), we could say require(isRunning) and this is really just a command in here. It's like a function call and this also takes an error string, require(isRunning) and if there is an exception from then this string will be returned, "The smart contract is not running currently!" So, if I'm going to run this smart contract, I'll deploy this and initially I set this isRunning to false, so I can directly try and deposit one ether and over the left side here you will see the transaction has an error, deposit money error and somewhere it says reason provided by the contract. The smart contract is not running currently. And I can debug this, I can drill into this transaction and see why this happened. And what you also see that I still have my 97.999 ether in here, so I can also try to do this with 14 ether, everything will be returned and this is the whole point of having exceptions in Solidity, you cannot catch them. There is no try catch block here in Solidity itself, but you can react to exceptions outside of Solidity. So, when you interact with your smart contract from JavaScript, which we're going to do later and there is an exception, then you can catch this exception outside and react to it and give the user some feedback what happened. In this case, it's pretty obvious our smart contract isn't running at the moment, so we have to set this to running in order to deposit one ether. I have to set this to running first and then deposit one ether and this is working. And now our balance of our address is the ether that we sent to the smart contract. Then the second one is assert which it should work, it's the same but it doesn't allow you any error message. Assert is really just used in asserting internal stages which is so called invariants. So, for example if you withdraw money, then you would want to assert that the balance that you deducted from the amount that the person want to withdraw is smaller than the balance before, so you have no wrap-arounds. So, this is really just used for internal stage validation that nothing is out of order. So, most of the time you want to use a require with an error message like this, then there's a third one which is revert and revert does really just revert to the previous stage as you might have guessed it. It's like require false. But that would be the same as a revert. Let me show you how this is used in practice though I seldom use it. I've actually never used it anymore since we have require. if(isRunning) then do this or else revert with the error message; "The smart contract is not running currently!" So, if I'm going to deploy this and I try to deposit one ether then I will also get the exception, "The smart contract is not running currently!" There is one big difference between assert and require and that's the gas cost and we're going to talk about this later when we talk about gas costs, but the main difference is, you are going to use up some gas until you reach require or assert and with require the rest of the gas which isn't used up yet is going to return to your account and assert going to eat up all the rest of the gas. So, that's one of the main differences between assert and require. All right, let me revert this revert back to our require because I personally find this much easier to read, require(isRunning) and then the smart contract is not running currently. Okay, this is a deposit money function and it needs running. And this also means that this modifier, for example, could be rewritten to require message senders owner and you are not the owner aborting, in case somebody else is calling this function. Let's just give this a quick try, deploy, set running to true from the same account works, and if you're using another account then this gives an error. It says the message sender is not the owner and that will trigger this exception here, "You are not the owner aborting." Why is this working? Because the modifier is used by the set running function and we are going to run in this require and this will evaluate to false and this will trigger the exception. All right. Then we have, we can use this perfectly in a withdraw money function where when we deposit money we want to get it out again. So, let's say we have a function withdrawMoney and that gets to an address and we have an unsigned integer amount, and this is public. So, I will write the long version and the short version will be the coding challenge later. If the balance of the message sender is greater or equal to the amount then do the withdraw or else revert is not high enough. So, if you have enough balance in our account then we can withdraw to this address or else we revert, your balance is not high enough and then we have to do a little bit of a workaround and I will explain to you in a second why we have to do this workaround. So, first we have to store the amount to transfer in a helper variable. Where is the curly brackets? Here is it. And then because our Solidity best practices security guide anywhere, which you can find and I will tell you this now, this tells you whenever you're going to interact with an external address, when you send money to this address, when you call another smart contract, when you do anything with an external smart contract, the so called unsecure untrusted source, then you should not do anything with your stage variables after interacting with the untrusted outside source. All right. And all we have to do now is to send the balance to the outside address that is provided here. But before we do this we have to deduct the amount that somebody want to send from the balance mapping. And the reason for that is if you are interacting with an outside source, the best security best practice is not to write anything to your stage variables like this mapping here after interacting with an outside source. And in order to avoid that, we are going to deduct the amount from the balances mapping first, then we are going to assert that we have no wraparound, we are going to make sure that the message sender is smaller or equal and the balance message sender plus the amount. So, in case for whatever reason we made a mistake over here, which is hopefully highly unlikely, but in case we made this mistake here and we deduct some amount from a zero balance then we end up with a balance which is very very high and then we would have a wraparound. So, in this assert we make sure that the balance after deducting the amount is smaller than the balance before deducting the amount, which makes perfect sense. And all we have to do later is to transfer the amount, and that's it. Now in the coding challenge now, I want you to remove this if else and replace it with a require. If you want to do the coding challenge, then just pause the video and otherwise I'll show you in a second how this is done. All right. It's probably not that hard for you. You just require that the balance is larger or equal than the amount or else you just output this error message. Remove the if else and then to some nice formatting and that's it. All right. By now you should be able to read a very, very large amount of smart contracts out there, and in the next section we are going to summarize everything and I'll see you in the next lecture.
Tom is a CTO, senior back-end developer, and systems architect with over twenty years of hands-on development experience in a variety of languages and systems. He has a CS master's degree and has been working with Ethereum and blockchain technologies since 2016.