diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index 4a4ac2f..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -yarn lint-staged --verbose \ No newline at end of file diff --git a/packages/hardhat/contracts/Lottery.sol b/packages/hardhat/contracts/Lottery.sol new file mode 100644 index 0000000..1df27fd --- /dev/null +++ b/packages/hardhat/contracts/Lottery.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.7.0 <0.9.0; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.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, since there is no cliff period between the randao reveal and the actual usage in this contract +/// @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 Amount of tokens given per ETH paid + uint256 public purchaseRatio; + /// @notice Amount of tokens required for placing a bet that goes for the prize pool + uint256 public betPrice; + /// @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; + /// @notice Flag indicating whether the lottery is open for bets or not + bool public betsOpen; + /// @notice Timestamp of the lottery next closing date and time + 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 tokenName Name of the token used for payment + /// @param tokenSymbol Symbol of the token used for payment + /// @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( + string memory tokenName, + string memory tokenSymbol, + uint256 _purchaseRatio, + uint256 _betPrice, + uint256 _betFee + ) Ownable(msg.sender) { + paymentToken = new LotteryToken(tokenName, tokenSymbol); + purchaseRatio = _purchaseRatio; + betPrice = _betPrice; + betFee = _betFee; + } + + /// @notice Passes when the lottery is at closed state + modifier whenBetsClosed() { + require(!betsOpen, "Lottery is open"); + _; + } + + /// @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 Opens the lottery for receiving bets + function openBets(uint256 closingTime) external onlyOwner whenBetsClosed { + require( + closingTime > block.timestamp, + "Closing time must be in the future" + ); + betsClosingTime = closingTime; + betsOpen = true; + } + + /// @notice Gives tokens based on the amount of ETH sent + /// @dev This implementation is prone to rounding problems + function purchaseTokens() external payable { + paymentToken.mint(msg.sender, msg.value * purchaseRatio); + } + + /// @notice Charges the bet price and creates a new bet slot with the sender's address + function bet() public whenBetsOpen { + ownerPool += betFee; + prizePool += betPrice; + _slots.push(msg.sender); + paymentToken.transferFrom(msg.sender, address(this), betPrice + betFee); + } + + /// @notice Calls the bet function `times` times + function betMany(uint256 times) external { + require(times > 0); + while (times > 0) { + bet(); + times--; + } + } + + /// @notice Closes the lottery and calculates the prize, if any + /// @dev Anyone can call this function at any time after the closing time + function closeLottery() external { + 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; + prizePool = 0; + delete (_slots); + } + betsOpen = false; + } + + /// @notice Returns 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.prevrandao; + } + + /// @notice Withdraws `amount` from that accounts's prize pool + function prizeWithdraw(uint256 amount) external { + require(amount <= prize[msg.sender], "Not enough prize"); + prize[msg.sender] -= amount; + paymentToken.transfer(msg.sender, amount); + } + + /// @notice Withdraws `amount` from the owner's pool + function ownerWithdraw(uint256 amount) external onlyOwner { + require(amount <= ownerPool, "Not enough fees collected"); + ownerPool -= amount; + paymentToken.transfer(msg.sender, amount); + } + + /// @notice Burns `amount` tokens and give the equivalent ETH back to user + function returnTokens(uint256 amount) external { + paymentToken.burnFrom(msg.sender, amount); + payable(msg.sender).transfer(amount / purchaseRatio); + } +} \ No newline at end of file diff --git a/packages/hardhat/contracts/LotteryToken.sol b/packages/hardhat/contracts/LotteryToken.sol new file mode 100644 index 0000000..7f3d469 --- /dev/null +++ b/packages/hardhat/contracts/LotteryToken.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; +import "@openzeppelin/contracts/access/AccessControl.sol"; + +contract LotteryToken is ERC20, ERC20Burnable, AccessControl { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + + constructor(string memory name, string memory symbol) ERC20(name, symbol) { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(MINTER_ROLE, msg.sender); + } + + function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { + _mint(to, amount); + } +} \ No newline at end of file diff --git a/packages/hardhat/contracts/YourContract.sol b/packages/hardhat/contracts/YourContract.sol deleted file mode 100644 index 053581d..0000000 --- a/packages/hardhat/contracts/YourContract.sol +++ /dev/null @@ -1,78 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; - -// Useful for debugging. Remove when deploying to a live network. -import "hardhat/console.sol"; - -// Use openzeppelin to inherit battle-tested implementations (ERC20, ERC721, etc) -// import "@openzeppelin/contracts/access/Ownable.sol"; - -/** - * A smart contract that allows changing a state variable of the contract and tracking the changes - * It also allows the owner to withdraw the Ether in the contract - * @author BuidlGuidl - */ -contract YourContract { - // State Variables - address public immutable owner; - string public greeting = "Building Unstoppable Apps!!!"; - bool public premium = false; - uint256 public totalCounter = 0; - mapping(address => uint) public userGreetingCounter; - - // Events: a way to emit log statements from smart contract that can be listened to by external parties - event GreetingChange(address indexed greetingSetter, string newGreeting, bool premium, uint256 value); - - // Constructor: Called once on contract deployment - // Check packages/hardhat/deploy/00_deploy_your_contract.ts - constructor(address _owner) { - owner = _owner; - } - - // Modifier: used to define a set of rules that must be met before or after a function is executed - // Check the withdraw() function - modifier isOwner() { - // msg.sender: predefined variable that represents address of the account that called the current function - require(msg.sender == owner, "Not the Owner"); - _; - } - - /** - * Function that allows anyone to change the state variable "greeting" of the contract and increase the counters - * - * @param _newGreeting (string memory) - new greeting to save on the contract - */ - function setGreeting(string memory _newGreeting) public payable { - // Print data to the hardhat chain console. Remove when deploying to a live network. - console.log("Setting new greeting '%s' from %s", _newGreeting, msg.sender); - - // Change state variables - greeting = _newGreeting; - totalCounter += 1; - userGreetingCounter[msg.sender] += 1; - - // msg.value: built-in global variable that represents the amount of ether sent with the transaction - if (msg.value > 0) { - premium = true; - } else { - premium = false; - } - - // emit: keyword used to trigger an event - emit GreetingChange(msg.sender, _newGreeting, msg.value > 0, msg.value); - } - - /** - * Function that allows the owner to withdraw all the Ether in the contract - * The function can only be called by the owner of the contract as defined by the isOwner modifier - */ - function withdraw() public isOwner { - (bool success, ) = owner.call{ value: address(this).balance }(""); - require(success, "Failed to send Ether"); - } - - /** - * Function that allows the contract to receive ETH - */ - receive() external payable {} -} diff --git a/packages/hardhat/deploy/00_deploy_your_contract.ts b/packages/hardhat/deploy/00_deploy_your_contract.ts index 716fec7..99ba7d2 100644 --- a/packages/hardhat/deploy/00_deploy_your_contract.ts +++ b/packages/hardhat/deploy/00_deploy_your_contract.ts @@ -1,6 +1,7 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; -import { Contract } from "ethers"; +import { Contract, parseEther } from "ethers"; +import { parse } from "dotenv"; /** * Deploys a contract named "YourContract" using the deployer account and @@ -22,10 +23,16 @@ const deployYourContract: DeployFunction = async function (hre: HardhatRuntimeEn const { deployer } = await hre.getNamedAccounts(); const { deploy } = hre.deployments; - await deploy("YourContract", { + await deploy("Lottery", { from: deployer, // Contract constructor arguments - args: [deployer], + args: [ + "Group7LotteryToken", + "G7T", + 10000n, + parseEther('0.0001'), + parseEther("0.00002") + ], log: true, // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by // automatically mining the contract deployment transaction. There is no effect on live networks. @@ -33,12 +40,13 @@ const deployYourContract: DeployFunction = async function (hre: HardhatRuntimeEn }); // Get the deployed contract to interact with it after deploying. - const yourContract = await hre.ethers.getContract("YourContract", deployer); - console.log("👋 Initial greeting:", await yourContract.greeting()); + const lotteryContract = await hre.ethers.getContract("Lottery", deployer); + console.log("Lottery Contract address:", await lotteryContract.getAddress()) + console.log("Lottery Token address:", await lotteryContract.paymentToken()); }; export default deployYourContract; // Tags are useful if you have multiple deploy files and only want to run one of them. // e.g. yarn deploy --tags YourContract -deployYourContract.tags = ["YourContract"]; +deployYourContract.tags = ["Lottery"]; diff --git a/packages/hardhat/deployments/sepolia/.chainId b/packages/hardhat/deployments/sepolia/.chainId new file mode 100644 index 0000000..bd8d1cd --- /dev/null +++ b/packages/hardhat/deployments/sepolia/.chainId @@ -0,0 +1 @@ +11155111 \ No newline at end of file diff --git a/packages/hardhat/deployments/sepolia/Lottery.json b/packages/hardhat/deployments/sepolia/Lottery.json new file mode 100644 index 0000000..984b82e --- /dev/null +++ b/packages/hardhat/deployments/sepolia/Lottery.json @@ -0,0 +1,650 @@ +{ + "address": "0xdB6Fee2827292Cb4C64Ad5b0C8D2afa3adb0d4fE", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_purchaseRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_betPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_betFee", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "bet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "betFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "times", + "type": "uint256" + } + ], + "name": "betMany", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "betPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "betsClosingTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "betsOpen", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeLottery", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getRandomNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "randomNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "closingTime", + "type": "uint256" + } + ], + "name": "openBets", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ownerPool", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ownerWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paymentToken", + "outputs": [ + { + "internalType": "contract LotteryToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "prize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "prizePool", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "prizeWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "purchaseRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "purchaseTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "returnTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0xeb9866a9f73586572c6683cd16a9e771f6a264ec59f81ea50e777ec0def2d4f2", + "receipt": { + "to": null, + "from": "0x2f101Ee67aa478Fccee6e27386462CfFf814Fcd1", + "contractAddress": "0xdB6Fee2827292Cb4C64Ad5b0C8D2afa3adb0d4fE", + "transactionIndex": 54, + "gasUsed": "1640685", + "logsBloom": "0x00000004001000000000000000002000000000000000000000800000000000000000000008000000000000000000004000000000000000000000000000000000000000000000000004000000000000000001000000000010000000008000000000000000020000000042000000000800000000000000000000000000001000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000020008000000000000000000000001000080000000000000000000000000000000000000000001000000000000100000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x5fa17f8ff12c8ca92f459a0eadfb195664203a6c73592e167d3154dbc212736a", + "transactionHash": "0xeb9866a9f73586572c6683cd16a9e771f6a264ec59f81ea50e777ec0def2d4f2", + "logs": [ + { + "transactionIndex": 54, + "blockNumber": 7184847, + "transactionHash": "0xeb9866a9f73586572c6683cd16a9e771f6a264ec59f81ea50e777ec0def2d4f2", + "address": "0xdB6Fee2827292Cb4C64Ad5b0C8D2afa3adb0d4fE", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000002f101ee67aa478fccee6e27386462cfff814fcd1" + ], + "data": "0x", + "logIndex": 93, + "blockHash": "0x5fa17f8ff12c8ca92f459a0eadfb195664203a6c73592e167d3154dbc212736a" + }, + { + "transactionIndex": 54, + "blockNumber": 7184847, + "transactionHash": "0xeb9866a9f73586572c6683cd16a9e771f6a264ec59f81ea50e777ec0def2d4f2", + "address": "0x581EfE8e04837E496C451cA84DE6a605DFf28Ba2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000db6fee2827292cb4c64ad5b0c8d2afa3adb0d4fe", + "0x000000000000000000000000db6fee2827292cb4c64ad5b0c8d2afa3adb0d4fe" + ], + "data": "0x", + "logIndex": 94, + "blockHash": "0x5fa17f8ff12c8ca92f459a0eadfb195664203a6c73592e167d3154dbc212736a" + }, + { + "transactionIndex": 54, + "blockNumber": 7184847, + "transactionHash": "0xeb9866a9f73586572c6683cd16a9e771f6a264ec59f81ea50e777ec0def2d4f2", + "address": "0x581EfE8e04837E496C451cA84DE6a605DFf28Ba2", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6", + "0x000000000000000000000000db6fee2827292cb4c64ad5b0c8d2afa3adb0d4fe", + "0x000000000000000000000000db6fee2827292cb4c64ad5b0c8d2afa3adb0d4fe" + ], + "data": "0x", + "logIndex": 95, + "blockHash": "0x5fa17f8ff12c8ca92f459a0eadfb195664203a6c73592e167d3154dbc212736a" + } + ], + "blockNumber": 7184847, + "cumulativeGasUsed": "15171364", + "status": 1, + "byzantium": true + }, + "args": [ + "Group7LotteryToken", + "G7T", + "10000", + "100000000000000", + "20000000000000" + ], + "numDeployments": 4, + "solcInputHash": "e6f65ef1ca17a1556421d91028008841", + "metadata": "{\"compiler\":{\"version\":\"0.8.24+commit.e11b9ed9\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenSymbol\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"_purchaseRatio\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_betPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_betFee\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"bet\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"betFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"times\",\"type\":\"uint256\"}],\"name\":\"betMany\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"betPrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"betsClosingTime\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"betsOpen\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"closeLottery\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRandomNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"randomNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"closingTime\",\"type\":\"uint256\"}],\"name\":\"openBets\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ownerPool\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ownerWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paymentToken\",\"outputs\":[{\"internalType\":\"contract LotteryToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"prize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"prizePool\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"prizeWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"purchaseRatio\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"purchaseTokens\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"returnTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Matheus Pagani\",\"custom:teaching\":\"This is a contract meant for teaching only\",\"details\":\"This contract implements a relatively weak randomness source, since there is no cliff period between the randao reveal and the actual usage in this contract\",\"errors\":{\"OwnableInvalidOwner(address)\":[{\"details\":\"The owner is not a valid owner account. (eg. `address(0)`)\"}],\"OwnableUnauthorizedAccount(address)\":[{\"details\":\"The caller account is not authorized to perform an operation.\"}]},\"kind\":\"dev\",\"methods\":{\"closeLottery()\":{\"details\":\"Anyone can call this function at any time after the closing time\"},\"constructor\":{\"params\":{\"_betFee\":\"Amount of tokens required for placing a bet that goes for the owner pool\",\"_betPrice\":\"Amount of tokens required for placing a bet that goes for the prize pool\",\"_purchaseRatio\":\"Amount of tokens given per ETH paid\",\"tokenName\":\"Name of the token used for payment\",\"tokenSymbol\":\"Symbol of the token used for payment\"}},\"getRandomNumber()\":{\"details\":\"This only works after The Merge\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"purchaseTokens()\":{\"details\":\"This implementation is prone to rounding problems\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"stateVariables\":{\"_slots\":{\"details\":\"List of bet slots\"}},\"title\":\"A very simple lottery contract\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"bet()\":{\"notice\":\"Charges the bet price and creates a new bet slot with the sender's address\"},\"betFee()\":{\"notice\":\"Amount of tokens required for placing a bet that goes for the owner pool\"},\"betMany(uint256)\":{\"notice\":\"Calls the bet function `times` times\"},\"betPrice()\":{\"notice\":\"Amount of tokens required for placing a bet that goes for the prize pool\"},\"betsClosingTime()\":{\"notice\":\"Timestamp of the lottery next closing date and time\"},\"betsOpen()\":{\"notice\":\"Flag indicating whether the lottery is open for bets or not\"},\"closeLottery()\":{\"notice\":\"Closes the lottery and calculates the prize, if any\"},\"constructor\":{\"notice\":\"Constructor function\"},\"getRandomNumber()\":{\"notice\":\"Returns a random number calculated from the previous block randao\"},\"openBets(uint256)\":{\"notice\":\"Opens the lottery for receiving bets\"},\"ownerPool()\":{\"notice\":\"Amount of tokens in the owner pool\"},\"ownerWithdraw(uint256)\":{\"notice\":\"Withdraws `amount` from the owner's pool\"},\"paymentToken()\":{\"notice\":\"Address of the token used as payment for the bets\"},\"prize(address)\":{\"notice\":\"Mapping of prize available for withdraw for each account\"},\"prizePool()\":{\"notice\":\"Amount of tokens in the prize pool\"},\"prizeWithdraw(uint256)\":{\"notice\":\"Withdraws `amount` from that accounts's prize pool\"},\"purchaseRatio()\":{\"notice\":\"Amount of tokens given per ETH paid\"},\"purchaseTokens()\":{\"notice\":\"Gives tokens based on the amount of ETH sent\"},\"returnTokens(uint256)\":{\"notice\":\"Burns `amount` tokens and give the equivalent ETH back to user\"}},\"notice\":\"You can use this contract for running a very simple lottery\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Lottery.sol\":\"Lottery\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/access/AccessControl.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\\n\\npragma solidity ^0.8.20;\\n\\nimport {IAccessControl} from \\\"./IAccessControl.sol\\\";\\nimport {Context} from \\\"../utils/Context.sol\\\";\\nimport {ERC165} from \\\"../utils/introspection/ERC165.sol\\\";\\n\\n/**\\n * @dev Contract module that allows children to implement role-based access\\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\\n * members except through off-chain means by accessing the contract event logs. Some\\n * applications may benefit from on-chain enumerability, for those cases see\\n * {AccessControlEnumerable}.\\n *\\n * Roles are referred to by their `bytes32` identifier. These should be exposed\\n * in the external API and be unique. The best way to achieve this is by\\n * using `public constant` hash digests:\\n *\\n * ```solidity\\n * bytes32 public constant MY_ROLE = keccak256(\\\"MY_ROLE\\\");\\n * ```\\n *\\n * Roles can be used to represent a set of permissions. To restrict access to a\\n * function call, use {hasRole}:\\n *\\n * ```solidity\\n * function foo() public {\\n * require(hasRole(MY_ROLE, msg.sender));\\n * ...\\n * }\\n * ```\\n *\\n * Roles can be granted and revoked dynamically via the {grantRole} and\\n * {revokeRole} functions. Each role has an associated admin role, and only\\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\\n *\\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\\n * that only accounts with this role will be able to grant or revoke other\\n * roles. More complex role relationships can be created by using\\n * {_setRoleAdmin}.\\n *\\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\\n * grant and revoke this role. Extra precautions should be taken to secure\\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\\n * to enforce additional security measures for this role.\\n */\\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\\n struct RoleData {\\n mapping(address account => bool) hasRole;\\n bytes32 adminRole;\\n }\\n\\n mapping(bytes32 role => RoleData) private _roles;\\n\\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\\n\\n /**\\n * @dev Modifier that checks that an account has a specific role. Reverts\\n * with an {AccessControlUnauthorizedAccount} error including the required role.\\n */\\n modifier onlyRole(bytes32 role) {\\n _checkRole(role);\\n _;\\n }\\n\\n /**\\n * @dev See {IERC165-supportsInterface}.\\n */\\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\\n }\\n\\n /**\\n * @dev Returns `true` if `account` has been granted `role`.\\n */\\n function hasRole(bytes32 role, address account) public view virtual returns (bool) {\\n return _roles[role].hasRole[account];\\n }\\n\\n /**\\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\\n * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\\n */\\n function _checkRole(bytes32 role) internal view virtual {\\n _checkRole(role, _msgSender());\\n }\\n\\n /**\\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\\n * is missing `role`.\\n */\\n function _checkRole(bytes32 role, address account) internal view virtual {\\n if (!hasRole(role, account)) {\\n revert AccessControlUnauthorizedAccount(account, role);\\n }\\n }\\n\\n /**\\n * @dev Returns the admin role that controls `role`. See {grantRole} and\\n * {revokeRole}.\\n *\\n * To change a role's admin, use {_setRoleAdmin}.\\n */\\n function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\\n return _roles[role].adminRole;\\n }\\n\\n /**\\n * @dev Grants `role` to `account`.\\n *\\n * If `account` had not been already granted `role`, emits a {RoleGranted}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n *\\n * May emit a {RoleGranted} event.\\n */\\n function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\\n _grantRole(role, account);\\n }\\n\\n /**\\n * @dev Revokes `role` from `account`.\\n *\\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n *\\n * May emit a {RoleRevoked} event.\\n */\\n function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\\n _revokeRole(role, account);\\n }\\n\\n /**\\n * @dev Revokes `role` from the calling account.\\n *\\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\\n * purpose is to provide a mechanism for accounts to lose their privileges\\n * if they are compromised (such as when a trusted device is misplaced).\\n *\\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must be `callerConfirmation`.\\n *\\n * May emit a {RoleRevoked} event.\\n */\\n function renounceRole(bytes32 role, address callerConfirmation) public virtual {\\n if (callerConfirmation != _msgSender()) {\\n revert AccessControlBadConfirmation();\\n }\\n\\n _revokeRole(role, callerConfirmation);\\n }\\n\\n /**\\n * @dev Sets `adminRole` as ``role``'s admin role.\\n *\\n * Emits a {RoleAdminChanged} event.\\n */\\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\\n bytes32 previousAdminRole = getRoleAdmin(role);\\n _roles[role].adminRole = adminRole;\\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\\n }\\n\\n /**\\n * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\\n *\\n * Internal function without access restriction.\\n *\\n * May emit a {RoleGranted} event.\\n */\\n function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\\n if (!hasRole(role, account)) {\\n _roles[role].hasRole[account] = true;\\n emit RoleGranted(role, account, _msgSender());\\n return true;\\n } else {\\n return false;\\n }\\n }\\n\\n /**\\n * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\\n *\\n * Internal function without access restriction.\\n *\\n * May emit a {RoleRevoked} event.\\n */\\n function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\\n if (hasRole(role, account)) {\\n _roles[role].hasRole[account] = false;\\n emit RoleRevoked(role, account, _msgSender());\\n return true;\\n } else {\\n return false;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa0e92d42942f4f57c5be50568dac11e9d00c93efcb458026e18d2d9b9b2e7308\",\"license\":\"MIT\"},\"@openzeppelin/contracts/access/IAccessControl.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)\\n\\npragma solidity ^0.8.20;\\n\\n/**\\n * @dev External interface of AccessControl declared to support ERC165 detection.\\n */\\ninterface IAccessControl {\\n /**\\n * @dev The `account` is missing a role.\\n */\\n error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\\n\\n /**\\n * @dev The caller of a function is not the expected one.\\n *\\n * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\\n */\\n error AccessControlBadConfirmation();\\n\\n /**\\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\\n *\\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\\n * {RoleAdminChanged} not being emitted signaling this.\\n */\\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\\n\\n /**\\n * @dev Emitted when `account` is granted `role`.\\n *\\n * `sender` is the account that originated the contract call, an admin role\\n * bearer except when using {AccessControl-_setupRole}.\\n */\\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Emitted when `account` is revoked `role`.\\n *\\n * `sender` is the account that originated the contract call:\\n * - if using `revokeRole`, it is the admin role bearer\\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\\n */\\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Returns `true` if `account` has been granted `role`.\\n */\\n function hasRole(bytes32 role, address account) external view returns (bool);\\n\\n /**\\n * @dev Returns the admin role that controls `role`. See {grantRole} and\\n * {revokeRole}.\\n *\\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\\n */\\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\\n\\n /**\\n * @dev Grants `role` to `account`.\\n *\\n * If `account` had not been already granted `role`, emits a {RoleGranted}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function grantRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from `account`.\\n *\\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function revokeRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from the calling account.\\n *\\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\\n * purpose is to provide a mechanism for accounts to lose their privileges\\n * if they are compromised (such as when a trusted device is misplaced).\\n *\\n * If the calling account had been granted `role`, emits a {RoleRevoked}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must be `callerConfirmation`.\\n */\\n function renounceRole(bytes32 role, address callerConfirmation) external;\\n}\\n\",\"keccak256\":\"0xb6b36edd6a2999fd243ff226d6cbf84bd71af2432bbd0dfe19392996a1d9cb41\",\"license\":\"MIT\"},\"@openzeppelin/contracts/access/Ownable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\\n\\npragma solidity ^0.8.20;\\n\\nimport {Context} from \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * The initial owner is set to the address provided by the deployer. This can\\n * later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract Ownable is Context {\\n address private _owner;\\n\\n /**\\n * @dev The caller account is not authorized to perform an operation.\\n */\\n error OwnableUnauthorizedAccount(address account);\\n\\n /**\\n * @dev The owner is not a valid owner account. (eg. `address(0)`)\\n */\\n error OwnableInvalidOwner(address owner);\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\\n */\\n constructor(address initialOwner) {\\n if (initialOwner == address(0)) {\\n revert OwnableInvalidOwner(address(0));\\n }\\n _transferOwnership(initialOwner);\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n _checkOwner();\\n _;\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if the sender is not the owner.\\n */\\n function _checkOwner() internal view virtual {\\n if (owner() != _msgSender()) {\\n revert OwnableUnauthorizedAccount(_msgSender());\\n }\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby disabling any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _transferOwnership(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n if (newOwner == address(0)) {\\n revert OwnableInvalidOwner(address(0));\\n }\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n}\\n\",\"keccak256\":\"0xff6d0bb2e285473e5311d9d3caacb525ae3538a80758c10649a4d61029b017bb\",\"license\":\"MIT\"},\"@openzeppelin/contracts/interfaces/draft-IERC6093.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)\\npragma solidity ^0.8.20;\\n\\n/**\\n * @dev Standard ERC20 Errors\\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.\\n */\\ninterface IERC20Errors {\\n /**\\n * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\\n * @param sender Address whose tokens are being transferred.\\n * @param balance Current balance for the interacting account.\\n * @param needed Minimum amount required to perform a transfer.\\n */\\n error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\\n\\n /**\\n * @dev Indicates a failure with the token `sender`. Used in transfers.\\n * @param sender Address whose tokens are being transferred.\\n */\\n error ERC20InvalidSender(address sender);\\n\\n /**\\n * @dev Indicates a failure with the token `receiver`. Used in transfers.\\n * @param receiver Address to which tokens are being transferred.\\n */\\n error ERC20InvalidReceiver(address receiver);\\n\\n /**\\n * @dev Indicates a failure with the `spender`\\u2019s `allowance`. Used in transfers.\\n * @param spender Address that may be allowed to operate on tokens without being their owner.\\n * @param allowance Amount of tokens a `spender` is allowed to operate with.\\n * @param needed Minimum amount required to perform a transfer.\\n */\\n error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\\n\\n /**\\n * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\\n * @param approver Address initiating an approval operation.\\n */\\n error ERC20InvalidApprover(address approver);\\n\\n /**\\n * @dev Indicates a failure with the `spender` to be approved. Used in approvals.\\n * @param spender Address that may be allowed to operate on tokens without being their owner.\\n */\\n error ERC20InvalidSpender(address spender);\\n}\\n\\n/**\\n * @dev Standard ERC721 Errors\\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.\\n */\\ninterface IERC721Errors {\\n /**\\n * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.\\n * Used in balance queries.\\n * @param owner Address of the current owner of a token.\\n */\\n error ERC721InvalidOwner(address owner);\\n\\n /**\\n * @dev Indicates a `tokenId` whose `owner` is the zero address.\\n * @param tokenId Identifier number of a token.\\n */\\n error ERC721NonexistentToken(uint256 tokenId);\\n\\n /**\\n * @dev Indicates an error related to the ownership over a particular token. Used in transfers.\\n * @param sender Address whose tokens are being transferred.\\n * @param tokenId Identifier number of a token.\\n * @param owner Address of the current owner of a token.\\n */\\n error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);\\n\\n /**\\n * @dev Indicates a failure with the token `sender`. Used in transfers.\\n * @param sender Address whose tokens are being transferred.\\n */\\n error ERC721InvalidSender(address sender);\\n\\n /**\\n * @dev Indicates a failure with the token `receiver`. Used in transfers.\\n * @param receiver Address to which tokens are being transferred.\\n */\\n error ERC721InvalidReceiver(address receiver);\\n\\n /**\\n * @dev Indicates a failure with the `operator`\\u2019s approval. Used in transfers.\\n * @param operator Address that may be allowed to operate on tokens without being their owner.\\n * @param tokenId Identifier number of a token.\\n */\\n error ERC721InsufficientApproval(address operator, uint256 tokenId);\\n\\n /**\\n * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\\n * @param approver Address initiating an approval operation.\\n */\\n error ERC721InvalidApprover(address approver);\\n\\n /**\\n * @dev Indicates a failure with the `operator` to be approved. Used in approvals.\\n * @param operator Address that may be allowed to operate on tokens without being their owner.\\n */\\n error ERC721InvalidOperator(address operator);\\n}\\n\\n/**\\n * @dev Standard ERC1155 Errors\\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.\\n */\\ninterface IERC1155Errors {\\n /**\\n * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\\n * @param sender Address whose tokens are being transferred.\\n * @param balance Current balance for the interacting account.\\n * @param needed Minimum amount required to perform a transfer.\\n * @param tokenId Identifier number of a token.\\n */\\n error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);\\n\\n /**\\n * @dev Indicates a failure with the token `sender`. Used in transfers.\\n * @param sender Address whose tokens are being transferred.\\n */\\n error ERC1155InvalidSender(address sender);\\n\\n /**\\n * @dev Indicates a failure with the token `receiver`. Used in transfers.\\n * @param receiver Address to which tokens are being transferred.\\n */\\n error ERC1155InvalidReceiver(address receiver);\\n\\n /**\\n * @dev Indicates a failure with the `operator`\\u2019s approval. Used in transfers.\\n * @param operator Address that may be allowed to operate on tokens without being their owner.\\n * @param owner Address of the current owner of a token.\\n */\\n error ERC1155MissingApprovalForAll(address operator, address owner);\\n\\n /**\\n * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\\n * @param approver Address initiating an approval operation.\\n */\\n error ERC1155InvalidApprover(address approver);\\n\\n /**\\n * @dev Indicates a failure with the `operator` to be approved. Used in approvals.\\n * @param operator Address that may be allowed to operate on tokens without being their owner.\\n */\\n error ERC1155InvalidOperator(address operator);\\n\\n /**\\n * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.\\n * Used in batch transfers.\\n * @param idsLength Length of the array of token identifiers\\n * @param valuesLength Length of the array of token amounts\\n */\\n error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);\\n}\\n\",\"keccak256\":\"0x60c65f701957fdd6faea1acb0bb45825791d473693ed9ecb34726fdfaa849dd7\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/ERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)\\n\\npragma solidity ^0.8.20;\\n\\nimport {IERC20} from \\\"./IERC20.sol\\\";\\nimport {IERC20Metadata} from \\\"./extensions/IERC20Metadata.sol\\\";\\nimport {Context} from \\\"../../utils/Context.sol\\\";\\nimport {IERC20Errors} from \\\"../../interfaces/draft-IERC6093.sol\\\";\\n\\n/**\\n * @dev Implementation of the {IERC20} interface.\\n *\\n * This implementation is agnostic to the way tokens are created. This means\\n * that a supply mechanism has to be added in a derived contract using {_mint}.\\n *\\n * TIP: For a detailed writeup see our guide\\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\\n * to implement supply mechanisms].\\n *\\n * The default value of {decimals} is 18. To change this, you should override\\n * this function so it returns a different value.\\n *\\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\\n * instead returning `false` on failure. This behavior is nonetheless\\n * conventional and does not conflict with the expectations of ERC20\\n * applications.\\n *\\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\\n * This allows applications to reconstruct the allowance for all accounts just\\n * by listening to said events. Other implementations of the EIP may not emit\\n * these events, as it isn't required by the specification.\\n */\\nabstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {\\n mapping(address account => uint256) private _balances;\\n\\n mapping(address account => mapping(address spender => uint256)) private _allowances;\\n\\n uint256 private _totalSupply;\\n\\n string private _name;\\n string private _symbol;\\n\\n /**\\n * @dev Sets the values for {name} and {symbol}.\\n *\\n * All two of these values are immutable: they can only be set once during\\n * construction.\\n */\\n constructor(string memory name_, string memory symbol_) {\\n _name = name_;\\n _symbol = symbol_;\\n }\\n\\n /**\\n * @dev Returns the name of the token.\\n */\\n function name() public view virtual returns (string memory) {\\n return _name;\\n }\\n\\n /**\\n * @dev Returns the symbol of the token, usually a shorter version of the\\n * name.\\n */\\n function symbol() public view virtual returns (string memory) {\\n return _symbol;\\n }\\n\\n /**\\n * @dev Returns the number of decimals used to get its user representation.\\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\\n *\\n * Tokens usually opt for a value of 18, imitating the relationship between\\n * Ether and Wei. This is the default value returned by this function, unless\\n * it's overridden.\\n *\\n * NOTE: This information is only used for _display_ purposes: it in\\n * no way affects any of the arithmetic of the contract, including\\n * {IERC20-balanceOf} and {IERC20-transfer}.\\n */\\n function decimals() public view virtual returns (uint8) {\\n return 18;\\n }\\n\\n /**\\n * @dev See {IERC20-totalSupply}.\\n */\\n function totalSupply() public view virtual returns (uint256) {\\n return _totalSupply;\\n }\\n\\n /**\\n * @dev See {IERC20-balanceOf}.\\n */\\n function balanceOf(address account) public view virtual returns (uint256) {\\n return _balances[account];\\n }\\n\\n /**\\n * @dev See {IERC20-transfer}.\\n *\\n * Requirements:\\n *\\n * - `to` cannot be the zero address.\\n * - the caller must have a balance of at least `value`.\\n */\\n function transfer(address to, uint256 value) public virtual returns (bool) {\\n address owner = _msgSender();\\n _transfer(owner, to, value);\\n return true;\\n }\\n\\n /**\\n * @dev See {IERC20-allowance}.\\n */\\n function allowance(address owner, address spender) public view virtual returns (uint256) {\\n return _allowances[owner][spender];\\n }\\n\\n /**\\n * @dev See {IERC20-approve}.\\n *\\n * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on\\n * `transferFrom`. This is semantically equivalent to an infinite approval.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n */\\n function approve(address spender, uint256 value) public virtual returns (bool) {\\n address owner = _msgSender();\\n _approve(owner, spender, value);\\n return true;\\n }\\n\\n /**\\n * @dev See {IERC20-transferFrom}.\\n *\\n * Emits an {Approval} event indicating the updated allowance. This is not\\n * required by the EIP. See the note at the beginning of {ERC20}.\\n *\\n * NOTE: Does not update the allowance if the current allowance\\n * is the maximum `uint256`.\\n *\\n * Requirements:\\n *\\n * - `from` and `to` cannot be the zero address.\\n * - `from` must have a balance of at least `value`.\\n * - the caller must have allowance for ``from``'s tokens of at least\\n * `value`.\\n */\\n function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {\\n address spender = _msgSender();\\n _spendAllowance(from, spender, value);\\n _transfer(from, to, value);\\n return true;\\n }\\n\\n /**\\n * @dev Moves a `value` amount of tokens from `from` to `to`.\\n *\\n * This internal function is equivalent to {transfer}, and can be used to\\n * e.g. implement automatic token fees, slashing mechanisms, etc.\\n *\\n * Emits a {Transfer} event.\\n *\\n * NOTE: This function is not virtual, {_update} should be overridden instead.\\n */\\n function _transfer(address from, address to, uint256 value) internal {\\n if (from == address(0)) {\\n revert ERC20InvalidSender(address(0));\\n }\\n if (to == address(0)) {\\n revert ERC20InvalidReceiver(address(0));\\n }\\n _update(from, to, value);\\n }\\n\\n /**\\n * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`\\n * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding\\n * this function.\\n *\\n * Emits a {Transfer} event.\\n */\\n function _update(address from, address to, uint256 value) internal virtual {\\n if (from == address(0)) {\\n // Overflow check required: The rest of the code assumes that totalSupply never overflows\\n _totalSupply += value;\\n } else {\\n uint256 fromBalance = _balances[from];\\n if (fromBalance < value) {\\n revert ERC20InsufficientBalance(from, fromBalance, value);\\n }\\n unchecked {\\n // Overflow not possible: value <= fromBalance <= totalSupply.\\n _balances[from] = fromBalance - value;\\n }\\n }\\n\\n if (to == address(0)) {\\n unchecked {\\n // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.\\n _totalSupply -= value;\\n }\\n } else {\\n unchecked {\\n // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.\\n _balances[to] += value;\\n }\\n }\\n\\n emit Transfer(from, to, value);\\n }\\n\\n /**\\n * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).\\n * Relies on the `_update` mechanism\\n *\\n * Emits a {Transfer} event with `from` set to the zero address.\\n *\\n * NOTE: This function is not virtual, {_update} should be overridden instead.\\n */\\n function _mint(address account, uint256 value) internal {\\n if (account == address(0)) {\\n revert ERC20InvalidReceiver(address(0));\\n }\\n _update(address(0), account, value);\\n }\\n\\n /**\\n * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.\\n * Relies on the `_update` mechanism.\\n *\\n * Emits a {Transfer} event with `to` set to the zero address.\\n *\\n * NOTE: This function is not virtual, {_update} should be overridden instead\\n */\\n function _burn(address account, uint256 value) internal {\\n if (account == address(0)) {\\n revert ERC20InvalidSender(address(0));\\n }\\n _update(account, address(0), value);\\n }\\n\\n /**\\n * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.\\n *\\n * This internal function is equivalent to `approve`, and can be used to\\n * e.g. set automatic allowances for certain subsystems, etc.\\n *\\n * Emits an {Approval} event.\\n *\\n * Requirements:\\n *\\n * - `owner` cannot be the zero address.\\n * - `spender` cannot be the zero address.\\n *\\n * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.\\n */\\n function _approve(address owner, address spender, uint256 value) internal {\\n _approve(owner, spender, value, true);\\n }\\n\\n /**\\n * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.\\n *\\n * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by\\n * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any\\n * `Approval` event during `transferFrom` operations.\\n *\\n * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to\\n * true using the following override:\\n * ```\\n * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {\\n * super._approve(owner, spender, value, true);\\n * }\\n * ```\\n *\\n * Requirements are the same as {_approve}.\\n */\\n function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {\\n if (owner == address(0)) {\\n revert ERC20InvalidApprover(address(0));\\n }\\n if (spender == address(0)) {\\n revert ERC20InvalidSpender(address(0));\\n }\\n _allowances[owner][spender] = value;\\n if (emitEvent) {\\n emit Approval(owner, spender, value);\\n }\\n }\\n\\n /**\\n * @dev Updates `owner` s allowance for `spender` based on spent `value`.\\n *\\n * Does not update the allowance value in case of infinite allowance.\\n * Revert if not enough allowance is available.\\n *\\n * Does not emit an {Approval} event.\\n */\\n function _spendAllowance(address owner, address spender, uint256 value) internal virtual {\\n uint256 currentAllowance = allowance(owner, spender);\\n if (currentAllowance != type(uint256).max) {\\n if (currentAllowance < value) {\\n revert ERC20InsufficientAllowance(spender, currentAllowance, value);\\n }\\n unchecked {\\n _approve(owner, spender, currentAllowance - value, false);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3e1fa9d1987f8d349dfb4d6fe93bf2ca014b52ba335cfac30bfe71e357e6f80\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.20;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the value of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the value of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves a `value` amount of tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 value) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\\n * caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 value) external returns (bool);\\n\\n /**\\n * @dev Moves a `value` amount of tokens from `from` to `to` using the\\n * allowance mechanism. `value` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 value) external returns (bool);\\n}\\n\",\"keccak256\":\"0xc6a8ff0ea489379b61faa647490411b80102578440ab9d84e9a957cc12164e70\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Burnable.sol)\\n\\npragma solidity ^0.8.20;\\n\\nimport {ERC20} from \\\"../ERC20.sol\\\";\\nimport {Context} from \\\"../../../utils/Context.sol\\\";\\n\\n/**\\n * @dev Extension of {ERC20} that allows token holders to destroy both their own\\n * tokens and those that they have an allowance for, in a way that can be\\n * recognized off-chain (via event analysis).\\n */\\nabstract contract ERC20Burnable is Context, ERC20 {\\n /**\\n * @dev Destroys a `value` amount of tokens from the caller.\\n *\\n * See {ERC20-_burn}.\\n */\\n function burn(uint256 value) public virtual {\\n _burn(_msgSender(), value);\\n }\\n\\n /**\\n * @dev Destroys a `value` amount of tokens from `account`, deducting from\\n * the caller's allowance.\\n *\\n * See {ERC20-_burn} and {ERC20-allowance}.\\n *\\n * Requirements:\\n *\\n * - the caller must have allowance for ``accounts``'s tokens of at least\\n * `value`.\\n */\\n function burnFrom(address account, uint256 value) public virtual {\\n _spendAllowance(account, _msgSender(), value);\\n _burn(account, value);\\n }\\n}\\n\",\"keccak256\":\"0x2659248df25e34000ed214b3dc8da2160bc39874c992b477d9e2b1b3283dc073\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)\\n\\npragma solidity ^0.8.20;\\n\\nimport {IERC20} from \\\"../IERC20.sol\\\";\\n\\n/**\\n * @dev Interface for the optional metadata functions from the ERC20 standard.\\n */\\ninterface IERC20Metadata is IERC20 {\\n /**\\n * @dev Returns the name of the token.\\n */\\n function name() external view returns (string memory);\\n\\n /**\\n * @dev Returns the symbol of the token.\\n */\\n function symbol() external view returns (string memory);\\n\\n /**\\n * @dev Returns the decimals places of the token.\\n */\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0xaa761817f6cd7892fcf158b3c776b34551cde36f48ff9703d53898bc45a94ea2\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\\n\\npragma solidity ^0.8.20;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n\\n function _contextSuffixLength() internal view virtual returns (uint256) {\\n return 0;\\n }\\n}\\n\",\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/introspection/ERC165.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)\\n\\npragma solidity ^0.8.20;\\n\\nimport {IERC165} from \\\"./IERC165.sol\\\";\\n\\n/**\\n * @dev Implementation of the {IERC165} interface.\\n *\\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\\n * for the additional interface id that will be supported. For example:\\n *\\n * ```solidity\\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\\n * }\\n * ```\\n */\\nabstract contract ERC165 is IERC165 {\\n /**\\n * @dev See {IERC165-supportsInterface}.\\n */\\n function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\\n return interfaceId == type(IERC165).interfaceId;\\n }\\n}\\n\",\"keccak256\":\"0x9e8778b14317ba9e256c30a76fd6c32b960af621987f56069e1e819c77c6a133\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\\n\\npragma solidity ^0.8.20;\\n\\n/**\\n * @dev Interface of the ERC165 standard, as defined in the\\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\\n *\\n * Implementers can declare support of contract interfaces, which can then be\\n * queried by others ({ERC165Checker}).\\n *\\n * For an implementation, see {ERC165}.\\n */\\ninterface IERC165 {\\n /**\\n * @dev Returns true if this contract implements the interface defined by\\n * `interfaceId`. See the corresponding\\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\\n * to learn more about how these ids are created.\\n *\\n * This function call must use less than 30 000 gas.\\n */\\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x4296879f55019b23e135000eb36896057e7101fb7fb859c5ef690cf14643757b\",\"license\":\"MIT\"},\"contracts/Lottery.sol\":{\"content\":\"// SPDX-License-Identifier: GPL-3.0\\r\\npragma solidity >=0.7.0 <0.9.0;\\r\\n\\r\\nimport {Ownable} from \\\"@openzeppelin/contracts/access/Ownable.sol\\\";\\r\\nimport {LotteryToken} from \\\"./LotteryToken.sol\\\";\\r\\n\\r\\n/// @title A very simple lottery contract\\r\\n/// @author Matheus Pagani\\r\\n/// @notice You can use this contract for running a very simple lottery\\r\\n/// @dev This contract implements a relatively weak randomness source, since there is no cliff period between the randao reveal and the actual usage in this contract\\r\\n/// @custom:teaching This is a contract meant for teaching only\\r\\ncontract Lottery is Ownable {\\r\\n /// @notice Address of the token used as payment for the bets\\r\\n LotteryToken public paymentToken;\\r\\n /// @notice Amount of tokens given per ETH paid\\r\\n uint256 public purchaseRatio;\\r\\n /// @notice Amount of tokens required for placing a bet that goes for the prize pool\\r\\n uint256 public betPrice;\\r\\n /// @notice Amount of tokens required for placing a bet that goes for the owner pool\\r\\n uint256 public betFee;\\r\\n /// @notice Amount of tokens in the prize pool\\r\\n uint256 public prizePool;\\r\\n /// @notice Amount of tokens in the owner pool\\r\\n uint256 public ownerPool;\\r\\n /// @notice Flag indicating whether the lottery is open for bets or not\\r\\n bool public betsOpen;\\r\\n /// @notice Timestamp of the lottery next closing date and time\\r\\n uint256 public betsClosingTime;\\r\\n /// @notice Mapping of prize available for withdraw for each account\\r\\n mapping(address => uint256) public prize;\\r\\n\\r\\n /// @dev List of bet slots\\r\\n address[] _slots;\\r\\n\\r\\n /// @notice Constructor function\\r\\n /// @param tokenName Name of the token used for payment\\r\\n /// @param tokenSymbol Symbol of the token used for payment\\r\\n /// @param _purchaseRatio Amount of tokens given per ETH paid\\r\\n /// @param _betPrice Amount of tokens required for placing a bet that goes for the prize pool\\r\\n /// @param _betFee Amount of tokens required for placing a bet that goes for the owner pool\\r\\n constructor(\\r\\n string memory tokenName,\\r\\n string memory tokenSymbol,\\r\\n uint256 _purchaseRatio,\\r\\n uint256 _betPrice,\\r\\n uint256 _betFee\\r\\n ) Ownable(msg.sender) {\\r\\n paymentToken = new LotteryToken(tokenName, tokenSymbol);\\r\\n purchaseRatio = _purchaseRatio;\\r\\n betPrice = _betPrice;\\r\\n betFee = _betFee;\\r\\n }\\r\\n\\r\\n /// @notice Passes when the lottery is at closed state\\r\\n modifier whenBetsClosed() {\\r\\n require(!betsOpen, \\\"Lottery is open\\\");\\r\\n _;\\r\\n }\\r\\n\\r\\n /// @notice Passes when the lottery is at open state and the current block timestamp is lower than the lottery closing date\\r\\n modifier whenBetsOpen() {\\r\\n require(\\r\\n betsOpen && block.timestamp < betsClosingTime,\\r\\n \\\"Lottery is closed\\\"\\r\\n );\\r\\n _;\\r\\n }\\r\\n\\r\\n /// @notice Opens the lottery for receiving bets\\r\\n function openBets(uint256 closingTime) external onlyOwner whenBetsClosed {\\r\\n require(\\r\\n closingTime > block.timestamp,\\r\\n \\\"Closing time must be in the future\\\"\\r\\n );\\r\\n betsClosingTime = closingTime;\\r\\n betsOpen = true;\\r\\n }\\r\\n\\r\\n /// @notice Gives tokens based on the amount of ETH sent\\r\\n /// @dev This implementation is prone to rounding problems\\r\\n function purchaseTokens() external payable {\\r\\n paymentToken.mint(msg.sender, msg.value * purchaseRatio);\\r\\n }\\r\\n\\r\\n /// @notice Charges the bet price and creates a new bet slot with the sender's address\\r\\n function bet() public whenBetsOpen {\\r\\n ownerPool += betFee;\\r\\n prizePool += betPrice;\\r\\n _slots.push(msg.sender);\\r\\n paymentToken.transferFrom(msg.sender, address(this), betPrice + betFee);\\r\\n }\\r\\n\\r\\n /// @notice Calls the bet function `times` times\\r\\n function betMany(uint256 times) external {\\r\\n require(times > 0);\\r\\n while (times > 0) {\\r\\n bet();\\r\\n times--;\\r\\n }\\r\\n }\\r\\n\\r\\n /// @notice Closes the lottery and calculates the prize, if any\\r\\n /// @dev Anyone can call this function at any time after the closing time\\r\\n function closeLottery() external {\\r\\n require(block.timestamp >= betsClosingTime, \\\"Too soon to close\\\");\\r\\n require(betsOpen, \\\"Already closed\\\");\\r\\n if (_slots.length > 0) {\\r\\n uint256 winnerIndex = getRandomNumber() % _slots.length;\\r\\n address winner = _slots[winnerIndex];\\r\\n prize[winner] += prizePool;\\r\\n prizePool = 0;\\r\\n delete (_slots);\\r\\n }\\r\\n betsOpen = false;\\r\\n }\\r\\n\\r\\n /// @notice Returns a random number calculated from the previous block randao\\r\\n /// @dev This only works after The Merge\\r\\n function getRandomNumber() public view returns (uint256 randomNumber) {\\r\\n randomNumber = block.prevrandao;\\r\\n }\\r\\n\\r\\n /// @notice Withdraws `amount` from that accounts's prize pool\\r\\n function prizeWithdraw(uint256 amount) external {\\r\\n require(amount <= prize[msg.sender], \\\"Not enough prize\\\");\\r\\n prize[msg.sender] -= amount;\\r\\n paymentToken.transfer(msg.sender, amount);\\r\\n }\\r\\n\\r\\n /// @notice Withdraws `amount` from the owner's pool\\r\\n function ownerWithdraw(uint256 amount) external onlyOwner {\\r\\n require(amount <= ownerPool, \\\"Not enough fees collected\\\");\\r\\n ownerPool -= amount;\\r\\n paymentToken.transfer(msg.sender, amount);\\r\\n }\\r\\n\\r\\n /// @notice Burns `amount` tokens and give the equivalent ETH back to user\\r\\n function returnTokens(uint256 amount) external {\\r\\n paymentToken.burnFrom(msg.sender, amount);\\r\\n payable(msg.sender).transfer(amount / purchaseRatio);\\r\\n }\\r\\n}\",\"keccak256\":\"0x2112ac7eee8a56b87eb1282370251e89aec96083d55d561d0e83ec2ec620fbdc\",\"license\":\"GPL-3.0\"},\"contracts/LotteryToken.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\r\\npragma solidity ^0.8.24;\\r\\n\\r\\nimport \\\"@openzeppelin/contracts/token/ERC20/ERC20.sol\\\";\\r\\nimport \\\"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\\\";\\r\\nimport \\\"@openzeppelin/contracts/access/AccessControl.sol\\\";\\r\\n\\r\\ncontract LotteryToken is ERC20, ERC20Burnable, AccessControl {\\r\\n bytes32 public constant MINTER_ROLE = keccak256(\\\"MINTER_ROLE\\\");\\r\\n\\r\\n constructor(string memory name, string memory symbol) ERC20(name, symbol) {\\r\\n _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\\r\\n _grantRole(MINTER_ROLE, msg.sender);\\r\\n }\\r\\n\\r\\n function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {\\r\\n _mint(to, amount);\\r\\n }\\r\\n}\",\"keccak256\":\"0x636e613d45d4eadf2bbeeb4de6cb12d719516434d2baf14ff1336608e9b69b53\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60806040523480156200001157600080fd5b5060405162001eb938038062001eb9833981016040819052620000349162000202565b33806200005b57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006681620000d7565b508484604051620000779062000127565b62000084929190620002b5565b604051809103906000f080158015620000a1573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039290921691909117905560029290925560035560045550620002e79050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610fdc8062000edd83390190565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620001685781810151838201526020016200014e565b50506000910152565b600082601f8301126200018357600080fd5b81516001600160401b0380821115620001a057620001a062000135565b604051601f8301601f19908116603f01168101908282118183101715620001cb57620001cb62000135565b81604052838152866020858801011115620001e557600080fd5b620001f88460208301602089016200014b565b9695505050505050565b600080600080600060a086880312156200021b57600080fd5b85516001600160401b03808211156200023357600080fd5b6200024189838a0162000171565b965060208801519150808211156200025857600080fd5b50620002678882890162000171565b60408801516060890151608090990151979a919950979695509350505050565b60008151808452620002a18160208601602086016200014b565b601f01601f19169290920160200192915050565b604081526000620002ca604083018562000287565b8281036020840152620002de818562000287565b95945050505050565b610be680620002f76000396000f3fe60806040526004361061012a5760003560e01c8063719ce73e116100ab57806395cadc471161006f57806395cadc47146102e6578063990a49f5146102fc578063cfd8a1751461031c578063d1b258ff14610332578063dbdff2c11461035c578063f2fde38b1461036f57600080fd5b8063719ce73e1461024f578063729e91e914610265578063841e6ce9146102855780638adf9c1f1461029b5780638da5cb5b146102c857600080fd5b806333f707d1116100f257806333f707d1146101c55780633ae1786f146101e55780633cc121e6146102055780636fd0981614610225578063715018a61461023a57600080fd5b806311610c251461012f57806318c34965146101465780632b65e0401461016f5780633013ce29146101855780633290ce29146101bd575b600080fd5b34801561013b57600080fd5b5061014461038f565b005b34801561015257600080fd5b5061015c60085481565b6040519081526020015b60405180910390f35b34801561017b57600080fd5b5061015c60065481565b34801561019157600080fd5b506001546101a5906001600160a01b031681565b6040516001600160a01b039091168152602001610166565b610144610504565b3480156101d157600080fd5b506101446101e0366004610a81565b610588565b3480156101f157600080fd5b50610144610200366004610a81565b610672565b34801561021157600080fd5b50610144610220366004610a81565b610711565b34801561023157600080fd5b50610144610782565b34801561024657600080fd5b5061014461089d565b34801561025b57600080fd5b5061015c60055481565b34801561027157600080fd5b50610144610280366004610a81565b6108b1565b34801561029157600080fd5b5061015c60045481565b3480156102a757600080fd5b5061015c6102b6366004610a9a565b60096020526000908152604090205481565b3480156102d457600080fd5b506000546001600160a01b03166101a5565b3480156102f257600080fd5b5061015c60025481565b34801561030857600080fd5b50610144610317366004610a81565b6108de565b34801561032857600080fd5b5061015c60035481565b34801561033e57600080fd5b5060075461034c9060ff1681565b6040519015158152602001610166565b34801561036857600080fd5b504461015c565b34801561037b57600080fd5b5061014461038a366004610a9a565b610997565b60075460ff1680156103a2575060085442105b6103e75760405162461bcd60e51b8152602060048201526011602482015270131bdd1d195c9e481a5cc818db1bdcd959607a1b60448201526064015b60405180910390fd5b600454600660008282546103fb9190610ae0565b909155505060035460058054600090610415908490610ae0565b9091555050600a8054600181810183556000929092527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319163390811790915590546004546003546001600160a01b0392909216926323b872dd92909130916104899190610ae0565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156104dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105019190610af9565b50565b6001546002546001600160a01b03909116906340c10f199033906105289034610b1b565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561056e57600080fd5b505af1158015610582573d6000803e3d6000fd5b50505050565b6105906109d2565b6006548111156105e25760405162461bcd60e51b815260206004820152601960248201527f4e6f7420656e6f756768206665657320636f6c6c65637465640000000000000060448201526064016103de565b80600660008282546105f49190610b32565b909155505060015460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af115801561064a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066e9190610af9565b5050565b60015460405163079cc67960e41b8152336004820152602481018390526001600160a01b03909116906379cc679090604401600060405180830381600087803b1580156106be57600080fd5b505af11580156106d2573d6000803e3d6000fd5b50506002543392506108fc91506106e99084610b5b565b6040518115909202916000818181858888f1935050505015801561066e573d6000803e3d6000fd5b336000908152600960205260409020548111156107635760405162461bcd60e51b815260206004820152601060248201526f4e6f7420656e6f756768207072697a6560801b60448201526064016103de565b33600090815260096020526040812080548392906105f4908490610b32565b6008544210156107c85760405162461bcd60e51b8152602060048201526011602482015270546f6f20736f6f6e20746f20636c6f736560781b60448201526064016103de565b60075460ff1661080b5760405162461bcd60e51b815260206004820152600e60248201526d105b1c9958591e4818db1bdcd95960921b60448201526064016103de565b600a541561089157600a546000906108239044610b6f565b90506000600a828154811061083a5761083a610b83565b60009182526020808320909101546005546001600160a01b03909116808452600990925260408320805492945090929091610876908490610ae0565b90915550506000600581905561088e90600a90610a4f565b50505b6007805460ff19169055565b6108a56109d2565b6108af60006109ff565b565b600081116108be57600080fd5b8015610501576108cc61038f565b806108d681610b99565b9150506108be565b6108e66109d2565b60075460ff161561092b5760405162461bcd60e51b815260206004820152600f60248201526e2637ba3a32b93c9034b99037b832b760891b60448201526064016103de565b4281116109855760405162461bcd60e51b815260206004820152602260248201527f436c6f73696e672074696d65206d75737420626520696e207468652066757475604482015261726560f01b60648201526084016103de565b6008556007805460ff19166001179055565b61099f6109d2565b6001600160a01b0381166109c957604051631e4fbdf760e01b8152600060048201526024016103de565b610501816109ff565b6000546001600160a01b031633146108af5760405163118cdaa760e01b81523360048201526024016103de565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b508054600082559060005260206000209081019061050191905b80821115610a7d5760008155600101610a69565b5090565b600060208284031215610a9357600080fd5b5035919050565b600060208284031215610aac57600080fd5b81356001600160a01b0381168114610ac357600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610af357610af3610aca565b92915050565b600060208284031215610b0b57600080fd5b81518015158114610ac357600080fd5b8082028115828204841417610af357610af3610aca565b81810381811115610af357610af3610aca565b634e487b7160e01b600052601260045260246000fd5b600082610b6a57610b6a610b45565b500490565b600082610b7e57610b7e610b45565b500690565b634e487b7160e01b600052603260045260246000fd5b600081610ba857610ba8610aca565b50600019019056fea264697066735822122075df9060b16feaa1f9d33f6e6447d0d9f92610890737bdf7a249f52f158c8c4564736f6c6343000818003360806040523480156200001157600080fd5b5060405162000fdc38038062000fdc833981016040819052620000349162000216565b8181600362000044838262000311565b50600462000053828262000311565b5062000065915060009050336200009b565b50620000927f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6336200009b565b505050620003dd565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff16620001445760008381526005602090815260408083206001600160a01b03861684529091529020805460ff19166001179055620000fb3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600162000148565b5060005b92915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200017657600080fd5b81516001600160401b03808211156200019357620001936200014e565b604051601f8301601f19908116603f01168101908282118183101715620001be57620001be6200014e565b8160405283815260209250866020858801011115620001dc57600080fd5b600091505b83821015620002005785820183015181830184015290820190620001e1565b6000602085830101528094505050505092915050565b600080604083850312156200022a57600080fd5b82516001600160401b03808211156200024257600080fd5b620002508683870162000164565b935060208501519150808211156200026757600080fd5b50620002768582860162000164565b9150509250929050565b600181811c908216806200029557607f821691505b602082108103620002b657634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200030c576000816000526020600020601f850160051c81016020861015620002e75750805b601f850160051c820191505b818110156200030857828155600101620002f3565b5050505b505050565b81516001600160401b038111156200032d576200032d6200014e565b62000345816200033e845462000280565b84620002bc565b602080601f8311600181146200037d5760008415620003645750858301515b600019600386901b1c1916600185901b17855562000308565b600085815260208120601f198616915b82811015620003ae578886015182559484019460019091019084016200038d565b5085821015620003cd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610bef80620003ed6000396000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c806342966c68116100ad578063a217fddf11610071578063a217fddf1461027d578063a9059cbb14610285578063d539139314610298578063d547741f146102bf578063dd62ed3e146102d257600080fd5b806342966c681461021357806370a082311461022657806379cc67901461024f57806391d148541461026257806395d89b411461027557600080fd5b8063248a9ca3116100f4578063248a9ca3146101a65780632f2ff15d146101c9578063313ce567146101de57806336568abe146101ed57806340c10f191461020057600080fd5b806301ffc9a71461013157806306fdde0314610159578063095ea7b31461016e57806318160ddd1461018157806323b872dd14610193575b600080fd5b61014461013f3660046109d2565b61030b565b60405190151581526020015b60405180910390f35b610161610342565b6040516101509190610a03565b61014461017c366004610a6e565b6103d4565b6002545b604051908152602001610150565b6101446101a1366004610a98565b6103ec565b6101856101b4366004610ad4565b60009081526005602052604090206001015490565b6101dc6101d7366004610aed565b610410565b005b60405160128152602001610150565b6101dc6101fb366004610aed565b61043b565b6101dc61020e366004610a6e565b610473565b6101dc610221366004610ad4565b6104a7565b610185610234366004610b19565b6001600160a01b031660009081526020819052604090205490565b6101dc61025d366004610a6e565b6104b4565b610144610270366004610aed565b6104cd565b6101616104f8565b610185600081565b610144610293366004610a6e565b610507565b6101857f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6101dc6102cd366004610aed565b610515565b6101856102e0366004610b34565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60006001600160e01b03198216637965db0b60e01b148061033c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606003805461035190610b5e565b80601f016020809104026020016040519081016040528092919081815260200182805461037d90610b5e565b80156103ca5780601f1061039f576101008083540402835291602001916103ca565b820191906000526020600020905b8154815290600101906020018083116103ad57829003601f168201915b5050505050905090565b6000336103e281858561053a565b5060019392505050565b6000336103fa858285610547565b6104058585856105c4565b506001949350505050565b60008281526005602052604090206001015461042b81610623565b610435838361062d565b50505050565b6001600160a01b03811633146104645760405163334bd91960e11b815260040160405180910390fd5b61046e82826106c1565b505050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a661049d81610623565b61046e838361072e565b6104b13382610764565b50565b6104bf823383610547565b6104c98282610764565b5050565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60606004805461035190610b5e565b6000336103e28185856105c4565b60008281526005602052604090206001015461053081610623565b61043583836106c1565b61046e838383600161079a565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461043557818110156105b557604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b6104358484848403600061079a565b6001600160a01b0383166105ee57604051634b637e8f60e11b8152600060048201526024016105ac565b6001600160a01b0382166106185760405163ec442f0560e01b8152600060048201526024016105ac565b61046e83838361086f565b6104b18133610999565b600061063983836104cd565b6106b95760008381526005602090815260408083206001600160a01b03861684529091529020805460ff191660011790556106713390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161033c565b50600061033c565b60006106cd83836104cd565b156106b95760008381526005602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161033c565b6001600160a01b0382166107585760405163ec442f0560e01b8152600060048201526024016105ac565b6104c96000838361086f565b6001600160a01b03821661078e57604051634b637e8f60e11b8152600060048201526024016105ac565b6104c98260008361086f565b6001600160a01b0384166107c45760405163e602df0560e01b8152600060048201526024016105ac565b6001600160a01b0383166107ee57604051634a1406b160e11b8152600060048201526024016105ac565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561043557826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161086191815260200190565b60405180910390a350505050565b6001600160a01b03831661089a57806002600082825461088f9190610b98565b9091555061090c9050565b6001600160a01b038316600090815260208190526040902054818110156108ed5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016105ac565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b03821661092857600280548290039055610947565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161098c91815260200190565b60405180910390a3505050565b6109a382826104cd565b6104c95760405163e2517d3f60e01b81526001600160a01b0382166004820152602481018390526044016105ac565b6000602082840312156109e457600080fd5b81356001600160e01b0319811681146109fc57600080fd5b9392505050565b60006020808352835180602085015260005b81811015610a3157858101830151858201604001528201610a15565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610a6957600080fd5b919050565b60008060408385031215610a8157600080fd5b610a8a83610a52565b946020939093013593505050565b600080600060608486031215610aad57600080fd5b610ab684610a52565b9250610ac460208501610a52565b9150604084013590509250925092565b600060208284031215610ae657600080fd5b5035919050565b60008060408385031215610b0057600080fd5b82359150610b1060208401610a52565b90509250929050565b600060208284031215610b2b57600080fd5b6109fc82610a52565b60008060408385031215610b4757600080fd5b610b5083610a52565b9150610b1060208401610a52565b600181811c90821680610b7257607f821691505b602082108103610b9257634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561033c57634e487b7160e01b600052601160045260246000fdfea2646970667358221220b5f5772c113210feaa0d2cdb5e3010cd588e75e202db1483ce290bd86ef7657964736f6c63430008180033", + "deployedBytecode": "0x60806040526004361061012a5760003560e01c8063719ce73e116100ab57806395cadc471161006f57806395cadc47146102e6578063990a49f5146102fc578063cfd8a1751461031c578063d1b258ff14610332578063dbdff2c11461035c578063f2fde38b1461036f57600080fd5b8063719ce73e1461024f578063729e91e914610265578063841e6ce9146102855780638adf9c1f1461029b5780638da5cb5b146102c857600080fd5b806333f707d1116100f257806333f707d1146101c55780633ae1786f146101e55780633cc121e6146102055780636fd0981614610225578063715018a61461023a57600080fd5b806311610c251461012f57806318c34965146101465780632b65e0401461016f5780633013ce29146101855780633290ce29146101bd575b600080fd5b34801561013b57600080fd5b5061014461038f565b005b34801561015257600080fd5b5061015c60085481565b6040519081526020015b60405180910390f35b34801561017b57600080fd5b5061015c60065481565b34801561019157600080fd5b506001546101a5906001600160a01b031681565b6040516001600160a01b039091168152602001610166565b610144610504565b3480156101d157600080fd5b506101446101e0366004610a81565b610588565b3480156101f157600080fd5b50610144610200366004610a81565b610672565b34801561021157600080fd5b50610144610220366004610a81565b610711565b34801561023157600080fd5b50610144610782565b34801561024657600080fd5b5061014461089d565b34801561025b57600080fd5b5061015c60055481565b34801561027157600080fd5b50610144610280366004610a81565b6108b1565b34801561029157600080fd5b5061015c60045481565b3480156102a757600080fd5b5061015c6102b6366004610a9a565b60096020526000908152604090205481565b3480156102d457600080fd5b506000546001600160a01b03166101a5565b3480156102f257600080fd5b5061015c60025481565b34801561030857600080fd5b50610144610317366004610a81565b6108de565b34801561032857600080fd5b5061015c60035481565b34801561033e57600080fd5b5060075461034c9060ff1681565b6040519015158152602001610166565b34801561036857600080fd5b504461015c565b34801561037b57600080fd5b5061014461038a366004610a9a565b610997565b60075460ff1680156103a2575060085442105b6103e75760405162461bcd60e51b8152602060048201526011602482015270131bdd1d195c9e481a5cc818db1bdcd959607a1b60448201526064015b60405180910390fd5b600454600660008282546103fb9190610ae0565b909155505060035460058054600090610415908490610ae0565b9091555050600a8054600181810183556000929092527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319163390811790915590546004546003546001600160a01b0392909216926323b872dd92909130916104899190610ae0565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156104dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105019190610af9565b50565b6001546002546001600160a01b03909116906340c10f199033906105289034610b1b565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561056e57600080fd5b505af1158015610582573d6000803e3d6000fd5b50505050565b6105906109d2565b6006548111156105e25760405162461bcd60e51b815260206004820152601960248201527f4e6f7420656e6f756768206665657320636f6c6c65637465640000000000000060448201526064016103de565b80600660008282546105f49190610b32565b909155505060015460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af115801561064a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066e9190610af9565b5050565b60015460405163079cc67960e41b8152336004820152602481018390526001600160a01b03909116906379cc679090604401600060405180830381600087803b1580156106be57600080fd5b505af11580156106d2573d6000803e3d6000fd5b50506002543392506108fc91506106e99084610b5b565b6040518115909202916000818181858888f1935050505015801561066e573d6000803e3d6000fd5b336000908152600960205260409020548111156107635760405162461bcd60e51b815260206004820152601060248201526f4e6f7420656e6f756768207072697a6560801b60448201526064016103de565b33600090815260096020526040812080548392906105f4908490610b32565b6008544210156107c85760405162461bcd60e51b8152602060048201526011602482015270546f6f20736f6f6e20746f20636c6f736560781b60448201526064016103de565b60075460ff1661080b5760405162461bcd60e51b815260206004820152600e60248201526d105b1c9958591e4818db1bdcd95960921b60448201526064016103de565b600a541561089157600a546000906108239044610b6f565b90506000600a828154811061083a5761083a610b83565b60009182526020808320909101546005546001600160a01b03909116808452600990925260408320805492945090929091610876908490610ae0565b90915550506000600581905561088e90600a90610a4f565b50505b6007805460ff19169055565b6108a56109d2565b6108af60006109ff565b565b600081116108be57600080fd5b8015610501576108cc61038f565b806108d681610b99565b9150506108be565b6108e66109d2565b60075460ff161561092b5760405162461bcd60e51b815260206004820152600f60248201526e2637ba3a32b93c9034b99037b832b760891b60448201526064016103de565b4281116109855760405162461bcd60e51b815260206004820152602260248201527f436c6f73696e672074696d65206d75737420626520696e207468652066757475604482015261726560f01b60648201526084016103de565b6008556007805460ff19166001179055565b61099f6109d2565b6001600160a01b0381166109c957604051631e4fbdf760e01b8152600060048201526024016103de565b610501816109ff565b6000546001600160a01b031633146108af5760405163118cdaa760e01b81523360048201526024016103de565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b508054600082559060005260206000209081019061050191905b80821115610a7d5760008155600101610a69565b5090565b600060208284031215610a9357600080fd5b5035919050565b600060208284031215610aac57600080fd5b81356001600160a01b0381168114610ac357600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610af357610af3610aca565b92915050565b600060208284031215610b0b57600080fd5b81518015158114610ac357600080fd5b8082028115828204841417610af357610af3610aca565b81810381811115610af357610af3610aca565b634e487b7160e01b600052601260045260246000fd5b600082610b6a57610b6a610b45565b500490565b600082610b7e57610b7e610b45565b500690565b634e487b7160e01b600052603260045260246000fd5b600081610ba857610ba8610aca565b50600019019056fea264697066735822122075df9060b16feaa1f9d33f6e6447d0d9f92610890737bdf7a249f52f158c8c4564736f6c63430008180033", + "devdoc": { + "author": "Matheus Pagani", + "custom:teaching": "This is a contract meant for teaching only", + "details": "This contract implements a relatively weak randomness source, since there is no cliff period between the randao reveal and the actual usage in this contract", + "errors": { + "OwnableInvalidOwner(address)": [ + { + "details": "The owner is not a valid owner account. (eg. `address(0)`)" + } + ], + "OwnableUnauthorizedAccount(address)": [ + { + "details": "The caller account is not authorized to perform an operation." + } + ] + }, + "kind": "dev", + "methods": { + "closeLottery()": { + "details": "Anyone can call this function at any time after the closing time" + }, + "constructor": { + "params": { + "_betFee": "Amount of tokens required for placing a bet that goes for the owner pool", + "_betPrice": "Amount of tokens required for placing a bet that goes for the prize pool", + "_purchaseRatio": "Amount of tokens given per ETH paid", + "tokenName": "Name of the token used for payment", + "tokenSymbol": "Symbol of the token used for payment" + } + }, + "getRandomNumber()": { + "details": "This only works after The Merge" + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "purchaseTokens()": { + "details": "This implementation is prone to rounding problems" + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner." + }, + "transferOwnership(address)": { + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + } + }, + "stateVariables": { + "_slots": { + "details": "List of bet slots" + } + }, + "title": "A very simple lottery contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "bet()": { + "notice": "Charges the bet price and creates a new bet slot with the sender's address" + }, + "betFee()": { + "notice": "Amount of tokens required for placing a bet that goes for the owner pool" + }, + "betMany(uint256)": { + "notice": "Calls the bet function `times` times" + }, + "betPrice()": { + "notice": "Amount of tokens required for placing a bet that goes for the prize pool" + }, + "betsClosingTime()": { + "notice": "Timestamp of the lottery next closing date and time" + }, + "betsOpen()": { + "notice": "Flag indicating whether the lottery is open for bets or not" + }, + "closeLottery()": { + "notice": "Closes the lottery and calculates the prize, if any" + }, + "constructor": { + "notice": "Constructor function" + }, + "getRandomNumber()": { + "notice": "Returns a random number calculated from the previous block randao" + }, + "openBets(uint256)": { + "notice": "Opens the lottery for receiving bets" + }, + "ownerPool()": { + "notice": "Amount of tokens in the owner pool" + }, + "ownerWithdraw(uint256)": { + "notice": "Withdraws `amount` from the owner's pool" + }, + "paymentToken()": { + "notice": "Address of the token used as payment for the bets" + }, + "prize(address)": { + "notice": "Mapping of prize available for withdraw for each account" + }, + "prizePool()": { + "notice": "Amount of tokens in the prize pool" + }, + "prizeWithdraw(uint256)": { + "notice": "Withdraws `amount` from that accounts's prize pool" + }, + "purchaseRatio()": { + "notice": "Amount of tokens given per ETH paid" + }, + "purchaseTokens()": { + "notice": "Gives tokens based on the amount of ETH sent" + }, + "returnTokens(uint256)": { + "notice": "Burns `amount` tokens and give the equivalent ETH back to user" + } + }, + "notice": "You can use this contract for running a very simple lottery", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 387, + "contract": "contracts/Lottery.sol:Lottery", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 1407, + "contract": "contracts/Lottery.sol:Lottery", + "label": "paymentToken", + "offset": 0, + "slot": "1", + "type": "t_contract(LotteryToken)1818" + }, + { + "astId": 1410, + "contract": "contracts/Lottery.sol:Lottery", + "label": "purchaseRatio", + "offset": 0, + "slot": "2", + "type": "t_uint256" + }, + { + "astId": 1413, + "contract": "contracts/Lottery.sol:Lottery", + "label": "betPrice", + "offset": 0, + "slot": "3", + "type": "t_uint256" + }, + { + "astId": 1416, + "contract": "contracts/Lottery.sol:Lottery", + "label": "betFee", + "offset": 0, + "slot": "4", + "type": "t_uint256" + }, + { + "astId": 1419, + "contract": "contracts/Lottery.sol:Lottery", + "label": "prizePool", + "offset": 0, + "slot": "5", + "type": "t_uint256" + }, + { + "astId": 1422, + "contract": "contracts/Lottery.sol:Lottery", + "label": "ownerPool", + "offset": 0, + "slot": "6", + "type": "t_uint256" + }, + { + "astId": 1425, + "contract": "contracts/Lottery.sol:Lottery", + "label": "betsOpen", + "offset": 0, + "slot": "7", + "type": "t_bool" + }, + { + "astId": 1428, + "contract": "contracts/Lottery.sol:Lottery", + "label": "betsClosingTime", + "offset": 0, + "slot": "8", + "type": "t_uint256" + }, + { + "astId": 1433, + "contract": "contracts/Lottery.sol:Lottery", + "label": "prize", + "offset": 0, + "slot": "9", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 1437, + "contract": "contracts/Lottery.sol:Lottery", + "label": "_slots", + "offset": 0, + "slot": "10", + "type": "t_array(t_address)dyn_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(LotteryToken)1818": { + "encoding": "inplace", + "label": "contract LotteryToken", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/packages/hardhat/deployments/sepolia/solcInputs/e6f65ef1ca17a1556421d91028008841.json b/packages/hardhat/deployments/sepolia/solcInputs/e6f65ef1ca17a1556421d91028008841.json new file mode 100644 index 0000000..e2bd2ea --- /dev/null +++ b/packages/hardhat/deployments/sepolia/solcInputs/e6f65ef1ca17a1556421d91028008841.json @@ -0,0 +1,72 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"./IAccessControl.sol\";\nimport {Context} from \"../utils/Context.sol\";\nimport {ERC165} from \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address account => bool) hasRole;\n bytes32 adminRole;\n }\n\n mapping(bytes32 role => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with an {AccessControlUnauthorizedAccount} error including the required role.\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role);\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n return _roles[role].hasRole[account];\n }\n\n /**\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n */\n function _checkRole(bytes32 role) internal view virtual {\n _checkRole(role, _msgSender());\n }\n\n /**\n * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n * is missing `role`.\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert AccessControlUnauthorizedAccount(account, role);\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleGranted} event.\n */\n function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleRevoked} event.\n */\n function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `callerConfirmation`.\n *\n * May emit a {RoleRevoked} event.\n */\n function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n if (callerConfirmation != _msgSender()) {\n revert AccessControlBadConfirmation();\n }\n\n _revokeRole(role, callerConfirmation);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleGranted} event.\n */\n function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n if (!hasRole(role, account)) {\n _roles[role].hasRole[account] = true;\n emit RoleGranted(role, account, _msgSender());\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleRevoked} event.\n */\n function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n if (hasRole(role, account)) {\n _roles[role].hasRole[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n return true;\n } else {\n return false;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev The `account` is missing a role.\n */\n error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n /**\n * @dev The caller of a function is not the expected one.\n *\n * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n */\n error AccessControlBadConfirmation();\n\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `callerConfirmation`.\n */\n function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n /**\n * @dev The caller account is not authorized to perform an operation.\n */\n error OwnableUnauthorizedAccount(address account);\n\n /**\n * @dev The owner is not a valid owner account. (eg. `address(0)`)\n */\n error OwnableInvalidOwner(address owner);\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n */\n constructor(address initialOwner) {\n if (initialOwner == address(0)) {\n revert OwnableInvalidOwner(address(0));\n }\n _transferOwnership(initialOwner);\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n if (owner() != _msgSender()) {\n revert OwnableUnauthorizedAccount(_msgSender());\n }\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n if (newOwner == address(0)) {\n revert OwnableInvalidOwner(address(0));\n }\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/interfaces/draft-IERC6093.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard ERC20 Errors\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.\n */\ninterface IERC20Errors {\n /**\n * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n * @param sender Address whose tokens are being transferred.\n * @param balance Current balance for the interacting account.\n * @param needed Minimum amount required to perform a transfer.\n */\n error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\n\n /**\n * @dev Indicates a failure with the token `sender`. Used in transfers.\n * @param sender Address whose tokens are being transferred.\n */\n error ERC20InvalidSender(address sender);\n\n /**\n * @dev Indicates a failure with the token `receiver`. Used in transfers.\n * @param receiver Address to which tokens are being transferred.\n */\n error ERC20InvalidReceiver(address receiver);\n\n /**\n * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.\n * @param spender Address that may be allowed to operate on tokens without being their owner.\n * @param allowance Amount of tokens a `spender` is allowed to operate with.\n * @param needed Minimum amount required to perform a transfer.\n */\n error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\n\n /**\n * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n * @param approver Address initiating an approval operation.\n */\n error ERC20InvalidApprover(address approver);\n\n /**\n * @dev Indicates a failure with the `spender` to be approved. Used in approvals.\n * @param spender Address that may be allowed to operate on tokens without being their owner.\n */\n error ERC20InvalidSpender(address spender);\n}\n\n/**\n * @dev Standard ERC721 Errors\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.\n */\ninterface IERC721Errors {\n /**\n * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.\n * Used in balance queries.\n * @param owner Address of the current owner of a token.\n */\n error ERC721InvalidOwner(address owner);\n\n /**\n * @dev Indicates a `tokenId` whose `owner` is the zero address.\n * @param tokenId Identifier number of a token.\n */\n error ERC721NonexistentToken(uint256 tokenId);\n\n /**\n * @dev Indicates an error related to the ownership over a particular token. Used in transfers.\n * @param sender Address whose tokens are being transferred.\n * @param tokenId Identifier number of a token.\n * @param owner Address of the current owner of a token.\n */\n error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);\n\n /**\n * @dev Indicates a failure with the token `sender`. Used in transfers.\n * @param sender Address whose tokens are being transferred.\n */\n error ERC721InvalidSender(address sender);\n\n /**\n * @dev Indicates a failure with the token `receiver`. Used in transfers.\n * @param receiver Address to which tokens are being transferred.\n */\n error ERC721InvalidReceiver(address receiver);\n\n /**\n * @dev Indicates a failure with the `operator`’s approval. Used in transfers.\n * @param operator Address that may be allowed to operate on tokens without being their owner.\n * @param tokenId Identifier number of a token.\n */\n error ERC721InsufficientApproval(address operator, uint256 tokenId);\n\n /**\n * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n * @param approver Address initiating an approval operation.\n */\n error ERC721InvalidApprover(address approver);\n\n /**\n * @dev Indicates a failure with the `operator` to be approved. Used in approvals.\n * @param operator Address that may be allowed to operate on tokens without being their owner.\n */\n error ERC721InvalidOperator(address operator);\n}\n\n/**\n * @dev Standard ERC1155 Errors\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.\n */\ninterface IERC1155Errors {\n /**\n * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n * @param sender Address whose tokens are being transferred.\n * @param balance Current balance for the interacting account.\n * @param needed Minimum amount required to perform a transfer.\n * @param tokenId Identifier number of a token.\n */\n error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);\n\n /**\n * @dev Indicates a failure with the token `sender`. Used in transfers.\n * @param sender Address whose tokens are being transferred.\n */\n error ERC1155InvalidSender(address sender);\n\n /**\n * @dev Indicates a failure with the token `receiver`. Used in transfers.\n * @param receiver Address to which tokens are being transferred.\n */\n error ERC1155InvalidReceiver(address receiver);\n\n /**\n * @dev Indicates a failure with the `operator`’s approval. Used in transfers.\n * @param operator Address that may be allowed to operate on tokens without being their owner.\n * @param owner Address of the current owner of a token.\n */\n error ERC1155MissingApprovalForAll(address operator, address owner);\n\n /**\n * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n * @param approver Address initiating an approval operation.\n */\n error ERC1155InvalidApprover(address approver);\n\n /**\n * @dev Indicates a failure with the `operator` to be approved. Used in approvals.\n * @param operator Address that may be allowed to operate on tokens without being their owner.\n */\n error ERC1155InvalidOperator(address operator);\n\n /**\n * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.\n * Used in batch transfers.\n * @param idsLength Length of the array of token identifiers\n * @param valuesLength Length of the array of token amounts\n */\n error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC20Metadata} from \"./extensions/IERC20Metadata.sol\";\nimport {Context} from \"../../utils/Context.sol\";\nimport {IERC20Errors} from \"../../interfaces/draft-IERC6093.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n */\nabstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {\n mapping(address account => uint256) private _balances;\n\n mapping(address account => mapping(address spender => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the default value returned by this function, unless\n * it's overridden.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `value`.\n */\n function transfer(address to, uint256 value) public virtual returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, value);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 value) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, value);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `value`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `value`.\n */\n function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, value);\n _transfer(from, to, value);\n return true;\n }\n\n /**\n * @dev Moves a `value` amount of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead.\n */\n function _transfer(address from, address to, uint256 value) internal {\n if (from == address(0)) {\n revert ERC20InvalidSender(address(0));\n }\n if (to == address(0)) {\n revert ERC20InvalidReceiver(address(0));\n }\n _update(from, to, value);\n }\n\n /**\n * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`\n * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding\n * this function.\n *\n * Emits a {Transfer} event.\n */\n function _update(address from, address to, uint256 value) internal virtual {\n if (from == address(0)) {\n // Overflow check required: The rest of the code assumes that totalSupply never overflows\n _totalSupply += value;\n } else {\n uint256 fromBalance = _balances[from];\n if (fromBalance < value) {\n revert ERC20InsufficientBalance(from, fromBalance, value);\n }\n unchecked {\n // Overflow not possible: value <= fromBalance <= totalSupply.\n _balances[from] = fromBalance - value;\n }\n }\n\n if (to == address(0)) {\n unchecked {\n // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.\n _totalSupply -= value;\n }\n } else {\n unchecked {\n // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.\n _balances[to] += value;\n }\n }\n\n emit Transfer(from, to, value);\n }\n\n /**\n * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).\n * Relies on the `_update` mechanism\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead.\n */\n function _mint(address account, uint256 value) internal {\n if (account == address(0)) {\n revert ERC20InvalidReceiver(address(0));\n }\n _update(address(0), account, value);\n }\n\n /**\n * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.\n * Relies on the `_update` mechanism.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead\n */\n function _burn(address account, uint256 value) internal {\n if (account == address(0)) {\n revert ERC20InvalidSender(address(0));\n }\n _update(account, address(0), value);\n }\n\n /**\n * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n *\n * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.\n */\n function _approve(address owner, address spender, uint256 value) internal {\n _approve(owner, spender, value, true);\n }\n\n /**\n * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.\n *\n * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by\n * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any\n * `Approval` event during `transferFrom` operations.\n *\n * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to\n * true using the following override:\n * ```\n * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {\n * super._approve(owner, spender, value, true);\n * }\n * ```\n *\n * Requirements are the same as {_approve}.\n */\n function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {\n if (owner == address(0)) {\n revert ERC20InvalidApprover(address(0));\n }\n if (spender == address(0)) {\n revert ERC20InvalidSpender(address(0));\n }\n _allowances[owner][spender] = value;\n if (emitEvent) {\n emit Approval(owner, spender, value);\n }\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `value`.\n *\n * Does not update the allowance value in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Does not emit an {Approval} event.\n */\n function _spendAllowance(address owner, address spender, uint256 value) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n if (currentAllowance < value) {\n revert ERC20InsufficientAllowance(spender, currentAllowance, value);\n }\n unchecked {\n _approve(owner, spender, currentAllowance - value, false);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Burnable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ERC20} from \"../ERC20.sol\";\nimport {Context} from \"../../../utils/Context.sol\";\n\n/**\n * @dev Extension of {ERC20} that allows token holders to destroy both their own\n * tokens and those that they have an allowance for, in a way that can be\n * recognized off-chain (via event analysis).\n */\nabstract contract ERC20Burnable is Context, ERC20 {\n /**\n * @dev Destroys a `value` amount of tokens from the caller.\n *\n * See {ERC20-_burn}.\n */\n function burn(uint256 value) public virtual {\n _burn(_msgSender(), value);\n }\n\n /**\n * @dev Destroys a `value` amount of tokens from `account`, deducting from\n * the caller's allowance.\n *\n * See {ERC20-_burn} and {ERC20-allowance}.\n *\n * Requirements:\n *\n * - the caller must have allowance for ``accounts``'s tokens of at least\n * `value`.\n */\n function burnFrom(address account, uint256 value) public virtual {\n _spendAllowance(account, _msgSender(), value);\n _burn(account, value);\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the value of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the value of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 value) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n * caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 value) external returns (bool);\n\n /**\n * @dev Moves a `value` amount of tokens from `from` to `to` using the\n * allowance mechanism. `value` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "contracts/Lottery.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0\r\npragma solidity >=0.7.0 <0.9.0;\r\n\r\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\r\nimport {LotteryToken} from \"./LotteryToken.sol\";\r\n\r\n/// @title A very simple lottery contract\r\n/// @author Matheus Pagani\r\n/// @notice You can use this contract for running a very simple lottery\r\n/// @dev This contract implements a relatively weak randomness source, since there is no cliff period between the randao reveal and the actual usage in this contract\r\n/// @custom:teaching This is a contract meant for teaching only\r\ncontract Lottery is Ownable {\r\n /// @notice Address of the token used as payment for the bets\r\n LotteryToken public paymentToken;\r\n /// @notice Amount of tokens given per ETH paid\r\n uint256 public purchaseRatio;\r\n /// @notice Amount of tokens required for placing a bet that goes for the prize pool\r\n uint256 public betPrice;\r\n /// @notice Amount of tokens required for placing a bet that goes for the owner pool\r\n uint256 public betFee;\r\n /// @notice Amount of tokens in the prize pool\r\n uint256 public prizePool;\r\n /// @notice Amount of tokens in the owner pool\r\n uint256 public ownerPool;\r\n /// @notice Flag indicating whether the lottery is open for bets or not\r\n bool public betsOpen;\r\n /// @notice Timestamp of the lottery next closing date and time\r\n uint256 public betsClosingTime;\r\n /// @notice Mapping of prize available for withdraw for each account\r\n mapping(address => uint256) public prize;\r\n\r\n /// @dev List of bet slots\r\n address[] _slots;\r\n\r\n /// @notice Constructor function\r\n /// @param tokenName Name of the token used for payment\r\n /// @param tokenSymbol Symbol of the token used for payment\r\n /// @param _purchaseRatio Amount of tokens given per ETH paid\r\n /// @param _betPrice Amount of tokens required for placing a bet that goes for the prize pool\r\n /// @param _betFee Amount of tokens required for placing a bet that goes for the owner pool\r\n constructor(\r\n string memory tokenName,\r\n string memory tokenSymbol,\r\n uint256 _purchaseRatio,\r\n uint256 _betPrice,\r\n uint256 _betFee\r\n ) Ownable(msg.sender) {\r\n paymentToken = new LotteryToken(tokenName, tokenSymbol);\r\n purchaseRatio = _purchaseRatio;\r\n betPrice = _betPrice;\r\n betFee = _betFee;\r\n }\r\n\r\n /// @notice Passes when the lottery is at closed state\r\n modifier whenBetsClosed() {\r\n require(!betsOpen, \"Lottery is open\");\r\n _;\r\n }\r\n\r\n /// @notice Passes when the lottery is at open state and the current block timestamp is lower than the lottery closing date\r\n modifier whenBetsOpen() {\r\n require(\r\n betsOpen && block.timestamp < betsClosingTime,\r\n \"Lottery is closed\"\r\n );\r\n _;\r\n }\r\n\r\n /// @notice Opens the lottery for receiving bets\r\n function openBets(uint256 closingTime) external onlyOwner whenBetsClosed {\r\n require(\r\n closingTime > block.timestamp,\r\n \"Closing time must be in the future\"\r\n );\r\n betsClosingTime = closingTime;\r\n betsOpen = true;\r\n }\r\n\r\n /// @notice Gives tokens based on the amount of ETH sent\r\n /// @dev This implementation is prone to rounding problems\r\n function purchaseTokens() external payable {\r\n paymentToken.mint(msg.sender, msg.value * purchaseRatio);\r\n }\r\n\r\n /// @notice Charges the bet price and creates a new bet slot with the sender's address\r\n function bet() public whenBetsOpen {\r\n ownerPool += betFee;\r\n prizePool += betPrice;\r\n _slots.push(msg.sender);\r\n paymentToken.transferFrom(msg.sender, address(this), betPrice + betFee);\r\n }\r\n\r\n /// @notice Calls the bet function `times` times\r\n function betMany(uint256 times) external {\r\n require(times > 0);\r\n while (times > 0) {\r\n bet();\r\n times--;\r\n }\r\n }\r\n\r\n /// @notice Closes the lottery and calculates the prize, if any\r\n /// @dev Anyone can call this function at any time after the closing time\r\n function closeLottery() external {\r\n require(block.timestamp >= betsClosingTime, \"Too soon to close\");\r\n require(betsOpen, \"Already closed\");\r\n if (_slots.length > 0) {\r\n uint256 winnerIndex = getRandomNumber() % _slots.length;\r\n address winner = _slots[winnerIndex];\r\n prize[winner] += prizePool;\r\n prizePool = 0;\r\n delete (_slots);\r\n }\r\n betsOpen = false;\r\n }\r\n\r\n /// @notice Returns a random number calculated from the previous block randao\r\n /// @dev This only works after The Merge\r\n function getRandomNumber() public view returns (uint256 randomNumber) {\r\n randomNumber = block.prevrandao;\r\n }\r\n\r\n /// @notice Withdraws `amount` from that accounts's prize pool\r\n function prizeWithdraw(uint256 amount) external {\r\n require(amount <= prize[msg.sender], \"Not enough prize\");\r\n prize[msg.sender] -= amount;\r\n paymentToken.transfer(msg.sender, amount);\r\n }\r\n\r\n /// @notice Withdraws `amount` from the owner's pool\r\n function ownerWithdraw(uint256 amount) external onlyOwner {\r\n require(amount <= ownerPool, \"Not enough fees collected\");\r\n ownerPool -= amount;\r\n paymentToken.transfer(msg.sender, amount);\r\n }\r\n\r\n /// @notice Burns `amount` tokens and give the equivalent ETH back to user\r\n function returnTokens(uint256 amount) external {\r\n paymentToken.burnFrom(msg.sender, amount);\r\n payable(msg.sender).transfer(amount / purchaseRatio);\r\n }\r\n}" + }, + "contracts/LotteryToken.sol": { + "content": "// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.24;\r\n\r\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\";\r\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\r\n\r\ncontract LotteryToken is ERC20, ERC20Burnable, AccessControl {\r\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\r\n\r\n constructor(string memory name, string memory symbol) ERC20(name, symbol) {\r\n _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\r\n _grantRole(MINTER_ROLE, msg.sender);\r\n }\r\n\r\n function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {\r\n _mint(to, amount);\r\n }\r\n}" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "storageLayout", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} \ No newline at end of file diff --git a/packages/hardhat/hardhat.config.ts b/packages/hardhat/hardhat.config.ts index e388612..8fed1e5 100644 --- a/packages/hardhat/hardhat.config.ts +++ b/packages/hardhat/hardhat.config.ts @@ -23,7 +23,7 @@ const config: HardhatUserConfig = { solidity: { compilers: [ { - version: "0.8.20", + version: "0.8.24", settings: { optimizer: { enabled: true, diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 008d4eb..4b58b00 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -4,6 +4,685 @@ */ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; -const deployedContracts = {} as const; +const deployedContracts = { + 31337: { + Lottery: { + address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + abi: [ + { + inputs: [ + { + internalType: "string", + name: "tokenName", + type: "string", + }, + { + internalType: "string", + name: "tokenSymbol", + type: "string", + }, + { + internalType: "uint256", + name: "_purchaseRatio", + type: "uint256", + }, + { + internalType: "uint256", + name: "_betPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "_betFee", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "OwnableInvalidOwner", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "OwnableUnauthorizedAccount", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + inputs: [], + name: "bet", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "betFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "times", + type: "uint256", + }, + ], + name: "betMany", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "betPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "betsClosingTime", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "betsOpen", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "closeLottery", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "getRandomNumber", + outputs: [ + { + internalType: "uint256", + name: "randomNumber", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "closingTime", + type: "uint256", + }, + ], + name: "openBets", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "ownerPool", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "ownerWithdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "paymentToken", + outputs: [ + { + internalType: "contract LotteryToken", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "prize", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "prizePool", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "prizeWithdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "purchaseRatio", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "purchaseTokens", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "returnTokens", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + inheritedFunctions: { + owner: "@openzeppelin/contracts/access/Ownable.sol", + renounceOwnership: "@openzeppelin/contracts/access/Ownable.sol", + transferOwnership: "@openzeppelin/contracts/access/Ownable.sol", + }, + }, + }, + 11155111: { + Lottery: { + address: "0xdB6Fee2827292Cb4C64Ad5b0C8D2afa3adb0d4fE", + abi: [ + { + inputs: [ + { + internalType: "string", + name: "tokenName", + type: "string", + }, + { + internalType: "string", + name: "tokenSymbol", + type: "string", + }, + { + internalType: "uint256", + name: "_purchaseRatio", + type: "uint256", + }, + { + internalType: "uint256", + name: "_betPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "_betFee", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "OwnableInvalidOwner", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "OwnableUnauthorizedAccount", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + inputs: [], + name: "bet", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "betFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "times", + type: "uint256", + }, + ], + name: "betMany", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "betPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "betsClosingTime", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "betsOpen", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "closeLottery", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "getRandomNumber", + outputs: [ + { + internalType: "uint256", + name: "randomNumber", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "closingTime", + type: "uint256", + }, + ], + name: "openBets", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "ownerPool", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "ownerWithdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "paymentToken", + outputs: [ + { + internalType: "contract LotteryToken", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "prize", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "prizePool", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "prizeWithdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "purchaseRatio", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "purchaseTokens", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "returnTokens", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + inheritedFunctions: { + owner: "@openzeppelin/contracts/access/Ownable.sol", + renounceOwnership: "@openzeppelin/contracts/access/Ownable.sol", + transferOwnership: "@openzeppelin/contracts/access/Ownable.sol", + }, + }, + }, +} as const; export default deployedContracts satisfies GenericContractsDeclaration; diff --git a/packages/nextjs/public/Sepolia.ts b/packages/nextjs/public/Sepolia.ts new file mode 100644 index 0000000..d7b4ca1 --- /dev/null +++ b/packages/nextjs/public/Sepolia.ts @@ -0,0 +1,37 @@ +import { defineChain } from "viem"; + +export const Sepolia = defineChain({ + id: 11_155_111, + name: "Sepolia", + nativeCurrency: { + name: "Sepolia Ether", + symbol: "ETH", + decimals: 18 + }, + rpcUrls: { + default: { + http: ["https://sepolia.drpc.org/"], + }, + }, + blockExplorers: { + default: { + name: "Etherscan", + url: "https://sepolia.etherscan.io/", + apiUrl: "https://api-sepolia.etherscan.io/api", + }, + }, + contracts: { + multicall3: { + address: "0xca11bde05977b3631167028862be2a173976ca11", + blockCreated: 751532, + }, + ensRegistry: { + address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" + }, + ensUniversalResolver: { + address: "0xc8Af999e38273D658BE1b921b88A9Ddf005769cC", + blockCreated: 5317080, + }, + }, + testnet: true, +}); \ No newline at end of file diff --git a/packages/nextjs/scaffold.config.ts b/packages/nextjs/scaffold.config.ts index 86c737a..46f6b67 100644 --- a/packages/nextjs/scaffold.config.ts +++ b/packages/nextjs/scaffold.config.ts @@ -1,4 +1,5 @@ import * as chains from "viem/chains"; +import {Sepolia} from "./public/Sepolia"; export type ScaffoldConfig = { targetNetworks: readonly chains.Chain[]; @@ -10,7 +11,7 @@ export type ScaffoldConfig = { const scaffoldConfig = { // The networks on which your DApp is live - targetNetworks: [chains.hardhat], + targetNetworks: [Sepolia], // The interval at which your front-end polls the RPC servers for new data // it has no effect if you only target the local network (default is 4000)