Build a Raffle system in Solidity
How to send and receive Ethereum to a smart contract
This is a continuation from my previos blog post: Hello, World in Solidity. In this next part we're going to start implementing the logic for our raffle system.
Let's start off with describing what the smart contract is going to do.
Creating the logic
Here is a more detailed explanation from the system description above.
We're going to create a function called enter
. This function will require the person who wants to enter the raffle to send 1 ETH or more to it.
If he is the fifth person to enter, the method will pick a random winner.
After that, we want to send the ETH to the winner and empty all of the entries.
Here's the complete code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Raffle {
address[] entries;
constructor() {
console.log("Deployed!");
}
function pickWinner() private view returns (uint) {
uint random = uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, entries)));
uint index = random % entries.length;
return index;
}
function enter() public payable {
require(msg.value >= 1 ether, "Pay 1 Ether or more to enter the raffle");
entries.push(msg.sender);
if (entries.length >= 5) {
uint winnerIndex = pickWinner();
address winner = entries[winnerIndex];
console.log(winner);
uint256 prizeAmount = address(this).balance;
(bool success, ) = (winner).call{value: prizeAmount}("");
require(success, "Failed to withdraw money from the contact");
delete entries;
}
}
function getLength() public view returns (uint) {
return entries.length;
}
}
Let's look at the outer scope first:
address[] entries;
We've added this line of code before our constructors. This is what stores all of the entered public addresses. From this, we will pick a winner.
function pickWinner() private view returns (unit)
This function returns a random number between 0 and the number of entries. The private
part means that the function is not accessible from anywhere but this contract.
function enter() public payable
This is the meat and bones of this contract. First, we can see that this function is public
, which means that we can call it from outside this contract (we will do this with Remix). Then the following modifier is payable
. This indicates that we can transfer ethereum to this smart contract when we call it.
Inside the enter
function:
require(msg.value >= 1 ether, "Pay 1 Ether or more to enter the raffle");
require
statements are fancy if statements. We verify that the value that was sent to the enter
function was more or equals 1 ether. If it fails, the string provided by the second argument is sent back to the user, and the whole transaction gets reverted. msg
is a reserved keyword in Solidity. In fact, it's a global variable that stores information about the transaction (who sent it, how much money, the signature, etc.)
entries.push(msg.sender);
This pushes the address of the person who made the call/transaction to enter
to the entries
array
if (entries.length >= 5)
We check if he's the fifth person who entered this Raffle.
uint winnerIndex = pickWinner();
address winner = entries[winnerIndex];
console.log(winner);
We call the pickWinner
to get the winner's array index and get his address from the entries
array. For debugging purposes, we also log it with console.log
.
uint256 prizeAmount = address(this).balance;
We get the prize amount from address(this).balance
. This gets all of the ethereum that is assigned to the contract.
(bool success, ) = (winner).call{value: prizeAmount}("");
require(success, "Failed to withdraw money from the contact");
And to send the money, we write the magic (winner).call{value: prizeAmount}("");
, this sends the money to the winner's address with the prize amount!, the (bool success, )
tells us if the transfer was successful or not. We check it with another require
to see if we have to revert the transaction.
delete entries;
Lastly, we delete all of the entries. This empties the array and is ready for another raffle!
Phew! We made it through! Give yourself an applause!
Some notes on this
Our pickWinner
function uses block difficulty and block timestamp to generate a random function. Since this values could actually be predicted, this makes our random function not so random after all.
For the purpose of demonstration I went with using this, since it simplifies the solution for now.
If you want to read more about it, I would recommend this StackOverflow post: stackoverflow.com/questions/48848948/how-to..
Testing our raffle
We're using Remix IDE for this. If you're using it first build the contract and then deploy it, so you see something like this:
Let's enter this raffle with 5 different people. Let's send some ETH to the contract:
The first dropdown (marked orange) is the account you're interacting with, change this every time you send a request, then you'll enter 5 times with different accounts.
The second dropdown is the amount of ETH to send to the smart contract. You can choose as much as you want (or as much money as you have on your account to be precise ๐).
Open the contract under DEPLOYED CONTRACTS
and you should see something like this:
You can see the public functions from the contract. Press enter
and you will send the money to the contract and you should see something like this:
Now repeat this 5 times with different accounts until you get this output:
Now, check the accounts dropdown and you should see one of the accounts you have entered with to have quite a lot more ETH than the other ones:
This means that this was our winner! ๐
Summary
In this tutorial it we explained:
- How to send money to a Smart Contracts
- How to send money to an address from a Smart Contracts
- Using
require
to revert if a condition is not met - How to store addresses on a Smart Contract
- Simple randomisation logic
- Debugging using
console.log
Conclusion
We have tested our smart contract and it works! It would still need a couple of tweaks to be production ready, we would need to use something like Chainlink VRF to ensure randomness, we could make the raffle even time based!
This is where you can play around with this and explore more through this smart contract.
I hope this two posts have given you an introduction into Solidity and Smart Contract development!
Thanks for reading ๐