Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xe2A1499ca2caD493b2fA25206Faa150cA5396212
Balance 0 ETH
Nonce 1
Code Size 7766 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

7766 bytes
0x6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c8063019948851461190757806303b4a479146118e957806303c669e3146118bd57806303ebbf341461189f57806308ac900a1461188157806309c1ba2e146118635780630a0090971461183a57806318643d371461181c5780631a96ab2b146117fb5780631bfce853146117d55780631cecfaad146116af5780631fe543e31461148e57806324f746971461146a57806327b3bf111461144c5780632866ed211461142957806329687c53146114035780632a29e67a146113a157806332b7ac641461138357806334fccc1d1461136557806336ffc8b0146113475780633b3041471461117557806348a93a0d1461113f5780634c846e46146110fd5780634e71d92d1461105b57806352bca4c31461103d578063532f11791461101757806353d1f59d14610fb557806355a16c2414610f97578063595d221314610f795780635bf8633a14610f4c5780635d0eaaaf14610edb57806361728f3914610ebe578063715018a614610e655780637313ee5a14610e47578063790ca41314610e295780637d7af28014610e0b5780637d7bc63914610dda5780638007634f14610c815780638098004314610c6057806380c028a814610ad8578063856ed8c914610a935780638c52dc4114610a675780638da5cb5b14610a3e5780639755e04e146109ff57806398544710146109de5780639b27349d146108695780639d76ea5814610840578063a277d5b114610822578063a4bfb13014610804578063b0fb162f146107e0578063b2af127c1461071a578063baa39fbe146106db578063bcf257291461067a578063c94028c21461065c578063d1abca7a1461063e578063d85349f714610620578063db584d25146105db578063dbe65f59146105bd578063e086e5ec14610592578063e259f636146104f5578063e365d863146104d7578063ea28edad146103f7578063ec9cd924146103d4578063ee7d72b4146103a2578063f2fde38b146103195763f8d867940361000e5734610314576000366003190112610314576020601554604051908152f35b600080fd5b34610314576020366003190112610314576103326119b3565b61033a611c2c565b6001600160a01b0316801561038c57600080546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b600052600060045260246000fd5b34610314576020366003190112610314576103bb611c2c565b6021805463ffffffff191660043563ffffffff16179055005b3461031457600036600319011261031457602060ff601d54166040519015158152f35b3461031457600036600319011261031457610410611c2c565b600f54620189c081018091116104c15742106104b057600160ff196010541617601055426011556000602060018060a01b03602254166064600d5460405194859384926323b872dd60e01b845233600485015230602485015260448401525af180156104a45761047c57005b6100199060203d60201161049d575b6104958183611979565b810190611bf5565b503d61048b565b6040513d6000823e3d90fd5b63ae20f3a560e01b60005260046000fd5b634e487b7160e01b600052601160045260246000fd5b34610314576000366003190112610314576020600e54604051908152f35b346103145760003660031901126103145760ff601d5460101c16156105815733600090815260016020526040902060048101546003909101546105459161053c9190611ae8565b60155490611ad5565b6103cf8102908082046103cf14901517156104c157600080806103e88194338352600160205282600460408220015504335af150610019611af5565b633f54562b60e11b60005260046000fd5b34610314576000366003190112610314576105ab611c2c565b600080808047335af150610019611af5565b34610314576000366003190112610314576020601354604051908152f35b34610314576000366003190112610314576040517f00000000000000000000000000000000000000447e69651d841bd8d104bed4936001600160a01b03168152602090f35b34610314576000366003190112610314576020601b54604051908152f35b34610314576000366003190112610314576020600c54604051908152f35b34610314576000366003190112610314576020601654604051908152f35b346103145760e036600319011261031457610693611c2c565b600f546106ca5742600f5560043560185560243560195560443560125560643560135560843560145560a43560155560c435601655005b6319f4db0f60e31b60005260046000fd5b34610314576020366003190112610314576001600160a01b036106fc6119b3565b166000526004602052602060ff604060002054166040519015158152f35b34610314576020366003190112610314576107336119b3565b61073b611c2c565b6040516370a0823160e01b8152306004820152906001600160a01b0316602082602481845afa9182156104a4576000926107aa575b5060405163a9059cbb60e01b81523360048201526024810192909252602090829081600081604481015b03925af180156104a45761047c57005b91506020823d6020116107d8575b816107c560209383611979565b810103126103145790519061079a610770565b3d91506107b8565b3461031457600036600319011261031457602061ffff602154821c16604051908152f35b34610314576000366003190112610314576020600954604051908152f35b34610314576000366003190112610314576020600d54604051908152f35b34610314576000366003190112610314576022546040516001600160a01b039091168152602090f35b602036600319011261031457600435600f5461384081018082116104c15742109081156109c7575b506104b057601d5460ff8160081c161561098b575b506108b381601554611ad5565b340361097a573360005260016020526108d481600460406000200154611ac8565b601754106109695733600052600160205260046040600020016108f8828254611ac8565b905560005b81811061090657005b60055490680100000000000000008210156109535761094d61092f836001809501600555611a66565b81546001600160a01b0360039290921b91821b19163390911b179055565b016108fd565b634e487b7160e01b600052604160045260246000fd5b63b1c0823160e01b60005260046000fd5b6399b5cb1d60e01b60005260046000fd5b610100906109b56109ad6008546109a7600954600a5490611ac8565b90611ae8565b600b54611ac8565b600b5561ff00191617601d55816108a6565b9050620189c081018091116104c157421182610891565b34610314576020366003190112610314576109f7611c2c565b600435602055005b34610314576020366003190112610314576001600160a01b03610a206119b3565b166000526003602052602060ff604060002054166040519015158152f35b34610314576000366003190112610314576000546040516001600160a01b039091168152602090f35b3461031457600036600319011261031457610a80611c2c565b601d805462ff0000191662010000179055005b34610314576000366003190112610314576040517f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b6001600160a01b03168152602090f35b60403660031901126103145760043567ffffffffffffffff811161031457610b04903690600401611a97565b60243567ffffffffffffffff811161031457610b24903690600401611a97565b919092600f54611c2081018091116104c15742116104b0576000805b848110610c405750340361097a5760005b828110610b6957610b6434600e54611ac8565b600e55005b610b74818484611c0d565b3590610b81818688611c0d565b35610b8b83611c55565b15610c2f57601854610b9c91611ad5565b91806000526002602052610bb583604060002054611ac8565b6012541061096957610bc983600754611ac8565b6006541061096957600192610c269133600052846020526002604060002001610bf3838254611ac8565b905560005260026020526040600020610c0d828254611ac8565b9055610c1b81600754611ac8565b600755600d54611ac8565b600d5501610b51565b635f68eddf60e11b60005260046000fd5b90610c59600191610c5284888a611c0d565b3590611ac8565b9101610b40565b3461031457602036600319011261031457610c79611c2c565b600435601e55005b600036600319011261031457600f54611c2081018082116104c1574210908115610dc4575b506104b057601d5460ff811615610d96575b5033600052600460205260ff6040600020541615610d8557610cdc60195434611ad5565b336000526001602052610cf781600160406000200154611ac8565b6014541061096957610d1781610d12600954600a5490611ac8565b611ac8565b6008541061096957610d76903360005260016020526001604060002001610d3f828254611ac8565b90553360005260016020526002604060002001610d5d828254611ac8565b9055610d6b81600a54611ac8565b600a55600d54611ac8565b600d55610b6434600e54611ac8565b634f5fb6c960e11b60005260046000fd5b600190610db3610dab60065460075490611ae8565b600854611ac8565b60085560ff191617601d5580610cb8565b905061384081018091116104c157421181610ca6565b3461031457600036600319011261031457610df3611c2c565b6000808080600e5481600e55335af150610019611af5565b34610314576000366003190112610314576020601854604051908152f35b34610314576000366003190112610314576020600f54604051908152f35b34610314576000366003190112610314576020601a54604051908152f35b3461031457600036600319011261031457610e7e611c2c565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103145760003660031901126103145760208054604051908152f35b34610314576020366003190112610314576001600160a01b03610efc6119b3565b16600052600160205260c0604060002080549060018101549060028101546003820154906005600484015493015493604051958652602086015260408501526060840152608083015260a0820152f35b346103145760003660031901126103145760215460405160309190911c6001600160a01b03168152602090f35b34610314576000366003190112610314576020600854604051908152f35b34610314576000366003190112610314576020600654604051908152f35b3461031457610fc3366119c9565b90610fcc611c2c565b60005b8151811015610019576001906001600160a01b03610fed8285611be1565b511660005260046020526110118460406000209060ff801983541691151516179055565b01610fcf565b3461031457600036600319011261031457602060ff601d5460101c166040519015158152f35b34610314576000366003190112610314576020601254604051908152f35b346103145760003660031901126103145760ff6010541615610581573360005260016020526000602061079a6110976002604085200154611b35565b338452600183526110af600560408620015482611ae8565b3380865260018552604080872060050193909355602254925163a9059cbb60e01b8152600481019190915260248101919091529384926001600160a01b039092169183919082906044820190565b346103145760203660031901126103145760043560055481101561031457611126602091611a66565b905460405160039290921b1c6001600160a01b03168152f35b3461031457602036600319011261031457611158611c2c565b600f5461384081018091116104c15742116104b057600435601555005b3461031457602036600319011261031457600435611191611c2c565b600f54620189c081018091116104c15742106104b0576111bf6111b660165483611ad5565b600c5490611ac8565b600b541061096957602154906040516020810181811067ffffffffffffffff82111761095357604052600081526040519063125fa26760e31b6020830152511515602482015260248152611214604482611979565b602054601e54926040519460c086019086821067ffffffffffffffff831117610953579391869363ffffffff9260405284526020840195865281604085019561ffff8160201c16875281606087019116815281608087019316835260a0860194855261ffff60018060a01b03601f541697604051998a98634d8e1c2f60e11b8a52602060048b01525160248a01525160448901525116606487015251166084850152511660a48301525160c060c48301528051908160e484015260005b82811061132e575050918160006101048286838360209984010152601f801991011681010301925af180156104a45761130657005b6100199060203d602011611327575b61131f8183611979565b810190611c1d565b503d611315565b60208282018101516101048884010152869450016112d1565b34610314576000366003190112610314576020600754604051908152f35b34610314576000366003190112610314576020601c54604051908152f35b34610314576000366003190112610314576020601454604051908152f35b34610314576113af366119c9565b906113b8611c2c565b60005b8151811015610019576001906001600160a01b036113d98285611be1565b511660005260036020526113fd8460406000209060ff801983541691151516179055565b016113bb565b3461031457600036600319011261031457602060ff601d5460081c166040519015158152f35b3461031457600036600319011261031457602060ff601054166040519015158152f35b34610314576000366003190112610314576020601154604051908152f35b3461031457600036600319011261031457602063ffffffff60215416604051908152f35b346103145760403660031901126103145760243567ffffffffffffffff81116103145736602382011215610314578060040135906114cb8261199b565b916114d96040519384611979565b8083526024602084019160051b8301019136831161031457602401905b82821061169f57837f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b031633819003611687575060005b8151811015610019576115478183611be1565b51600554908115611671570661155c81611a66565b60018060a01b0391549060031b1c1660165481600052600160205261158a6002604060002001918254611ac8565b90556115a960165461159e81600d54611ac8565b600d55600c54611ac8565b600c5560005260016020526003604060002001805490600182018092116104c157556005546000198101919082116104c1576116026115ea61162693611a66565b905460039190911b1c6001600160a01b031691611a66565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b60055490811561165b576001916000190161164081611a66565b815490858060a01b039060031b1b1916905560055501611534565b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b63073e64fd60e21b6000523360045260245260446000fd5b81358152602091820191016114f6565b600036600319011261031457600f54611c2081018082116104c15742109081156117bf575b506104b057601d5460ff811615611799575b5033600052600360205260ff6040600020541615610d855761170a60195434611ad5565b33600052600160205261172281604060002054611ac8565b601354106109695761173d81610d12600954600a5490611ac8565b6008541061096957610d76903360005260016020526040600020611762828254611ac8565b90553360005260016020526002604060002001611780828254611ac8565b905561178e81600954611ac8565b600955600d54611ac8565b6001906117ae610dab60065460075490611ae8565b60085560ff191617601d55806116e6565b905061384081018091116104c1574211816116d4565b346103145760203660031901126103145760206117f3600435611b35565b604051908152f35b3461031457602036600319011261031457611814611c2c565b600435601655005b34610314576000366003190112610314576020601754604051908152f35b3461031457600036600319011261031457601f546040516001600160a01b039091168152602090f35b34610314576000366003190112610314576020601e54604051908152f35b34610314576000366003190112610314576020601954604051908152f35b34610314576000366003190112610314576020600a54604051908152f35b346103145760203660031901126103145760043560005260026020526020604060002054604051908152f35b34610314576000366003190112610314576020600b54604051908152f35b3461031457600036600319011261031457611920611c2c565b600f54620189c081018091116104c15742106104b05761194560055460155490611ad5565b6103cf8102908082046103cf14901517156104c1576000808061196d6103e882950447611ae8565b335af150610019611af5565b90601f8019910116810190811067ffffffffffffffff82111761095357604052565b67ffffffffffffffff81116109535760051b60200190565b600435906001600160a01b038216820361031457565b6040600319820112610314576004359067ffffffffffffffff8211610314578060238301121561031457816004013590611a028261199b565b92611a106040519485611979565b8284526024602085019360051b82010191821161031457602401915b818310611a46575050509060243580151581036103145790565b82356001600160a01b038116810361031457815260209283019201611a2c565b600554811015611a8157600560005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b9181601f840112156103145782359167ffffffffffffffff8311610314576020808501948460051b01011161031457565b919082018092116104c157565b818102929181159184041417156104c157565b919082039182116104c157565b3d15611b30573d9067ffffffffffffffff82116109535760405191611b24601f8201601f191660200184611979565b82523d6000602084013e565b606090565b601154908115611bda57611b498242611ae8565b601c546064611b588285611ad5565b0493601b5490818410600014611b7057505050505090565b601a54949593611b808684611ac8565b11611b8d57505050505090565b611ba491611b9e9196949596611ac8565b42611ae8565b90606403606481116104c157611bbf606491611bc594611ad5565b04611ad5565b91801561167157611bd7920490611ac8565b90565b5050600090565b8051821015611a815760209160051b010190565b90816020910312610314575180151581036103145790565b9190811015611a815760051b0190565b90816020910312610314575190565b6000546001600160a01b03163303611c4057565b63118cdaa760e01b6000523360045260246000fd5b6021546040516331a9108f60e11b81526004810183905260309190911c6001600160a01b03169190602081602481865afa9081156104a457600091611dd7575b506001600160a01b031691338314611db057604051632e7cda1d60e21b81523360048201526024810184905260448101829052606481018390526000608482015260208160a4817f00000000000000000000000000000000000000447e69651d841bd8d104bed4936001600160a01b03165afa9081156104a457600091611db8575b50611db057604051631574d39f60e31b81523360048201526024810193909352604483015260648201526020816084817f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b6001600160a01b03165afa9081156104a457600091611d91575b50611d8c57600090565b600190565b611daa915060203d60201161049d576104958183611979565b38611d82565b505050600190565b611dd1915060203d60201161049d576104958183611979565b38611d17565b6020813d602011611e18575b81611df060209383611979565b81010312611e145751906001600160a01b0382168203611e11575038611c95565b80fd5b5080fd5b3d9150611de356fea2646970667358221220750f3b635c6a5b231aad4a74db8a5d18531a18fbffe87b6ca0007cb9a2a6374064736f6c634300081a0033

Verified Source Code Full Match

Compiler: v0.8.26+commit.8a97fa7a EVM: paris Optimization: Yes (200 runs)
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);
}
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;
}
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);
  }
}
VRFConsumerBaseV2.sol 135 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/** ****************************************************************************
 * @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 VRFCoordinator
 * @dev 2. The consumer contract implements fulfillRandomWords.
 * *****************************************************************************
 * @dev USAGE
 *
 * @dev Calling contracts must inherit from VRFConsumerBase, and can
 * @dev initialize VRFConsumerBase's attributes in their constructor as
 * @dev shown:
 *
 * @dev   contract VRFConsumer {
 * @dev     constructor(<other arguments>, address _vrfCoordinator, address _link)
 * @dev       VRFConsumerBase(_vrfCoordinator) 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 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),
 * @dev see (VRFCoordinatorInterface for a description of the arguments).
 *
 * @dev Once the VRFCoordinator 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 VRFConsumerBase.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 VRFConsumerBaseV2 {
  error OnlyCoordinatorCanFulfill(address have, address want);
  // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i
  address private immutable vrfCoordinator;

  /**
   * @param _vrfCoordinator address of VRFCoordinator contract
   */
  constructor(address _vrfCoordinator) {
    vrfCoordinator = _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 VRFConsumerBaseV2 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[] memory 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[] memory randomWords) external {
    if (msg.sender != vrfCoordinator) {
      revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
    }
    fulfillRandomWords(requestId, randomWords);
  }
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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);
}
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
IDelegateRegistry.sol 221 lines
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.13;

/**
 * @title IDelegateRegistry
 * @custom:version 2.0
 * @custom:author foobar (0xfoobar)
 * @notice A standalone immutable registry storing delegated permissions from one address to another
 */
interface IDelegateRegistry {
    /// @notice Delegation type, NONE is used when a delegation does not exist or is revoked
    enum DelegationType {
        NONE,
        ALL,
        CONTRACT,
        ERC721,
        ERC20,
        ERC1155
    }

    /// @notice Struct for returning delegations
    struct Delegation {
        DelegationType type_;
        address to;
        address from;
        bytes32 rights;
        address contract_;
        uint256 tokenId;
        uint256 amount;
    }

    /// @notice Emitted when an address delegates or revokes rights for their entire wallet
    event DelegateAll(address indexed from, address indexed to, bytes32 rights, bool enable);

    /// @notice Emitted when an address delegates or revokes rights for a contract address
    event DelegateContract(address indexed from, address indexed to, address indexed contract_, bytes32 rights, bool enable);

    /// @notice Emitted when an address delegates or revokes rights for an ERC721 tokenId
    event DelegateERC721(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, bool enable);

    /// @notice Emitted when an address delegates or revokes rights for an amount of ERC20 tokens
    event DelegateERC20(address indexed from, address indexed to, address indexed contract_, bytes32 rights, uint256 amount);

    /// @notice Emitted when an address delegates or revokes rights for an amount of an ERC1155 tokenId
    event DelegateERC1155(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, uint256 amount);

    /// @notice Thrown if multicall calldata is malformed
    error MulticallFailed();

    /**
     * -----------  WRITE -----------
     */

    /**
     * @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
     * @param data The encoded function data for each of the calls to make to this contract
     * @return results The results from each of the calls passed in via data
     */
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
     * @param to The address to act as delegate
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param enable Whether to enable or disable this delegation, true delegates and false revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateAll(address to, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
     * @param to The address to act as delegate
     * @param contract_ The contract whose rights are being delegated
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param enable Whether to enable or disable this delegation, true delegates and false revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateContract(address to, address contract_, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
     * @param to The address to act as delegate
     * @param contract_ The contract whose rights are being delegated
     * @param tokenId The token id to delegate
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param enable Whether to enable or disable this delegation, true delegates and false revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
     * @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
     * @param to The address to act as delegate
     * @param contract_ The address for the fungible token contract
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param amount The amount to delegate, > 0 delegates and 0 revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateERC20(address to, address contract_, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
     * @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
     * @param to The address to act as delegate
     * @param contract_ The address of the contract that holds the token
     * @param tokenId The token id to delegate
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param amount The amount of that token id to delegate, > 0 delegates and 0 revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateERC1155(address to, address contract_, uint256 tokenId, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);

    /**
     * ----------- CHECKS -----------
     */

    /**
     * @notice Check if `to` is a delegate of `from` for the entire wallet
     * @param to The potential delegate address
     * @param from The potential address who delegated rights
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return valid Whether delegate is granted to act on the from's behalf
     */
    function checkDelegateForAll(address to, address from, bytes32 rights) external view returns (bool);

    /**
     * @notice Check if `to` is a delegate of `from` for the specified `contract_` or the entire wallet
     * @param to The delegated address to check
     * @param contract_ The specific contract address being checked
     * @param from The cold wallet who issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return valid Whether delegate is granted to act on from's behalf for entire wallet or that specific contract
     */
    function checkDelegateForContract(address to, address from, address contract_, bytes32 rights) external view returns (bool);

    /**
     * @notice Check if `to` is a delegate of `from` for the specific `contract` and `tokenId`, the entire `contract_`, or the entire wallet
     * @param to The delegated address to check
     * @param contract_ The specific contract address being checked
     * @param tokenId The token id for the token to delegating
     * @param from The wallet that issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return valid Whether delegate is granted to act on from's behalf for entire wallet, that contract, or that specific tokenId
     */
    function checkDelegateForERC721(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (bool);

    /**
     * @notice Returns the amount of ERC20 tokens the delegate is granted rights to act on the behalf of
     * @param to The delegated address to check
     * @param contract_ The address of the token contract
     * @param from The cold wallet who issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return balance The delegated balance, which will be 0 if the delegation does not exist
     */
    function checkDelegateForERC20(address to, address from, address contract_, bytes32 rights) external view returns (uint256);

    /**
     * @notice Returns the amount of a ERC1155 tokens the delegate is granted rights to act on the behalf of
     * @param to The delegated address to check
     * @param contract_ The address of the token contract
     * @param tokenId The token id to check the delegated amount of
     * @param from The cold wallet who issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return balance The delegated balance, which will be 0 if the delegation does not exist
     */
    function checkDelegateForERC1155(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (uint256);

    /**
     * ----------- ENUMERATIONS -----------
     */

    /**
     * @notice Returns all enabled delegations a given delegate has received
     * @param to The address to retrieve delegations for
     * @return delegations Array of Delegation structs
     */
    function getIncomingDelegations(address to) external view returns (Delegation[] memory delegations);

    /**
     * @notice Returns all enabled delegations an address has given out
     * @param from The address to retrieve delegations for
     * @return delegations Array of Delegation structs
     */
    function getOutgoingDelegations(address from) external view returns (Delegation[] memory delegations);

    /**
     * @notice Returns all hashes associated with enabled delegations an address has received
     * @param to The address to retrieve incoming delegation hashes for
     * @return delegationHashes Array of delegation hashes
     */
    function getIncomingDelegationHashes(address to) external view returns (bytes32[] memory delegationHashes);

    /**
     * @notice Returns all hashes associated with enabled delegations an address has given out
     * @param from The address to retrieve outgoing delegation hashes for
     * @return delegationHashes Array of delegation hashes
     */
    function getOutgoingDelegationHashes(address from) external view returns (bytes32[] memory delegationHashes);

    /**
     * @notice Returns the delegations for a given array of delegation hashes
     * @param delegationHashes is an array of hashes that correspond to delegations
     * @return delegations Array of Delegation structs, return empty structs for nonexistent or revoked delegations
     */
    function getDelegationsFromHashes(bytes32[] calldata delegationHashes) external view returns (Delegation[] memory delegations);

    /**
     * ----------- STORAGE ACCESS -----------
     */

    /**
     * @notice Allows external contracts to read arbitrary storage slots
     */
    function readSlot(bytes32 location) external view returns (bytes32);

    /**
     * @notice Allows external contracts to read an arbitrary array of storage slots
     */
    function readSlots(bytes32[] calldata locations) external view returns (bytes32[] memory);
}
IDelegationRegistry.sol 184 lines
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;

/**
 * @title An immutable registry contract to be deployed as a standalone primitive
 * @dev See EIP-5639, new project launches can read previous cold wallet -> hot wallet delegations
 * from here and integrate those permissions into their flow
 */
interface IDelegationRegistry {
    /// @notice Delegation type
    enum DelegationType {
        NONE,
        ALL,
        CONTRACT,
        TOKEN
    }

    /// @notice Info about a single delegation, used for onchain enumeration
    struct DelegationInfo {
        DelegationType type_;
        address vault;
        address delegate;
        address contract_;
        uint256 tokenId;
    }

    /// @notice Info about a single contract-level delegation
    struct ContractDelegation {
        address contract_;
        address delegate;
    }

    /// @notice Info about a single token-level delegation
    struct TokenDelegation {
        address contract_;
        uint256 tokenId;
        address delegate;
    }

    /// @notice Emitted when a user delegates their entire wallet
    event DelegateForAll(address vault, address delegate, bool value);

    /// @notice Emitted when a user delegates a specific contract
    event DelegateForContract(address vault, address delegate, address contract_, bool value);

    /// @notice Emitted when a user delegates a specific token
    event DelegateForToken(address vault, address delegate, address contract_, uint256 tokenId, bool value);

    /// @notice Emitted when a user revokes all delegations
    event RevokeAllDelegates(address vault);

    /// @notice Emitted when a user revoes all delegations for a given delegate
    event RevokeDelegate(address vault, address delegate);

    /**
     * -----------  WRITE -----------
     */

    /**
     * @notice Allow the delegate to act on your behalf for all contracts
     * @param delegate The hotwallet to act on your behalf
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForAll(address delegate, bool value) external;

    /**
     * @notice Allow the delegate to act on your behalf for a specific contract
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForContract(address delegate, address contract_, bool value) external;

    /**
     * @notice Allow the delegate to act on your behalf for a specific token
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param tokenId The token id for the token you're delegating
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForToken(address delegate, address contract_, uint256 tokenId, bool value) external;

    /**
     * @notice Revoke all delegates
     */
    function revokeAllDelegates() external;

    /**
     * @notice Revoke a specific delegate for all their permissions
     * @param delegate The hotwallet to revoke
     */
    function revokeDelegate(address delegate) external;

    /**
     * @notice Remove yourself as a delegate for a specific vault
     * @param vault The vault which delegated to the msg.sender, and should be removed
     */
    function revokeSelf(address vault) external;

    /**
     * -----------  READ -----------
     */

    /**
     * @notice Returns all active delegations a given delegate is able to claim on behalf of
     * @param delegate The delegate that you would like to retrieve delegations for
     * @return info Array of DelegationInfo structs
     */
    function getDelegationsByDelegate(address delegate) external view returns (DelegationInfo[] memory);

    /**
     * @notice Returns an array of wallet-level delegates for a given vault
     * @param vault The cold wallet who issued the delegation
     * @return addresses Array of wallet-level delegates for a given vault
     */
    function getDelegatesForAll(address vault) external view returns (address[] memory);

    /**
     * @notice Returns an array of contract-level delegates for a given vault and contract
     * @param vault The cold wallet who issued the delegation
     * @param contract_ The address for the contract you're delegating
     * @return addresses Array of contract-level delegates for a given vault and contract
     */
    function getDelegatesForContract(address vault, address contract_) external view returns (address[] memory);

    /**
     * @notice Returns an array of contract-level delegates for a given vault's token
     * @param vault The cold wallet who issued the delegation
     * @param contract_ The address for the contract holding the token
     * @param tokenId The token id for the token you're delegating
     * @return addresses Array of contract-level delegates for a given vault's token
     */
    function getDelegatesForToken(address vault, address contract_, uint256 tokenId)
        external
        view
        returns (address[] memory);

    /**
     * @notice Returns all contract-level delegations for a given vault
     * @param vault The cold wallet who issued the delegations
     * @return delegations Array of ContractDelegation structs
     */
    function getContractLevelDelegations(address vault)
        external
        view
        returns (ContractDelegation[] memory delegations);

    /**
     * @notice Returns all token-level delegations for a given vault
     * @param vault The cold wallet who issued the delegations
     * @return delegations Array of TokenDelegation structs
     */
    function getTokenLevelDelegations(address vault) external view returns (TokenDelegation[] memory delegations);

    /**
     * @notice Returns true if the address is delegated to act on the entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForAll(address delegate, address vault) external view returns (bool);

    /**
     * @notice Returns true if the address is delegated to act on your behalf for a token contract or an entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForContract(address delegate, address vault, address contract_)
        external
        view
        returns (bool);

    /**
     * @notice Returns true if the address is delegated to act on your behalf for a specific token, the token's contract or an entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param tokenId The token id for the token you're delegating
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForToken(address delegate, address vault, address contract_, uint256 tokenId)
        external
        view
        returns (bool);
}
Presale.sol 522 lines
// SPDX-License-Identifier: None

pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {IVRFCoordinatorV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/interfaces/IVRFCoordinatorV2Plus.sol";
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";

import './interfaces/IDelegateRegistry.sol';
import './interfaces/IDelegationRegistry.sol';

contract Presale is VRFConsumerBaseV2, Ownable {

    // Maintains various token balances for users
    struct userInfo {
        uint256 KOLPurchased;
        uint256 superKOLPurchased;
        uint256 totalPurchased;
        uint256 rafflesWon;
        uint256 rafflesPurchased;
        uint256 amountClaimed;
    }

    // Each user's info 
    mapping(address => userInfo) public usersInfo;

    // Purchased per NFT ID
    mapping(uint256 => uint256) public phaseOnePurchased; 

    // Phase 2 whitelists 
    mapping(address => bool) public KOLWhitelist; 
    mapping(address => bool) public superKOLWhitelist;

    // List of users who have entered the raffle - one entry per ticket purchased
    address[] public raffleEntries;

    // Total tokens that can be purchased in phase 1
    uint256 public phaseOneSupply = 33000000 * 10**18;
    uint256 public phaseOnePurchasedTotal;
    
    // Total tokens that can be purchased in phase 2 - rolls over from phase 1
    uint256 public phaseTwoSupply = 132000000 * 10**18;
    uint256 public KOLPurchasedTotal;
    uint256 public superKOLPurchasedTotal;

    // Total tokens that can be purchased in phase 3 - rolls over from phase 1 and 2
    uint256 public phaseThreeSupply;
    uint256 public phaseThreePurchasedTotal;

    // Tokens purchased across all phases 
    uint256 public totalTokensPurchased;
    // ETH collected in phase 1 and 2 - allows for withdrawing during phase 3
    uint256 public phaseOneTwoETH;

    // Used to maintain the contract's current phase
    uint256 public launchTime;
    bool public claimEnabled;
    uint256 public claimTime;

    // Limit of number of tokens that can be purchased by each user in each phase
    uint256 public phaseOneCap;
    uint256 public KOLCap;
    uint256 public superKOLCap;

    // Cost per entry and winning token amount for raffles
    uint256 public rafflePrice;
    uint256 public raffleAmount;
    uint256 public raffleLimit = 20;

    // Tokens per ETH for each phase
    uint256 public phaseOneRatio;
    uint256 public phaseTwoRatio;

    // Used to calculate each user's vested amount when claiming
    uint256 public vestingPeriod = 14 days;
    uint256 public cliffDuration = 12 hours;
    uint256 public initialUnlockPercent = 75;

    // Used to rollover tokens between phases
    bool public phaseOneEnded;
    bool public phaseTwoEnded;

    // Used to enable users to refund losing tickets
    bool public refundsEnabled;

    // Vars required for Chainlink VRF
    uint256 public subscriptionId;
    IVRFCoordinatorV2Plus public coordinator;
    bytes32 public keyHash = 0x3fd2fec10d06ee8f65e7f2e95f5c56511359ece3f33960ad8a866ae24a8ff10b;
    uint32 public callbackGasLimit = 15000000;
    uint16 public requestConfirmations = 3;

    // NFTs required for phase one purchase
    IERC721 public nftAddress;
    // Tokens being bought in each phase
    IERC20 public tokenAddress;
    // Delegate registries to support delegating NFTs
    IDelegateRegistry public immutable delegateRegistryV2 =
        IDelegateRegistry(0x00000000000000447e69651d841bD8D104Bed493);
    IDelegationRegistry public immutable delegateRegistryV1 =
        IDelegationRegistry(0x00000000000076A84feF008CDAbe6409d2FE638B);

    error OutsideTimeWindow();
    error ExceedsPurchaseCap();
    error NFTOwner();
    error Whitelist();
    error IncorrectPrice();
    error NotEnabled();
    error AlreadyLaunched();

    // Creates the presale and sets the required values
    constructor(address _nftAddress, address _tokenAddress, address _vrfCoordinator, uint256 _subscriptionId) VRFConsumerBaseV2(_vrfCoordinator) Ownable(msg.sender) {
        coordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
        subscriptionId = _subscriptionId;
        nftAddress = IERC721(_nftAddress);
        tokenAddress = IERC20(_tokenAddress);
    }

    // Begins the presale process and sets prices and caps
    function launch(uint256 _phaseOneRatio, uint256 _phaseTwoRatio, uint256 _phaseOneCap, uint256 _KOLCap, uint256 _superKOLCap, uint256 _rafflePrice, uint256 _raffleAmount) external onlyOwner {
        if(launchTime != 0) { // 2 hours phase one + 2 hours phase 2 + 24 hours phase 3
            revert AlreadyLaunched();
        }
        launchTime = block.timestamp;
        phaseOneRatio = _phaseOneRatio;
        phaseTwoRatio = _phaseTwoRatio;
        phaseOneCap = _phaseOneCap;
        KOLCap = _KOLCap;
        superKOLCap = _superKOLCap;
        rafflePrice = _rafflePrice;
        raffleAmount = _raffleAmount;
    }

    // Allows users to refund their losing tickets - no draws must happen after 
    function enableRefunds() external onlyOwner {
        refundsEnabled = true;
    }

    // Allows users to claim 
    function enableClaims() external onlyOwner {
        // Ensures phase three has ended 
        if(block.timestamp < launchTime + 100800) { // 2 hours phase one + 2 hours phase 2 + 24 hours phase 3
            revert OutsideTimeWindow();
        }
        claimEnabled = true;
        claimTime = block.timestamp;
        // Transfers the total required tokens to the contract 
        tokenAddress.transferFrom(msg.sender, address(this), totalTokensPurchased);
    }

    // Allows an NFT holder to purchase tokens 
    function phaseOnePurchase(uint256[] calldata nftIds, uint256[] calldata amounts) external payable {
        if(block.timestamp > launchTime + 7200) { 
            // Two hours from launch to purchase in phase one 
            revert OutsideTimeWindow();
        }

        uint256 totalAmount;
        for(uint256 i = 0; i < amounts.length; i++) {
            totalAmount += amounts[i];
        }

        if(msg.value != totalAmount) {
            revert IncorrectPrice();
        }

        for(uint256 i = 0; i < nftIds.length; i++) {
            _phaseOnePurchase(nftIds[i], amounts[i]);
        }

        phaseOneTwoETH += msg.value;

    }

    function _phaseOnePurchase(uint256 nftId, uint256 amount) internal {
        // User must own the specified NFT or be a delegate
        if(!_verifyTokenOwner(nftId)) {
            revert NFTOwner();
        }

        // Convert ETH to tokens 
        uint256 purchasedAmount = amount * phaseOneRatio;
        if(phaseOnePurchased[nftId] + purchasedAmount > phaseOneCap) {
            revert ExceedsPurchaseCap();
        }

        // Check we dont exceed the phase one supply
        if(phaseOnePurchasedTotal + purchasedAmount > phaseOneSupply) {
            revert ExceedsPurchaseCap();
        }

        // Increment all associated values
        usersInfo[msg.sender].totalPurchased += purchasedAmount;
        phaseOnePurchased[nftId] += purchasedAmount;
        phaseOnePurchasedTotal += purchasedAmount;
        totalTokensPurchased += purchasedAmount;
    }

    // Allows whitelisted users to purchase tokens after phase one ends
    function phaseTwoKOLPurchase() external payable {
        if(block.timestamp < launchTime + 7200 || block.timestamp > launchTime + 14400) {
            // Phase two starts two hours after launch and lasts another two hours 
            revert OutsideTimeWindow();
        }

        // Carries over any not purchased phase one tokens - should only trigger after phase one due to above time constraints
        if(!phaseOneEnded) {
            phaseTwoSupply += (phaseOneSupply - phaseOnePurchasedTotal);
            phaseOneEnded = true;
        }

        // User must be whitelisted
        if(KOLWhitelist[msg.sender] == false) {
            revert Whitelist();
        }

        // Convert ETH to tokens 
        uint256 purchasedAmount = msg.value * phaseTwoRatio;
        if(usersInfo[msg.sender].KOLPurchased + purchasedAmount > KOLCap) {
            revert ExceedsPurchaseCap();
        }

        // Check total phase two purchased tokens dont exceed phase two supply
        if(KOLPurchasedTotal + superKOLPurchasedTotal + purchasedAmount > phaseTwoSupply) {
            revert ExceedsPurchaseCap();
        }

        // Increment all associated values
        usersInfo[msg.sender].KOLPurchased += purchasedAmount;
        usersInfo[msg.sender].totalPurchased += purchasedAmount;
        KOLPurchasedTotal += purchasedAmount;
        totalTokensPurchased += purchasedAmount;
        phaseOneTwoETH += msg.value;
    }

    // Allows whitelisted users to purchase tokens after phase one ends
    function phaseTwoSuperKOLPurchase() external payable {
        if(block.timestamp < launchTime + 7200 || block.timestamp > launchTime + 14400) {
            // Phase two starts two hours after launch and lasts another two hours
            revert OutsideTimeWindow();
        }

        // Carries over any not purchased phase one tokens - should only trigger after phase one due to above time constraints
        if(!phaseOneEnded) {
            phaseTwoSupply += (phaseOneSupply - phaseOnePurchasedTotal);
            phaseOneEnded = true;
        }

        // User must be whitelisted
        if(superKOLWhitelist[msg.sender] == false) {
            revert Whitelist();
        }

        // Convert ETH to tokens 
        uint256 purchasedAmount = msg.value * phaseTwoRatio;
        if(usersInfo[msg.sender].superKOLPurchased + purchasedAmount > superKOLCap) {
            revert ExceedsPurchaseCap();
        }

        // Check total phase two purchased tokens dont exceed phase two supply
        if(KOLPurchasedTotal + superKOLPurchasedTotal + purchasedAmount > phaseTwoSupply) {
            revert ExceedsPurchaseCap();
        }

        // Increment all associated values
        usersInfo[msg.sender].superKOLPurchased += purchasedAmount;
        usersInfo[msg.sender].totalPurchased += purchasedAmount;
        superKOLPurchasedTotal += purchasedAmount;
        totalTokensPurchased += purchasedAmount;
        phaseOneTwoETH += msg.value;
    }

    // Allows any users to purchase a raffle ticket after phase two ends
    function phaseThreePurchase(uint256 numTickets) external payable {
        if(block.timestamp < launchTime + 14400 || block.timestamp > launchTime + 100800) {
            // Phase three starts four hours after launch and lasts another 24 hours
            revert OutsideTimeWindow();
        }

        // Carries over any not purchased phase two tokens - should only trigger after phase one due to above time constraints
        if(!phaseTwoEnded) {
            phaseThreeSupply += (phaseTwoSupply - (KOLPurchasedTotal + superKOLPurchasedTotal));
            phaseTwoEnded = true;
        }

        // Ensure user provided correct amount of ETH 
        if(msg.value != rafflePrice * numTickets) {
            revert IncorrectPrice();
        }

        if(usersInfo[msg.sender].rafflesPurchased + numTickets > raffleLimit) {
            revert ExceedsPurchaseCap();
        }
        
        // Credit the user with the number of tickets
        usersInfo[msg.sender].rafflesPurchased += numTickets;
        for(uint256 i = 0; i < numTickets; i++) {
            // Add user to the actual entries
            raffleEntries.push(msg.sender);
        }
    }

    // Allows the owner to draw the specified number of winners
    function draw(uint256 numDraws) external onlyOwner() {
        if(block.timestamp < launchTime + 100800) {
            // Raffle can be drawn after phase three ends
            revert OutsideTimeWindow();
        }

        // Check total phase three purchased tokens dont exceed phase three supply
        if((numDraws * raffleAmount) + phaseThreePurchasedTotal > phaseThreeSupply) {
            revert ExceedsPurchaseCap();
        }

        // Request random values from Chainlink VRF provider
        requestRandomWords(callbackGasLimit, requestConfirmations, uint32(numDraws));
    }

    // Sends the request for random values to Chainlink VRF provider
    function requestRandomWords(
        uint32 _callbackGasLimit,
        uint16 _requestConfirmations,
        uint32 _numWords
    ) internal {
        bytes memory args = VRFV2PlusClient._argsToBytes(
            VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
        );

        VRFV2PlusClient.RandomWordsRequest memory request = VRFV2PlusClient
            .RandomWordsRequest({
                keyHash: keyHash,
                subId: subscriptionId,
                requestConfirmations: _requestConfirmations,
                callbackGasLimit: _callbackGasLimit,
                numWords: _numWords,
                extraArgs: args
            });

        coordinator.requestRandomWords(request);
    }

    // Called by Chainklink VRF provider to provide requested random values
    function fulfillRandomWords(
        uint256 _requestId,
        uint256[] memory randomWords
    ) internal override {
        selectWinners(randomWords);
    }

    // Used to select winners based on provided random values
    function selectWinners(uint256[] memory randomness) internal {
        // Selects the correct number of winners
        for (uint256 i; i < randomness.length; i++) {
            // Find the right winner from the entries
            uint256 winnerIndex = randomness[i] % raffleEntries.length;
            address winner = raffleEntries[winnerIndex];
            // Credit the user with the amount and the win
            usersInfo[winner].totalPurchased += raffleAmount;
            totalTokensPurchased += raffleAmount;
            phaseThreePurchasedTotal += raffleAmount;
            usersInfo[winner].rafflesWon += 1;
            // Remove the user from the entries 
            raffleEntries[winnerIndex] = raffleEntries[raffleEntries.length-1];
            raffleEntries.pop();
        }
    }

    // Allows the users to claim tokens once enabled
    function claim() external {
        if(!claimEnabled) {
            revert NotEnabled();
        }
        // Claimable tokens vest over time 
        uint256 vested = vestedAmount(usersInfo[msg.sender].totalPurchased);
        // Calculate claimable, update claimed amount, send user their tokens
        uint256 claimable = vested - usersInfo[msg.sender].amountClaimed;
        usersInfo[msg.sender].amountClaimed = vested;
        tokenAddress.transfer(msg.sender, claimable);
    }

    // Calculates the amount of vested tokens from the time claiming was enabled
    function vestedAmount(uint256 totalPurchased) public view returns (uint256) {
        if(claimTime == 0) {
            return 0;
        }
        uint256 elapsedTime = block.timestamp - claimTime;
        uint256 initalUnlockAmount = (totalPurchased * initialUnlockPercent) / 100;
        if (elapsedTime < cliffDuration) {
            // Before 12 hours, only the initial 75% is available
            return initalUnlockAmount;
        } else if (elapsedTime >= cliffDuration + vestingPeriod) {
            // After the vesting period, 100% is vested
            return totalPurchased;
        } else { // calculate vested amount 
            elapsedTime = block.timestamp - (claimTime + cliffDuration); // vesting starts from cliff duration
            uint256 nonInitialAmount = (totalPurchased * (100 - initialUnlockPercent)) / 100; // amount that vests after initial unlock
            uint256 vestedAmount = (nonInitialAmount * elapsedTime) / vestingPeriod; // amount of non-initial that has vested
            return initalUnlockAmount + vestedAmount; // initial unlock + vested amount 
        }
    }

    // Allows users to refund raffle entries that were not selected
    function refundRaffle() external { 
        if(!refundsEnabled) {
            // Cannot claim until team is done drawing winners
            revert NotEnabled();
        }
        uint256 ticketsRefunded =  usersInfo[msg.sender].rafflesPurchased -  usersInfo[msg.sender].rafflesWon;
        // Users receive a 97.5% refund
        uint256 refundAmount = (ticketsRefunded * rafflePrice * 975) / 1000;
        usersInfo[msg.sender].rafflesPurchased = 0; // will cause revert if they call this again 
        msg.sender.call{value: refundAmount}("");
    }

    // Allows owner to add users to the KOL Whitelist
    function setKOLWhitelist(address[] memory users, bool value) external onlyOwner {
        for(uint256 i = 0; i < users.length; i++) {
            KOLWhitelist[users[i]] = value;
        }
    }

    // Allows owner to add users to the Super KOL Whitelist
    function setSuperKOLWhitelist(address[] memory users, bool value) external onlyOwner {
        for(uint256 i = 0; i < users.length; i++) {
            superKOLWhitelist[users[i]] = value;
        }
    }

    // Allows the owner to set the number of tokens won per raffle
    function setRaffleAmount(uint256 amount) external onlyOwner {
        raffleAmount = amount;
    }

    // Allows the owner to set the price of each raffle entry
    function setRafflePrice(uint256 amount) external onlyOwner {
        if(block.timestamp > launchTime + 14400) {
            // Can only change the price before raffles are purchased
            revert OutsideTimeWindow();
        }

        rafflePrice = amount;
    }

    // Sets the Chainlink VRF gas callback limit
    function setGasLimit(uint256 limit) external onlyOwner {
        callbackGasLimit = uint32(limit);
    }

    // Sets the Chainlink VRF gas callback limit
    function setSubId(uint256 id) external onlyOwner {
        subscriptionId = id;
    }

    // Sets the Chainlink keyhash
    function setKeyHash(bytes32 newHash) external onlyOwner {
        keyHash = newHash;
    }

    // Withdraws all ETH from the contract - should not be used until users are given sufficient time to refund their tickets
    function withdrawETH() external onlyOwner {
        msg.sender.call{value: address(this).balance}(""); // This will withdraw all ETH including potential ETH for refunds
    }

    // Withdraws the ETH collected in phases one and two - should only be called after phase 2 and before calling withdrawETHNoRefunds
    function withdrawETHNoRaffle() external onlyOwner {
        uint256 amount = phaseOneTwoETH;
        phaseOneTwoETH = 0;
        msg.sender.call{value: amount}(""); 
    }

    // Withdraws all ETH not allocated to refunds 
    function withdrawETHNoRefunds() external onlyOwner {
        if(block.timestamp < launchTime + 100800 ) {
            revert OutsideTimeWindow();
        }
        uint256 totalRefunds = raffleEntries.length; // all winners should be popped from the array by now 
        uint256 refundAmount = (totalRefunds * rafflePrice * 975) / 1000;
        msg.sender.call{value: address(this).balance - refundAmount}(""); 
    }

    // Withdraws all of a specified token to a contract - should only be used on the presale token in case of emergency
    function emergencyWithdrawTokens(address token) external onlyOwner {
        IERC20(token).transfer(msg.sender, IERC20(token).balanceOf(address(this)));
    }

    receive() external payable {}

    function _verifyTokenOwner(uint256 NFTId) internal view returns(bool) {
        address NFTOwner = nftAddress.ownerOf(NFTId);

        // check sender is owner
        if (NFTOwner == msg.sender) return true;

        // check with delegate registry v2
        if (
            delegateRegistryV2.checkDelegateForERC721(
                msg.sender,
                NFTOwner,
                address(nftAddress),
                NFTId,
                ''
            )
        ) return true;

        // check with delegate registry v1
        if (
            delegateRegistryV1.checkDelegateForToken(
                msg.sender,
                NFTOwner,
                address(nftAddress),
                NFTId
            )
        ) return true;

        // false if not owner or delegate
        return false;
    }
}

Read Contract

KOLCap 0xdbe65f59 → uint256
KOLPurchasedTotal 0xa4bfb130 → uint256
KOLWhitelist 0x9755e04e → bool
callbackGasLimit 0x24f74697 → uint32
claimEnabled 0x2866ed21 → bool
claimTime 0x27b3bf11 → uint256
cliffDuration 0xd85349f7 → uint256
coordinator 0x0a009097 → address
delegateRegistryV1 0x856ed8c9 → address
delegateRegistryV2 0xdb584d25 → address
initialUnlockPercent 0x34fccc1d → uint256
keyHash 0x61728f39 → bytes32
launchTime 0x790ca413 → uint256
nftAddress 0x5bf8633a → address
owner 0x8da5cb5b → address
phaseOneCap 0x52bca4c3 → uint256
phaseOneEnded 0xec9cd924 → bool
phaseOnePurchased 0x03c669e3 → uint256
phaseOnePurchasedTotal 0x36ffc8b0 → uint256
phaseOneRatio 0x7d7af280 → uint256
phaseOneSupply 0x55a16c24 → uint256
phaseOneTwoETH 0xe365d863 → uint256
phaseThreePurchasedTotal 0xd1abca7a → uint256
phaseThreeSupply 0x03b4a479 → uint256
phaseTwoEnded 0x29687c53 → bool
phaseTwoRatio 0x08ac900a → uint256
phaseTwoSupply 0x595d2213 → uint256
raffleAmount 0xc94028c2 → uint256
raffleEntries 0x4c846e46 → address
raffleLimit 0x18643d37 → uint256
rafflePrice 0xf8d86794 → uint256
refundsEnabled 0x532f1179 → bool
requestConfirmations 0xb0fb162f → uint16
subscriptionId 0x09c1ba2e → uint256
superKOLCap 0x32b7ac64 → uint256
superKOLPurchasedTotal 0x03ebbf34 → uint256
superKOLWhitelist 0xbaa39fbe → bool
tokenAddress 0x9d76ea58 → address
totalTokensPurchased 0xa277d5b1 → uint256
usersInfo 0x5d0eaaaf → uint256, uint256, uint256, uint256, uint256, uint256
vestedAmount 0x1bfce853 → uint256
vestingPeriod 0x7313ee5a → uint256

Write Contract 24 functions

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

claim 0x4e71d92d
No parameters
draw 0x3b304147
uint256 numDraws
emergencyWithdrawTokens 0xb2af127c
address token
enableClaims 0xea28edad
No parameters
enableRefunds 0x8c52dc41
No parameters
launch 0xbcf25729
uint256 _phaseOneRatio
uint256 _phaseTwoRatio
uint256 _phaseOneCap
uint256 _KOLCap
uint256 _superKOLCap
uint256 _rafflePrice
uint256 _raffleAmount
phaseOnePurchase 0x80c028a8
uint256[] nftIds
uint256[] amounts
phaseThreePurchase 0x9b27349d
uint256 numTickets
phaseTwoKOLPurchase 0x1cecfaad
No parameters
phaseTwoSuperKOLPurchase 0x8007634f
No parameters
rawFulfillRandomWords 0x1fe543e3
uint256 requestId
uint256[] randomWords
refundRaffle 0xe259f636
No parameters
renounceOwnership 0x715018a6
No parameters
setGasLimit 0xee7d72b4
uint256 limit
setKOLWhitelist 0x2a29e67a
address[] users
bool value
setKeyHash 0x98544710
bytes32 newHash
setRaffleAmount 0x1a96ab2b
uint256 amount
setRafflePrice 0x48a93a0d
uint256 amount
setSubId 0x80980043
uint256 id
setSuperKOLWhitelist 0x53d1f59d
address[] users
bool value
transferOwnership 0xf2fde38b
address newOwner
withdrawETH 0xe086e5ec
No parameters
withdrawETHNoRaffle 0x7d7bc639
No parameters
withdrawETHNoRefunds 0x01994885
No parameters

Recent Transactions

No transactions found for this address