Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xd7b9996bA0a6Af9748e01e9E1CBeaaE09A6A780c
Balance 0 ETH
Nonce 1
Code Size 5338 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5338 bytes
0x608060405234801561000f575f5ffd5b5060043610610187575f3560e01c80638da5cb5b116100d9578063bb20995a11610093578063f50d390d1161006e578063f50d390d146103bc578063f851a440146103cf578063fc2a88c3146103e2578063fee439d8146103eb575f5ffd5b8063bb20995a14610386578063de5b24c914610396578063f2fde38b146103a9575f5ffd5b80638da5cb5b146102f95780638ea9811714610309578063929066f51461031c5780639eccacf61461033f578063b0fb162f14610352578063b91038c714610373575f5ffd5b806359974e3811610144578063662fac391161011f578063662fac391461028457806379ba5097146102ab5780637d234e22146102b357806381d12c58146102bb575f5ffd5b806359974e38146102605780635d495aea1461027357806361728f391461027b575f5ffd5b806309c1ba2e1461018b5780631df47acc146101c55780631fe543e3146101ce57806324f74697146101e35780632f68f4821461020e5780633fc8cef314610221575b5f5ffd5b6101b27fff69e45a452760adf8bbae506631197cc41f0e2d83580febb35bc2548a878dc081565b6040519081526020015b60405180910390f35b6101b260095481565b6101e16101dc3660046111d3565b6103f5565b005b6005546101f99062010000900463ffffffff1681565b60405163ffffffff90911681526020016101bc565b6101e161021c36600461124d565b61044a565b6102487f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b0390911681526020016101bc565b6101e161026e36600461124d565b610457565b6101b2610478565b6101b260045481565b6102487f000000000000000000000000efd63ad292f2a6979b7ee3ff08d6d8867f5b8b1481565b6101e1610667565b6101e1610710565b6102e46102c936600461124d565b60076020525f90815260409020805460019091015460ff1682565b604080519283529015156020830152016101bc565b5f546001600160a01b0316610248565b6101e1610317366004611264565b61073a565b61032f61032a366004611264565b61082a565b60405190151581526020016101bc565b600254610248906001600160a01b031681565b6005546103609061ffff1681565b60405161ffff90911681526020016101bc565b6101e1610381366004611264565b61083c565b6008546101f99063ffffffff1681565b6101e16103a436600461128a565b61086b565b6101e16103b7366004611264565b6108d2565b6101e16103ca3660046112ad565b6108e3565b600354610248906001600160a01b031681565b6101b260065481565b6101b26212750081565b6002546001600160a01b0316331461043a5760025460405163073e64fd60e21b81523360048201526001600160a01b0390911660248201526044015b60405180910390fd5b61044583838361093a565b505050565b6104526109be565b600455565b61045f6109eb565b8060095f82825461047091906112e2565b909155505050565b5f610481610a34565b6009545f036104a357604051633372d42d60e21b815260040160405180910390fd5b60085463ffffffff428116916104be916212750091166112e2565b11156104dd57604051635ded5d6b60e01b815260040160405180910390fd5b6104e7600a610a8c565b5f0361050657604051632ac217f560e11b815260040160405180910390fd5b6005610512600a610a8c565b116105385761051f610a95565b61053460095461052f600a610a8c565b610afe565b5090565b6002546040805160c08101825260045481527fff69e45a452760adf8bbae506631197cc41f0e2d83580febb35bc2548a878dc06020808301919091526005805461ffff81168486015262010000900463ffffffff166060840152608083015282519081019092525f82526001600160a01b0390921691639b1c385e9160a08201906105c290610c39565b8152506040518263ffffffff1660e01b81526004016105e191906112f5565b6020604051808303815f875af11580156105fd573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610621919061138e565b600681905560408051808201825260095481525f602080830182815285835260079091529290209051815590516001909101805460ff1916911515919091179055919050565b6001546001600160a01b031633146106ba5760405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606401610431565b5f8054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6107186109be565b6006545f9081526007602052604090206001908101805460ff19169091179055565b5f546001600160a01b0316331480159061075f57506002546001600160a01b03163314155b156107af57336107765f546001600160a01b031690565b60025460405163061db9c160e01b81526001600160a01b0393841660048201529183166024830152919091166044820152606401610431565b6001600160a01b0381166107d65760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be69060200160405180910390a150565b5f610836600a83610caa565b92915050565b6108446109eb565b61084c610a34565b610857600a82610caa565b61086857610866600a82610cce565b505b50565b6108736109be565b8063ffffffff1661088381610ce2565b6206ddd08263ffffffff1610156108ad576040516304c5ed9760e51b815260040160405180910390fd5b506005805463ffffffff909216620100000265ffffffff000019909216919091179055565b6108da610d02565b61086881610d54565b6108eb6109be565b60038161ffff16101580156109055750600a8161ffff1611155b61092257604051637f5e576160e11b815260040160405180910390fd5b6005805461ffff191661ffff92909216919091179055565b5f838152600760205260409020600181015460ff161561095a5750505050565b610962610a95565b5f6109a2825f01548585808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250610dfc92505050565b600192909201805460ff19169215159290921790915550505050565b6003546001600160a01b031633146109e957604051634755657960e01b815260040160405180910390fd5b565b336001600160a01b037f000000000000000000000000efd63ad292f2a6979b7ee3ff08d6d8867f5b8b1416146109e9576040516366b35e3960e01b815260040160405180910390fd5b6006545f818152600760209081526040918290208251808401909352805483526001015460ff16151590820152901580610a6f575080602001515b61086857604051639ce4cccf60e01b815260040160405180910390fd5b5f610836825490565b6008545f906212750090610aaf9063ffffffff16426113a5565b63ffffffff16610abf91906113d5565b9050610ace81621275006113e8565b600854610ae1919063ffffffff166112e2565b6008805463ffffffff191663ffffffff9290921691909117905550565b5f80610b0a83856113d5565b90505f5b838163ffffffff161015610c0f575f610b31600a63ffffffff8085169061103416565b905082816001600160a01b03167f75060f9e79552df167b73353fee6237a75bb5ba8ea022f77224e32f152138bcb60405160405180910390a360405163a9059cbb60e01b81526001600160a01b038281166004830152602482018590527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169063a9059cbb906044016020604051808303815f875af1158015610bd6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfa91906113ff565b50508080610c079061141e565b915050610b0e565b50610c1a83826113e8565b60095f828254610c2a9190611442565b90915550600195945050505050565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401610c7291511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b6001600160a01b0381165f90815260018301602052604081205415155b9392505050565b5f610cc7836001600160a01b03841661103f565b805f0361086857604051635a53a6e960e01b815260040160405180910390fd5b5f546001600160a01b031633146109e95760405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606401610431565b336001600160a01b03821603610dac5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610431565b600180546001600160a01b0319166001600160a01b038381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b5f5f825167ffffffffffffffff811115610e1857610e18611455565b604051908082528060200260200182016040528015610e41578160200160208202803683370190505b5090505f5b83518163ffffffff161015610fca575f848263ffffffff1681518110610e6e57610e6e611469565b602002602001015190505f610e98610e86600a610a8c565b610e90908461147d565b600a90611034565b90505f610ead886702c68af0bb14000061108b565b905080826001600160a01b03167f75060f9e79552df167b73353fee6237a75bb5ba8ea022f77224e32f152138bcb60405160405180910390a381858563ffffffff1681518110610eff57610eff611469565b6001600160a01b0390921660209283029190910190910152610f22600a836110b6565b5060405163a9059cbb60e01b81526001600160a01b038381166004830152602482018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169063a9059cbb906044016020604051808303815f875af1158015610f8f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fb391906113ff565b505050508080610fc29061141e565b915050610e46565b505f5b81518163ffffffff1610156110225761100f828263ffffffff1681518110610ff757610ff7611469565b6020026020010151600a610cce90919063ffffffff16565b508061101a8161141e565b915050610fcd565b508360095f828254610c2a9190611442565b5f610cc783836110ca565b5f81815260018301602052604081205461108457508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610836565b505f610836565b5f815f190483118202156110a65763c4c5d7f55f526004601cfd5b50670de0b6b3a764000091020490565b5f610cc7836001600160a01b0384166110f0565b5f825f0182815481106110df576110df611469565b905f5260205f200154905092915050565b5f81815260018301602052604081205480156111ca575f611112600183611442565b85549091505f9061112590600190611442565b9050808214611184575f865f01828154811061114357611143611469565b905f5260205f200154905080875f01848154811061116357611163611469565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061119557611195611490565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610836565b5f915050610836565b5f5f5f604084860312156111e5575f5ffd5b83359250602084013567ffffffffffffffff811115611202575f5ffd5b8401601f81018613611212575f5ffd5b803567ffffffffffffffff811115611228575f5ffd5b8660208260051b840101111561123c575f5ffd5b939660209190910195509293505050565b5f6020828403121561125d575f5ffd5b5035919050565b5f60208284031215611274575f5ffd5b81356001600160a01b0381168114610cc7575f5ffd5b5f6020828403121561129a575f5ffd5b813563ffffffff81168114610cc7575f5ffd5b5f602082840312156112bd575f5ffd5b813561ffff81168114610cc7575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610836576108366112ce565b60208152815160208201526020820151604082015261ffff604083015116606082015263ffffffff606083015116608082015263ffffffff60808301511660a08201525f60a083015160c08084015280518060e08501525f5b8181101561136c57602081840181015161010087840101520161134e565b505f6101008286010152610100601f19601f8301168501019250505092915050565b5f6020828403121561139e575f5ffd5b5051919050565b63ffffffff8281168282160390811115610836576108366112ce565b634e487b7160e01b5f52601260045260245ffd5b5f826113e3576113e36113c1565b500490565b8082028115828204841417610836576108366112ce565b5f6020828403121561140f575f5ffd5b81518015158114610cc7575f5ffd5b5f63ffffffff821663ffffffff8103611439576114396112ce565b60010192915050565b81810381811115610836576108366112ce565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f8261148b5761148b6113c1565b500690565b634e487b7160e01b5f52603160045260245ffdfea2646970667358221220513d5129a1c33d771615d60a3cb44238baf3f81a4567dbaecb3b4a90923342a064736f6c634300081b0033

Verified Source Code Partial Match

Compiler: v0.8.27+commit.40a35a09 EVM: shanghai Optimization: Yes (200 runs)
LamboPool.sol 371 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

import "@const/Constants.sol";
import {Time} from "@utils/Time.sol";
import {wmul} from "@utils/Math.sol";
import {Errors} from "@utils/Errors.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";

/**
 * @title WinnerRequest
 * @notice Struct to track the state and details of a winner selection request
 * @param upForGrabs The total rewards available for distribution at the time of the request
 * @param fulfilled Boolean indicating whether the winner selection has been completed
 */
struct WinnerRequest {
    uint256 upForGrabs;
    bool fulfilled;
}

/**
 * @title LamboPool
 * @notice A decentralized mining pool contract that manages participants and distributes rewards using
 * Chainlink VRF for fair winner selection
 * @dev Implements VRFConsumerBaseV2Plus for secure randomness generation and inherits error definitions from Errors contract
 * @custom:security-contact [email protected]
 */
contract LamboPool is VRFConsumerBaseV2Plus, Errors {
    using EnumerableSet for EnumerableSet.AddressSet;

    uint256 public constant COOLDOWN_PER_DRAW = 2 weeks;

    /// @notice Address of the mining contract authorized to add participants and distribute rewards
    address public immutable mining;

    /// @notice Address of the contract administrator
    address public admin;

    /// @notice The key hash used for Chainlink VRF requests
    /// @dev Identifies the gas lane price that you want to use for VRF requests
    bytes32 public keyHash;

    /// @notice Number of block confirmations to wait before fulfilling VRF request
    /// @dev Must be between 3 and 200 blocks
    uint16 public requestConfirmations = 3;

    /// @notice Maximum gas allowed for the VRF callback
    /// @dev Should be sufficient to process the maximum number of winners
    uint32 public callbackGasLimit = 700_000;

    /// @notice The WETH token contract used for reward distributions
    IERC20 public immutable weth;

    /// @notice Chainlink VRF subscription ID used for funding randomness requests
    uint256 public immutable subscriptionId;

    /// @notice ID of the most recent VRF request
    /// @dev Used to track pending randomness requests
    uint256 public lastRequestId;

    /// @notice Mapping to store details of each winner selection request
    /// @dev Links VRF request IDs to their corresponding WinnerRequest structs
    mapping(uint256 requestId => WinnerRequest) public requests;

    /// @notice Timestamp of the last interval calculation
    /// @dev Used to enforce cooldown periods between draws
    uint32 public lastIntervalCall;

    /// @notice Current total rewards available for distribution
    /// @dev Accumulated from mining rewards and updated after each distribution
    uint256 public upForGrabs;

    /// @notice Set of all current pool participants
    /// @dev Uses OpenZeppelin's EnumerableSet for efficient membership checks and enumeration
    EnumerableSet.AddressSet private participants;

    /**
     * @notice Ensures caller is the mining contract
     * @dev Reverts with OnlyMining error if caller is not authorized
     */
    modifier onlyMining() {
        _onlyMining();
        _;
    }

    /**
     * @notice Ensures no VRF request is currently pending
     * @dev Reverts with RandomnessAlreadyRequested if a request is unfulfilled
     */
    modifier noPendingRandomness() {
        _noPendingRandomness();
        _;
    }

    /**
     * @notice Restricts access to admin functions
     * @dev Reverts with OnlyAdmin if caller is not the administrator
     */
    modifier onlyAdmin() {
        _onlyAdmin();
        _;
    }

    /// @notice Thrown when non-mining contract calls restricted functions
    error OnlyMining();

    /// @notice Thrown when attempting a new VRF request while one is pending
    error RandomnessAlreadyRequested();

    /// @notice Thrown when attempting operations before cooldown period ends
    error OnlyAfterIntervalTime();

    /// @notice Thrown when attempting to distribute zero rewards
    error EmptyTreasury();

    /// @notice Thrown when attempting winner selection with no participants
    error NoParticipation();

    /// @notice Thrown when non-admin calls admin functions
    error OnlyAdmin();

    /// @notice Thrown when invalid gas limit is provided
    error InvalidGasLimit();

    /// @notice Thrown when invalid confirmation count is provided
    error InvalidRequestConfirmations();

    /**
     * @notice Emitted when a winner is selected and rewards are distributed
     * @param winner Address of the selected winner
     * @param amountWon Amount of WETH rewards distributed to the winner
     */
    event WinnerSelected(address indexed winner, uint256 indexed amountWon);

    /**
     * @notice Initializes the LamboPool contract
     * @dev Sets up VRF consumer, mining contract reference, and initial state
     * @param _mining Address of authorized mining contract
     * @param _vrfCoordinator Address of Chainlink VRF coordinator
     * @param _subscriptionId Chainlink VRF subscription ID
     * @param _weth Address of WETH token contract
     * @param _keyHash VRF gas lane key hash
     * @param _admin Address of contract administrator
     * @param _startTimestamp Initial timestamp for interval calculations
     */
    constructor(
        address _mining,
        address _vrfCoordinator,
        uint256 _subscriptionId,
        address _weth,
        bytes32 _keyHash,
        address _admin,
        uint32 _startTimestamp
    ) VRFConsumerBaseV2Plus(_vrfCoordinator) {
        mining = _mining;
        weth = IERC20(_weth);
        lastIntervalCall = _startTimestamp;
        keyHash = _keyHash;
        subscriptionId = _subscriptionId;
        admin = _admin;
    }

    /* == ADMIN FUNCTIONS == */

    /**
     * @notice Updates the number of block confirmations required for VRF requests
     * @param _newRequestConfirmations New confirmation count (must be 3-10)
     * @dev Only callable by admin, ensures reasonable confirmation range
     */
    function changeRequestConfirmations(uint16 _newRequestConfirmations) external onlyAdmin {
        require(_newRequestConfirmations >= 3 && _newRequestConfirmations <= 10, InvalidRequestConfirmations());
        requestConfirmations = _newRequestConfirmations;
    }

    /**
     * @notice Updates the gas limit for VRF callback
     * @param _newGasLimit New gas limit (minimum 450,000)
     * @dev Only callable by admin, ensures sufficient gas for processing
     */
    function changeCallBackGasLimimt(uint32 _newGasLimit) external onlyAdmin notAmount0(_newGasLimit) {
        require(_newGasLimit >= 450_000, InvalidGasLimit());
        callbackGasLimit = _newGasLimit;
    }

    /**
     * @notice Updates the VRF key hash
     * @param _newKeyHash New key hash for VRF requests
     * @dev Only callable by admin
     */
    function changeKeyHash(bytes32 _newKeyHash) external onlyAdmin {
        keyHash = _newKeyHash;
    }

    /**
     * @notice Emergency function to resolve stuck VRF requests
     * @dev Only callable by admin, marks last request as fulfilled
     */
    function unblockPendingRequest() external onlyAdmin {
        WinnerRequest storage _winnerReq = requests[lastRequestId];
        _winnerReq.fulfilled = true;
    }

    /**
     * @notice Initiates the winner selection process
     * @dev If participants <= WINNERS_PER_DRAW, distributes evenly; otherwise uses VRF
     * @return requestId The VRF request ID (if randomness was requested)
     */
    function pickWinner() external noPendingRandomness returns (uint256 requestId) {
        require(upForGrabs != 0, EmptyTreasury());
        require(lastIntervalCall + COOLDOWN_PER_DRAW <= Time.blockTs(), OnlyAfterIntervalTime());
        require(participants.length() != 0, NoParticipation());

        if (participants.length() <= WINNERS_PER_DRAW) {
            _updateInterval();
            _distributeEvenlyToParticipants(upForGrabs, participants.length());
        } else {
            requestId = s_vrfCoordinator.requestRandomWords(
                VRFV2PlusClient.RandomWordsRequest({
                    keyHash: keyHash,
                    subId: subscriptionId,
                    requestConfirmations: requestConfirmations,
                    callbackGasLimit: callbackGasLimit,
                    numWords: WINNERS_PER_DRAW,
                    extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false}))
                })
            );

            lastRequestId = requestId;
            requests[requestId] = WinnerRequest({fulfilled: false, upForGrabs: upForGrabs});
        }
    }

    /**
     * @notice Checks if an address is a current participant
     * @param _user Address to check
     * @return bool True if address is a participant
     */
    function isParticipant(address _user) public view returns (bool) {
        return participants.contains(_user);
    }

    /**
     * @notice Callback function for VRF randomness fulfillment
     * @dev Processes random words to select winners and distribute rewards
     * @param requestId The ID of the fulfilled VRF request
     * @param randomWords Array of random values from VRF
     */
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
        WinnerRequest storage _winnerReq = requests[requestId];

        if (_winnerReq.fulfilled) return;

        _updateInterval();

        bool fulfilled = _selectWinnerAccordingToRandomness(_winnerReq.upForGrabs, randomWords);

        _winnerReq.fulfilled = fulfilled;
    }

    /**
     * @notice Distributes rewards evenly among participants
     * @dev Used when participant count <= WINNERS_PER_DRAW
     * @param toDistribute Total amount to distribute
     * @param totalParticipants Number of participants
     * @return bool Success status
     */
    function _distributeEvenlyToParticipants(uint256 toDistribute, uint256 totalParticipants) internal returns (bool) {
        uint256 perUser = toDistribute / totalParticipants;

        for (uint32 i; i < totalParticipants; i++) {
            address winner = participants.at(i);

            emit WinnerSelected(winner, perUser);
            weth.transfer(winner, perUser);
        }

        upForGrabs -= perUser * totalParticipants;

        return true;
    }

    /**
     * @notice Updates the interval timestamp for cooldown tracking
     * @dev Accounts for multiple missed intervals
     */
    function _updateInterval() internal {
        uint256 missedIntervals = (Time.blockTs() - lastIntervalCall) / COOLDOWN_PER_DRAW;
        lastIntervalCall = uint32(lastIntervalCall + (COOLDOWN_PER_DRAW * missedIntervals));
    }

    /**
     * @notice Processes random values to select winners and distribute rewards
     * @dev Removes winners temporarily to prevent duplicate selection
     * @param toDistribute Total amount to distribute
     * @param randomWords Array of random values
     * @return bool Success status
     */
    function _selectWinnerAccordingToRandomness(uint256 toDistribute, uint256[] memory randomWords)
        internal
        returns (bool)
    {
        address[] memory winners = new address[](randomWords.length);

        for (uint32 i; i < randomWords.length; i++) {
            uint256 randomness = randomWords[i];
            address winner = participants.at(randomness % participants.length());
            uint256 toWinner = wmul(toDistribute, uint256(0.2e18));

            emit WinnerSelected(winner, toWinner);

            winners[i] = winner;
            participants.remove(winner);
            weth.transfer(winner, toWinner);
        }

        for (uint32 i; i < winners.length; i++) {
            participants.add(winners[i]);
        }

        upForGrabs -= toDistribute;

        return true;
    }

    /**
     * @notice Adds a new participant to the pool
     * @dev Only callable by mining contract, checks for duplicates
     * @param _participant Address to add as participant
     */
    function participate(address _participant) external onlyMining noPendingRandomness {
        if (participants.contains(_participant)) return;
        participants.add(_participant);
    }

    /**
     * @notice Adds rewards to the distribution pool
     * @dev Only callable by mining contract
     * @param _amount Amount of WETH to add to rewards
     */
    function distributeRewards(uint256 _amount) external onlyMining {
        upForGrabs += _amount;
    }

    /**
     * @notice Validates caller is mining contract
     * @dev Internal function used by onlyMining modifier
     */
    function _onlyMining() internal view {
        require(msg.sender == mining, OnlyMining());
    }

    /**
     * @notice Checks for pending randomness requests
     * @dev Internal function used by noPendingRandomness modifier
     */
    function _noPendingRandomness() internal view {
        WinnerRequest memory _lastReq = requests[lastRequestId];
        require(lastRequestId == 0 || _lastReq.fulfilled, RandomnessAlreadyRequested());
    }

    /**
     * @notice Validates caller is admin
     * @dev Internal function used by onlyAdmin modifier
     */
    function _onlyAdmin() internal view {
        require(msg.sender == admin, OnlyAdmin());
    }
}
Constants.sol 64 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

