A Tour of Web 3: Ethereum & Smart Contracts with Solidity Game Contract Setup
Transcript from the "Game Contract Setup" Lesson
>> Let's build another contract. We're gonna build a small game okay, very very small and by game I don't mean we're actually gonna build a game we're actually gonna build like a little part of something, okay. And really this is where I want to actually use more complex types and do some more complex items.
[00:00:15] Now this, I didn't include the code I can include the code, it just, I change it every time I type it. It's kind of unique. Try to follow along, try to build it with me. Build it in a way that makes sense for you and see how it goes.
[00:00:27] We'll keep it pretty defined. I'll introduce new concepts but I always find the best way to learn of course is time in the saddle and of course what is, the saddle? Or that's your ID and the thing you want to write and of course I don't know if you know this but I use vim by the way vim's fantastic.
[00:00:42] He should check out this car, okay. Anyway, so the idea is I love the ideas of RPG's I don't really like playing them I just love the idea. They see it just seems so fantastic. So I wanna build something that can generate random heroes. I want the user to be able to select their class as a mage, a healer or a barbarian, I do not want classes to influence how the stats are created.
[00:01:05] I wanna be paid of course, 0.05 ETH per hero created. I should be able to get all the heroes that I have generated. Heroes should be stored on the chain. Stats are gonna be strength, health, intellect, magic and dexterity, pretty classic RBG stats right there. Stats are randomly generated on a scale of 1 to 18.
[00:01:25] The stats are randomly picked and their amplitude is randomly determined according to the following rule. The first step picked should be at Max 18. The next one should be at Max 17, the next one at Max, 16, and so forth. You could imagine that this is effectively the beginnings of an NFT, right?
[00:01:42] You're now creating something we're going to stored on chain. It has stats it's random. Could we create like a trading nature yes, obviously these are not divisible. If you make a barbarian divisible you've effectively defeated him, and he's dead because he is now in twain. So, this just makes perfect sense here.
[00:02:00] So let's go and let's actually create the contract. So I copied the little design things so we could just put it into the code. Yep, okay, perfect. So I'm just gonna copy this nice little beautiful piece of text here and we're going to put it into some code, okay.
[00:02:12] I'm gonna go back here, and let's go back to our contract area. And let's create a contract called Hero dot soul. And, of course do the standard pragma statement at the top letting solidity know, you like this compiler. And then I'm going to personally paste this in. You don't have to paste this in.
[00:02:34] But I'm gonna paste in all of these things. So I can just do these things in order. And then of course, we're gonna create our contract, which is gonna be a hero. And there we go, we are well onto our way. So the first thing we have to be able to do is I wanna be able to randomly generate these things, the user should be able to input their class, here are the classes mage healer barbarian.
[00:02:55] So I'm gonna go down here and I'm going to go enum class and do that. Now you guys can follow along this. This makes perfect sense. Enums are a bit more restrictive in aetherium you can't assign it the value you want. I wish I could do like certain values in these because I would definitely use enums more often, but enums come with a very nice thing.
[00:03:16] If you accept enums into a function, a function cannot be called with non enum values. Meaning that if meaning that major zero healers, one barbarian is two, if I provide a three to that function, it will simply fail because it did not pass the solidity check. So that's very very nice right that actually adds a little bit of safety to your program that makes it kind of nice to use these enums.
[00:03:39] But nonetheless you can assign at random values which does hurt my feelings a little bit sad this really cool idea and then that's how I feel about it. All right now I left that out alright, so there we go, we have the class and let's create a function that we can create our hero with.
[00:03:55] And we're gonna take in the class class, and it's going to be a public function. And we're gonna add this extra word in here called payable. I think we can all guess what the word payable means. It means that this function can accept money into it. If you do not include this you cannot pay for this, it's just part of life that's what it is.
[00:04:16] Next we're gonna include another statement right here called require. Require effectively is like a cert, it allows you to put assertions into your code. But if certain conditions aren't met, this contract will revert itself automatically for you and even provide an error if you would like to. So I'm gonna do this message dot value values how much money was sent to the contract, if message dot value is required to be greater than or equal 0.05 ether.
[00:04:46] Ether is a suffix to a number that actually multiplies it by 10 to the 18 because all units are fundamentally in we remember that. So there is no such thing as one ether there is 10 end of the 18th we and so this is just kind of like a little shortcut.
[00:05:01] So if they don't send us enough money, we're gonna say, please send more money, right? Boom, we now have the basis of our first few lines of design, right? We have three classes. The user gets to select the class that comes in, and we want to be paid Money.
[00:05:21] So let's go back up here and delete those two lines. So look at that. We're already like flying through this, right? So just to make sure that we are doing the right things, let's write a test right away for what we have thus far. I think that would probably be a good plan, right?
[00:05:39] So I'm gonna go. Let's erase these two. Let's erase that. There we go, sorry, I have to race those things. And let's open up a test file. And I'm just gonna copy it again and do hero.ts. Now remember, we always have to include nomic labs hardhead ethers, because we want those additional functions on ethers.
[00:05:59] We're also wanting to grab the ether object off of hard hat fact. Even though it technically is globally provided when you run hard hat as I've said LSPs and things kind of sometimes struggle with altered environments and so I just always prefer to do it the manual way.
[00:06:13] So I get the best LSP results. Alright, let's go to test gas and let's call it hero. And I'm just gonna pretty much delete that whole function because it's disgusting. Let's just get rid of it look. Alright, so what I'm gonna do is I'm going to create a function called Create hero.
[00:06:30] And that's gonna do our standard deploy. I think everybody by now should be able to mostly write a deploy by themselves, but I'll do it just in case cost. Hero equals await ethers dot get contract factory heroes, the name of our contract and then on top of it go back here and make sure that it's spelled correctly ethers.
[00:06:53] Then we're going to take that and we're going to deploy it equals Wait, Hero deploy and then of course, await up one more time dot deployed. There we go. This function will simply create the hero, deploy it, wait for it to be deployed and then we can return.
[00:07:16] it's good to kinda go through this enough times by now you should probably create a function you can just require in and do this yourself. But I always like to do these basic primitive operations like 10 to 15 times just so in my own head, it's just like always at my fingertips.
[00:07:30] I don't ever struggle with thinking what does something happen? It's just kind of one of the things that I like to do. I know it's kind of is can be boring and sometimes I don't know people like that. But now remember we're using Chai not as fun or Mocha or whatever it is so there is a before function we'll do an async function.
[00:07:47] And we'll go hero equals true create hero of course await that there we go so before any of the test run will always have at least one hero ready. So perhaps I don't want state to persist so I can always call my own create hero or we have the one that will run for the test, creating and deploying and communicating with the network does take time.
[00:08:09] So if you have a test that ends up having like 1000 calls, this thing will take 6070 seconds to complete. So reducing the amount of calls you make, always a good plan. Since we don't have any state we really need to keep track of our test right now.
[00:08:21] I'm just not gonna worry about so I'm gonna go like this ,let's see should fail at creating hero cause of payment. Yeah, you can call whatever you want to do async function, you can even use an arrow function in there, I will only judge you slightly. So there we go.
[00:08:37] We have this and now we need to call the Create hero function and have it fail. So what I'm gonna do is I'm going to do this you can do it how you want to do it I forget how to test Chi stuff, where you can catch errors and all that I know Jess has a much better version of said errors.
[00:08:54] But we'll just do this e equals error and then I'm gonna go expect e dot m Massage, let's see two No wait, wait, wait, includes some phrase to equal true. I wanna just make sure that I see our proper phrase, I'm gonna jump back to my contract and I'm going to yank out that beautiful phrase please send me money.
[00:09:18] And then I'm in jump back in here and go to the double quotes and paste that in awesome. There we go expects this to happen. Now I just realized I haven't included expect someone to go up to the top do expect shy there we go we're looking good and so now all we need to do is actually do the hero function creation.
[00:09:38] But we wanna do it without enough money correct. So let's do a nice little awake hero creates hero Is that the name yep create here we need to provide a class let's just do zero that would be of course I think the major at this point. And now when you call functions you can also provide additional metadata Add to that function.
[00:09:59] And so that's what I'm gonna do right now. This is something that ethers will take care of. It assumes that that one plus the amount of arguments is gonna be kind of like a little option bag that you can pass to it. You've seen a lot of option bags before.
[00:10:12] So what I'm gonna do is I'm going to take this value. Now notice that I call it value. What is it called right here value, value, value, value value, right is right there. It's very obvious. So I'm gonna set this value that I'm gonna pass in this is how many ethers I want to send to this.
[00:10:28] You'll use the exact same thing when you're doing a real contract that you want to communicate with real money from saying no you'll provide a value. And so now remember ethers is in the units of we so we're going to use ethers utils, parse Eeher and I would say 0.049999999 like right it's just so close.
[00:10:50] We almost made 5 but it's not five. So I would expect this test to fail correct because we expect at least 0.05 ethers. And so we're gonna take this test. We're gonna run this test. And if I've done my stuff correctly, we should get an error out that has this.
[00:11:07] I don't know if there's a better Chai way to do this. I hate using Chai, but didn't wanna get just set up as I said before. All right, there we go. So let's go over to here. I'm going to remove my test gas function, we don't need to see that.
[00:11:20] And I'm gonna go over here I'm gonna run test test is going to come out. It's also gonna be lucky enough to give me all that good stuff. Now look at what I got here. Heroes is not this first off, I misspelt heroes that kinda hurts a little bit emotionally but on top of it I misspelled I just totally asked for the wrong one so let's go in here.
[00:11:38] And just to test you guys I wanted to provide a message where you actually provide the wrong name and now I provide the right name and we can redeploy it and look at this it works out perfectly. And we can see that we actually did fail at creating the hero.
[00:11:53] So there we go we have one of our unhappy cases and that we can ensure that our contract always does work at that cost.