Contract Name:
sdeamon0xRandom
Contract Source Code:
// File: contracts\openzeppelin\contracts\utils\Context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @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;
}
}
// File: contracts\openzeppelin\contracts\access\Ownable.sol
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.19;
/**
* @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);
}
}
// File: contracts\openzeppelin\contracts\access\Ownable2Step.sol
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.19;
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
// File: contracts\openzeppelin\contracts\utils\Panic.sol
pragma solidity ^0.8.19;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}
// File: contracts/sdaemon0x/sdaemon0xRandomizer.sol
pragma solidity ^0.8.19;
// ------------------------------------------------------------
//
// DICE BY RND ASK COMMIT REVEAL (ACR)
//
// Constructed waiting Chainlink or good VRF on Sonic...
// and prevent double TX to create pseudo-random.
// Designed for sDeamon
// inspired by lib STL from C++ and openzeppelin double deque.
//
// -----------------------------------------------------------
interface sdeamon0xRandomCallback
{
function onRandomCallback(uint256 uid, uint256 res) external;
}
interface sdeamon0xRandomItf
{
function beginRandom(sdeamon0xRandomCallback CA) external returns (uint256);
}
contract sdeamon0xRandom is sdeamon0xRandomItf, Ownable2Step {
constructor() Ownable(0x88524E752144C15dc1a12BA3978c2d700dc97498) {
randomizer[0x88524E752144C15dc1a12BA3978c2d700dc97498] = true;
nonceOne = uint256(keccak256(abi.encodePacked(block.prevrandao, msg.sender, block.timestamp, uint256(0x666))));
nonceTwo = uint256(keccak256(abi.encodePacked(uint256(0x666), block.timestamp, block.prevrandao, msg.sender, uint256(69))));
for(uint256 i=0; i<8; ++i)
queues.push();
}
mapping(address => bool) public randomizer;
function setRandomizer(address addr, bool b) external onlyOwner {
randomizer[addr] = b;
}
modifier onlyRandomizer() {
require(randomizer[msg.sender] == true);
_;
}
mapping(address => sdeamon0xRandomCallback) public registrars;
mapping(sdeamon0xRandomCallback => bool) public registereds;
function registerCA(sdeamon0xRandomCallback CA) payable public {
if (msg.sender != owner()) {
require(msg.value >= 500_000 ether, "No sufficient fund"); //price to buy my validator
}
registrars[msg.sender] = CA;
registereds[CA] = true;
}
function unregisterCA(sdeamon0xRandomCallback CA) public {
require(registrars[msg.sender] == CA, "not your CA");
registereds[CA] = false;
registrars[msg.sender] = sdeamon0xRandomCallback(address(0));
}
uint256 private nonceOne;
uint256 private nonceTwo;
uint256 public uniqueId = 0x666;
struct Commit {
uint256 uniqueId;
sdeamon0xRandomCallback addr;
uint256 salt;
}
struct CommitQueue {
uint128 _begin;
uint128 _end;
mapping(uint128 index => Commit) _data;
}
CommitQueue[] private queues;
// return the number of canal
function canal_length() public view returns (uint256) {
return queues.length;
}
function pushFront(uint256 canal, Commit memory value) internal {
require(canal < queues.length);
unchecked {
CommitQueue storage queue = queues[canal];
uint128 frontIndex = queue._begin - 1;
if (frontIndex == queue._end) Panic.panic(Panic.RESOURCE_ERROR);
queue._data[frontIndex] = value;
queue._begin = frontIndex;
}
}
function popBack(uint256 canal) internal returns (Commit memory value) {
require(canal < queues.length);
unchecked {
CommitQueue storage queue = queues[canal];
uint128 backIndex = queue._end;
if (backIndex == queue._begin) Panic.panic(Panic.EMPTY_ARRAY_POP);
--backIndex;
value = queue._data[backIndex];
delete queue._data[backIndex];
queue._end = backIndex;
}
}
function queueLen(uint256 canal) public view returns (uint256) {
require(canal < queues.length);
unchecked {
CommitQueue storage queue = queues[canal];
return uint256(queue._end - queue._begin);
}
}
uint256 canal_counter = 0;
// Ask.
function beginRandom(sdeamon0xRandomCallback CA) public returns (uint256) {
require(registereds[CA] == true, "contract not registered");
unchecked {
nonceOne += 13;
nonceTwo += 7;
}
++uniqueId;
uint256 salt = uint256(keccak256(abi.encodePacked(nonceOne, block.timestamp, block.prevrandao, msg.sender, nonceTwo)));
pushFront(canal_counter, Commit(uniqueId, CA, salt));
canal_counter++;
canal_counter %= queues.length;
return uniqueId;
}
// Reveal.
function fireRandom(uint256 canal) public onlyRandomizer {
require(queueLen(canal) > 0, 'nothing to launch');
Commit memory c = popBack(canal);
uint256 random = uint256(keccak256(abi.encodePacked(c.salt, nonceOne, c.uniqueId, uniqueId, block.prevrandao, msg.sender, nonceTwo)));
c.addr.onRandomCallback(c.uniqueId, random);
}
// clean to debug if onRandomCallback failed !
function cleanQueue(uint256 canal) public onlyRandomizer {
while(queueLen(canal) > 0) {
popBack(canal);
}
}
// withdraw to buy a validator "Sonic"
function withdraw(uint256 amount) external onlyOwner {
(bool success,) = payable(msg.sender).call{value: amount}("");
require(success);
}
}