Search code examples
structmappingethereumsoliditysmartcontracts

Solidity: Using mapping and code design for lottery contract address


I'm writing the Lottery Contract. Since gas cost is high, I wanted to use mapping instead of an array and I defined id in struct. However, I could not get out of the code structure. How to determine the winner and how to empty the address book after the winner.

// SPDX-License-Identifier: MIT

    pragma solidity ^0.8.7;
    
    contract freeLottery {
        address public owner;
        uint public lotteryId;
        mapping (uint => address payable) public lotteryHistory;
        
       struct Players {
            bool isWinner;
            uint id;
        }
    
        mapping (address => Players) players;
        uint playersCount=0;
        
        constructor() {
            owner = msg.sender;
            lotteryId = 1;
        }
    
        function getWinner(uint lottery) public view returns (address payable) {
            return lotteryHistory[lottery];
        }
    
        function getBalance() public view returns (uint) {
            return address(this).balance;
        }
    
        function enter() public payable {        
            players[msg.sender];
            playersCount++;    
        }               
    
        
    
        function contribute() public payable onlyowner{
            require(msg.value>0,"Please send an amount greater than 0");
        }       
    
        function getRandomNumber() internal view returns (uint) {
            return uint(keccak256(abi.encodePacked(owner, block.timestamp)));
        }
    
        function pickWinner() public onlyowner {
            require(address(this).balance>0,"Please upload balance");
            uint index = getRandomNumber() % ???;
            ???
    
            ???
    
            lotteryHistory[lotteryId] = players[id];
            lotteryId++;
    
            // reset the state of the contract
            ???;
        }
    
        modifier onlyowner() {
          require(msg.sender == owner);
          _;
        }
    }   

Solution

  • You do not neet Players struct. in lotteryHistory you are already tracking the winner address. define your players mapping like this:

        // this will take care of storing in mapping instead of array
        mapping (uint => address) players;
        uint playersCount=0;
    

    then in enter function:

    function enter() public payable {    
                // initially playersCount is 0. so increase it first  
                playersCount++;
                // payable vs normal address has different methods. for storing it wont matter  
                players[playersCount]=payable(msg.sender);       
            }               
               
    

    then pickWinner:

           function pickWinner() public onlyowner returns (address payable) {
                require(address(this).balance>0,"Please upload balance");
                uint index = getRandomNumber() % playersCount;
                address payable winner=payable(players[index]);
                
                lotteryHistory[lotteryId] = winner;
                // since you inialized lotteryId=1 in constructor
                lotteryId++;
        
                // for resetting, you have to do for loop
                for (uint i=0; i< playersCount ; i++) {
                     delete players[i];
                }     
                return winner   ;   
            }
    

    Instead of doing for-loop do delete the players, we can define the players mapping as a nested mapping, using the lotteryId as the key. Our mapping will be like this

       lotteryId => playersCount => address
    

    So in the first lottery you would have

      1 => playersCount => address
    

    Inside pickWinner you already increase lotteryId++. After first lottery, you will start a fresh mapping

     2 => playersCount => address
    

    Now additionally, in pickWinner after selecting the winner, you should reset the playersCount=0, Your enter function will look like this:

    function enter() public payable {    
                // initially playersCount is 0. so increase it first  
                playersCount++;
                // payable vs normal address has different methods. for storing it wont matter  
                players[lotteryId][playersCount]=msg.sender;       
            }