Blockchain / Global / Address
Contract
0x16FbFc293166F21859a43da5967e0a6EF2Db4412
- Balance
- 0 SOMI
- Transactions sent
- 1
- Type
- Contract · 2,799 bytes of code
CommitRevealRandomness Verified
- Compiler
- solc 0.8.28+commit.7893614a
- Language
- Solidity
- Optimization
- Yes, 200 runs
- EVM version
- paris
- License
- —
- Verified
- 2026-10-12
contracts/Utils/CommitRevealRandomness.sol
and 2 more files
Solidity
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title Commit-Reveal Randomness
* @notice A contract that provides a commit-reveal mechanism for generating random numbers
* @dev Uses future block hash as a source of randomness
*/
contract CommitRevealRandomness is Ownable {
// Struct to store randomization request details
struct RandomizationRequest {
string id;
uint256 numberFrom;
uint256 numberTo;
uint256 targetBlock;
bool revealed;
uint256 result;
}
// Mapping from id to randomization request
mapping(string => RandomizationRequest) private _requests;
// Events
event RandomizationRequested(
string id,
uint256 numberFrom,
uint256 numberTo,
uint256 targetBlock
);
event RandomizationRevealed(string id, uint256 randomNumber);
// Custom errors
error InvalidRange();
error RequestNotFound();
error BlockNotReached();
error AlreadyRequested();
error RequestAlreadyRevealed();
error BlockTooOld();
error ResultNotRevealed();
/**
* @notice Initializes the CommitRevealRandomness contract
*/
constructor() Ownable(msg.sender) {}
/**
* @notice Requests randomization for a given id and range
* @param id The unique identifier for this randomization request
* @param numberFrom The inclusive lower bound of the random number range
* @param numberTo The inclusive upper bound of the random number range
* @return The target block number for the reveal phase
*/
function randomize(
string calldata id,
uint256 numberFrom,
uint256 numberTo
) external returns (uint256) {
// Validate input parameters
if (numberFrom > numberTo) revert InvalidRange();
// Calculate target block (current block + 100)
uint256 targetBlock = block.number + 100;
if (_requests[id].targetBlock != 0) revert AlreadyRequested();
// Store the request
_requests[id] = RandomizationRequest({
id: id,
numberFrom: numberFrom,
numberTo: numberTo,
targetBlock: targetBlock,
revealed: false,
result: 0
});
// Emit event
emit RandomizationRequested(id, numberFrom, numberTo, targetBlock);
return targetBlock;
}
/**
* @notice Reveals and permanently saves the random number for a given id
* @param id The unique identifier for the randomization request
* @return The generated random number within the specified range
*/
function reveal(string calldata id) external returns (uint256) {
// Get the request
RandomizationRequest storage request = _requests[id];
// Validate request exists
if (request.targetBlock == 0) revert RequestNotFound();
// Validate request is not already revealed
if (request.revealed) revert RequestAlreadyRevealed();
// Validate target block has been reached
if (block.number < request.targetBlock) revert BlockNotReached();
// Validate block is not too old (blockhash only works for 256 most recent blocks)
if (block.number - request.targetBlock >= 256) revert BlockTooOld();
// Get the block hash of the target block
bytes32 blockHash = blockhash(request.targetBlock);
// Generate random number using block hash, id, and timestamp
uint256 randomNumber = uint256(
keccak256(abi.encodePacked(blockHash, id, block.timestamp))
);
// Scale the random number to the desired range (inclusive)
uint256 range = request.numberTo - request.numberFrom + 1;
uint256 result = request.numberFrom + (randomNumber % range);
// Save the result and mark as revealed
request.result = result;
request.revealed = true;
// Emit event
emit RandomizationRevealed(id, result);
return result;
}
/**
* @notice Checks if a randomization request exists
* @param id The unique identifier for the randomization request
* @return True if the request exists, false otherwise
*/
function requestExists(string calldata id) external view returns (bool) {
return _requests[id].targetBlock != 0;
}
/**
* @notice Gets the target block for a randomization request
* @param id The unique identifier for the randomization request
* @return The target block number
*/
function getTargetBlock(string calldata id) external view returns (uint256) {
if (_requests[id].targetBlock == 0) revert RequestNotFound();
return _requests[id].targetBlock;
}
/**
* @notice Shows the result of a randomization request if it has been revealed
* @param id The unique identifier for the randomization request
* @return The revealed random number
*/
function showResult(string calldata id) external view returns (uint256) {
// Get the request
RandomizationRequest memory request = _requests[id];
// Validate request exists
if (request.targetBlock == 0) revert RequestNotFound();
// Validate request has been revealed
if (!request.revealed) revert ResultNotRevealed();
return request.result;
}
}
Other files
@openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
ABI · 21 entries
json
[
{
"type": "constructor",
"inputs": [],
"stateMutability": "nonpayable"
},
{
"name": "AlreadyRequested",
"type": "error",
"inputs": []
},
{
"name": "BlockNotReached",
"type": "error",
"inputs": []
},
{
"name": "BlockTooOld",
"type": "error",
"inputs": []
},
{
"name": "InvalidRange",
"type": "error",
"inputs": []
},
{
"name": "OwnableInvalidOwner",
"type": "error",
"inputs": [
{
"name": "owner",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "OwnableUnauthorizedAccount",
"type": "error",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "RequestAlreadyRevealed",
"type": "error",
"inputs": []
},
{
"name": "RequestNotFound",
"type": "error",
"inputs": []
},
{
"name": "ResultNotRevealed",
"type": "error",
"inputs": []
},
{
"name": "OwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "RandomizationRequested",
"type": "event",
"inputs": [
{
"name": "id",
"type": "string",
"indexed": false,
"internalType": "string"
},
{
"name": "numberFrom",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "numberTo",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "targetBlock",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "RandomizationRevealed",
"type": "event",
"inputs": [
{
"name": "id",
"type": "string",
"indexed": false,
"internalType": "string"
},
{
"name": "randomNumber",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "getTargetBlock",
"type": "function",
"inputs": [
{
"name": "id",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "randomize",
"type": "function",
"inputs": [
{
"name": "id",
"type": "string",
"internalType": "string"
},
{
"name": "numberFrom",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "numberTo",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "renounceOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "requestExists",
"type": "function",
"inputs": [
{
"name": "id",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "reveal",
"type": "function",
"inputs": [
{
"name": "id",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "showResult",
"type": "function",
"inputs": [
{
"name": "id",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "transferOwnership",
"type": "function",
"inputs": [
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]