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: https://stackoverflow.com/questions/48848948/how-to-generate-a-random-number-in-solidity

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:

Screenshot 2021-12-14 at 21.43.20.png

Let's enter this raffle with 5 different people. Let's send some ETH to the contract:

Screenshot 2021-12-14 at 21.45.48.png

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:

Screenshot 2021-12-14 at 22.02.45.png

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:

Screenshot 2021-12-14 at 22.04.41.png

Now repeat this 5 times with different accounts until you get this output:

Screenshot 2021-12-14 at 22.06.02.png

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:

Screenshot 2021-12-14 at 22.05.57.png

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 👋👋

Build a Raffle system in Solidity