Building a Smart Contract Lottery Application on Celo with Python

Building a Smart Contract Lottery Application on Celo with Python https://celo.academy/uploads/default/optimized/2X/4/401e65950fd8e6c3129dd24848480b6628b40730_2_1024x576.png
none 0.0 0

Introduction

Lottery applications are one of the major applications of blockchain technology since the early days of Ethereum. Smart contracts enable the operation of secure and transparent lotteries that cannot be manipulated by centralized parties. Furthermore, they can automate many lottery-related processes, such as ticket sales, winner selection, and prize distribution.
In this tutorial, we will learn how to to build a lottery smart contract and deploy it on the Celo blockchain with Python using brownie framework.

Prerequisites

To understand this tutorial, you must be familiar with:

  • Building Smart contracts
  • The Python Programming Language

Requirements

You should have the following installed on your computer to follow along:

Setting Up The Project

To get started, we have to create a new directory for our project and install the following dependencies:

mkdir lottery
cd lottery
# Create virtual environment
python3.10 -m venv venv

# activate virtual environment
source venv/bin/activate

Note: For some reason, brownie works best with python3.10

# Install ganache
npm install -g ganache

# Install eth-brownie and python-dotenv
pip3 install eth-brownie python-dotenv

After installing dependencies, we need to initialize our project as a brownie project.

brownie init

This command generates some folders which look like this:

After initializing brownie into our project, in your root directory, create two files called .env and brownie-config.yaml. The .env file is used to store environment variables that shouldn’t be exposed to the public such as our private key, mnemonic phrase, etc, while brownie-config.yaml is used to configure brownie in our project.

.env

MNEMONIC='...'
PRIVATE_KEY='0x...'

brownie-config.yaml

compiler:
    solc:
        version: 0.8.15
        optimizer:
            enabled: true
            runs: 200
networks:
    default: celo-alfajores
console:
    show_colors: true
    color_style: monokai
    auto_suggest: true
    completions: true
    editing_mode: emacs
dotenv: .env
wallets:
    from_mnemonic: ${MNEMONIC}
		from_key: ${PRIVATE_KEY}

The next step is to add Celo Testnet or Alfajores to our brownie project:

brownie networks add Celo celo-alfajores host=https://alfajores-forno.celo-testnet.org chainid=44787 explorer=https://alfajores-blockscout.celo-testnet.org

You can see the list of networks that have been added to our brownie project:

brownie networks list

Implementing the Smart Contract

Next, we have to write the smart contract for our Lottery application. In your contracts directory, create a new file called Lottery.sol

Lottery.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

contract Lottery {
    address public manager;
    address payable[] public players;
    uint public minimumBet;
    uint public numTickets;

    constructor(uint _minimumBet, uint _numTickets) {
        manager = msg.sender;
        minimumBet = _minimumBet;
        numTickets = _numTickets;
    }

    function enter() public payable {
        require(msg.value >= minimumBet, "Not enough ether to enter the lottery.");
        require(players.length < numTickets, "The lottery is full.");

        players.push(payable(msg.sender));
    }

    function pickWinner() public restricted {
        require(players.length == numTickets, "The lottery is not full yet.");

        uint index = random() % numTickets;
        players[index].transfer(address(this).balance);

        // reset the lottery
        delete players;
    }

    function getPlayers() public view returns (address payable[] memory) {
        return players;
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }

    function random() private view returns (uint) {
        return uint(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, players.length)));
    }

    modifier restricted() {
        require(msg.sender == manager, "Only the manager can call this function.");
        _;
    }
}

Let’s go through the code step by step:

	address public manager;
    address payable[] public players;
    uint public minimumBet;
    uint public numTickets;

    constructor(uint _minimumBet, uint _numTickets) {
        manager = msg.sender;
        minimumBet = _minimumBet;
        numTickets = _numTickets;
    }

The manager is the address of the owner of the contract who is the only one allowed to pick the winner of the lottery. The player’s array holds the addresses of the lottery players. The minimumBet is the minimum amount of ether required to place a bet in the lottery, and the numTickets is the number of tickets available to purchase. The constructor sets the manager as the lottery owner, the minimum bet, and the total number of tickets available.

 function enter() public payable {
        require(msg.value >= minimumBet, "Not enough ether to enter the lottery.");
        require(players.length < numTickets, "The lottery is full.");

        players.push(payable(msg.sender));
    }

The enter function enables players to enter the lottery and makes sure they deposit at least the minimum bet amount.

    function pickWinner() public restricted {
        require(players.length == numTickets, "The lottery is not full yet.");

        uint index = random() % numTickets;
        players[index].transfer(address(this).balance);

        // reset the lottery
        delete players;
    }

The pickWinner function picks at random the winner of the lottery and makes sure it’s only the contract manager that can send the balance of the contract

function getPlayers() public view returns (address payable[] memory) {
        return players;
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }

    function random() private view returns (uint) {
        return uint(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, players.length)));
    }

    modifier restricted() {
        require(msg.sender == manager, "Only the manager can call this function.");
        _;
    }

The getPlayers function returns an array of all the players that entered the lottery. The getBalance function returns the balance of the lottery contract. The random function picks at random the winner of the lottery and the restricted modifier is what allows a function to be restricted to just the lottery manager.

Deploying the Contract

Next, we need to compile and deploy the contract on the Celo Testnet, Run the following command to compile the contract.

brownie compile

To deploy the smart contract on Celo, create a new file called deploy.py in the scripts directory of your project.

deploy.py

from brownie import Lottery, accounts, config, network

def deploy_lottery():
    # Load the account to deploy from
    dev = accounts.add(config["wallets"]["from_key"])
    print(f"Deploying from {dev.address}")

    # Deploy the contract
    lottery = Lottery.deploy({"from": dev})

    print(f"NFTMarketplace contract deployed to {lottery.address}")

def main():
    # Set the network to deploy to
    network_name = network.show_active()
    print(f"Deploying to {network_name} network")

    # Call the deploy function
    deploy_lottery()

The deploy_lottery function gets the account we would use to deploy the contract

Conclusion

Lottery applications are one of the major applications of blockchain technology since the early days of Ethereum. Smart contracts enable the operation of secure and transparent lotteries that cannot be manipulated by centralized parties. Furthermore, they can automate many lottery-related processes, such as ticket sales, winner selection, and prize distribution.
In this tutorial, we will learn how to to build a lottery smart contract and deploy it on the Celo blockchain with Python using brownie framework.

Next Steps

Here are some additional resources to help you learn more about smart contract development and lottery applications:

  • Chainlink VRF: A decentralized and secure method for generating random numbers on the blockchain, Chainlink VRF. It can be used to ensure that lottery applications and other games of chance are fair.
  • CryptoZombies: CryptoZombies is a gamified interactive code school that teaches you how to build Ethereum dApps. A lesson on creating a lottery application is included in the tutorial series.

About the Author

Israel Okunaya is an ace writer with a flair for simplifying complexities and a knack for storytelling. He leverages over four years of experience to meet the most demanding writing needs in different niches, especially food and travel, blockchain, and marketing. He sees blockchain as a fascinating yet tricky affair. So, he’s given to simplifying its complexities with text and video tutorials.

References

  1. Celo Developer Documentation
  2. Solidity Documentation
  3. Brownie Documentation
  4. Github Repo
1 Like