Skip to content

Instantly share code, notes, and snippets.

@levancho
Created July 29, 2024 07:50
Show Gist options
  • Save levancho/86b3b02276a15595e3de46d95d0614cf to your computer and use it in GitHub Desktop.
Save levancho/86b3b02276a15595e3de46d95d0614cf to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.26+commit.8a97fa7a.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
import "hardhat/console.sol";
contract LuckyChainSimple {
address public manager;
uint public level = 1;
uint public totalSeats = 0;
uint public lotteryPoolInWei = 0;
uint public lotteryContributionPercentage;
uint public entryPriceInWei;
struct Seat {
address owner;
uint seatNumber;
uint level;
uint localSeatNumber; // Local seat number within the level
}
// Mapping from level to array of seats
mapping(uint => Seat[]) public levelSeats;
// Keep track of seat numbers for the lottery
uint[] public seatNumbers;
event SeatPurchasedEvent(address indexed buyer, uint level, uint seatNumber, uint localSeatNumber);
event LotteryWinnersEvent(address[] winners, uint amountPerWinnerInWei);
event DistributionEvent(address indexed recipient, uint amountInWei, uint level, string distributionType);
event SeatAddedToLotteryEvent(uint seatNumber, uint level);
event LevelChangedEvent(uint oldLevel, uint newLevel);
event LotteryContributionEvent(uint contributionInWei);
event RemainingRevenueEvent(uint remainingRevenueInWei);
event LotteryContributionPercentageChangedEvent(uint oldPercentage, uint newPercentage);
constructor(uint _entryPriceInWei, uint _lotteryContributionPercentage) {
manager = msg.sender;
entryPriceInWei = _entryPriceInWei;
setLotteryContributionPercentage(_lotteryContributionPercentage);
console.log("Contract deployed by:", manager);
console.log("Entry price in Wei:", entryPriceInWei);
console.log("Lottery contribution percentage:", lotteryContributionPercentage);
}
// Modifier to check if the caller is the manager
modifier onlyManager() {
require(msg.sender == manager, "Caller is not the manager");
_;
}
function setLotteryContributionPercentage(uint newPercentage) private {
require(newPercentage >= 0 && newPercentage <= 100, "Percentage must be between 0 and 100");
uint oldPercentage = lotteryContributionPercentage;
lotteryContributionPercentage = newPercentage == 0 ? 20 : newPercentage;
emit LotteryContributionPercentageChangedEvent(oldPercentage, lotteryContributionPercentage);
console.log("Lottery contribution percentage changed from %s to %s", oldPercentage, lotteryContributionPercentage);
}
function buySeat() public payable {
console.log("Entering buySeat()");
require(msg.value == entryPriceInWei, "Incorrect seat price");
console.log("Buying seat with price:", msg.value);
console.log("by:", msg.sender);
// Check if the current level is full
uint oldLevel = level;
if (levelSeats[level].length == getSeatsForLevel(level)) {
level++; // Move to the next level if current level is full
console.log("Moving to next level:", level);
emit LevelChangedEvent(oldLevel, level);
}
uint seatNumber = totalSeats + 1;
uint localSeatNumber = levelSeats[level].length + 1;
totalSeats++;
// Add the seat to the levelSeats mapping
levelSeats[level].push(Seat(msg.sender, seatNumber, level, localSeatNumber));
console.log("Seat added: global seatNumber:", seatNumber);
console.log("localSeatNumber:", localSeatNumber);
console.log("level:", level);
addSeatToLottery(seatNumber, level);
// Revenue distribution
uint rowRevenueInWei = msg.value;
uint lotteryContributionInWei = (rowRevenueInWei * lotteryContributionPercentage) / 100;
lotteryPoolInWei += lotteryContributionInWei;
emit LotteryContributionEvent(lotteryContributionInWei);
console.log("Lottery contribution:", lotteryContributionInWei);
uint remainingRevenueInWei = rowRevenueInWei - lotteryContributionInWei;
emit RemainingRevenueEvent(remainingRevenueInWei);
console.log("Remaining revenue:", remainingRevenueInWei);
uint bottomUpAmountInWei = remainingRevenueInWei / 2;
uint topDownAmountInWei = remainingRevenueInWei - bottomUpAmountInWei;
console.log("Bottom-up amount:", bottomUpAmountInWei);
console.log("Top-down amount:", topDownAmountInWei);
distributeBottomUp(bottomUpAmountInWei, level);
distributeTopDown(topDownAmountInWei, level);
emit SeatPurchasedEvent(msg.sender, level, seatNumber, localSeatNumber);
console.log("Seat purchased: level %s, seatNumber %s, localSeatNumber %s", level, seatNumber, localSeatNumber);
console.log("Exiting buySeat()");
}
function distributeBottomUp(uint amountInWei, uint currentLevel) private {
console.log("Entering distributeBottomUp()");
console.log("Distributing bottom up from level:", currentLevel - 1);
console.log("amount:", amountInWei);
for (uint lvl = currentLevel - 1; lvl > 0 && amountInWei > 0; lvl--) {
uint seatsAtLevel = levelSeats[lvl].length;
console.log("Level:", lvl);
console.log("Seats at level:", seatsAtLevel);
console.log("Current amount:", amountInWei);
if (seatsAtLevel > 0) {
uint amountPerSeatInWei = amountInWei / 2 / seatsAtLevel;
for (uint i = 0; i < seatsAtLevel; i++) {
payable(levelSeats[lvl][i].owner).transfer(amountPerSeatInWei);
emit DistributionEvent(levelSeats[lvl][i].owner, amountPerSeatInWei, lvl, "BottomUp");
console.log("Distributed %s to %s at level %s", amountPerSeatInWei, levelSeats[lvl][i].owner, lvl);
}
amountInWei /= 2;
}
}
console.log("Exiting distributeBottomUp()");
}
function distributeTopDown(uint amountInWei, uint currentLevel) private {
console.log("Entering distributeTopDown()");
console.log("Distributing top down to level:", currentLevel);
console.log("amount:", amountInWei);
for (uint lvl = 1; lvl <= currentLevel && amountInWei > 0; lvl++) {
uint seatsAtLevel = levelSeats[lvl].length;
console.log("Level:", lvl);
console.log("Seats at level:", seatsAtLevel);
console.log("Current amount:", amountInWei);
if (seatsAtLevel > 0) {
uint amountPerSeatInWei = amountInWei / 2 / seatsAtLevel;
for (uint i = 0; i < seatsAtLevel; i++) {
payable(levelSeats[lvl][i].owner).transfer(amountPerSeatInWei);
emit DistributionEvent(levelSeats[lvl][i].owner, amountPerSeatInWei, lvl, "TopDown");
console.log("Distributed %s to %s at level %s", amountPerSeatInWei, levelSeats[lvl][i].owner, lvl);
}
amountInWei /= 2;
}
}
console.log("Exiting distributeTopDown()");
}
function addSeatToLottery(uint seatNumber, uint level) private {
console.log("Entering addSeatToLottery()");
for (uint i = 0; i < level; i++) {
seatNumbers.push(seatNumber);
}
console.log("Added seat number %s to lottery %s times", seatNumber, level);
emit SeatAddedToLotteryEvent(seatNumber, level);
console.log("Exiting addSeatToLottery()");
}
function drawLottery() public onlyManager {
console.log("Entering drawLottery()");
require(seatNumbers.length > 0, "No seats in the lottery");
console.log("Drawing lottery");
uint numWinners = totalSeats / level;
address[] memory winners = new address[](numWinners);
uint prizePerWinnerInWei = lotteryPoolInWei / numWinners;
for (uint i = 0; i < numWinners; i++) {
uint winnerIndex = random() % seatNumbers.length;
uint winningSeat = seatNumbers[winnerIndex];
winners[i] = getSeatOwner(winningSeat);
payable(winners[i]).transfer(prizePerWinnerInWei);
// Remove the winning seat number from the pool
seatNumbers[winnerIndex] = seatNumbers[seatNumbers.length - 1];
seatNumbers.pop();
console.log("Winner: %s, Prize: %s", winners[i], prizePerWinnerInWei);
}
lotteryPoolInWei = 0;
emit LotteryWinnersEvent(winners, prizePerWinnerInWei);
console.log("Exiting drawLottery()");
}
function random() private view returns (uint) {
console.log("Entering random()");
uint result = uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, seatNumbers)));
console.log("Exiting random() with result:", result);
return result;
}
function getSeatOwner(uint seatNumber) private view returns (address) {
console.log("Entering getSeatOwner() with seatNumber:", seatNumber);
for (uint lvl = 1; lvl <= level; lvl++) {
Seat[] storage seats = levelSeats[lvl];
for (uint i = 0; i < seats.length; i++) {
if (seats[i].seatNumber == seatNumber) {
console.log("Exiting getSeatOwner() with owner:", seats[i].owner);
return seats[i].owner;
}
}
}
revert("Seat owner not found");
}
function getSeatsByLevel(uint lvl) public view returns (Seat[] memory) {
console.log("Entering getSeatsByLevel() with level:", lvl);
Seat[] memory result = levelSeats[lvl];
console.log("Exiting getSeatsByLevel() with result:");
for (uint i = 0; i < result.length; i++) {
console.log("Seat owner:", result[i].owner);
console.log("Seat number:", result[i].seatNumber);
console.log("Seat level:", result[i].level);
console.log("Local seat number:", result[i].localSeatNumber);
}
return result;
}
function getAllSeats() public view returns (Seat[] memory) {
console.log("Entering getAllSeats()");
Seat[] memory allSeats = new Seat[](totalSeats);
uint index = 0;
for (uint lvl = 1; lvl <= level; lvl++) {
Seat[] storage seats = levelSeats[lvl];
for (uint i = 0; i < seats.length; i++) {
allSeats[index] = seats[i];
index++;
}
}
console.log("Exiting getAllSeats() with result:");
for (uint i = 0; i < allSeats.length; i++) {
console.log("Seat owner:", allSeats[i].owner);
console.log("Seat number:", allSeats[i].seatNumber);
console.log("Seat level:", allSeats[i].level);
console.log("Local seat number:", allSeats[i].localSeatNumber);
}
return allSeats;
}
function getSeatsForLevel(uint lvl) public pure returns (uint) {
console.log("Entering getSeatsForLevel() with level:", lvl);
require(lvl > 0, "Level must be greater than 0");
uint result = 2 ** (lvl - 1);
console.log("Exiting getSeatsForLevel() with result:", result);
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment