SC-20200130

Smart Contracts Intermediate January 30, 2020

Back to All Tasks

Problem Statement

You are tasked with creating a simple smart contract that simulates a basic lottery system. The contract should allow users to participate by sending an amount of Ether, and the winner will be selected randomly from all participants at a fixed interval (e.g., once every 24 hours). Winners receive all Ether collected in the lottery pool.

Requirements:
- Implement a function that allows users to buy a ticket by sending Ether. Each participant can enter multiple times but must send enough Ether each time they participate (minimum of 0.1 Ether).
- Once a day, randomly select one participant as the winner and transfer all Ether in the contract to this account.
- Emit an event whenever someone buys a ticket and when the lottery is won.

Considerations:
- Ensure that your solution is fair and not easily predictable.
- Handle potential issues such as what happens if there are no participants or if the random selection fails.

Concepts

  • Solidity basics
  • Events in Ethereum
  • Reentrancy attacks

Constraints

  • Minimum Ether per entry is 0.1 Ether
  • Lottery should reset after every winner is declared

Security Notes

  • Avoid reentrancy vulnerabilities in your transfer function
  • Ensure randomness is achieved securely, possibly using blockhashes and timestamps

Solutions

Php Solution

pragma solidity ^0.8.0;

contract Lottery {
    // Define an event that logs a ticket purchase
    event TicketPurchased(address indexed buyer);
    // Define an event that logs the lottery winner
    event LotteryWon(address indexed winner, uint prizeAmount);

    // Minimum amount of Ether required to buy a ticket
    uint public constant MINIMUM_BET = 0.1 ether;
    // Mapping to store all participants' addresses
    address[] public players;
    // The timestamp of the last lottery draw
    uint public lastDrawTimestamp;
    // Duration after which a new lottery draw is triggered (24 hours)
    uint public constant LOTTERY_DURATION = 1 days;

    // Constructor sets the initial timestamp
    constructor() {
        lastDrawTimestamp = block.timestamp;
    }

    // Function to buy a ticket. Participants must send at least MINIMUM_BET Ether.
    function buyTicket() public payable {
        require(msg.value >= MINIMUM_BET, "Minimum bet amount not met");
        players.push(msg.sender);
        emit TicketPurchased(msg.sender);
        // Check if it's time to draw the lottery
        checkAndDrawLottery();
    }

    // Function to securely select a winner using blockhash and timestamp
    function selectWinner() internal {
        require(players.length > 0, "No participants in the lottery");
        uint index = uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp))) % players.length;
        address winner = players[index];
        // Transfer all Ether to the winner
        payable(winner).transfer(address(this).balance);
        emit LotteryWon(winner, address(this).balance);
        // Reset the lottery state
        resetLottery();
    }

    // Function to check if it's time to draw the lottery and perform the draw
    function checkAndDrawLottery() internal {
        if (block.timestamp - lastDrawTimestamp >= LOTTERY_DURATION) {
            selectWinner();
        }
    }

    // Function to reset the state of the lottery for a new round
    function resetLottery() internal {
        delete players;
        lastDrawTimestamp = block.timestamp;
    }
}

This Solidity smart contract implements a basic lottery system where participants can buy tickets by sending Ether. Each ticket purchase requires a minimum of 0.1 Ether, and all funds are pooled until a winner is selected once every 24 hours.
The contract maintains a list of participant addresses in the `players` array and keeps track of when the last draw occurred with `lastDrawTimestamp`. The `buyTicket()` function handles ticket purchases, ensuring that each purchase meets the minimum bet requirement. It also checks if enough time has passed since the last draw to trigger a new lottery round.
The `selectWinner()` function securely selects a winner using a pseudo-random number generated from the block difficulty and timestamp. This method helps achieve randomness without relying on external sources. The selected winner receives all Ether in the contract, and the contract state is reset for the next round through the `resetLottery()` function.
Events are emitted whenever a ticket is purchased or when the lottery is won to provide transparency and allow front-end applications to listen for these actions. Security considerations include avoiding reentrancy vulnerabilities by using internal functions for sensitive operations and ensuring randomness in the winner selection process.