// Distribution addresses

address constant DEAD_ADDR = 0x000000000000000000000000000000000000dEaD;
address constant GENESIS_2 = 0xE2A8315e1219361F7fCD2De5872e457bC6A804Fe;
address constant OWNER = 0x5da227386E0FD73329FE3923394913ecA3A624f7;
address constant GENESIS_WALLET = 0x8881243728be21c9DF8804B527fB33085d59CFbF;
address constant FEES_WALLET = 0x248da8895EEda78f41611E4d0960962E74518E93;
address constant LIQUIDITY_BONDING = 0x3eB390EC0816c8ADC22A29379050BeC45Caf9AED;
address constant EDEN_BNB = 0x1681EB21026104Fa63121fD517e065cEc21A4b4C;
address constant LOTUS_BNB = 0xfa7FB02eF0E934c6c2106617eBbbD0b7a01f57D5;

// Percentages in WAD
uint64 constant INCENTIVE_FEE = 0.015e18; //1.5%

uint64 constant TO_BUY_AND_BURN = 0.35e18; // 35%
uint64 constant TO_REWARD_POOLS = 0.2e18; // 20%
uint64 constant TO_CONVERT_TO_WBTC = 0.15e18; // 15%
uint64 constant TO_LAMBO_REWARD = 0.04e18; // 4%
uint64 constant TO_LOTUS_EDEN_BNB = 0.14e18; // 14%
uint64 constant TO_LIQUIDITY_BONDING = 0.04e18; // 4%
uint64 constant TO_GENESIS = 0.06e18; // 6%
uint64 constant TO_GENESIS_2 = 0.02e18; // 2%

