Client Contract Integration (For Developers Using RandProof)
If you are building a protocol that consumes RandProof (e.g., a raffle, NFT mint, or DAO tool), integrate the following interface:
If you are building a protocol that consumes RandProof (e.g., a raffle, NFT mint, or DAO tool), integrate the following interface:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IKeeperCoordinator {
// Request a PoFR event. Returns eventId.
// fee must be sent with the call (msg.value or ERC20 approve first)
// Request a PoFR event. Returns eventId.\
// fee must be >= MIN_FEE (spam protection, AV-27 fix)\
// Rate limited: 1 request per 10 blocks per address\
function requestRandomness(bytes32 userSeed) external payable returns (bytes32 eventId);
// Fulfill with verified entropy inputs (called by keepers)
function fulfill(bytes32 eventId, bytes32[15] memory entropyInputs, uint256 entropyBlock, bytes memory aggregatedSig, address[] memory signers) external;
// Submit entropy attestation (Layer 2: multi-keeper attestation)
function submitEntropy(bytes32 eventId, uint8 sourceId, bytes32 value, bytes memory proof) external;
// Submit fraud proof during challenge window (Layer 3)
function submitFraudProof(bytes32 eventId, uint8 sourceId, bytes memory proof) external;
// Get verified entropy inputs for a fulfilled event
function getEntropyInputs(bytes32 eventId) external view returns (bytes32[15] memory);
// Check if an event has been fulfilled
function isFulfilled(bytes32 eventId) external view returns (bool);
// Get the RandProof artifact for a fulfilled event
function getRandProof(bytes32 eventId) external view returns (
bytes32 seed,
bytes aggregatedBLSSig,
address[] memory signers,
uint256 fulfillBlock,
uint256 fulfillTimestamp
);
}
contract MyRaffle is IRandProofConsumer {
IKeeperCoordinator public coordinator;
mapping(bytes32 => uint256) public pendingDraws; // eventId => totalEntrants
constructor(address _coordinator) {
coordinator = IKeeperCoordinator(_coordinator);
}
// Step 1: Request randomness when draw is ready
function requestDraw(uint256 totalEntrants) external payable {
bytes32 userSeed = keccak256(abi.encode(block.number, msg.sender, totalEntrants));
bytes32 eventId = coordinator.requestRandomness{value: msg.value}(userSeed);
pendingDraws[eventId] = totalEntrants;
emit DrawRequested(eventId, totalEntrants);
}
// Step 2: Receive the fulfilled randomness (called by KeeperCoordinator)
function fulfillRandomness(bytes32 eventId, bytes32 seed) external override {
require(msg.sender == address(coordinator), "Only coordinator");
uint256 totalEntrants = pendingDraws[eventId];
require(totalEntrants > 0, "Unknown event");
uint256 winnerIndex = uint256(seed) % totalEntrants;
emit WinnerSelected(eventId, winnerIndex);
delete pendingDraws[eventId];
}
event DrawRequested(bytes32 indexed eventId, uint256 totalEntrants);
event WinnerSelected(bytes32 indexed eventId, uint256 winnerIndex);
}KeeperCoordinator contract addresses (current deployments):
-
Base Mainnet: 0xRPKC...Base (deploy address TBD, Phase 1 Q3 2026)
-
OP Mainnet: 0xRPKC...OP
-
Arbitrum One: 0xRPKC...ARB
-
Linea: 0xRPKC...Linea
-
Ethereum Mainnet: 0xRPKC...ETH
-
Polygon: 0xRPKC...MATIC
-
BNB Chain: 0xRPKC...BNB
-
Avalanche C-Chain: 0xRPKC...AVAX
-
Moonbeam: 0xRPKC...GLMR
-
World Chain: 0xRPKC...WLD
-
HyperEVM: 0xRPKC...HYPE
⚠ NOTE: Contract addresses will be published at randproof.network/contracts upon Phase 1 mainnet deployment (Q3 2026). Always verify addresses against the official repository before use.