Skip to content

Instantly share code, notes, and snippets.

@quangnle
Last active August 28, 2022 13:36
Show Gist options
  • Save quangnle/61c2eb1934885750234ae6fa3cbc9a63 to your computer and use it in GitHub Desktop.
Save quangnle/61c2eb1934885750234ae6fa3cbc9a63 to your computer and use it in GitHub Desktop.
Randao
// 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