// SPDX-License-Identifier: MIT pragma solidity 0.8.30; import {IEntropyConsumer} from "../interfaces/IEntropyConsumer.sol"; import {IRandomnessHub} from "../interfaces/IRandomnessHub.sol"; /// @title ExampleConsumer /// @notice Minimal integration: the caller pays the exact fee, the draw is bound to the returned /// sequence number, the callback is accepted only from the hub, and storing is idempotent. /// If the callback failed (for example, too little callback gas), anyone can copy the stored /// result with `recover`. Zero is a valid result. No stakes, deadlines or refunds. contract ExampleConsumer is IEntropyConsumer { IRandomnessHub public immutable hub; struct Draw { address requester; bool requested; bool stored; bytes32 random; } mapping(uint64 => Draw) public draws; event DrawRequested(uint64 indexed sequenceNumber, address indexed requester, uint128 fee); event DrawStored(uint64 indexed sequenceNumber, bytes32 random, bool viaCallback); error WrongFee(uint256 expected, uint256 paid); error UnknownDraw(uint64 sequenceNumber); error WrongProvider(address provider); error NotFulfilled(uint64 sequenceNumber); constructor(IRandomnessHub hub_) { hub = hub_; } /// @notice The caller pays the hub fee for its own request; the contract holds no budget. function request(uint32 gasLimit, bytes32 userContribution) external payable returns (uint64 sequenceNumber) { uint128 fee = hub.getFeeV2(address(hub), gasLimit); if (msg.value != fee) revert WrongFee(fee, msg.value); sequenceNumber = hub.requestV2{value: fee}(address(hub), userContribution, gasLimit); draws[sequenceNumber] = Draw({requester: msg.sender, requested: true, stored: false, random: bytes32(0)}); emit DrawRequested(sequenceNumber, msg.sender, fee); } /// @notice Stores a result whose callback failed, reading it from the hub. function recover(uint64 sequenceNumber) external { IRandomnessHub.Request memory r = hub.requestOf(sequenceNumber); if (r.consumer != address(this) || r.status != IRandomnessHub.Status.Fulfilled) revert NotFulfilled(sequenceNumber); _store(sequenceNumber, hub.randomOf(sequenceNumber), false); } function getEntropy() internal view override returns (address) { return address(hub); } function entropyCallback(uint64 sequenceNumber, address provider, bytes32 random) internal override { if (provider != address(hub)) revert WrongProvider(provider); _store(sequenceNumber, random, true); } function _store(uint64 sequenceNumber, bytes32 random, bool viaCallback) private { Draw storage draw = draws[sequenceNumber]; if (!draw.requested) revert UnknownDraw(sequenceNumber); if (draw.stored) return; // a repeat never changes the stored result draw.stored = true; draw.random = random; emit DrawStored(sequenceNumber, random, viaCallback); } }