uint256 constant FOR_VOLT_TREASURY = 0.168e18; // 16.7%

uint256 constant MIN_SHAO_FOR_WBTC_POOL = 125_000e18;
uint256 constant MIN_SHAO_FOR_LAMBO_POOL = 375_000e18;

// Reward pools distribution
uint64 constant DAY8POOL_DIST = 0.5e18; // 50%
uint64 constant DAY16POOL_DIST = 0.5e18; // 50%

// ERANK BONUSES
uint64 constant MINING_ERANK_25DAYS = 0.03e18; // 3%
uint64 constant MINING_ERANK_50DAYS = 0.08e18; // 8%
uint64 constant MINING_ERANK_75DAYS = 0.13e18; // 13%
uint64 constant MINING_ERANK_100DAYS = 0.18e18; // 18%

uint64 constant STAKING_ERANK_MINIMUM_BONUS = 0.05e18; // 5%
uint64 constant STAKING_ERANK_MAXIMUM_BONUS = 1e18; // 100%

// PRECISION
uint64 constant WAD = 1e18;

// INTERVALS
uint16 constant INTERVAL_TIME = 5 minutes;
uint16 constant INTERVALS_PER_DAY = uint16(24 hours / INTERVAL_TIME);

//UNIV3
uint24 constant POOL_FEE = 10_000; //1%
int16 constant TICK_SPACING = 200; // Uniswap's tick spacing for 1% pools is 200

//LIQUIDITY CONFIG

///@dev The initial ETH amount needed to create liquidity pool
uint96 constant INITIAL_ETH_FOR_LIQ = 1.53e18;

///@dev The intial LOTUS that pairs with WETH
uint96 constant INITIAL_SHAO_FOR_LP = 1_888_888e18;

uint32 constant WINNERS_PER_DRAW = 5;
Time.sol 94 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

library Time {
    ///@notice The cut-off time in seconds from the start of the day for a day turnover, equivalent to 18 hours (64,800 seconds).
    uint32 constant TURN_OVER_TIME = 64800;

    ///@notice The total number of seconds in a day.
    uint32 constant SECONDS_PER_DAY = 86400;

    /**
     * @notice Returns the current block timestamp.
     * @dev This function retrieves the timestamp using assembly for gas efficiency.
     * @return ts The current block timestamp.
     */
    function blockTs() internal view returns (uint32 ts) {
        assembly {
            ts := timestamp()
        }
    }

    /**
     * @notice Calculates the number of weeks passed since a given timestamp.
     * @dev Uses assembly to retrieve the current timestamp and calculates the number of turnover time periods passed.
     * @param t The starting timestamp.
     * @return weeksPassed The number of weeks that have passed since the provided timestamp.
     */
    function weekSince(uint32 t) internal view returns (uint32 weeksPassed) {
        assembly {
            let currentTime := timestamp()
            let timeElapsed := sub(currentTime, t)

            weeksPassed := div(timeElapsed, TURN_OVER_TIME)
        }
    }

    /**
     * @notice Calculates the number of full days between two timestamps.
     * @dev Subtracts the start time from the end time and divides by the seconds per day.
     * @param start The starting timestamp.
     * @param end The ending timestamp.
     * @return daysPassed The number of full days between the two timestamps.
     */
    function dayGap(uint32 start, uint256 end) public pure returns (uint32 daysPassed) {
        assembly {
            daysPassed := div(sub(end, start), SECONDS_PER_DAY)
        }
    }

    /**
     * @notice Get the day count for a timestamp
     * @param t The timestamp from which to get the timestamp
     */
    function dayCountByT(uint32 t) public pure returns (uint32) {
        // Adjust the timestamp to the cut-off time
        uint32 adjustedTime = t - TURN_OVER_TIME;

        // Calculate the number of days since Unix epoch
        return adjustedTime / SECONDS_PER_DAY;
    }

    function weekDayByT(uint32 t) public pure returns (uint8 weekDay) {
        assembly {
            // Subtract 14 hours from the timestamp
            let adjustedTimestamp := sub(t, TURN_OVER_TIME)

            // Divide by the number of seconds in a day (86400)
            let days := div(adjustedTimestamp, SECONDS_PER_DAY)

            // Add 4 to align with weekday and calculate mod 7
            let result := mod(add(days, 4), 7)

            // Store result as uint8
            weekDay := result
        }
    }

    /**
     * @notice Calculates the end of the day at 2 PM UTC based on a given timestamp.
     * @dev Adjusts the provided timestamp by subtracting the turnover time, calculates the next day's timestamp at 2 PM UTC.
     * @param t The starting timestamp.
     * @return nextDayStartAt2PM The timestamp for the next day ending at 2 PM UTC.
     */
    function getDayEnd(uint32 t) public pure returns (uint32 nextDayStartAt2PM) {
        // Adjust the timestamp to the cutoff time (2 PM UTC)
        uint32 adjustedTime = t - TURN_OVER_TIME;

        // Calculate the number of days since Unix epoch
        uint32 daysSinceEpoch = adjustedTime / SECONDS_PER_DAY;

        // Calculate the start of the next day at 2 PM UTC
        nextDayStartAt2PM = (daysSinceEpoch + 1) * SECONDS_PER_DAY + TURN_OVER_TIME;
    }
}
Math.sol 407 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

/* solhint-disable func-visibility, no-inline-assembly */

error Math__toInt256_overflow();
error Math__toUint64_overflow();
error Math__add_overflow_signed();
error Math__sub_overflow_signed();
error Math__mul_overflow_signed();
error Math__mul_overflow();
error Math__div_overflow();

uint256 constant WAD = 1e18;

