diff --git a/hardhat/contracts/Lottery.sol b/hardhat/contracts/Lottery.sol index 93cf222..17c8960 100644 --- a/hardhat/contracts/Lottery.sol +++ b/hardhat/contracts/Lottery.sol @@ -1,95 +1,138 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.7.0 <0.9.0; +pragma solidity ^0.8.4; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {LotteryToken} from "./Token.sol"; +import {LotteryToken} from "./LotteryToken.sol"; +/// @title A very simple lottery contract +/// @author Matheus Pagani +/// @notice You can use this contract for running a very simple lottery +/// @dev This contract implements a relatively weak randomness source +/// @custom:teaching This is a contract meant for teaching only contract Lottery is Ownable { - + /// @notice Address of the token used as payment for the bets LotteryToken public paymentToken; - - /// @notice Flag indicating if lottery is open for bets - bool public betsOpen; - - - /// @notice Timestamp of the lottery next closing date - uint256 public betsClosingTime; - - /// @notice Amount of Tokens given per ETH paid + /// @notice Amount of tokens given per ETH paid uint256 public purchaseRatio; - - /// @notice + /// @notice Amount of tokens required for placing a bet that goes for the prize pool uint256 public betPrice; - - /// @notice + /// @notice Amount of tokens required for placing a bet that goes for the owner pool uint256 public betFee; - + /// @notice Amount of tokens in the prize pool uint256 public prizePool; + /// @notice Amount of tokens in the owner pool uint256 public ownerPool; - - address [] _slots; - - /// @notice constructor - // @param tokenName Name of the token - // @param tokenSymbol Symbol of tetoken - // @param _purchaseratio AMount of the token given per ETH paid - constructor(uint256 _purchaseRatio) { + /// @notice Flag indicating if the lottery is open for bets + bool public betsOpen; + /// @notice Timestamp of the lottery next closing date + uint256 public betsClosingTime; + /// @notice Mapping of prize available for withdraw for each account + mapping(address => uint256) public prize; + + /// @dev List of bet slots + address[] _slots; + + /// @notice Constructor function + /// @param _purchaseRatio Amount of tokens given per ETH paid + /// @param _betPrice Amount of tokens required for placing a bet that goes for the prize pool + /// @param _betFee Amount of tokens required for placing a bet that goes for the owner pool + constructor(uint256 _purchaseRatio, uint256 _betPrice, uint256 _betFee) { + paymentToken = new LotteryToken("MyToken", "TKN"); purchaseRatio = _purchaseRatio; - paymentToken = new LotteryToken(tokenName,tokenSymbol,); + betPrice = _betPrice; + betFee = _betFee; } - /// @notice open the lottery for receiving betx - function openBets(uint256 closingTime) external onlyOwner { - require( - closingTime > block.timestamp, - "Lottery: Closing time must be in the future" - ); - require(!betsOpen, "Lottery: bets are already open"); - betsClosingTime = closingTime; - betsOpen = true; + /// @notice Passes when the lottery is at closed state + modifier whenBetsClosed() { + require(!betsOpen, "Lottery is open"); + _; } - function purchaseToken() external payable { - paymentToken.mint(msg.sender,msg.value * purchaseratio); + /// @notice Passes when the lottery is at open state and the current block timestamp is lower than the lottery closing date + modifier whenBetsOpen() { + require(betsOpen && block.timestamp < betsClosingTime, "Lottery is closed"); + _; } + /// @notice Open the lottery for receiving bets + function openBets(uint256 closingTime) public onlyOwner whenBetsClosed { + require(closingTime > block.timestamp, "Closing time must be in the future"); + betsClosingTime = closingTime; + betsOpen = true; + } + /// @notice Give tokens based on the amount of ETH sent + function purchaseTokens() public payable { + paymentToken.mint(msg.sender, msg.value * purchaseRatio); + } - - function bet()public whenBetsOpen { + /// @notice Charge the bet price and create a new bet slot with the sender address + function bet() public whenBetsOpen { ownerPool += betFee; - prizePool += betprice; - // register the player - paymentToken.transferFrom(msg.sender,address(this),betPrice+betFee); + prizePool += betPrice; + _slots.push(msg.sender); + paymentToken.transferFrom(msg.sender, address(this), betPrice + betFee); } - function betMany(uint256 times) public{ - require ( times >0); - while (times>0){ - bet(); - times--; - } + /// @notice Call the bet function `times` times + // function betMany(uint256 times) public { + // require(times > 0); + // while (times > 0) { + // bet(); + // times--; + // } + // } + + function betMany(uint256 times) public whenBetsOpen { + require(times > 0); + ownerPool += times * betFee; + prizePool += times * betPrice; + + for (uint256 index = 0; index < times; index++) { + _slots.push(msg.sender); + } + paymentToken.transferFrom(msg.sender, address(this), times * (betPrice + betFee)); } - - funcion closeLottery() public { - require(block.timestamp>=betsClosingTime,"To soon to close"); - require(betsOpen,"already closed") - if(slots.lenght >0 ){ - uint256 winnerIndex = ?; + /// @notice Close the lottery and calculates the prize, if any + /// @dev Anyone can call this function if the owner fails to do so + function closeLottery() public { + require(block.timestamp >= betsClosingTime, "Too soon to close"); + require(betsOpen, "Already closed"); + if (_slots.length > 0) { + uint256 winnerIndex = getRandomNumber() % _slots.length; address winner = _slots[winnerIndex]; - prize [winner] += prizepool; + prize[winner] += prizePool; + prizePool = 0; + delete (_slots); } - } - function getRandomNumber() { - return randomNumber = block.difficulty; + betsOpen = false; } - function prizeWithdraw(uint256 amount)public { - require(amount <= ownerPool,"not enough fee) + /// @notice Get a random number calculated from the previous block randao + /// @dev This only works after The Merge + function getRandomNumber() public view returns (uint256 randomNumber) { + randomNumber = block.difficulty; } - func + /// @notice Withdraw `amount` from that accounts prize pool + function prizeWithdraw(uint256 amount) public { + require(amount <= prize[msg.sender], "Not enough prize"); + prize[msg.sender] -= amount; + paymentToken.transfer(msg.sender, amount); + } + /// @notice Withdraw `amount` from the owner pool + function ownerWithdraw(uint256 amount) public onlyOwner { + require(amount <= ownerPool, "Not enough fees collected"); + ownerPool -= amount; + paymentToken.transfer(msg.sender, amount); + } -} \ No newline at end of file + /// @notice Burn `amount` tokens and give the equivalent ETH back to user + function returnTokens(uint256 amount) public { + paymentToken.burnFrom(msg.sender, amount); + payable(msg.sender).transfer(amount / purchaseRatio); + } +}