Solidity 101 | Session 6 (@HemaKarthickCrypto)
Solidity 101 — dev3pack & Cyfrin Updraft · 40:26
Hema Karthick demonstrates a FundMe contract in Remix, including owner-restricted withdrawals, funder bookkeeping and Ether transfers. The lesson also covers constants versus immutable variables, custom errors, and fallback and receive functions.
Speakers
Topics
From Solidity 101 — dev3pack & Cyfrin Updraft, hosted by dev3pack.
Transcript
Hi. How are you doing? Yes, it's the weekend. Yeah. Well, I'm home so nauseious going well. Where are you living already? In India. Yeah. But in India where especially uh it's it's Tamil Nadu. It's the southernmost state. uh almost at the tip of India that you see in the map. It's on the down. Yeah. Yeah. On the southern. All right. Nice.
Um so I will put the remainder. Yesterday we were here 12 13 something like this I guess. Yeah. Mhm. Yeah, it's okay. And the session is short too for the uh for today. So nauseious. All right. I pro good link. Yes, it's a good link.
We are alive. It's human. I I put the pin like pin the message and notify everyone.
Usually I'm not doing that too much. Mhm. Um but this week um as it's super important I prefer doing that. All right. Uh where are you now? Sorry. Where are you now? Are you traveling? Uh no. I'm in France actually. uh until June uh because after that I'm going to Serbia Belgrad.
Belgrade. Okay. Belgrade. Yeah. Belgrade. We have totally different pronunciations on opposite direction. Exactly. Um then probably Berlin. Beron and um after that can can ah yes I remember people say said canes can kis canis rather than can can how do you pronounce it?
I say K, but I don't know if it's the right pronunciation. I I I think um all all people living there in can be like, "Wow, what is pronunciation?" They're going to be shocked. Please don't kill that city pronunciation. And you know that whole um grocery shop like Yeah. Yeah. Rhino in can are accepting uh crypto.
So that's pretty cool. Yeah. You don't have to spend fat. Cool. All right. I think let's start. Um okay. And then people will will join um in five minutes I guess like actually uh three people are there. That's nice. Always the same. Hello everyone. Nice. Let's go. Okay. Hi everyone. Hi. I need to start.
Okay. Oh, in India you put a lot like um some points when you talk like you word something you put some uh point after Google hello point. Uh I'm not sure what you're referring to. Is it highlighting or something? No, it's like uh two uh comma. Uh no, no. Uh that's not common. Okay. Because when I talk with some um indie every time um I I have this thing okay.
All right. Yeah, maybe I didn't know this. Let's go. So we are here with our contracts of Funme. Okay. A quick reminder. We having a fun contract which is used by for example creators. Anybody can send money to the contract through this fund function and they must send a minimum of five USD worth of ETH otherwise the contract would not accept it. That's uh the app we are building and also uh the creator would be the owner of the contract and since they own it they should be able to withdraw whatever earnings they get from the contract. So
for that we have the withdraw function here. So we saw until this point yesterday. Today it's all going to be optimizing our contracts for mostly gas efficiency. So first let's go over with immutables. Okay. So constants and immutables. You see uh there are certain variables in our contract. So we have uh address public I owner and we also have minimum USD. If you actually notice uh minimum USD is a constant and owner is uh we haven't
changed it but uh it it is going to be immutable. Both are state variables but we also include another keyword in here as constant and immutable. So what is a constant? Like the word says it means the value of that particular variable is not going to change. So when you actually start writing the contracts and you already know that certain values will never change. you are like 100% fixated that it it should not change and you do not anticipate it to change at all then you can put it as a constant that is what what I would say as during
compile time that is you write your contract and before you compile at the time of compilation itself you already know the value that you need uh you know uh a fixated value so in that case you can put it as a constant now why we do do it is simply because when you don't have a constant and let's say you don't have a constant here and minimum USD is not set uh in here like this. So then at some point for example within a function or something you're going to have to insert a value for this. You're going to declare hey uh this minimum be here you're using minimum USD. So before this point you would actually uh set a value
of uh minimum USD here but that's like a double work we if we already know it why do we want to include it uh at a later point and if we also know that we are not going to change it we might as well put it as a state variable in here and with the keyword as constant in simple that is what a constant does. Now there's a very slight difference between constant and immutable. You know both are not going uh when you know a certain value is not going to change. You either choose constant or immutable. Constant you do
it when you know it at the compile time itself. Immutable you do it at the runtime. So what do we mean by runtime in this reference? when you actually are done with the contract, you have compiled it and then after you uh after you compile it, you go and you go here and start deploying the contract. So at this point only at this point if you know some kind of data for example only the per uh what are we doing in this constructor here? We are setting that message dot sender is the owner. So what is message dots sender?
Whoever is deploying the contract. So ideally this contract will know who is deploying the contract only when they deploy the contract. So when when there are special cases like this you actually put them uh as an immutable variable. how uh you get the value of the variable that you want and you save it within an immutable variable in here like this.
So immutable is also constant. You cannot change it after you set. Usually when there is other uh variables for example in funders remember that we also reset our array when we were uh you know withdrawing such things cannot happen. Both cannot be changed but constant is when you already know it while you write your contracts during compilation. And uh immutable is when you know that uh when you come to know certain values only when you start running or deploying the contracts. So you can get the data from the constructor when they deploy it and then you can save it once. this uh
so when we we already know that constructor runs only once right so whatever value you set within this constructor will uh get assigned only once it will get saved here and after that it will not change that's the uh only time you you'll be able to actually uh write over it so in short uh that that is the difference between them And here's an example. Okay. So, uh I if you already know a fixed value, you just set it up. If you don't know, you don't have a constant at all in the first place. So, since we have a fixated
value, we have 100. But for example, why why uh think about the scenarios when you choose to have immutable? Uh one is owner. Second uh any any kind of uh data that you would come to know uh when they reply. For example, the message do value or the message dot uh sender or anything related to that. For example, uh I if we have a variable of uh you know uh int 256 the first transaction value. Okay, if you have something like
that, the first transaction value would be what came into the constructor and you can put it as immutable and it would get saved within this. Okay, this is messing up the structure. So, I'll just remove it. Uh so yes that is the only difference in our case while we actually were writing the contract itself we needed minimum USD and we already knew that we uh if you should at least send me five uh you know USD worth of uh ETH that's why we
already declare it as a constant in here the owner is uh known only when they deploy it. So we are actually putting it them as a immutable variable. But there is uh you know there are special cases uh where you can also insert uh immutable directly when let's say let's go ahead with this. when you actually write your contract itself, you can use it. Actually, it's a very uh useful gas-saving technique. Immutable is slightly more uh gas-saving than
constant. So, a lot of times instead of using constant, we also choose uh immutable. And uh but but you have to be careful that you you can change your constant to immutable. But if you change your immutable to constant then uh that that that actually does not make sense because uh how would you get a value from runtime when when when you when you don't know at a compile time. So yeah and then okay why do we use them?
It's mostly to save gas and prevent any accidental changes. For example in here uh we don't want anybody else to be declared as owner. Let's say uh I I put this value as hey this person is the owner and this one was not even there. I have compiled the contracts. I have deployed it but later I come to realize that there is just a slight mistake in it and I forgot to include another. So that that's going to cause a lot of problems. So it's u it's a good practice to get your uh immutable variables from your constructor and then move it to there. Let's remove them.
Okay. Can constant be set in a uh constructor? No, because we should know it before we compile itself. So that's not okay. And can immutable be updated after deployment? No. Only once when it is uh deployed you can write uh or assign a value to this immutable variable. After that you can never change. It's almost like a constant. That was it. This is one of the gas saving techniques uh where you can actually you know put as uh immutable and constant
for things that don't change. Next is errors. Okay, why do we have errors? Let's first think in that sense. when someone calls a function and they uh let's say I I I am calling this uh withdraw function and just like hey that okay there is a function withdraw let me see what happens and I go and uh call this withdraw function but I'm not the owner I don't know the privileges or what happens uh within the code I'm just an outsider I'm just trying things out and seeing if if I call any of the uh functions uh am I
going to get some money? So I'm I I'm like very playful and I'm trying it. So when I with click this withdraw and nothing I am sorry uh when I click this withdraw and nothing happens. Uh so how how how would I know that why my transaction failed? If let let's take a require statement. Let's bring this back. So in here uh hold
on sorry guys. Okay, in here if I call the withdraw function but I'm not the owner then in that case it is returning something called not owner but as a require the require will return as hey you are not the owner. But if those messages and checks are not given explicitly then the person who is calling it would not know why the transaction failed at all. That is why we explicitly state hey this is the error this is why your transaction reverted. It's like giving them more
context of where it failed. That's it. So for this there are uh two ways to do it. One is using require statements which we saw earlier uh yesterday when we had a require a certain condition you check if the condition is met. If the condition is not met then you revert the transaction. If is very very similar to it. The syntax alone changes. When you want to use custom errors, you first declare the name of the error. For uh for our case, we have uh within our modifier, we had we are
checking uh is the person who is calling the you know calling the function the owner or not. That is the condition we are checking in here. So first if you have to use custom errors for this you first uh you know uh declare the declare the error in the first place. You first put error not owner. You're just saying hey I'm creating a new type of error. This error is only specific to my contract and it is not used anywhere else in Solidity. It's just for the context of my contracts. So you're first giving uh the context and uh that it hey this is the error and this is the name of the
error that I will be using. After that you can come up anywhere and keep using the same error. So if uh in our case we are using it as if check this condition. If the condition is not met then you can revert the transaction. It's same like require but uh you are having it within an if statement and you are reverting it as a custommade error that's just uh specific to your own contract. Let's look at the s uh syntax even further. So here you have a you you do not uh specify the errors within your function. you should always specify them
uh within your contract or or before your contract itself. That's uh that's syntactically correct. So you can't put uh you know you know if you're using an error you can't just put it in here. You have to already uh declare it and then use that. So in our case it is if is the keyword you check the condition. You check if message sender is not equal to owner and then you revert and you revert with the error name exactly the same error name that you declared. Now with custom errors there's one more advantage that is you can actually also return a
certain value uh return as in u not in the context of solidity but in here you give or showcase a specific values I can show you an example here so message sender is not equal to owner okay so then you revert unauthorized And you can put it as this is the value I received. So you can have it as address. That's it. Address is fine. So you can just uh mention that this is the address I received and that address is unauthorized. And questions on it. Why
use custom errors instead of strings with rewards? Uh this is mainly because um in in solidity strings are stored in bytes and in general strings are very expensive to store in a blockchain even when you have it as uh in any format by 32 or any type of arrays you are going to be paying a very huge amount because each of them uh take up a separate slot. So even if you have a little bit of longer string, uh sometimes it it's going to take a 2x or even 3x the gas
fees you would uh pay for other uh for for the normal transaction. So that is why at most we avoid using strings and but in require statements we had to use because we had to give context to whoever is sending the transaction. But uh with custom errors we just simplified it because uh this is not stored as a string. It is like uh it is a separate data structure that is uh storing just the name of the errors. So it has nothing to do do with strings that uh and it's also good practice usually to you know uh prefix the contract name
before your error. Now we are actually working with only one contract. But uh when you have multiple contracts and let's say there is also another contract that has uh not owner. Then it might be slightly confusing even for you to find out uh hey where did this error come from? I'm not uh I'm not even able to identify the contract. So it's a good practice to actually prefix it with the contract name so that it's just easier for identification. This is uh another technique to save gas because uh strings are expensive. That's the main reason. So what happens when an error
occurs? Same thing that would happen with your require statement. It just reverts and any state changes that happen until that point it will be re uh it it it will get uh how do you say rewind and undone and the transaction will revert. That's it. Our c okay so custom errors was not always there. It was uh introduced only after uh solidity version 0.8.4. So right now we are in the version 0.8.18. So we can use it. That's not a problem at all. Uh why is it important to know these versions is now you're
learning to develop with solidity smart contracts. At some point you would progress to also auditing it. So when when when someone brings you a contract, it's not necessary that they will give you contracts with the latest version. they might have started development earlier. So they may have been using uh a different version of uh solidity. So it's important to know when features uh came and have a timeline of it. So you know uh when uh if correct uh you know syntax was used or correct variables was used or if there was security issues in certain uh versions then you have to
know it. That's why otherwise you don't have to think too much about it if you're just sticking to development because after uh 0.8.4 all of the versions carry on the same uh pattern. They all support custom token uh custom errors. Next, so this is pretty important. Uh we have fallback and receive functions. Okay. So what are fallback and receive? Remember constructor constructor is also a function but it is quite different. We don't have function and we don't give a function name and it gets uh executed only once. This is a feature specific to
constructor. We call it a special function. Similarly, there are two other special functions that is receive and uh fall back. Let's get into what each of them does. So when uh when there is a contract and there are so many functions within it but let's say I am uh you you you know for all these contracts there is an address you can attempt to send a transaction directly to this address just like how you would send uh you know e uh to any other person. How how do you send it? You just copy their address,
paste it in there, send your transaction of how muchever amount you want. Similarly, you can also do it for contracts as well. But when you send this transaction, it will only accept any ETH if it has a receive function. Uh let me make it clear. If you uh this is a contract and if you call the fund function with some E uh value then it will go through. But if you simply send a transaction directly to this uh contract with some ETH then it will only get accepted if it has a receive function.
Okay. Uh wh why why does this happen? Uh because somebody may have mistakenly listen or somebody may be uh trying to exploit something. It it you let's not go too much into why they do it. But you must understand that all contracts uh are uh having the ability to accept ether directly without calling the uh without calling the specific functions. For example, you you should not assume that uh since only fund payable uh fund is here, people would only call fund.
No, they may call any payable function with amount or they may directly send ether to the address as well. They may attempt uh to send it. So if you want to actually receive the ether then you should have receive function. So this is only specific to ether. If someone sends uh an ERC20 token, it is not going to accept. Those are all very uh specifics. Uh but ether is is the native currency. So all contracts by default must accept it. That's why we have this receive
function. And uh fall back is also uh a special function. How is it different from receive is when uh so fallback happens when someone calls any of these functions with uh by mistake if they try to send E to the contract uh by calling a function that does not accept ether that is one case let's say someone is calling withdraw function but they are uh uh let's say uh this is not payable uh and they are
calling this function with some e value in that case the solidity does not know what it has to do that is why we have something called fallback to accept any uh uh any mistakenly called functions you can say so how does it actually look like um in a let's let's briefly go over okay we have call data here okay when someone simply sends receive how how would they send it would just be like uh in here I'm just sending from my account and I'm directly putting
in some value for example uh I'm not uh actually calling any specific function. I'm just directly trying to send a value to this contract. Okay, it is getting reverted. Why missing uh revert data transaction invocation is learn? Okay, gas estimation failed. Do you want to force sending? No, I don't want to force send it. So, our uh receive, hold on, let me just check. Okay, it's not uh enough. Um,
okay, I'm running out of gas. We can't go ahead with the transaction. Uh, okay. So let's let let's just let me just explain it just with this. So call data is where you usually uh insert what function you want to call. If you want to call withdraw then let's say it is 0x00 0 2 but I am calling it as 0x00005. So what does is it takes this call data and it goes to every function and checks hey does this call data match with you okay it doesn't then it goes to next function and it sees hey does this call data match with uh this function
I'm trying to uh it will be like uh there's a transaction incoming and it's having this specific name but that name is not matching with any uh function name what what should I do then s is going to be so confused In that case it is going to call this fall back function. So there are two conditions. So both receive and fall back should be external and it should be payable. We already know what is external. External is that function can only be called by someone outside uh of this contract. It can be another person or another contract as well. And what is payable? table just uh is a
modifier that signifies that hey this function will accept any amount of E uh and you it is mandatory to have these special functions with external and payable modifiers and in our case let's look at why do we have one in here there is receive if someone is send sending you they didn't call it directly via the fund function. They are simply sending a transaction. They are like so excited. Hey, okay, this creator's contract is this. Let me just directly send it. If they are like that, what you want to do? You you for them.
It does not make a difference for you. You actually want to keep track of uh who all sent you uh you know eat or who all contributed to to the growth. So what do you do? you any um so how does this work is if uh if someone sends a transaction with ETH this receive gets executed. So if you put fund within this then it will receive the ETH and it will call this function with that E. So what happens in fund? In fund it is checking uh the conversation uh the conversion
rate and it is uh adding the value to the balance of this contract. That is what is our intention itself. Essentially we are just making it all a straightforward uh process instead of uh having a separate uh fund array funders array where everyone is uh calibrated of who sent how much. But if someone randomly sends you ETH, you don't want them to left out. You want them also to be added here in that case. That is why we include fund. And the same goes with fallback. you don't want uh someone to call a wrong function or uh someone who doesn't send uh call data. So that's why
you also reroute that ETH to the fund function and did we cover everything? Yes, it okay. What is this data? uh this data is nothing but whatever comes up in here. The call data is what the data is referring to. So in this call data usually the function signature will be available. Each function has its own signature that is uniquely identifiable. Um we learned this in AI but uh in simple you can remember it as a very unique uh identification for each function will be there. So that is the
data we are talking about. So if it is without data then it will go through receive but if the function uh if the transaction comes with uh data then it will go through fallback. This is the simplified version of it. Any questions team? U my my mom replied to a few question. Okay. My mom stopped the blowing contract from being executed.
Yeah. Yeah. Okay. Good work, Mahima. That was I probably you know already each other. No, you and Mima. Uh we actually were in one call I guess. uh uh but but but I do remember uh you mentioning her. She she joined dev back dev pack and then she became an ambassador right? Uh she she was an ambassador uh recently um like yeah with more than one month or two months from cyprine. Yeah in all right nice to meet you that's nice.
So Himma is also an ambassador from Cipher and if you have any doubts in this and if you need more clarification please get back to me. I'll reply on telegram or onx. All right. Um so for uh for citrine um the courses try to do it before our community course that's going to be in 29. Yes. So you have like uh around 10 days more than 10 10 days to complete the cipher. So take your time and as we mentioned yesterday um it's like a way to to first um going through and then
you can come back to uh whole recording session uh provided by our amazing motors um with some more explanation about it and and yeah thanks again uh Emma for everything uh you are really clear uh with everything So, thank you for that. Please do ping me if you are stuck at any point of or if you get errors. I'll be happy to review it. Yeah. And by the way, congrats everyone. Um because uh some people are participating to the bason. Um Oh, yeah.
Like around 20 people. Nice. I think it ended today, right? Today. Yes, it was yesterday. And so we got around 13 or 14 uh application like project submission. Nice. Yeah. Nice. Congrats because it's like this you're going to get uh your first um satisfaction and also um is also about taking some methods. uh first it's also super important to to to deal with agen on the long-term perspective.
So congrats everyone and and yeah um see you uh next week. So just for your information on Monday uh we're going to launch um application for joining the the fellowship program. So de pack um so you're going to have like an email about it and normally a specifi a special referral code included your name um so you can use it uh to invite your friends. All right. All good. Bye bye.
Bye bye. Thank you.