/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/SafeCastLib.sol#L367
function toInt256(uint256 x) pure returns (int256) {
    if (x >= 1 << 255) revert Math__toInt256_overflow();
    return int256(x);
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/SafeCastLib.sol#L53
function toUint64(uint256 x) pure returns (uint64) {
    if (x >= 1 << 64) revert Math__toUint64_overflow();
    return uint64(x);
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/FixedPointMathLib.sol#L602
function abs(int256 x) pure returns (uint256 z) {
    assembly ("memory-safe") {
        let mask := sub(0, shr(255, x))
        z := xor(mask, add(mask, x))
    }
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/FixedPointMathLib.sol#L620
function min(uint256 x, uint256 y) pure returns (uint256 z) {
    assembly ("memory-safe") {
        z := xor(x, mul(xor(x, y), lt(y, x)))
    }
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/FixedPointMathLib.sol#L628
function min(int256 x, int256 y) pure returns (int256 z) {
    assembly ("memory-safe") {
        z := xor(x, mul(xor(x, y), slt(y, x)))
    }
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/FixedPointMathLib.sol#L636
function max(uint256 x, uint256 y) pure returns (uint256 z) {
    assembly ("memory-safe") {
        z := xor(x, mul(xor(x, y), gt(y, x)))
    }
}

/// @dev Taken from https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/vat.sol#L74
function add(uint256 x, int256 y) pure returns (uint256 z) {
    assembly ("memory-safe") {
        z := add(x, y)
    }
    if ((y > 0 && z < x) || (y < 0 && z > x)) {
        revert Math__add_overflow_signed();
    }
}

/// @dev Taken from https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/vat.sol#L79
function sub(uint256 x, uint256 y) pure returns (uint256 z) {
    assembly ("memory-safe") {
        z := sub(x, y)
    }
    if ((y > 0 && z > x) || (y < 0 && z < x)) {
        revert Math__sub_overflow_signed();
    }
}

/// @dev Taken from https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/vat.sol#L84
function mul(uint256 x, int256 y) pure returns (int256 z) {
    unchecked {
        z = int256(x) * y;
        if (int256(x) < 0 || (y != 0 && z / y != int256(x))) {
            revert Math__mul_overflow_signed();
        }
    }
}

/// @dev Equivalent to `(x * y) / WAD` rounded down.
/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/FixedPointMathLib.sol#L54
function wmul(uint256 x, uint256 y) pure returns (uint256 z) {
    assembly ("memory-safe") {
        // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
        if mul(y, gt(x, div(not(0), y))) {
            // Store the function selector of `Math__mul_overflow()`.
            mstore(0x00, 0xc4c5d7f5)

            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
        z := div(mul(x, y), WAD)
    }
}

function wmul(uint256 x, int256 y) pure returns (int256 z) {
    unchecked {
        z = mul(x, y) / int256(WAD);
    }
}

/// @dev Equivalent to `(x * y) / WAD` rounded up.
/// @dev Taken from https://github.com/Vectorized/solady/blob/969a78905274b32cdb7907398c443f7ea212e4f4/src/utils/FixedPointMathLib.sol#L69C22-L69C22
function wmulUp(uint256 x, uint256 y) pure returns (uint256 z) {
    /// @solidity memory-safe-assembly
    assembly {
        // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
        if mul(y, gt(x, div(not(0), y))) {
            // Store the function selector of `Math__mul_overflow()`.
            mstore(0x00, 0xc4c5d7f5)
            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
        z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
    }
}

/// @dev Equivalent to `(x * WAD) / y` rounded down.
/// @dev Taken from https://github.com/Vectorized/solady/blob/6d706e05ef43cbed234c648f83c55f3a4bb0a520/src/utils/FixedPointMathLib.sol#L84
function wdiv(uint256 x, uint256 y) pure returns (uint256 z) {
    assembly ("memory-safe") {
        // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
        if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
            // Store the function selector of `Math__div_overflow()`.
            mstore(0x00, 0xbcbede65)

            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
        z := div(mul(x, WAD), y)
    }
}

/// @dev Equivalent to `(x * WAD) / y` rounded up.
/// @dev Taken from https://github.com/Vectorized/solady/blob/969a78905274b32cdb7907398c443f7ea212e4f4/src/utils/FixedPointMathLib.sol#L99
function wdivUp(uint256 x, uint256 y) pure returns (uint256 z) {
    /// @solidity memory-safe-assembly
    assembly {
        // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
        if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
            // Store the function selector of `Math__div_overflow()`.
            mstore(0x00, 0xbcbede65)
            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
        z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
    }
}

/// @dev Taken from https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/jug.sol#L62
function wpow(uint256 x, uint256 n, uint256 b) pure returns (uint256 z) {
    unchecked {
        assembly ("memory-safe") {
            switch n
            case 0 { z := b }
            default {
                switch x
                case 0 { z := 0 }
                default {
                    switch mod(n, 2)
                    case 0 { z := b }
                    default { z := x }
                    let half := div(b, 2) // for rounding.
                    for { n := div(n, 2) } n { n := div(n, 2) } {
                        let xx := mul(x, x)
                        if shr(128, x) { revert(0, 0) }
                        let xxRound := add(xx, half)
                        if lt(xxRound, xx) { revert(0, 0) }
                        x := div(xxRound, b)
                        if mod(n, 2) {
                            let zx := mul(z, x)
                            if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }
                            let zxRound := add(zx, half)
                            if lt(zxRound, zx) { revert(0, 0) }
                            z := div(zxRound, b)
                        }
                    }
                }
            }
        }
    }
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/cde0a5fb594da8655ba6bfcdc2e40a7c870c0cc0/src/utils/FixedPointMathLib.sol#L110
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
function wpow(int256 x, int256 y) pure returns (int256) {
    // Using `ln(x)` means `x` must be greater than 0.
    return wexp((wln(x) * y) / int256(WAD));
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/cde0a5fb594da8655ba6bfcdc2e40a7c870c0cc0/src/utils/FixedPointMathLib.sol#L116
/// @dev Returns `exp(x)`, denominated in `WAD`.
function wexp(int256 x) pure returns (int256 r) {
    unchecked {
        // When the result is < 0.5 we return zero. This happens when
        // x <= floor(log(0.5e18) * 1e18) ~ -42e18
        if (x <= -42139678854452767551) return r;

        /// @solidity memory-safe-assembly
        assembly {
            // When the result is > (2**255 - 1) / 1e18 we can not represent it as an
            // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
            if iszero(slt(x, 135305999368893231589)) {
                mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                revert(0x1c, 0x04)
            }
        }

        // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
        // for more intermediate precision and a binary basis. This base conversion
        // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
        x = (x << 78) / 5 ** 18;

        // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
        // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
        // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
        int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
        x = x - k * 54916777467707473351141471128;

        // k is in the range [-61, 195].

        // Evaluate using a (6, 7)-term rational approximation.
        // p is made monic, we'll multiply by a scale factor later.
        int256 y = x + 1346386616545796478920950773328;
        y = ((y * x) >> 96) + 57155421227552351082224309758442;
        int256 p = y + x - 94201549194550492254356042504812;
        p = ((p * y) >> 96) + 28719021644029726153956944680412240;
        p = p * x + (4385272521454847904659076985693276 << 96);

        // We leave p in 2**192 basis so we don't need to scale it back up for the division.
        int256 q = x - 2855989394907223263936484059900;
        q = ((q * x) >> 96) + 50020603652535783019961831881945;
        q = ((q * x) >> 96) - 533845033583426703283633433725380;
        q = ((q * x) >> 96) + 3604857256930695427073651918091429;
        q = ((q * x) >> 96) - 14423608567350463180887372962807573;
        q = ((q * x) >> 96) + 26449188498355588339934803723976023;

        /// @solidity memory-safe-assembly
        assembly {
            // Div in assembly because solidity adds a zero check despite the unchecked.
            // The q polynomial won't have zeros in the domain as all its roots are complex.
            // No scaling is necessary because p is already 2**96 too large.
            r := sdiv(p, q)
        }

        // r should be in the range (0.09, 0.25) * 2**96.

        // We now need to multiply r by:
        // * the scale factor s = ~6.031367120.
        // * the 2**k factor from the range reduction.
        // * the 1e18 / 2**96 factor for base conversion.
        // We do this all at once, with an intermediate result in 2**213
        // basis, so the final right shift is always by a positive amount.
        r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
    }
}

/// @dev Taken from https://github.com/Vectorized/solady/blob/cde0a5fb594da8655ba6bfcdc2e40a7c870c0cc0/src/utils/FixedPointMathLib.sol#L184
/// @dev Returns `ln(x)`, denominated in `WAD`.
function wln(int256 x) pure returns (int256 r) {
    unchecked {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
        }

        // We want to convert x from 10**18 fixed point to 2**96 fixed point.
        // We do this by multiplying by 2**96 / 10**18. But since
        // ln(x * C) = ln(x) + ln(C), we can simply do nothing here
        // and add ln(2**96 / 10**18) at the end.

        // Compute k = log2(x) - 96, t = 159 - k = 255 - log2(x) = 255 ^ log2(x).
        int256 t;
        /// @solidity memory-safe-assembly
        assembly {
            t := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            t := or(t, shl(6, lt(0xffffffffffffffff, shr(t, x))))
            t := or(t, shl(5, lt(0xffffffff, shr(t, x))))
            t := or(t, shl(4, lt(0xffff, shr(t, x))))
            t := or(t, shl(3, lt(0xff, shr(t, x))))
            // forgefmt: disable-next-item
            t := xor(
                t,
                byte(
                    and(
                        0x1f,
                        shr(shr(t, x), 0x8421084210842108cc6318c6db6d54be)
                    ),
                    0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff
                )
            )
        }

        // Reduce range of x to (1, 2) * 2**96
        // ln(2^k * x) = k * ln(2) + ln(x)
        x = int256(uint256(x << uint256(t)) >> 159);

        // Evaluate using a (8, 8)-term rational approximation.
        // p is made monic, we will multiply by a scale factor later.
        int256 p = x + 3273285459638523848632254066296;
        p = ((p * x) >> 96) + 24828157081833163892658089445524;
        p = ((p * x) >> 96) + 43456485725739037958740375743393;
        p = ((p * x) >> 96) - 11111509109440967052023855526967;
        p = ((p * x) >> 96) - 45023709667254063763336534515857;
        p = ((p * x) >> 96) - 14706773417378608786704636184526;
        p = p * x - (795164235651350426258249787498 << 96);

        // We leave p in 2**192 basis so we don't need to scale it back up for the division.
        // q is monic by convention.
        int256 q = x + 5573035233440673466300451813936;
        q = ((q * x) >> 96) + 71694874799317883764090561454958;
        q = ((q * x) >> 96) + 283447036172924575727196451306956;
        q = ((q * x) >> 96) + 401686690394027663651624208769553;
        q = ((q * x) >> 96) + 204048457590392012362485061816622;
        q = ((q * x) >> 96) + 31853899698501571402653359427138;
        q = ((q * x) >> 96) + 909429971244387300277376558375;
        /// @solidity memory-safe-assembly
        assembly {
            // Div in assembly because solidity adds a zero check despite the unchecked.
            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already 2**96 too large.
            r := sdiv(p, q)
        }

        // r is in the range (0, 0.125) * 2**96

        // Finalization, we need to:
        // * multiply by the scale factor s = 5.549…
        // * add ln(2**96 / 10**18)
        // * add k * ln(2)
        // * multiply by 10**18 / 2**96 = 5**18 >> 78

        // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
        r *= 1677202110996718588342820967067443963516166;
        // add ln(2) * k * 5e18 * 2**192
        r += 16597577552685614221487285958193947469193820559219878177908093499208371 * (159 - t);
        // add ln(2**96 / 10**18) * 5e18 * 2**192
        r += 600920179829731861736702779321621459595472258049074101567377883020018308;
        // base conversion: mul 2**18 / 2**192
        r >>= 174;
    }
}

/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) pure returns (uint256 z) {
    /// @solidity memory-safe-assembly
    assembly {
        // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
        z := 181 // The "correct" value is 1, but this saves a multiplication later.

        // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
        // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

        // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
        // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
        let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
        r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
        r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
        r := or(r, shl(4, lt(0xffffff, shr(r, x))))
        z := shl(shr(1, r), z)

        // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
        // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
        // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
        // That's not possible if `x < 256` but we can just verify those cases exhaustively.

        // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
        // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
        // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

        // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
        // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
        // with largest error when `s = 1` and when `s = 256` or `1/256`.

        // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
        // Then we can estimate `sqrt(y)` using
        // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

        // There is no overflow risk here since `y < 2**136` after the first branch above.
        z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

        // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
        z := shr(1, add(z, div(x, z)))
        z := shr(1, add(z, div(x, z)))
        z := shr(1, add(z, div(x, z)))
        z := shr(1, add(z, div(x, z)))
        z := shr(1, add(z, div(x, z)))
        z := shr(1, add(z, div(x, z)))
        z := shr(1, add(z, div(x, z)))

        // If `x+1` is a perfect square, the Babylonian method cycles between
        // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
        // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
        z := sub(z, lt(div(x, z), z))
    }
}
Errors.sol 78 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

contract Errors {
    /// @notice Error thrown when an address is the zero address.
    error Address0();

    /// @notice Error thrown when an amount is zero.
    error Amount0();

    /// @notice Error thrown when an operation is attempted after the specified deadline.
    error Expired();

    /// @notice Error thrown when bytes32 is 0
    error Bytes0();

    /// @notice Error thrown when one value is greater than another.
    /// @param a The first value that is greater than the second value.
    /// @param b The second value which is smaller or equal to the first value.
    error GreaterThan(uint256 a, uint256 b);

    /**
     * @notice Modifier to prevent operations with a zero amount.
     * @dev Throws an `Amount0` error if the provided amount is zero.
     * @param a The amount to be checked.
     */
    modifier notAmount0(uint256 a) {
        _notAmount0(a);
        _;
    }

    /**
     * @notice Modifier to ensure a function is called before a specified deadline.
     * @dev Throws an `Expired` error if the current block timestamp exceeds the provided deadline.
     * @param _deadline The deadline timestamp by which the function must be called.
     */
    modifier notExpired(uint32 _deadline) {
        _notExpired(_deadline);
        _;
    }

    /**
     * @notice Modifier to prevent operations with the zero address.
     * @dev Throws an `Address0` error if the provided address is the zero address.
     * @param a The address to be checked.
     */
    modifier notAddress0(address a) {
        _notAddress0(a);
        _;
    }

    /**
     * @notice Modifier to ensure the first value is not greater than the second value.
     * @dev Throws a `GreaterThan` error if `b` is smaller than `a`.
     * @param a The first value to be compared.
     * @param b The second value to be compared.
     */
    modifier notGt(uint256 a, uint256 b) {
        _notGt(a, b);
        _;
    }

    function _notAddress0(address a) internal pure {
        require(a != address(0), Address0());
    }

    function _notAmount0(uint256 a) internal pure {
        require(a != 0, Amount0());
    }

    function _notGt(uint256 a, uint256 b) internal pure {
        require(b >= a, GreaterThan(a, b));
    }

    function _notExpired(uint32 _deadline) internal view {
        require(block.timestamp <= _deadline, Expired());
    }
}
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
EnumerableSet.sol 375 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}
VRFV2PlusClient.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

// End consumer library.
library VRFV2PlusClient {
  // extraArgs will evolve to support new features
  bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));
  struct ExtraArgsV1 {
    bool nativePayment;
  }

  struct RandomWordsRequest {
    bytes32 keyHash;
    uint256 subId;
    uint16 requestConfirmations;
    uint32 callbackGasLimit;
    uint32 numWords;
    bytes extraArgs;
  }

  function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
    return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
  }
}
VRFConsumerBaseV2Plus.sol 165 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IVRFMigratableConsumerV2Plus} from "./interfaces/IVRFMigratableConsumerV2Plus.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";

/** ****************************************************************************
 * @notice Interface for contracts using VRF randomness
 * *****************************************************************************
 * @dev PURPOSE
 *
 * @dev Reggie the Random Oracle (not his real job) wants to provide randomness
 * @dev to Vera the verifier in such a way that Vera can be sure he's not
 * @dev making his output up to suit himself. Reggie provides Vera a public key
 * @dev to which he knows the secret key. Each time Vera provides a seed to
 * @dev Reggie, he gives back a value which is computed completely
 * @dev deterministically from the seed and the secret key.
 *
 * @dev Reggie provides a proof by which Vera can verify that the output was
 * @dev correctly computed once Reggie tells it to her, but without that proof,
 * @dev the output is indistinguishable to her from a uniform random sample
 * @dev from the output space.
 *
 * @dev The purpose of this contract is to make it easy for unrelated contracts
 * @dev to talk to Vera the verifier about the work Reggie is doing, to provide
 * @dev simple access to a verifiable source of randomness. It ensures 2 things:
 * @dev 1. The fulfillment came from the VRFCoordinatorV2Plus.
 * @dev 2. The consumer contract implements fulfillRandomWords.
 * *****************************************************************************
 * @dev USAGE
 *
 * @dev Calling contracts must inherit from VRFConsumerBaseV2Plus, and can
 * @dev initialize VRFConsumerBaseV2Plus's attributes in their constructor as
 * @dev shown:
 *
 * @dev   contract VRFConsumerV2Plus is VRFConsumerBaseV2Plus {
 * @dev     constructor(<other arguments>, address _vrfCoordinator, address _subOwner)
 * @dev       VRFConsumerBaseV2Plus(_vrfCoordinator, _subOwner) public {
 * @dev         <initialization with other arguments goes here>
 * @dev       }
 * @dev   }
 *
 * @dev The oracle will have given you an ID for the VRF keypair they have
 * @dev committed to (let's call it keyHash). Create a subscription, fund it
 * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
 * @dev subscription management functions).
 * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
 * @dev callbackGasLimit, numWords, extraArgs),
 * @dev see (IVRFCoordinatorV2Plus for a description of the arguments).
 *
 * @dev Once the VRFCoordinatorV2Plus has received and validated the oracle's response
 * @dev to your request, it will call your contract's fulfillRandomWords method.
 *
 * @dev The randomness argument to fulfillRandomWords is a set of random words
 * @dev generated from your requestId and the blockHash of the request.
 *
 * @dev If your contract could have concurrent requests open, you can use the
 * @dev requestId returned from requestRandomWords to track which response is associated
 * @dev with which randomness request.
 * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
 * @dev if your contract could have multiple requests in flight simultaneously.
 *
 * @dev Colliding `requestId`s are cryptographically impossible as long as seeds
 * @dev differ.
 *
 * *****************************************************************************
 * @dev SECURITY CONSIDERATIONS
 *
 * @dev A method with the ability to call your fulfillRandomness method directly
 * @dev could spoof a VRF response with any random value, so it's critical that
 * @dev it cannot be directly called by anything other than this base contract
 * @dev (specifically, by the VRFConsumerBaseV2Plus.rawFulfillRandomness method).
 *
 * @dev For your users to trust that your contract's random behavior is free
 * @dev from malicious interference, it's best if you can write it so that all
 * @dev behaviors implied by a VRF response are executed *during* your
 * @dev fulfillRandomness method. If your contract must store the response (or
 * @dev anything derived from it) and use it later, you must ensure that any
 * @dev user-significant behavior which depends on that stored value cannot be
 * @dev manipulated by a subsequent VRF request.
 *
 * @dev Similarly, both miners and the VRF oracle itself have some influence
 * @dev over the order in which VRF responses appear on the blockchain, so if
 * @dev your contract could have multiple VRF requests in flight simultaneously,
 * @dev you must ensure that the order in which the VRF responses arrive cannot
 * @dev be used to manipulate your contract's user-significant behavior.
 *
 * @dev Since the block hash of the block which contains the requestRandomness
 * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
 * @dev miner could, in principle, fork the blockchain to evict the block
 * @dev containing the request, forcing the request to be included in a
 * @dev different block with a different hash, and therefore a different input
 * @dev to the VRF. However, such an attack would incur a substantial economic
 * @dev cost. This cost scales with the number of blocks the VRF oracle waits
 * @dev until it calls responds to a request. It is for this reason that
 * @dev that you can signal to an oracle you'd like them to wait longer before
 * @dev responding to the request (however this is not enforced in the contract
 * @dev and so remains effective only in the case of unmodified oracle software).
 */
abstract contract VRFConsumerBaseV2Plus is IVRFMigratableConsumerV2Plus, ConfirmedOwner {
  error OnlyCoordinatorCanFulfill(address have, address want);
  error OnlyOwnerOrCoordinator(address have, address owner, address coordinator);
  error ZeroAddress();

  // s_vrfCoordinator should be used by consumers to make requests to vrfCoordinator
  // so that coordinator reference is updated after migration
  IVRFCoordinatorV2Plus public s_vrfCoordinator;

  /**
   * @param _vrfCoordinator address of VRFCoordinator contract
   */
  constructor(address _vrfCoordinator) ConfirmedOwner(msg.sender) {
    if (_vrfCoordinator == address(0)) {
      revert ZeroAddress();
    }
    s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
  }

  /**
   * @notice fulfillRandomness handles the VRF response. Your contract must
   * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
   * @notice principles to keep in mind when implementing your fulfillRandomness
   * @notice method.
   *
   * @dev VRFConsumerBaseV2Plus expects its subcontracts to have a method with this
   * @dev signature, and will call it once it has verified the proof
   * @dev associated with the randomness. (It is triggered via a call to
   * @dev rawFulfillRandomness, below.)
   *
   * @param requestId The Id initially returned by requestRandomness
   * @param randomWords the VRF output expanded to the requested number of words
   */
  // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
  function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;

  // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
  // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
  // the origin of the call
  function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
    if (msg.sender != address(s_vrfCoordinator)) {
      revert OnlyCoordinatorCanFulfill(msg.sender, address(s_vrfCoordinator));
    }
    fulfillRandomWords(requestId, randomWords);
  }

  /**
   * @inheritdoc IVRFMigratableConsumerV2Plus
   */
  function setCoordinator(address _vrfCoordinator) external override onlyOwnerOrCoordinator {
    if (_vrfCoordinator == address(0)) {
      revert ZeroAddress();
    }
    s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);

    emit CoordinatorSet(_vrfCoordinator);
  }

  modifier onlyOwnerOrCoordinator() {
    if (msg.sender != owner() && msg.sender != address(s_vrfCoordinator)) {
      revert OnlyOwnerOrCoordinator(msg.sender, owner(), address(s_vrfCoordinator));
    }
    _;
  }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
