// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; import "./AereRandomnessBeacon.sol"; /** * @title AereDrandConsumer * @notice Reference example of consuming verifiable randomness on AERE. * Pattern: dApp picks a future drand round, waits for it to be * submitted (by anyone — typically the dApp itself), then reads * the randomness. * * This is a minimal lottery — illustration only, not a production lottery. * Any dApp on AERE can use the same pattern. Two lines of integration code. */ contract AereDrandConsumer { AereRandomnessBeacon public immutable beacon; uint64 public targetRound; bytes32 public winnerSeed; event RandomnessCommitted(uint64 targetRound, uint64 willResolveAt); event RandomnessRevealed(uint64 round, bytes32 randomness, bytes32 winnerSeed); constructor(AereRandomnessBeacon _beacon) { beacon = _beacon; } /** * @notice Commit to a future drand round as the source of randomness. * The round must be in the future at commit time so its * signature cannot already be known. */ function commitToFutureRound(uint64 minSecondsInFuture) external returns (uint64) { uint64 targetTime = uint64(block.timestamp) + minSecondsInFuture; targetRound = beacon.roundAtTime(targetTime); emit RandomnessCommitted(targetRound, beacon.DRAND_GENESIS() + targetRound * beacon.DRAND_PERIOD()); return targetRound; } /** * @notice Reveal the random outcome. Caller submits the drand signature * for `targetRound` (anyone can; signature is public on api.drand.sh). */ function reveal(bytes calldata signature) external returns (bytes32 randomness) { require(targetRound != 0, "Consumer: not committed"); randomness = beacon.submitRound(targetRound, signature); // Derive the winnerSeed deterministically from the randomness + this contract. winnerSeed = keccak256(abi.encodePacked(randomness, address(this), targetRound)); emit RandomnessRevealed(targetRound, randomness, winnerSeed); } /** * @notice If the round has already been submitted by someone else, * just consume it without paying gas for re-submission. */ function consumeExistingRound() external returns (bytes32 randomness) { require(targetRound != 0, "Consumer: not committed"); randomness = beacon.getRandomness(targetRound); require(randomness != bytes32(0), "Consumer: round not submitted yet"); winnerSeed = keccak256(abi.encodePacked(randomness, address(this), targetRound)); emit RandomnessRevealed(targetRound, randomness, winnerSeed); } }