Skip to content

Instantly share code, notes, and snippets.

@AE-0h
Created June 7, 2024 23:41
Show Gist options
  • Save AE-0h/1832410427f13ca9edc8f7ea061212dc to your computer and use it in GitHub Desktop.
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
/*
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ██ █████ ██ ██ █████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██ ██████ ███████ ████
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;
}
}
/*
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ██ █████ ██ ██ █████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██ ██████ ███████ ████
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;
}
/*
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ █████ ██████ ██ ██ ██ ██ █████ ██ ██ █████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██████ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ██ ██████ ███████ ████
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