IVRFCoordinatorV2Plus.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFSubscriptionV2Plus} from "./IVRFSubscriptionV2Plus.sol";

// Interface that enables consumers of VRFCoordinatorV2Plus to be future-proof for upgrades
// This interface is supported by subsequent versions of VRFCoordinatorV2Plus
interface IVRFCoordinatorV2Plus is IVRFSubscriptionV2Plus {
  /**
   * @notice Request a set of random words.
   * @param req - a struct containing following fields for randomness request:
   * keyHash - Corresponds to a particular oracle job which uses
   * that key for generating the VRF proof. Different keyHash's have different gas price
   * ceilings, so you can select a specific one to bound your maximum per request cost.
   * subId  - The ID of the VRF subscription. Must be funded
   * with the minimum subscription balance required for the selected keyHash.
   * requestConfirmations - How many blocks you'd like the
   * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
   * for why you may want to request more. The acceptable range is
   * [minimumRequestBlockConfirmations, 200].
   * callbackGasLimit - How much gas you'd like to receive in your
   * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
   * may be slightly less than this amount because of gas used calling the function
   * (argument decoding etc.), so you may need to request slightly more than you expect
   * to have inside fulfillRandomWords. The acceptable range is
   * [0, maxGasLimit]
   * numWords - The number of uint256 random values you'd like to receive
   * in your fulfillRandomWords callback. Note these numbers are expanded in a
   * secure way by the VRFCoordinator from a single random value supplied by the oracle.
   * extraArgs - abi-encoded extra args
   * @return requestId - A unique identifier of the request. Can be used to match
   * a request to a response in fulfillRandomWords.
   */
  function requestRandomWords(VRFV2PlusClient.RandomWordsRequest calldata req) external returns (uint256 requestId);
}
IVRFMigratableConsumerV2Plus.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice The IVRFMigratableConsumerV2Plus interface defines the
/// @notice method required to be implemented by all V2Plus consumers.
/// @dev This interface is designed to be used in VRFConsumerBaseV2Plus.
interface IVRFMigratableConsumerV2Plus {
  event CoordinatorSet(address vrfCoordinator);

