Raffle Draws on the Ethereum Blockchain: A Beginner's Guide
Tags: solidity, webdev, smartcontract, web3
Published: 2023-01-21 16:34:58

## Introduction

Raffle draws are a popular way to randomly select a winner or winners from a group of participants. In this tutorial, we'll take a look at how to create a smart contract that can be used to conduct a raffle draw on the Ethereum blockchain. We'll discuss the functions and state variables that make up the smart contract and look at the sample code for each function.

If you enjoy working on Web3 projects, [**subscribe to my channel**](https://www.youtube.com/@dappmentors?sub_confirmation=1) for all sorts of videos for learning web3 development. You can [**check out my services here**](https://www.buymeacoffee.com/dappmentors/commissions) if you want to connect with me.

Now, let’s jump into the tutorial…

## Creating a Raffle Draw Smart Contract


    //SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    
    contract RaffleDraw {
        address public owner;
        mapping(address => bool) public players;
        mapping(uint256 => address) public playerNumber;
        mapping(address => bool) erasePlayers;
        mapping(uint256 => address) eraseNo;
        uint256 public ticketPrice;
        uint256 public totalPlayers;
        uint256 public totalPot;
        address[] winners;
    
        constructor() {
            owner = msg.sender;
            ticketPrice = 1 ether;
        }
    
        function enter() public payable {
            require(msg.value == ticketPrice, "Not upto the ticket price");
            require(!players[msg.sender], "Player already entered");
            players[msg.sender] = true;
            playerNumber[totalPlayers] = msg.sender;
            totalPlayers++;
            totalPot += msg.value;
        }
    
        function draw() public {
            require(msg.sender == owner, "Caller must be the owner");
            require(totalPlayers > 0, "Total players must be more than zero");
            address\[] memory eligible = new address[\](totalPlayers);
    
            for (uint256 i = 0; i < totalPlayers; i++) {
                address player = playerNumber[i];
                eligible[i] = player;
            }
    
            uint256 winnerCount = thirtyPercent(totalPlayers) > 0
                ? thirtyPercent(totalPlayers)
                : 1;
    
            for (uint256 i = 0; i < winnerCount; i++) {
                uint256 randIndex = uint256(
                    keccak256(abi.encodePacked(block.timestamp, block.difficulty))
                ) % totalPlayers;
    
                winners.push(eligible[randIndex]);
                players[winners[i]] = true;
                eligible = removeAddress(eligible, eligible[randIndex]);
            }
        }
    
        function shareMoney() public {
            require(msg.sender == owner, "Caller must be the owner");
            for (uint256 i = 0; i < winners.length; i++) {
                payable(winners[i]).transfer(ticketPrice * 2);
            }
        }
    
        function getWinners() public view returns (address[] memory) {
            return winners;
        }
    
        function resetRaffle() public returns (bool) {
            require(msg.sender == owner, "Caller must be the owner");
            for (uint256 i = 0; i < winners.length; i++) {
                delete winners[i];
            }
    
            for (uint256 i = 0; i < totalPlayers; i++) {
                delete players[playerNumber[i]];
                delete playerNumber[i];
            }
    
            totalPlayers = 0;
            totalPot = 0;
            return true;
        }
    
        function thirtyPercent(uint256 x) public pure returns (uint256) {
            return (x * 3) / 10;
        }
    
        function removeAddress(address[] memory addresses, address toRemove)
            public
            pure
            returns (address[] memory)
        {
            address\[] memory newAddresses = new address[\](addresses.length - 1);
            uint256 index = 0;
    
            for (uint256 i = 0; i < addresses.length; i++) {
                if (addresses[i] != toRemove) {
                    newAddresses[index] = addresses[i];
                    index++;
                }
            }
    
            return newAddresses;
        }
    }

Check out one of my videos on how to code an NFT Marketplace below, you can use it to accelerate your web3 development skill.

<a href="http://www.youtube.com/watch?feature=player_embedded&v=fJIiqeevqoU" target="_blank">
 <img src="http://img.youtube.com/vi/fJIiqeevqoU/mqdefault.jpg" alt="Watch the video" width="1400" height="600" border="10" />
</a>



The smart contract above is called RaffleDraw. It starts with several state variables that are used to keep track of the players, the ticket price, and the total pot of ether collected. See the code block below:


    address public owner;
    mapping(address => bool) public players;
    mapping(uint256 => address) public playerNumber;
    mapping(address => bool) erasePlayers;
    mapping(uint256 => address) eraseNo;
    uint256 public ticketPrice;
    uint256 public totalPlayers;
    uint256 public totalPot;
    address[] winners;
    
    constructor() {
        owner = msg.sender;
        ticketPrice = 1 ether;
    }

The first function we'll look at is the **enter()** function. Players use this function to enter the raffle by sending a certain amount of ether to the smart contract. The function checks that the amount of ether sent is equal to the ticket price and that the player has not already entered the raffle. If the checks pass, the player is added to the **players** mapping and the **totalPlayers** and totalPot variables are incremented. See the snippet below:


    function enter() public payable {
        require(msg.value == ticketPrice, "Not upto the ticket price");
        require(!players[msg.sender], "Player already entered");
    
        players[msg.sender] = true;
        playerNumber[totalPlayers] = msg.sender;
    
        totalPlayers++;
        totalPot += msg.value;
    }

The next function is the **draw()** function. This function is used by the contract owner to conduct the raffle draw. It first checks that the caller is the owner of the contract and that there are more than zero players. It then creates a new address array called eligible that is the same size as the **totalPlayers** variable. This array is used to store all the eligible players for the draw. The draw is then conducted by selecting a random index from the eligible array and adding the player at that index to the **winners** array. The selected player is also removed from the eligible array using the **removeAddress()** function to prevent repeat winners. Observe the code below:


    function draw() public {
        require(msg.sender == owner, "Caller must be the owner");
        require(totalPlayers > 0, "Total players must be more than zero");
    
        address\[] memory eligible = new address[\](totalPlayers);
    
        for (uint256 i = 0; i < totalPlayers; i++) {
            address player = playerNumber[i];
            eligible[i] = player;
        }
    
        uint256 winnerCount = thirtyPercent(totalPlayers) > 0
            ? thirtyPercent(totalPlayers)
            : 1;
    
        for (uint256 i = 0; i < winnerCount; i++) {
            uint256 randIndex = uint256(
                keccak256(abi.encodePacked(block.timestamp, block.difficulty))
            ) % totalPlayers;
    
            winners.push(eligible[randIndex]);
            players[winners[i]] = true;
            eligible = removeAddress(eligible, eligible[randIndex]);
        }
    }

The **shareMoney()** function is used by the owner of the contract to distribute the winnings to the winners. This function iterates through the **winners** array and sends each winner 2 times the ticket price in ether. See the code below:


    function shareMoney() public {
        require(msg.sender == owner, "Caller must be the owner");
        for (uint256 i = 0; i < winners.length; i++) {
            payable(winners[i]).transfer(ticketPrice * 2);
        }
    }

The **getWinners()** function is a view function that returns the **winners** array. This function is useful for displaying the winners to users after the raffle has been conducted. See the snippet below:


    function getWinners() public view returns (address[] memory) {
        return winners;
    }

The **resetRaffle()** function is used by the contract owner to reset the raffle. This function deletes all the players, **winners**, and resets the **totalPlayers** and totalPot variables to zero.


    function resetRaffle() public returns (bool) {
        require(msg.sender == owner, "Caller must be the owner");
        for (uint256 i = 0; i < winners.length; i++) {
            delete winners[i];
        }
    
        for (uint256 i = 0; i < totalPlayers; i++) {
            delete players[playerNumber[i]];
            delete playerNumber[i];
        }
    
        totalPlayers = 0;
        totalPot = 0;
        return true;
    }

If you want to understand the nitty-gritty of smart contract development, checkout the book below to become an in-demand smart contract developer.

![Capturing Smart Contract Development](https://res.cloudinary.com/practicaldev/image/fetch/s--58IgnoDn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropboxusercontent.com/s_1EFFD390642114DC728164B3948A3C8FD66D56982DF906F4F025AA6ED38BE4E5_1671699371878_tier%2B1.png)


Grab a copy of my book titled, [**“capturing smart contract development”**](https://www.buymeacoffee.com/dappmentors/extras) to become an in-demand smart contract developer.

The last two functions are helper functions. The **thirtyPercent()** function is a pure function that takes in an argument and returns **30%** of that argument. The **removeAddress()** function is used to remove an address from an address array. See the code below:


    function thirtyPercent(uint256 x) public pure returns (uint256) {
        return (x * 3) / 10;
    }
    
    function removeAddress(address[] memory addresses, address toRemove)
        public
        pure
        returns (address[] memory)
    {
        address\[] memory newAddresses = new address[\](addresses.length - 1);
        uint256 index = 0;
    
        for (uint256 i = 0; i < addresses.length; i++) {
            if (addresses[i] != toRemove) {
                newAddresses[index] = addresses[i];
                index++;
            }
        }
    
        return newAddresses;
    }
## Conclusion

In this tutorial, we looked at how to create a smart contract for conducting a raffle draw on the Ethereum blockchain. We discussed the various functions and state variables that make up the contract and looked at sample codes for each function. 

With this solid understanding of the smart contract, you can now use it as a starting point for your own raffle draw projects or customize it to suit your specific needs. Keep in mind that this is just a basic example, and it is always important to thoroughly test and audit smart contracts before deploying to a live chain.

Don’t forget to [**subscribe to my channel**](https://www.youtube.com/@dappmentors?sub_confirmation=1) for all sorts of web3 development videos. Till next time, all the best.

## About the Author

Gospel Darlington is a full-stack blockchain developer with `6+` years of experience in the software development industry.

By combining Software Development, writing, and teaching, he demonstrates how to build decentralized applications on EVM-compatible blockchain networks.

For more information about him, kindly visit and follow his page on [Twitter](https://twitter.com/idaltonic), [Github](https://github.com/Daltonic), [LinkedIn](https://www.linkedin.com/in/darlington-gospel-aa626b125), or on his [website](https://daltonic.github.io/).