Address Contract Partially Verified
Address
0xd7b9996bA0a6Af9748e01e9E1CBeaaE09A6A780c
Balance
0 ETH
Nonce
1
Code Size
5338 bytes
Creator
0xEfD63Ad2...8B14 at tx 0x33c2fb1e...369b67
Indexed Transactions
0
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