  /// @notice Sets the VRF Coordinator address
  /// @notice This method should only be callable by the coordinator or contract owner
  function setCoordinator(address vrfCoordinator) external;
}
ConfirmedOwner.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";

/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
  constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
IVRFSubscriptionV2Plus.sol 98 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice The IVRFSubscriptionV2Plus interface defines the subscription
/// @notice related methods implemented by the V2Plus coordinator.
interface IVRFSubscriptionV2Plus {
  /**
   * @notice Add a consumer to a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - New consumer which can use the subscription
   */
  function addConsumer(uint256 subId, address consumer) external;

  /**
   * @notice Remove a consumer from a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - Consumer to remove from the subscription
   */
  function removeConsumer(uint256 subId, address consumer) external;

  /**
   * @notice Cancel a subscription
   * @param subId - ID of the subscription
   * @param to - Where to send the remaining LINK to
   */
  function cancelSubscription(uint256 subId, address to) external;

  /**
   * @notice Accept subscription owner transfer.
   * @param subId - ID of the subscription
   * @dev will revert if original owner of subId has
   * not requested that msg.sender become the new owner.
   */
  function acceptSubscriptionOwnerTransfer(uint256 subId) external;

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @param newOwner - proposed new owner of the subscription
   */
  function requestSubscriptionOwnerTransfer(uint256 subId, address newOwner) external;

  /**
   * @notice Create a VRF subscription.
   * @return subId - A unique subscription id.
   * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
   * @dev Note to fund the subscription with LINK, use transferAndCall. For example
   * @dev  LINKTOKEN.transferAndCall(
   * @dev    address(COORDINATOR),
   * @dev    amount,
   * @dev    abi.encode(subId));
   * @dev Note to fund the subscription with Native, use fundSubscriptionWithNative. Be sure
   * @dev  to send Native with the call, for example:
   * @dev COORDINATOR.fundSubscriptionWithNative{value: amount}(subId);
   */
  function createSubscription() external returns (uint256 subId);

  /**
   * @notice Get a VRF subscription.
   * @param subId - ID of the subscription
   * @return balance - LINK balance of the subscription in juels.
   * @return nativeBalance - native balance of the subscription in wei.
   * @return reqCount - Requests count of subscription.
   * @return owner - owner of the subscription.
   * @return consumers - list of consumer address which are able to use this subscription.
   */
  function getSubscription(
    uint256 subId
  )
    external
    view
    returns (uint96 balance, uint96 nativeBalance, uint64 reqCount, address owner, address[] memory consumers);

  /*
   * @notice Check to see if there exists a request commitment consumers
   * for all consumers and keyhashes for a given sub.
   * @param subId - ID of the subscription
   * @return true if there exists at least one unfulfilled request for the subscription, false
   * otherwise.
   */
  function pendingRequestExists(uint256 subId) external view returns (bool);

  /**
   * @notice Paginate through all active VRF subscriptions.
   * @param startIndex index of the subscription to start from
   * @param maxCount maximum number of subscriptions to return, 0 to return all
   * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one
   * @dev should consider keeping the blockheight constant to ensure a holistic picture of the contract state
   */
  function getActiveSubscriptionIds(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory);

  /**
   * @notice Fund a subscription with native.
   * @param subId - ID of the subscription
   * @notice This method expects msg.value to be greater than or equal to 0.
   */
  function fundSubscriptionWithNative(uint256 subId) external payable;
}
ConfirmedOwnerWithProposal.sol 68 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IOwnable} from "../interfaces/IOwnable.sol";

/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
  address private s_owner;
  address private s_pendingOwner;

  event OwnershipTransferRequested(address indexed from, address indexed to);
  event OwnershipTransferred(address indexed from, address indexed to);

  constructor(address newOwner, address pendingOwner) {
    // solhint-disable-next-line gas-custom-errors
    require(newOwner != address(0), "Cannot set owner to zero");

    s_owner = newOwner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /// @notice Allows an owner to begin transferring ownership to a new address.
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /// @notice Allows an ownership transfer to be completed by the recipient.
  function acceptOwnership() external override {
    // solhint-disable-next-line gas-custom-errors
    require(msg.sender == s_pendingOwner, "Must be proposed owner");

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /// @notice Get the current owner
  function owner() public view override returns (address) {
    return s_owner;
  }

  /// @notice validate, transfer ownership, and emit relevant events
  function _transferOwnership(address to) private {
    // solhint-disable-next-line gas-custom-errors
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /// @notice validate access
  function _validateOwnership() internal view {
    // solhint-disable-next-line gas-custom-errors
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /// @notice Reverts if called by anyone other than the contract owner.
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}
IOwnable.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IOwnable {
  function owner() external returns (address);

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

Read Contract

COOLDOWN_PER_DRAW 0xfee439d8 → uint256
admin 0xf851a440 → address
callbackGasLimit 0x24f74697 → uint32
isParticipant 0x929066f5 → bool
keyHash 0x61728f39 → bytes32
lastIntervalCall 0xbb20995a → uint32
lastRequestId 0xfc2a88c3 → uint256
mining 0x662fac39 → address
owner 0x8da5cb5b → address
requestConfirmations 0xb0fb162f → uint16
requests 0x81d12c58 → uint256, bool
s_vrfCoordinator 0x9eccacf6 → address
subscriptionId 0x09c1ba2e → uint256
upForGrabs 0x1df47acc → uint256
weth 0x3fc8cef3 → address

Write Contract 11 functions

These functions modify contract state and require a wallet transaction to execute.

acceptOwnership 0x79ba5097
No parameters
changeCallBackGasLimimt 0xde5b24c9
uint32 _newGasLimit
changeKeyHash 0x2f68f482
bytes32 _newKeyHash
changeRequestConfirmations 0xf50d390d
uint16 _newRequestConfirmations
distributeRewards 0x59974e38
uint256 _amount
participate 0xb91038c7
address _participant
pickWinner 0x5d495aea
No parameters
returns: uint256
rawFulfillRandomWords 0x1fe543e3
uint256 requestId
uint256[] randomWords
setCoordinator 0x8ea98117
address _vrfCoordinator
transferOwnership 0xf2fde38b
address to
unblockPendingRequest 0x7d234e22
No parameters

Recent Transactions

No transactions found for this address