Skip to content

Commit

Permalink
contract and scaffold-eth set
Browse files Browse the repository at this point in the history
  • Loading branch information
Evangel90 committed Nov 30, 2024
1 parent 99cb1fe commit 26a373b
Show file tree
Hide file tree
Showing 12 changed files with 1,618 additions and 88 deletions.
1 change: 0 additions & 1 deletion .husky/pre-commit

This file was deleted.

142 changes: 142 additions & 0 deletions packages/hardhat/contracts/Lottery.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
19 changes: 19 additions & 0 deletions packages/hardhat/contracts/LotteryToken.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
78 changes: 0 additions & 78 deletions packages/hardhat/contracts/YourContract.sol

This file was deleted.

20 changes: 14 additions & 6 deletions packages/hardhat/deploy/00_deploy_your_contract.ts
Original file line number Diff line number Diff line change
@@ -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";

Check failure on line 4 in packages/hardhat/deploy/00_deploy_your_contract.ts

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, lts/*)

'parse' is defined but never used

/**
* Deploys a contract named "YourContract" using the deployer account and
Expand All @@ -22,23 +23,30 @@ 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: [

Check warning on line 29 in packages/hardhat/deploy/00_deploy_your_contract.ts

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, lts/*)

Replace `⏎······"Group7LotteryToken",⏎······"G7T",⏎······10000n,⏎······parseEther('0.0001'),⏎······parseEther("0.00002")⏎····` with `"Group7LotteryToken",·"G7T",·10000n,·parseEther("0.0001"),·parseEther("0.00002")`
"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.
autoMine: true,
});

// Get the deployed contract to interact with it after deploying.
const yourContract = await hre.ethers.getContract<Contract>("YourContract", deployer);
console.log("👋 Initial greeting:", await yourContract.greeting());
const lotteryContract = await hre.ethers.getContract<Contract>("Lottery", deployer);
console.log("Lottery Contract address:", await lotteryContract.getAddress())

Check warning on line 44 in packages/hardhat/deploy/00_deploy_your_contract.ts

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, lts/*)

Insert `;`
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"];
1 change: 1 addition & 0 deletions packages/hardhat/deployments/sepolia/.chainId
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11155111
Loading

0 comments on commit 26a373b

Please sign in to comment.