Warning: this is not a secure way to add random numbers to your smart contract, if you plan to deploy this to any sort of mainnet- you should look into something like Chainlink’s VRF. Using the approach that we are going to discuss in the article is for learning purposes only, and deploying this code to mainnet will expose your contract to a number of attack vectors. This is your warning, don’t try to sue me if you don’t listen and end up losing your DAO’s treasury.
So you’ve been working on some simple web3 game or lottery for the past few days/weekends, and you are just about done. You’ve written your tests, spent hours reading log errors, bullet-proofed your contract (after deploying at least 300 contracts to Goerli along the way), and finished up the UI. The only problem: your game is super boring. There’s no spice, no variety. When your character attacks the boss, the hit lands. When the boss player attacks you, they hit. Back and forth the game goes. Not very compelling, is it?
We want to add some spice to this gaming experience because everyone knows a real battle is laden with chance. The game would be much more engaging to end users if there was some variety within the attack/defense functionality.
To establish a concrete goal: let’s say that when any character attacks, they may miss if the random number is less than 5. If it’s higher, we can say they hit, and deduct the requisite health points from their opponent. So in short, we need to add a function that will return us numbers 1–10 so that we can route the gameplay appropriately.
In this article, we will cover a fast and dirty way to accomplish this.
Keccak (pronounced ‘ketchak’) is best described as a series of ‘sponge functions’. Sponge functions are algorithms in which fixed-length inputs are accepted with an arbitrary amount of padding values, transformed, and mapped to a fixed-length output.
I know that may sound intimidating, but let’s break it down. I mentioned three main parts; the input, some padding value(s), and the output.
The input: this one is very straight forward. From the perspective of Keccak, it’s what you’re transforming.
The padding: these are the data points that should always be unique. As we said before, keccak maps fixed inputs to fixed outputs. If you were to pass a fixed variable, say, the unsigned int (uint) ‘7', the output would always be the same. With this noted, you may opt to use something like the ‘now’ variable, a variable which references the global system timestamp within the node that is proposing the next block. Adding a number of these variables will make it harder to fake the randomness or predict the outcome, but this should still not be confused for secure by any means.
The output: a result of the input + whatever padding variables you added to the keccak function. We’ll use a subset of this output to generate our ‘random’ numbers 1–10.
When it gets broken down and extracted from the discrete math talk, it’s not so bad (here’s a link to more of that by the way, if that’s your thing).
The code itself is very straight forward, credit to GeeksForGeeks for introducing this concept to me.
pragma solidity ^0.6.6;contract YourContract {//One of our state variables to add padding to Keccak
uint nonce = 0;//the meat and potatoes, what will yield our 'random' numbers
function randMod(uint _modulus) internal returns(uint) { //increment to ensure no repeated inputs
nonce++; //cast the value returned from the modulus of the
return uint(keccak256(abi.encodePacked(
//the current timestamp, mentioned previously
now,
//a reference to whomever sent the transaction
msg.sender,
//the nonce that we incremented in the lines above
nonce)
)) % _modulus;
}
}
By adding the nonce state variable and the randMod function to your code, you will be off to the races. Simply call randMod passing in the _modulus argument wherever you want to add a logic gate based on the returned value within your contract!
To address the problem statement from earlier on, we would make a standard if-else logic gate and check to see if the random number was greater than 5, if it is, the attacking character will successfully land, otherwise — they would miss. Easy!
If you don’t already have a smart contract that you’re working on but want to try this out for yourself, you can spin up a quick session of remix to explore, OR you can go to buildspace!
If you don’t know what buildspace is, it’s a very well-structured free resource for learning the basics of web3 development. I in no way benefit financially from driving people to the site, so you can rest assured that my recommendation is sincere. Buildspace is by far the best web3 learning resource that I am aware of at present, so I want to make as many people aware of it as I can.
However, I do contribute to the lessons on occasion with edits/improvements, and this is one such instance of a bonus challenge I have added! If you want to start building an Ethereum Turn-based NFT game and add pseudorandom chance using Keccak, the lesson plan that’s live on the sight right now contains everything you’ll need!
What on earth are you waiting for?
Thanks a million for reading, I hope you’ve enjoyed this introduction to Keccak, and also understand why it should only be used for learning purposes. Perhaps in a future article I will explain how to use truly, verifiably random numbers. Let me know if this would be of interest to you in the comments!
‘Till next time.
