Created
June 7, 2024 23:41
-
-
Save AE-0h/1832410427f13ca9edc8f7ea061212dc to your computer and use it in GitHub Desktop.
WorldIDRaffle by world-id-raffle. Find it at https://www.cookbook.dev/contracts/world-id-raffle-WorldIDRaffle
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
/* | |
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ██ █████ ██ ██ █████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██ ██████ ███████ ████ | |
Find any smart contract, and build your project faster: https://www.cookbook.dev | |
Twitter: https://twitter.com/cookbook_dev | |
Discord: https://discord.gg/WzsfPcfHrk | |
Find this contract on Cookbook: https://www.cookbook.dev/contracts/world-id-raffle-WorldIDRaffle/?utm=code | |
*/ | |
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.10; | |
library ByteHasher { | |
/// @dev Creates a keccak256 hash of a bytestring. | |
/// @param value The bytestring to hash | |
/// @return The hash of the specified value | |
/// @dev `>> 8` makes sure that the result is included in our field | |
function hashToField(bytes memory value) internal pure returns (uint256) { | |
return uint256(keccak256(abi.encodePacked(value))) >> 8; | |
} | |
} |
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
/* | |
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ██ █████ ██ ██ █████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██ ██████ ███████ ████ | |
Find any smart contract, and build your project faster: https://www.cookbook.dev | |
Twitter: https://twitter.com/cookbook_dev | |
Discord: https://discord.gg/WzsfPcfHrk | |
Find this contract on Cookbook: https://www.cookbook.dev/contracts/world-id-raffle-WorldIDRaffle/?utm=code | |
*/ | |
//SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.10; | |
interface IWorldID { | |
/// @notice Reverts if the zero-knowledge proof is invalid. | |
/// @param root The of the Merkle tree | |
/// @param groupId The id of the Semaphore group | |
/// @param signalHash A keccak256 hash of the Semaphore signal | |
/// @param nullifierHash The nullifier hash | |
/// @param externalNullifierHash A keccak256 hash of the external nullifier | |
/// @param proof The zero-knowledge proof | |
/// @dev Note that a double-signaling check is not included here, and should be carried by the caller. | |
function verifyProof( | |
uint256 root, | |
uint256 groupId, | |
uint256 signalHash, | |
uint256 nullifierHash, | |
uint256 externalNullifierHash, | |
uint256[8] calldata proof | |
) external view; | |
} |
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
/* | |
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ██ █████ ██ ██ █████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██ ██████ ███████ ████ | |
Find any smart contract, and build your project faster: https://www.cookbook.dev | |
Twitter: https://twitter.com/cookbook_dev | |
Discord: https://discord.gg/WzsfPcfHrk | |
Find this contract on Cookbook: https://www.cookbook.dev/contracts/world-id-raffle-WorldIDRaffle/?utm=code | |
*/ | |
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
import {IWorldID} from "./IWorldID.sol"; | |
import {ByteHasher} from "./ByteHasher.sol"; | |
contract WorldIDRaffle { | |
using ByteHasher for bytes; | |
error RaffleEnded(); | |
error RaffleRunning(); | |
error InvalidNullifier(); | |
event CreatedRaffle(uint256 raffleId, address indexed creator); | |
event EndedRaffle(uint256 indexed raffleId, address[] winners); | |
event JoinedRaffle(uint256 indexed raffleId, address indexed participant); | |
struct Raffle { | |
uint256 seed; | |
uint256 endsAt; | |
address[] winners; | |
uint256 noOfWinners; | |
uint256 participantCount; | |
} | |
string public actionId; | |
IWorldID public immutable worldId; | |
uint256 internal nextRaffleId = 1; | |
mapping(uint256 => Raffle) public getRaffle; | |
mapping(uint256 => mapping(uint256 => address)) public getParticipant; | |
mapping(uint256 => bool) internal nullifierHashes; | |
constructor(IWorldID _worldId, string memory _actionId) { | |
worldId = _worldId; | |
actionId = _actionId; | |
} | |
function create(uint256 endsAt, uint256 noOfWinners) public { | |
getRaffle[nextRaffleId].endsAt = endsAt; | |
getRaffle[nextRaffleId].noOfWinners = noOfWinners; | |
emit CreatedRaffle(nextRaffleId++, msg.sender); | |
} | |
function enter( | |
address receiver, | |
uint256 raffleId, | |
uint256 root, | |
uint256 nullifierHash, | |
uint256[8] calldata proof | |
) public { | |
if (nullifierHashes[nullifierHash]) revert InvalidNullifier(); | |
if (getRaffle[raffleId].endsAt < block.timestamp) revert RaffleEnded(); | |
worldId.verifyProof( | |
root, | |
1, | |
abi.encodePacked(receiver).hashToField(), | |
nullifierHash, | |
abi.encodePacked(actionId, raffleId).hashToField(), | |
proof | |
); | |
getParticipant[raffleId][ | |
getRaffle[raffleId].participantCount++ | |
] = receiver; | |
getRaffle[raffleId].seed ^= nullifierHash; | |
emit JoinedRaffle(raffleId, receiver); | |
} | |
function settle(uint256 raffleId) public { | |
Raffle storage raffle = getRaffle[raffleId]; | |
if (raffle.endsAt > block.timestamp) revert RaffleRunning(); | |
for (uint256 i = 0; i < raffle.noOfWinners; i++) { | |
raffle.winners.push( | |
getParticipant[raffleId][raffle.seed % raffle.participantCount] | |
); | |
raffle.seed ^= uint256(keccak256(abi.encodePacked(raffle.seed, i))); | |
} | |
emit EndedRaffle(raffleId, getRaffle[raffleId].winners); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment