Last active
August 28, 2022 13:36
-
-
Save quangnle/61c2eb1934885750234ae6fa3cbc9a63 to your computer and use it in GitHub Desktop.
Randao
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: Quang Le | |
pragma solidity ^0.8.9; | |
contract RANDAOLottery { | |
enum round_states { | |
Commit, | |
Reveal, | |
Finished, | |
Rolledback | |
} | |
struct Round{ | |
uint maxParticipants; | |
uint nParticipants; | |
uint startTime; | |
uint endTime; | |
uint price; | |
round_states state; | |
address creator; | |
mapping (address => uint256) commits; | |
mapping (address => uint256) reveals; | |
address[] revealedParticipants; | |
} | |
event RoundCreated(address indexed creator, uint startTime, uint endTime, uint maxParticipants, uint price); | |
event TicketCommited(address indexed creator, address indexed participant, uint256 hashValue); | |
event RevealActivated(address indexed creator); | |
event TicketRevealed(address indexed creator, address indexed participant, uint256 revealedValue, uint256 hashValue); | |
event RoundStopped(address indexed creator); | |
address _owner; | |
mapping (address => bool) _blacklist; | |
mapping (address => Round) _rounds; | |
constructor(){ | |
_owner = msg.sender; | |
} | |
modifier CanCreateNewRound(address creator){ | |
require( | |
(getRoundHash(_rounds[creator].startTime, _rounds[creator].endTime, msg.sender) == getRoundHash(0, 0, msg.sender)) || | |
(_rounds[creator].state == round_states.Finished), | |
"another on-going round created" | |
); | |
_; | |
} | |
modifier ExistedRound(address creator){ | |
require( | |
(getRoundHash(_rounds[creator].startTime, _rounds[creator].endTime, msg.sender) != getRoundHash(0, 0, msg.sender)), | |
"null match" | |
); | |
_; | |
} | |
modifier NotInBlackList() { | |
require( | |
_blacklist[msg.sender] == false, | |
"blacklisted person" | |
); | |
_; | |
} | |
function getRoundHash(uint startTime, uint endTime, address addr) public pure returns (bytes32){ | |
return keccak256(abi.encodePacked(startTime, endTime, addr)); | |
} | |
function getRound(address creator) public view returns (uint, uint, uint){ | |
return (_rounds[creator].startTime, _rounds[creator].endTime, _rounds[creator].price); | |
} | |
function getHash(uint value) public pure returns (bytes32){ | |
return keccak256(abi.encodePacked(value)); | |
} | |
function getCommit(address creator) public view returns (uint){ | |
return _rounds[creator].commits[msg.sender]; | |
} | |
function blockAnAddress(address addr) external { | |
require( | |
msg.sender == _owner, | |
"for boss only" | |
); | |
_blacklist[addr] = true; | |
} | |
function createNewRound(uint maxParticipants, uint duration, uint256 firstCommit) external payable CanCreateNewRound(msg.sender){ | |
require( | |
!_blacklist[msg.sender], | |
"blacklisted creator" | |
); | |
require( | |
duration <= 7200, | |
"must <2 hrs" | |
); | |
require( | |
(getRoundHash(_rounds[msg.sender].startTime, _rounds[msg.sender].endTime, msg.sender) == getRoundHash(0, 0, msg.sender)) || | |
(_rounds[msg.sender].state == round_states.Finished), | |
"there is another on-going round which is created by this address" | |
); | |
uint timeStamp = block.timestamp; | |
Round storage newRound = _rounds[msg.sender]; | |
newRound.maxParticipants = maxParticipants; | |
newRound.nParticipants = 1; | |
newRound.startTime = timeStamp; | |
newRound.endTime = timeStamp + duration; | |
newRound.price = msg.value; // in wei | |
newRound.state = round_states.Commit; | |
newRound.creator = msg.sender; | |
newRound.commits[msg.sender] = firstCommit; | |
emit RoundCreated(msg.sender, newRound.startTime, newRound.endTime, newRound.maxParticipants, newRound.price); | |
} | |
function commitATicket(address creator, uint256 hashValue) external payable ExistedRound(creator) NotInBlackList(){ | |
require( | |
creator != msg.sender, | |
"creator can't buy ticket" | |
); | |
Round storage round = _rounds[creator]; | |
require( | |
round.state == round_states.Commit, | |
"must be COMMIT state" | |
); | |
require( | |
round.price == msg.value, | |
"invalid amount" | |
); | |
require( | |
round.nParticipants < round.maxParticipants, | |
"maximum participants reached" | |
); | |
uint zero = 0; | |
require( | |
bytes32(hashValue) != keccak256(abi.encodePacked(zero)), | |
"do not commit predictable hash value (0)" | |
); | |
round.commits[msg.sender] = hashValue; | |
round.nParticipants++; | |
emit TicketCommited(creator, msg.sender, hashValue); | |
} | |
function activeRevealPhase(address creator) external ExistedRound(creator) { | |
require( | |
creator == msg.sender || msg.sender == _owner, | |
"unauthen person" | |
); | |
Round storage round = _rounds[creator]; | |
require( | |
round.state == round_states.Commit, | |
"must be in COMMIT state" | |
); | |
require( | |
block.timestamp > round.endTime, | |
"COMMIT on going" | |
); | |
round.state = round_states.Reveal; | |
emit RevealActivated(creator); | |
} | |
function revealATicket(address creator, uint256 revealedValue) external ExistedRound(creator) NotInBlackList(){ | |
require( | |
msg.sender != creator, | |
"creator must be last reveal" | |
); | |
Round storage round = _rounds[creator]; | |
require( | |
round.state == round_states.Reveal, | |
"must reveal in REVEAL status" | |
); | |
require( | |
keccak256(abi.encodePacked(revealedValue)) == keccak256(abi.encodePacked(round.commits[msg.sender])), | |
"dont matched" | |
); | |
round.revealedParticipants.push(msg.sender); | |
round.reveals[msg.sender] = revealedValue; | |
emit TicketRevealed(creator, msg.sender, revealedValue, round.commits[msg.sender]); | |
} | |
function claimPrize(address creator, uint256 revealedValue) external payable ExistedRound(creator) NotInBlackList() { | |
require( | |
creator == msg.sender, | |
"unauthen- person" | |
); | |
Round storage round = _rounds[creator]; | |
require( | |
round.state == round_states.Reveal, | |
"must in REVEAL phase" | |
); | |
require( | |
block.timestamp < round.endTime * 2, | |
"still in awaiting time" | |
); | |
uint result = revealedValue; | |
address[] storage participants = round.revealedParticipants; | |
if (participants.length == 0){ | |
payable(msg.sender).transfer(round.price); | |
} else { | |
for(uint i=0; i<participants.length; i++){ | |
result = result ^ round.reveals[participants[i]]; | |
} | |
result = result % (participants.length + 1); | |
if (result == 0){ | |
payable(msg.sender).transfer(round.price * (participants.length + 1)); | |
} else { | |
payable(participants[result-1]).transfer(round.price * (round.revealedParticipants.length + 1)); | |
} | |
} | |
} | |
function stopRound(address creator, bool creatorCanClaimBack) external { | |
require( | |
msg.sender == _owner, | |
"for owner only" | |
); | |
Round storage round = _rounds[creator]; | |
round.state = round_states.Rolledback; | |
// a litte trick to help creator claiming back money | |
if (creatorCanClaimBack) | |
round.commits[creator] = 1; | |
emit RoundStopped(creator); | |
} | |
function claimRolledback(address creator) external payable ExistedRound(creator) NotInBlackList() { | |
Round storage round = _rounds[creator]; | |
require( | |
round.state == round_states.Rolledback, | |
"ROLLEDBACK state only" | |
); | |
require( | |
round.commits[msg.sender] != 0, | |
"not yet commited or already claimed" | |
); | |
round.commits[msg.sender] = 0; | |
payable(msg.sender).transfer(round.price); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment