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"
  }
]