Blockchain / Global / Address

Contract

0x6dA08697F8Fd58E2f8d9132e3928c21c537D5BAb

Balance
0 SOMI
Transactions sent
1
Type
Contract · 5,225 bytes of code

NativeDistributor Verified

Compiler
solc 0.8.25+commit.b61c2a91
Language
Solidity
Optimization
Yes, 200 runs
EVM version
cancun
License
—
Verified
2026-10-12

src/sub/NativeDistributor.sol

and 13 more files

Solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import "solady/utils/MerkleProofLib.sol";
import "solady/utils/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
import {BaseDistributor} from "../sub/BaseDistributor.sol";

//   ____ _ _
//  / ___| (_) __ _ _   _  ___
// | |   | | |/ _` | | | |/ _ \
// | |___| | | (_| | |_| |  __/
//  \____|_|_|\__, |\__,_|\___|        _               _       __  __  ____
// |  _ \(_)___| |_|_ __(_) |__  _   _| |_ ___  _ __  / |     |  \/  |/ ___|
// | | | | / __| __| '__| | '_ \| | | | __/ _ \| '__| | |_____| |\/| | |
// | |_| | \__ \ |_| |  | | |_) | |_| | || (_) | |    | |_____| |  | | |___
// |____/|_|___/\__|_|  |_|_.__/ \__,_|\__\___/|_|    |_|     |_|  |_|\____|

interface IWithdrawable {
    function withdraw(uint256) external;
}

/// @title NativeDistributor
/// @notice Native token distributor contract
/// @author Alan (@alannotnerd)
contract NativeDistributor is BaseDistributor {
    using SafeERC20 for IERC20;

    // token to be airdroppped
    address public token;
    uint256 public fee;

    mapping(address => uint256) public nftAllocation;
    mapping(address => mapping(uint256 => bool)) public nftClaimed;

    error InvalidMerkleRoot();
    error NoNftToClaim();

    constructor(address _signer, address _token, address _projectAdmin, address _vault)
        BaseDistributor(_signer, _projectAdmin, _vault)
    {
        if (_token == address(0)) revert ZeroAddress();
        token = _token;
    }

    function version() external pure returns (string memory) {
        return "2.0.0";
    }

    function setFee(uint256 _fee) external onlyRoles(PROJECT_ADMIN) {
        fee = _fee;
        emit FeeSet(_fee);
    }

    function setNftAllocation(address[] calldata _nfts, uint256[] calldata _allocations)
        external
        onlyRoles(PROJECT_ADMIN)
    {
        for (uint256 i = 0; i < _nfts.length; i++) {
            address _nftAddress = _nfts[i];
            uint256 _allocation = _allocations[i];
            nftAllocation[_nftAddress] = _allocation;
        }
    }

    function claimWithNft(address[] calldata _nfts, uint256[] calldata _tokenIds) external {
        uint256 _totalAllocated = 0;
        for (uint256 i = 0; i < _nfts.length; i++) {
            address _nftAddress = _nfts[i];
            uint256 _tokenId = _tokenIds[i];

            uint256 _allocation = nftAllocation[_nftAddress];
            bool _claimed = nftClaimed[_nftAddress][_tokenId];

            if (_claimed) {
                continue;
            }

            _totalAllocated += _allocation;

            nftClaimed[_nftAddress][_tokenId] = true;
            address owner = IERC721(_nftAddress).ownerOf(_tokenId);
            bytes memory _extra = bytes("");
            _execute(vault, owner, bytes32(uint256(1)), _allocation, _extra);
        }

        if (_totalAllocated == 0) {
            revert NoNftToClaim();
        }
    }

    function _execute(address _vault, address _recipient, bytes32 _configurator, uint256 _amount, bytes memory)
        internal
        override
    {
        if (_configurator != bytes32(uint256(1))) {
            revert InvalidMerkleRoot();
        }

        IERC20(token).safeTransferFrom(_vault, address(this), _amount);
        IWithdrawable(address(token)).withdraw(_amount);

        (bool success,) = _recipient.call{value: _amount}("");
        require(success, "Native token transfer failed");
    }

    receive() external payable {}

    fallback() external payable {}
}

Other files

  • src/sub/BaseDistributor.sol
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    
    import "solady/utils/ECDSA.sol";
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
    import {StreamConfig, ICliqueLock} from "../ICliqueLock.sol";
    
    struct ClaimInfo {
        uint256 lastClaimedBlock;
        uint256 claimedAmount;
    }
    
    /// @title  Base Distributor
    /// @author Clique (@Clique2046)
    /// @author Alan (@alannotnerd)
    abstract contract BaseDistributor is OwnableRoles {
        using SafeERC20 for IERC20;
    
        // address signing the claims
        address public signer;
        // vault address
        address public vault;
        // whether the airdrop is active
        bool public active = false;
    
        mapping(bytes32 => mapping(address => ClaimInfo)) public claimed;
        mapping(bytes32 => bytes32) public merkleConfiguration;
    
        // errors
        error AlreadyClaimed();
        error InvalidSignature();
        error NotActive();
        error ZeroAddress();
        error InvalidMerkleProof(bytes32 root);
        error WithdrawFailed();
        error InvalidReleasedAmount();
    
        event AirdropClaimed(address indexed account, bytes32 indexed root, uint256 amount);
        event MerkleConfigurationUpdate(bytes32 indexed root, bytes32 indexed configurator);
        event Withdraw(address indexed to, uint256 amount);
        event FeeSet(uint256 fee);
        event VaultSet(address indexed vault);
    
        uint256 public constant PROJECT_ADMIN = _ROLE_0;
    
        /// @notice Construct a new Claim contract
        /// @param _signer address that can sign messages
        /// @param _projectAdmin address that can set the signer and claim root
        constructor(address _signer, address _projectAdmin, address _vault) {
            if (_signer == address(0)) revert ZeroAddress();
            signer = _signer;
            vault = _vault;
            _initializeOwner(msg.sender);
            _setRoles(_projectAdmin, PROJECT_ADMIN);
        }
    
        /// @notice Modifier to check if the airdrop is active
        modifier whenActive() {
            if (!active) revert NotActive();
            _;
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      ADMIN FUNCTIONS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @notice Set the signer
        /// @param _signer address that can sign messages
        function setSigner(address _signer) external onlyRoles(PROJECT_ADMIN) {
            signer = _signer;
        }
    
        /// @notice Set the claim root
        /// @param _claimRoot root of the merkle tree
        function setClaimRoot(bytes32 _claimRoot, bytes32 _configurator) external onlyRoles(PROJECT_ADMIN) {
            merkleConfiguration[_claimRoot] = _configurator;
            emit MerkleConfigurationUpdate(_claimRoot, _configurator);
        }
    
        /// @notice Set the vault
        /// @param _vault address of the vault
        function setVault(address _vault) external onlyRoles(PROJECT_ADMIN) {
            vault = _vault;
            emit VaultSet(_vault);
        }
    
        /// @notice Toggle the active state
        function toggleActive() external onlyRoles(PROJECT_ADMIN) {
            active = !active;
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                      FEE FUNCTIONS                         */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @notice Withdraw the fee
        /// @dev Only callable by the owner
        function withdrawFee(address _recipient) external onlyOwner {
            uint256 _balance = address(this).balance;
            if (_balance > 0) {
                (bool _success,) = payable(_recipient).call{value: _balance}("");
                if (!_success) revert WithdrawFailed();
                emit Withdraw(_recipient, _balance);
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EXTERNAL FUNCTIONS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @notice Claim airdrop tokens. Checks for both merkle proof
        //          and signature validation
        /// @param _proof merkle proof of the claim
        /// @param _signature signature of the claim
        /// @param _amount amount of tokens to claim
        /// @param _released amount of tokens that released
        /// @param _onBehalfOf address to claim on behalf of
        /// @param _extra extra data to pass to the claim function
        function claim(
            bytes32[] calldata _proof,
            bytes calldata _signature,
            uint256 _amount,
            uint256 _released,
            address _onBehalfOf,
            bytes calldata _extra
        ) external payable whenActive {
            (bytes32 _root, bytes32 _configurator) = _rootCheck(_proof, _amount, _onBehalfOf);
    
            ClaimInfo memory _claimInfo = claimed[_root][_onBehalfOf];
            uint256 claimedAmount_ = _claimInfo.claimedAmount;
    
            if (_amount < _released) {
                revert InvalidReleasedAmount();
            }
    
            if (claimedAmount_ == _released) {
                revert AlreadyClaimed();
            }
    
            claimed[_root][_onBehalfOf] = ClaimInfo({lastClaimedBlock: block.number, claimedAmount: _released});
    
            _signatureCheck(_amount, _released, _onBehalfOf, _signature, _root, signer, _extra);
    
            uint256 _claimableAmount = _released - claimedAmount_;
    
            _execute(vault, _onBehalfOf, _configurator, _claimableAmount, _extra);
            emit AirdropClaimed(_onBehalfOf, _root, _claimableAmount);
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       PRIVATE FUNCTIONS                    */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @notice Internal function to execute the claim
        /// @param _vault vault address
        /// @param _recipient recipient address
        /// @param _configurator configurator address
        /// @param _amount amount of tokens to claim
        /// @param _extra extra data to pass to the claim function
        function _execute(address _vault, address _recipient, bytes32 _configurator, uint256 _amount, bytes memory _extra)
            internal
            virtual;
    
        /// @notice Internal function to check the merkle proof
        /// @param _proof merkle proof of the claim
        /// @param _amount amount of tokens to claim
        /// @param _onBehalfOf address to claim on behalf of
        function _rootCheck(bytes32[] calldata _proof, uint256 _amount, address _onBehalfOf)
            internal
            view
            returns (bytes32 root, bytes32 configurator)
        {
            root = recover(_proof, keccak256(abi.encodePacked(_onBehalfOf, _amount)));
            configurator = merkleConfiguration[root];
            if (configurator == bytes32(0)) revert InvalidMerkleProof(root);
        }
    
        /// @notice Internal function to check the signature
        /// @param _amount amount of tokens to claim
        /// @param _released amount of tokens that released
        /// @param _onBehalfOf address to claim on behalf of
        /// @param _signature signature of the claim
        /// @param _root root of the merkle tree
        /// @param _signer signer to check
        function _signatureCheck(
            uint256 _amount,
            uint256 _released,
            address _onBehalfOf,
            bytes calldata _signature,
            bytes32 _root,
            address _signer,
            bytes calldata _extra
        ) internal view {
            if (_signature.length == 0) revert InvalidSignature();
    
            bytes32 messageHash =
                keccak256(abi.encodePacked(_onBehalfOf, _amount, _released, _root, address(this), block.chainid, _extra));
            bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(messageHash);
            address recoveredSigner = ECDSA.recoverCalldata(prefixedHash, _signature);
    
            if (recoveredSigner != _signer) revert InvalidSignature();
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*            MERKLE PROOF VERIFICATION OPERATIONS            */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function recover(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32 root) {
            /// @solidity memory-safe-assembly
            assembly {
                if mload(proof) {
                    // Initialize `offset` to the offset of `proof` elements in memory.
                    let offset := add(proof, 0x20)
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(offset, shl(5, mload(proof)))
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, mload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), mload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                root := leaf
            }
        }
    }
    
  • lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC20} from "./IERC20.sol";
    import {IERC165} from "./IERC165.sol";
    
    /**
     * @title IERC1363
     * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
     *
     * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
     * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
     */
    interface IERC1363 is IERC20, IERC165 {
        /*
         * Note: the ERC-165 identifier for this interface is 0xb0202a11.
         * 0xb0202a11 ===
         *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
         *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
         *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
         *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
         *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
         *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
         */
    
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
         * @param to The address which you want to transfer to.
         * @param value The amount of tokens to be transferred.
         * @return A boolean value indicating whether the operation succeeded unless throwing.
         */
        function transferAndCall(address to, uint256 value) external returns (bool);
    
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
         * @param to The address which you want to transfer to.
         * @param value The amount of tokens to be transferred.
         * @param data Additional data with no specified format, sent in call to `to`.
         * @return A boolean value indicating whether the operation succeeded unless throwing.
         */
        function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
    
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
         * @param from The address which you want to send tokens from.
         * @param to The address which you want to transfer to.
         * @param value The amount of tokens to be transferred.
         * @return A boolean value indicating whether the operation succeeded unless throwing.
         */
        function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
    
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
         * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
         * @param from The address which you want to send tokens from.
         * @param to The address which you want to transfer to.
         * @param value The amount of tokens to be transferred.
         * @param data Additional data with no specified format, sent in call to `to`.
         * @return A boolean value indicating whether the operation succeeded unless throwing.
         */
        function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
    
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
         * @param spender The address which will spend the funds.
         * @param value The amount of tokens to be spent.
         * @return A boolean value indicating whether the operation succeeded unless throwing.
         */
        function approveAndCall(address spender, uint256 value) external returns (bool);
    
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
         * @param spender The address which will spend the funds.
         * @param value The amount of tokens to be spent.
         * @param data Additional data with no specified format, sent in call to `spender`.
         * @return A boolean value indicating whether the operation succeeded unless throwing.
         */
        function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
    }
    
  • lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC165} from "../utils/introspection/IERC165.sol";
    
  • lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol
    // 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";
    
  • lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
    // 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);
    }
    
  • lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC20} from "../IERC20.sol";
    import {IERC1363} from "../../../interfaces/IERC1363.sol";
    
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC-20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        /**
         * @dev An operation with an ERC-20 token failed.
         */
        error SafeERC20FailedOperation(address token);
    
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
    
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
    
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
    
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         *
         * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
         * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
         * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
         * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
    
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         *
         * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
         * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
         * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
         * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
    
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         *
         * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
         * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
         * set here.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
    
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
    
        /**
         * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
         * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
         * targeting contracts.
         *
         * Reverts if the returned value is other than `true`.
         */
        function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
            if (to.code.length == 0) {
                safeTransfer(token, to, value);
            } else if (!token.transferAndCall(to, value, data)) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
    
        /**
         * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
         * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
         * targeting contracts.
         *
         * Reverts if the returned value is other than `true`.
         */
        function transferFromAndCallRelaxed(
            IERC1363 token,
            address from,
            address to,
            uint256 value,
            bytes memory data
        ) internal {
            if (to.code.length == 0) {
                safeTransferFrom(token, from, to, value);
            } else if (!token.transferFromAndCall(from, to, value, data)) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
    
        /**
         * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
         * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
         * targeting contracts.
         *
         * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
         * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
         * once without retrying, and relies on the returned value to be true.
         *
         * Reverts if the returned value is other than `true`.
         */
        function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
            if (to.code.length == 0) {
                forceApprove(token, to, value);
            } else if (!token.approveAndCall(to, value, data)) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
    
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            uint256 returnSize;
            uint256 returnValue;
            assembly ("memory-safe") {
                let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                // bubble errors
                if iszero(success) {
                    let ptr := mload(0x40)
                    returndatacopy(ptr, 0, returndatasize())
                    revert(ptr, returndatasize())
                }
                returnSize := returndatasize()
                returnValue := mload(0)
            }
    
            if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
    
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            bool success;
            uint256 returnSize;
            uint256 returnValue;
            assembly ("memory-safe") {
                success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                returnSize := returndatasize()
                returnValue := mload(0)
            }
            return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
        }
    }
    
  • lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC165} from "../../utils/introspection/IERC165.sol";
    
    /**
     * @dev Required interface of an ERC-721 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 ERC-721 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 ERC-721
         * 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);
    }
    
  • lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC-165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[ERC].
     *
     * 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[ERC 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);
    }
    
  • lib/solady/src/auth/Ownable.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Simple single owner authorization mixin.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
    ///
    /// @dev Note:
    /// This implementation does NOT auto-initialize the owner to `msg.sender`.
    /// You MUST call the `_initializeOwner` in the constructor / initializer.
    ///
    /// While the ownable portion follows
    /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
    /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
    abstract contract Ownable {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The caller is not authorized to call the function.
        error Unauthorized();
    
        /// @dev The `newOwner` cannot be the zero address.
        error NewOwnerIsZeroAddress();
    
        /// @dev The `pendingOwner` does not have a valid handover request.
        error NoHandoverRequest();
    
        /// @dev Cannot double-initialize.
        error AlreadyInitialized();
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                           EVENTS                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
        /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
        /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
        /// despite it not being as lightweight as a single argument event.
        event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
    
        /// @dev An ownership handover to `pendingOwner` has been requested.
        event OwnershipHandoverRequested(address indexed pendingOwner);
    
        /// @dev The ownership handover to `pendingOwner` has been canceled.
        event OwnershipHandoverCanceled(address indexed pendingOwner);
    
        /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
        uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
            0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
    
        /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
        uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
            0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
    
        /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
        uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
            0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          STORAGE                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The owner slot is given by:
        /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
        /// It is intentionally chosen to be a high value
        /// to avoid collision with lower slots.
        /// The choice of manual storage layout is to enable compatibility
        /// with both regular and upgradeable contracts.
        bytes32 internal constant _OWNER_SLOT =
            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
    
        /// The ownership handover slot of `newOwner` is given by:
        /// ```
        ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
        ///     let handoverSlot := keccak256(0x00, 0x20)
        /// ```
        /// It stores the expiry timestamp of the two-step ownership handover.
        uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     INTERNAL FUNCTIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
        function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
    
        /// @dev Initializes the owner directly without authorization guard.
        /// This function must be called upon initialization,
        /// regardless of whether the contract is upgradeable or not.
        /// This is to enable generalization to both regular and upgradeable contracts,
        /// and to save gas in case the initial owner is not the caller.
        /// For performance reasons, this function will not check if there
        /// is an existing owner.
        function _initializeOwner(address newOwner) internal virtual {
            if (_guardInitializeOwner()) {
                /// @solidity memory-safe-assembly
                assembly {
                    let ownerSlot := _OWNER_SLOT
                    if sload(ownerSlot) {
                        mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                        revert(0x1c, 0x04)
                    }
                    // Clean the upper 96 bits.
                    newOwner := shr(96, shl(96, newOwner))
                    // Store the new value.
                    sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                    // Emit the {OwnershipTransferred} event.
                    log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                }
            } else {
                /// @solidity memory-safe-assembly
                assembly {
                    // Clean the upper 96 bits.
                    newOwner := shr(96, shl(96, newOwner))
                    // Store the new value.
                    sstore(_OWNER_SLOT, newOwner)
                    // Emit the {OwnershipTransferred} event.
                    log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                }
            }
        }
    
        /// @dev Sets the owner directly without authorization guard.
        function _setOwner(address newOwner) internal virtual {
            if (_guardInitializeOwner()) {
                /// @solidity memory-safe-assembly
                assembly {
                    let ownerSlot := _OWNER_SLOT
                    // Clean the upper 96 bits.
                    newOwner := shr(96, shl(96, newOwner))
                    // Emit the {OwnershipTransferred} event.
                    log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                    // Store the new value.
                    sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                }
            } else {
                /// @solidity memory-safe-assembly
                assembly {
                    let ownerSlot := _OWNER_SLOT
                    // Clean the upper 96 bits.
                    newOwner := shr(96, shl(96, newOwner))
                    // Emit the {OwnershipTransferred} event.
                    log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                    // Store the new value.
                    sstore(ownerSlot, newOwner)
                }
            }
        }
    
        /// @dev Throws if the sender is not the owner.
        function _checkOwner() internal view virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // If the caller is not the stored owner, revert.
                if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                    mstore(0x00, 0x82b42900) // `Unauthorized()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    
        /// @dev Returns how long a two-step ownership handover is valid for in seconds.
        /// Override to return a different value if needed.
        /// Made internal to conserve bytecode. Wrap it in a public function if needed.
        function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
            return 48 * 3600;
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  PUBLIC UPDATE FUNCTIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Allows the owner to transfer the ownership to `newOwner`.
        function transferOwnership(address newOwner) public payable virtual onlyOwner {
            /// @solidity memory-safe-assembly
            assembly {
                if iszero(shl(96, newOwner)) {
                    mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                    revert(0x1c, 0x04)
                }
            }
            _setOwner(newOwner);
        }
    
        /// @dev Allows the owner to renounce their ownership.
        function renounceOwnership() public payable virtual onlyOwner {
            _setOwner(address(0));
        }
    
        /// @dev Request a two-step ownership handover to the caller.
        /// The request will automatically expire in 48 hours (172800 seconds) by default.
        function requestOwnershipHandover() public payable virtual {
            unchecked {
                uint256 expires = block.timestamp + _ownershipHandoverValidFor();
                /// @solidity memory-safe-assembly
                assembly {
                    // Compute and set the handover slot to `expires`.
                    mstore(0x0c, _HANDOVER_SLOT_SEED)
                    mstore(0x00, caller())
                    sstore(keccak256(0x0c, 0x20), expires)
                    // Emit the {OwnershipHandoverRequested} event.
                    log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                }
            }
        }
    
        /// @dev Cancels the two-step ownership handover to the caller, if any.
        function cancelOwnershipHandover() public payable virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to 0.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), 0)
                // Emit the {OwnershipHandoverCanceled} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
            }
        }
    
        /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
        /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
        function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to 0.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, pendingOwner)
                let handoverSlot := keccak256(0x0c, 0x20)
                // If the handover does not exist, or has expired.
                if gt(timestamp(), sload(handoverSlot)) {
                    mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                    revert(0x1c, 0x04)
                }
                // Set the handover slot to 0.
                sstore(handoverSlot, 0)
            }
            _setOwner(pendingOwner);
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   PUBLIC READ FUNCTIONS                    */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the owner of the contract.
        function owner() public view virtual returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := sload(_OWNER_SLOT)
            }
        }
    
        /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
        function ownershipHandoverExpiresAt(address pendingOwner)
            public
            view
            virtual
            returns (uint256 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the handover slot.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, pendingOwner)
                // Load the handover slot.
                result := sload(keccak256(0x0c, 0x20))
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         MODIFIERS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Marks a function as only callable by the owner.
        modifier onlyOwner() virtual {
            _checkOwner();
            _;
        }
    }
    
  • lib/solady/src/auth/OwnableRoles.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    import {Ownable} from "./Ownable.sol";
    
    /// @notice Simple single owner and multiroles authorization mixin.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
    ///
    /// @dev Note:
    /// This implementation does NOT auto-initialize the owner to `msg.sender`.
    /// You MUST call the `_initializeOwner` in the constructor / initializer.
    ///
    /// While the ownable portion follows
    /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
    /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
    abstract contract OwnableRoles is Ownable {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                           EVENTS                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The `user`'s roles is updated to `roles`.
        /// Each bit of `roles` represents whether the role is set.
        event RolesUpdated(address indexed user, uint256 indexed roles);
    
        /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
        uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
            0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          STORAGE                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The role slot of `user` is given by:
        /// ```
        ///     mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
        ///     let roleSlot := keccak256(0x00, 0x20)
        /// ```
        /// This automatically ignores the upper bits of the `user` in case
        /// they are not clean, as well as keep the `keccak256` under 32-bytes.
        ///
        /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
        uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     INTERNAL FUNCTIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Overwrite the roles directly without authorization guard.
        function _setRoles(address user, uint256 roles) internal virtual {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, user)
                // Store the new value.
                sstore(keccak256(0x0c, 0x20), roles)
                // Emit the {RolesUpdated} event.
                log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
            }
        }
    
        /// @dev Updates the roles directly without authorization guard.
        /// If `on` is true, each set bit of `roles` will be turned on,
        /// otherwise, each set bit of `roles` will be turned off.
        function _updateRoles(address user, uint256 roles, bool on) internal virtual {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, user)
                let roleSlot := keccak256(0x0c, 0x20)
                // Load the current value.
                let current := sload(roleSlot)
                // Compute the updated roles if `on` is true.
                let updated := or(current, roles)
                // Compute the updated roles if `on` is false.
                // Use `and` to compute the intersection of `current` and `roles`,
                // `xor` it with `current` to flip the bits in the intersection.
                if iszero(on) { updated := xor(current, and(current, roles)) }
                // Then, store the new value.
                sstore(roleSlot, updated)
                // Emit the {RolesUpdated} event.
                log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
            }
        }
    
        /// @dev Grants the roles directly without authorization guard.
        /// Each bit of `roles` represents the role to turn on.
        function _grantRoles(address user, uint256 roles) internal virtual {
            _updateRoles(user, roles, true);
        }
    
        /// @dev Removes the roles directly without authorization guard.
        /// Each bit of `roles` represents the role to turn off.
        function _removeRoles(address user, uint256 roles) internal virtual {
            _updateRoles(user, roles, false);
        }
    
        /// @dev Throws if the sender does not have any of the `roles`.
        function _checkRoles(uint256 roles) internal view virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the role slot.
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, caller())
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                    mstore(0x00, 0x82b42900) // `Unauthorized()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    
        /// @dev Throws if the sender is not the owner,
        /// and does not have any of the `roles`.
        /// Checks for ownership first, then lazily checks for roles.
        function _checkOwnerOrRoles(uint256 roles) internal view virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // If the caller is not the stored owner.
                // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                    // Compute the role slot.
                    mstore(0x0c, _ROLE_SLOT_SEED)
                    mstore(0x00, caller())
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                        mstore(0x00, 0x82b42900) // `Unauthorized()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
    
        /// @dev Throws if the sender does not have any of the `roles`,
        /// and is not the owner.
        /// Checks for roles first, then lazily checks for ownership.
        function _checkRolesOrOwner(uint256 roles) internal view virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the role slot.
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, caller())
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                    // If the caller is not the stored owner.
                    // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                    if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                        mstore(0x00, 0x82b42900) // `Unauthorized()`.
                        revert(0x1c, 0x04)
                    }
                }
            }
        }
    
        /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
        /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
        /// Not recommended to be called on-chain.
        /// Made internal to conserve bytecode. Wrap it in a public function if needed.
        function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
            /// @solidity memory-safe-assembly
            assembly {
                for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
                    // We don't need to mask the values of `ordinals`, as Solidity
                    // cleans dirty upper bits when storing variables into memory.
                    roles := or(shl(mload(add(ordinals, i)), 1), roles)
                }
            }
        }
    
        /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
        /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
        /// Not recommended to be called on-chain.
        /// Made internal to conserve bytecode. Wrap it in a public function if needed.
        function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
            /// @solidity memory-safe-assembly
            assembly {
                // Grab the pointer to the free memory.
                ordinals := mload(0x40)
                let ptr := add(ordinals, 0x20)
                let o := 0
                // The absence of lookup tables, De Bruijn, etc., here is intentional for
                // smaller bytecode, as this function is not meant to be called on-chain.
                for { let t := roles } 1 {} {
                    mstore(ptr, o)
                    // `shr` 5 is equivalent to multiplying by 0x20.
                    // Push back into the ordinals array if the bit is set.
                    ptr := add(ptr, shl(5, and(t, 1)))
                    o := add(o, 1)
                    t := shr(o, roles)
                    if iszero(t) { break }
                }
                // Store the length of `ordinals`.
                mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
                // Allocate the memory.
                mstore(0x40, ptr)
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  PUBLIC UPDATE FUNCTIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Allows the owner to grant `user` `roles`.
        /// If the `user` already has a role, then it will be an no-op for the role.
        function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
            _grantRoles(user, roles);
        }
    
        /// @dev Allows the owner to remove `user` `roles`.
        /// If the `user` does not have a role, then it will be an no-op for the role.
        function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
            _removeRoles(user, roles);
        }
    
        /// @dev Allow the caller to remove their own roles.
        /// If the caller does not have a role, then it will be an no-op for the role.
        function renounceRoles(uint256 roles) public payable virtual {
            _removeRoles(msg.sender, roles);
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   PUBLIC READ FUNCTIONS                    */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns the roles of `user`.
        function rolesOf(address user) public view virtual returns (uint256 roles) {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the role slot.
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, user)
                // Load the stored value.
                roles := sload(keccak256(0x0c, 0x20))
            }
        }
    
        /// @dev Returns whether `user` has any of `roles`.
        function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
            return rolesOf(user) & roles != 0;
        }
    
        /// @dev Returns whether `user` has all of `roles`.
        function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
            return rolesOf(user) & roles == roles;
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         MODIFIERS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Marks a function as only callable by an account with `roles`.
        modifier onlyRoles(uint256 roles) virtual {
            _checkRoles(roles);
            _;
        }
    
        /// @dev Marks a function as only callable by the owner or by an account
        /// with `roles`. Checks for ownership first, then lazily checks for roles.
        modifier onlyOwnerOrRoles(uint256 roles) virtual {
            _checkOwnerOrRoles(roles);
            _;
        }
    
        /// @dev Marks a function as only callable by an account with `roles`
        /// or the owner. Checks for roles first, then lazily checks for ownership.
        modifier onlyRolesOrOwner(uint256 roles) virtual {
            _checkRolesOrOwner(roles);
            _;
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       ROLE CONSTANTS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        // IYKYK
    
        uint256 internal constant _ROLE_0 = 1 << 0;
        uint256 internal constant _ROLE_1 = 1 << 1;
        uint256 internal constant _ROLE_2 = 1 << 2;
        uint256 internal constant _ROLE_3 = 1 << 3;
        uint256 internal constant _ROLE_4 = 1 << 4;
        uint256 internal constant _ROLE_5 = 1 << 5;
        uint256 internal constant _ROLE_6 = 1 << 6;
        uint256 internal constant _ROLE_7 = 1 << 7;
        uint256 internal constant _ROLE_8 = 1 << 8;
        uint256 internal constant _ROLE_9 = 1 << 9;
        uint256 internal constant _ROLE_10 = 1 << 10;
        uint256 internal constant _ROLE_11 = 1 << 11;
        uint256 internal constant _ROLE_12 = 1 << 12;
        uint256 internal constant _ROLE_13 = 1 << 13;
        uint256 internal constant _ROLE_14 = 1 << 14;
        uint256 internal constant _ROLE_15 = 1 << 15;
        uint256 internal constant _ROLE_16 = 1 << 16;
        uint256 internal constant _ROLE_17 = 1 << 17;
        uint256 internal constant _ROLE_18 = 1 << 18;
        uint256 internal constant _ROLE_19 = 1 << 19;
        uint256 internal constant _ROLE_20 = 1 << 20;
        uint256 internal constant _ROLE_21 = 1 << 21;
        uint256 internal constant _ROLE_22 = 1 << 22;
        uint256 internal constant _ROLE_23 = 1 << 23;
        uint256 internal constant _ROLE_24 = 1 << 24;
        uint256 internal constant _ROLE_25 = 1 << 25;
        uint256 internal constant _ROLE_26 = 1 << 26;
        uint256 internal constant _ROLE_27 = 1 << 27;
        uint256 internal constant _ROLE_28 = 1 << 28;
        uint256 internal constant _ROLE_29 = 1 << 29;
        uint256 internal constant _ROLE_30 = 1 << 30;
        uint256 internal constant _ROLE_31 = 1 << 31;
        uint256 internal constant _ROLE_32 = 1 << 32;
        uint256 internal constant _ROLE_33 = 1 << 33;
        uint256 internal constant _ROLE_34 = 1 << 34;
        uint256 internal constant _ROLE_35 = 1 << 35;
        uint256 internal constant _ROLE_36 = 1 << 36;
        uint256 internal constant _ROLE_37 = 1 << 37;
        uint256 internal constant _ROLE_38 = 1 << 38;
        uint256 internal constant _ROLE_39 = 1 << 39;
        uint256 internal constant _ROLE_40 = 1 << 40;
        uint256 internal constant _ROLE_41 = 1 << 41;
        uint256 internal constant _ROLE_42 = 1 << 42;
        uint256 internal constant _ROLE_43 = 1 << 43;
        uint256 internal constant _ROLE_44 = 1 << 44;
        uint256 internal constant _ROLE_45 = 1 << 45;
        uint256 internal constant _ROLE_46 = 1 << 46;
        uint256 internal constant _ROLE_47 = 1 << 47;
        uint256 internal constant _ROLE_48 = 1 << 48;
        uint256 internal constant _ROLE_49 = 1 << 49;
        uint256 internal constant _ROLE_50 = 1 << 50;
        uint256 internal constant _ROLE_51 = 1 << 51;
        uint256 internal constant _ROLE_52 = 1 << 52;
        uint256 internal constant _ROLE_53 = 1 << 53;
        uint256 internal constant _ROLE_54 = 1 << 54;
        uint256 internal constant _ROLE_55 = 1 << 55;
        uint256 internal constant _ROLE_56 = 1 << 56;
        uint256 internal constant _ROLE_57 = 1 << 57;
        uint256 internal constant _ROLE_58 = 1 << 58;
        uint256 internal constant _ROLE_59 = 1 << 59;
        uint256 internal constant _ROLE_60 = 1 << 60;
        uint256 internal constant _ROLE_61 = 1 << 61;
        uint256 internal constant _ROLE_62 = 1 << 62;
        uint256 internal constant _ROLE_63 = 1 << 63;
        uint256 internal constant _ROLE_64 = 1 << 64;
        uint256 internal constant _ROLE_65 = 1 << 65;
        uint256 internal constant _ROLE_66 = 1 << 66;
        uint256 internal constant _ROLE_67 = 1 << 67;
        uint256 internal constant _ROLE_68 = 1 << 68;
        uint256 internal constant _ROLE_69 = 1 << 69;
        uint256 internal constant _ROLE_70 = 1 << 70;
        uint256 internal constant _ROLE_71 = 1 << 71;
        uint256 internal constant _ROLE_72 = 1 << 72;
        uint256 internal constant _ROLE_73 = 1 << 73;
        uint256 internal constant _ROLE_74 = 1 << 74;
        uint256 internal constant _ROLE_75 = 1 << 75;
        uint256 internal constant _ROLE_76 = 1 << 76;
        uint256 internal constant _ROLE_77 = 1 << 77;
        uint256 internal constant _ROLE_78 = 1 << 78;
        uint256 internal constant _ROLE_79 = 1 << 79;
        uint256 internal constant _ROLE_80 = 1 << 80;
        uint256 internal constant _ROLE_81 = 1 << 81;
        uint256 internal constant _ROLE_82 = 1 << 82;
        uint256 internal constant _ROLE_83 = 1 << 83;
        uint256 internal constant _ROLE_84 = 1 << 84;
        uint256 internal constant _ROLE_85 = 1 << 85;
        uint256 internal constant _ROLE_86 = 1 << 86;
        uint256 internal constant _ROLE_87 = 1 << 87;
        uint256 internal constant _ROLE_88 = 1 << 88;
        uint256 internal constant _ROLE_89 = 1 << 89;
        uint256 internal constant _ROLE_90 = 1 << 90;
        uint256 internal constant _ROLE_91 = 1 << 91;
        uint256 internal constant _ROLE_92 = 1 << 92;
        uint256 internal constant _ROLE_93 = 1 << 93;
        uint256 internal constant _ROLE_94 = 1 << 94;
        uint256 internal constant _ROLE_95 = 1 << 95;
        uint256 internal constant _ROLE_96 = 1 << 96;
        uint256 internal constant _ROLE_97 = 1 << 97;
        uint256 internal constant _ROLE_98 = 1 << 98;
        uint256 internal constant _ROLE_99 = 1 << 99;
        uint256 internal constant _ROLE_100 = 1 << 100;
        uint256 internal constant _ROLE_101 = 1 << 101;
        uint256 internal constant _ROLE_102 = 1 << 102;
        uint256 internal constant _ROLE_103 = 1 << 103;
        uint256 internal constant _ROLE_104 = 1 << 104;
        uint256 internal constant _ROLE_105 = 1 << 105;
        uint256 internal constant _ROLE_106 = 1 << 106;
        uint256 internal constant _ROLE_107 = 1 << 107;
        uint256 internal constant _ROLE_108 = 1 << 108;
        uint256 internal constant _ROLE_109 = 1 << 109;
        uint256 internal constant _ROLE_110 = 1 << 110;
        uint256 internal constant _ROLE_111 = 1 << 111;
        uint256 internal constant _ROLE_112 = 1 << 112;
        uint256 internal constant _ROLE_113 = 1 << 113;
        uint256 internal constant _ROLE_114 = 1 << 114;
        uint256 internal constant _ROLE_115 = 1 << 115;
        uint256 internal constant _ROLE_116 = 1 << 116;
        uint256 internal constant _ROLE_117 = 1 << 117;
        uint256 internal constant _ROLE_118 = 1 << 118;
        uint256 internal constant _ROLE_119 = 1 << 119;
        uint256 internal constant _ROLE_120 = 1 << 120;
        uint256 internal constant _ROLE_121 = 1 << 121;
        uint256 internal constant _ROLE_122 = 1 << 122;
        uint256 internal constant _ROLE_123 = 1 << 123;
        uint256 internal constant _ROLE_124 = 1 << 124;
        uint256 internal constant _ROLE_125 = 1 << 125;
        uint256 internal constant _ROLE_126 = 1 << 126;
        uint256 internal constant _ROLE_127 = 1 << 127;
        uint256 internal constant _ROLE_128 = 1 << 128;
        uint256 internal constant _ROLE_129 = 1 << 129;
        uint256 internal constant _ROLE_130 = 1 << 130;
        uint256 internal constant _ROLE_131 = 1 << 131;
        uint256 internal constant _ROLE_132 = 1 << 132;
        uint256 internal constant _ROLE_133 = 1 << 133;
        uint256 internal constant _ROLE_134 = 1 << 134;
        uint256 internal constant _ROLE_135 = 1 << 135;
        uint256 internal constant _ROLE_136 = 1 << 136;
        uint256 internal constant _ROLE_137 = 1 << 137;
        uint256 internal constant _ROLE_138 = 1 << 138;
        uint256 internal constant _ROLE_139 = 1 << 139;
        uint256 internal constant _ROLE_140 = 1 << 140;
        uint256 internal constant _ROLE_141 = 1 << 141;
        uint256 internal constant _ROLE_142 = 1 << 142;
        uint256 internal constant _ROLE_143 = 1 << 143;
        uint256 internal constant _ROLE_144 = 1 << 144;
        uint256 internal constant _ROLE_145 = 1 << 145;
        uint256 internal constant _ROLE_146 = 1 << 146;
        uint256 internal constant _ROLE_147 = 1 << 147;
        uint256 internal constant _ROLE_148 = 1 << 148;
        uint256 internal constant _ROLE_149 = 1 << 149;
        uint256 internal constant _ROLE_150 = 1 << 150;
        uint256 internal constant _ROLE_151 = 1 << 151;
        uint256 internal constant _ROLE_152 = 1 << 152;
        uint256 internal constant _ROLE_153 = 1 << 153;
        uint256 internal constant _ROLE_154 = 1 << 154;
        uint256 internal constant _ROLE_155 = 1 << 155;
        uint256 internal constant _ROLE_156 = 1 << 156;
        uint256 internal constant _ROLE_157 = 1 << 157;
        uint256 internal constant _ROLE_158 = 1 << 158;
        uint256 internal constant _ROLE_159 = 1 << 159;
        uint256 internal constant _ROLE_160 = 1 << 160;
        uint256 internal constant _ROLE_161 = 1 << 161;
        uint256 internal constant _ROLE_162 = 1 << 162;
        uint256 internal constant _ROLE_163 = 1 << 163;
        uint256 internal constant _ROLE_164 = 1 << 164;
        uint256 internal constant _ROLE_165 = 1 << 165;
        uint256 internal constant _ROLE_166 = 1 << 166;
        uint256 internal constant _ROLE_167 = 1 << 167;
        uint256 internal constant _ROLE_168 = 1 << 168;
        uint256 internal constant _ROLE_169 = 1 << 169;
        uint256 internal constant _ROLE_170 = 1 << 170;
        uint256 internal constant _ROLE_171 = 1 << 171;
        uint256 internal constant _ROLE_172 = 1 << 172;
        uint256 internal constant _ROLE_173 = 1 << 173;
        uint256 internal constant _ROLE_174 = 1 << 174;
        uint256 internal constant _ROLE_175 = 1 << 175;
        uint256 internal constant _ROLE_176 = 1 << 176;
        uint256 internal constant _ROLE_177 = 1 << 177;
        uint256 internal constant _ROLE_178 = 1 << 178;
        uint256 internal constant _ROLE_179 = 1 << 179;
        uint256 internal constant _ROLE_180 = 1 << 180;
        uint256 internal constant _ROLE_181 = 1 << 181;
        uint256 internal constant _ROLE_182 = 1 << 182;
        uint256 internal constant _ROLE_183 = 1 << 183;
        uint256 internal constant _ROLE_184 = 1 << 184;
        uint256 internal constant _ROLE_185 = 1 << 185;
        uint256 internal constant _ROLE_186 = 1 << 186;
        uint256 internal constant _ROLE_187 = 1 << 187;
        uint256 internal constant _ROLE_188 = 1 << 188;
        uint256 internal constant _ROLE_189 = 1 << 189;
        uint256 internal constant _ROLE_190 = 1 << 190;
        uint256 internal constant _ROLE_191 = 1 << 191;
        uint256 internal constant _ROLE_192 = 1 << 192;
        uint256 internal constant _ROLE_193 = 1 << 193;
        uint256 internal constant _ROLE_194 = 1 << 194;
        uint256 internal constant _ROLE_195 = 1 << 195;
        uint256 internal constant _ROLE_196 = 1 << 196;
        uint256 internal constant _ROLE_197 = 1 << 197;
        uint256 internal constant _ROLE_198 = 1 << 198;
        uint256 internal constant _ROLE_199 = 1 << 199;
        uint256 internal constant _ROLE_200 = 1 << 200;
        uint256 internal constant _ROLE_201 = 1 << 201;
        uint256 internal constant _ROLE_202 = 1 << 202;
        uint256 internal constant _ROLE_203 = 1 << 203;
        uint256 internal constant _ROLE_204 = 1 << 204;
        uint256 internal constant _ROLE_205 = 1 << 205;
        uint256 internal constant _ROLE_206 = 1 << 206;
        uint256 internal constant _ROLE_207 = 1 << 207;
        uint256 internal constant _ROLE_208 = 1 << 208;
        uint256 internal constant _ROLE_209 = 1 << 209;
        uint256 internal constant _ROLE_210 = 1 << 210;
        uint256 internal constant _ROLE_211 = 1 << 211;
        uint256 internal constant _ROLE_212 = 1 << 212;
        uint256 internal constant _ROLE_213 = 1 << 213;
        uint256 internal constant _ROLE_214 = 1 << 214;
        uint256 internal constant _ROLE_215 = 1 << 215;
        uint256 internal constant _ROLE_216 = 1 << 216;
        uint256 internal constant _ROLE_217 = 1 << 217;
        uint256 internal constant _ROLE_218 = 1 << 218;
        uint256 internal constant _ROLE_219 = 1 << 219;
        uint256 internal constant _ROLE_220 = 1 << 220;
        uint256 internal constant _ROLE_221 = 1 << 221;
        uint256 internal constant _ROLE_222 = 1 << 222;
        uint256 internal constant _ROLE_223 = 1 << 223;
        uint256 internal constant _ROLE_224 = 1 << 224;
        uint256 internal constant _ROLE_225 = 1 << 225;
        uint256 internal constant _ROLE_226 = 1 << 226;
        uint256 internal constant _ROLE_227 = 1 << 227;
        uint256 internal constant _ROLE_228 = 1 << 228;
        uint256 internal constant _ROLE_229 = 1 << 229;
        uint256 internal constant _ROLE_230 = 1 << 230;
        uint256 internal constant _ROLE_231 = 1 << 231;
        uint256 internal constant _ROLE_232 = 1 << 232;
        uint256 internal constant _ROLE_233 = 1 << 233;
        uint256 internal constant _ROLE_234 = 1 << 234;
        uint256 internal constant _ROLE_235 = 1 << 235;
        uint256 internal constant _ROLE_236 = 1 << 236;
        uint256 internal constant _ROLE_237 = 1 << 237;
        uint256 internal constant _ROLE_238 = 1 << 238;
        uint256 internal constant _ROLE_239 = 1 << 239;
        uint256 internal constant _ROLE_240 = 1 << 240;
        uint256 internal constant _ROLE_241 = 1 << 241;
        uint256 internal constant _ROLE_242 = 1 << 242;
        uint256 internal constant _ROLE_243 = 1 << 243;
        uint256 internal constant _ROLE_244 = 1 << 244;
        uint256 internal constant _ROLE_245 = 1 << 245;
        uint256 internal constant _ROLE_246 = 1 << 246;
        uint256 internal constant _ROLE_247 = 1 << 247;
        uint256 internal constant _ROLE_248 = 1 << 248;
        uint256 internal constant _ROLE_249 = 1 << 249;
        uint256 internal constant _ROLE_250 = 1 << 250;
        uint256 internal constant _ROLE_251 = 1 << 251;
        uint256 internal constant _ROLE_252 = 1 << 252;
        uint256 internal constant _ROLE_253 = 1 << 253;
        uint256 internal constant _ROLE_254 = 1 << 254;
        uint256 internal constant _ROLE_255 = 1 << 255;
    }
    
  • lib/solady/src/utils/ECDSA.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Gas optimized ECDSA wrapper.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
    ///
    /// @dev Note:
    /// - The recovery functions use the ecrecover precompile (0x1).
    /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
    ///   This is for more safety by default.
    ///   Use the `tryRecover` variants if you need to get the zero address back
    ///   upon recovery failure instead.
    /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
    ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
    ///   See: https://eips.ethereum.org/EIPS/eip-2098
    ///   This is for calldata efficiency on smart accounts prevalent on L2s.
    ///
    /// WARNING! Do NOT directly use signatures as unique identifiers:
    /// - The recovery operations do NOT check if a signature is non-malleable.
    /// - Use a nonce in the digest to prevent replay attacks on the same contract.
    /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
    ///   EIP-712 also enables readable signing of typed data for better user safety.
    /// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
    library ECDSA {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The order of the secp256k1 elliptic curve.
        uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
    
        /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
        uint256 private constant _HALF_N_PLUS_1 =
            0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        CUSTOM ERRORS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev The signature is invalid.
        error InvalidSignature();
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                    RECOVERY OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                for { let m := mload(0x40) } 1 {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                } {
                    switch mload(signature)
                    case 64 {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    }
                    case 65 {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    }
                    default { continue }
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    if returndatasize() { break }
                }
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function recoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                for { let m := mload(0x40) } 1 {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                } {
                    switch signature.length
                    case 64 {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    }
                    case 65 {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                    }
                    default { continue }
                    mstore(0x00, hash)
                    result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    if returndatasize() { break }
                }
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   TRY-RECOVER OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        // WARNING!
        // These functions will NOT revert upon recovery failure.
        // Instead, they will return the zero address upon recovery failure.
        // It is critical that the returned address is NEVER compared against
        // a zero address (e.g. an uninitialized address variable).
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function tryRecover(bytes32 hash, bytes memory signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                for { let m := mload(0x40) } 1 {} {
                    switch mload(signature)
                    case 64 {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    }
                    case 65 {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    }
                    default { break }
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                    mstore(0x60, 0) // Restore the zero slot.
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    result := mload(xor(0x60, returndatasize()))
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                for { let m := mload(0x40) } 1 {} {
                    switch signature.length
                    case 64 {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    }
                    case 65 {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                    }
                    default { break }
                    mstore(0x00, hash)
                    pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                    mstore(0x60, 0) // Restore the zero slot.
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    result := mload(xor(0x60, returndatasize()))
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     HASHING OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns an Ethereum Signed Message, created from a `hash`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, hash) // Store into scratch space for keccak256.
                mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
                result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
            }
        }
    
        /// @dev Returns an Ethereum Signed Message, created from `s`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        /// Note: Supports lengths of `s` up to 999999 bytes.
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let sLength := mload(s)
                let o := 0x20
                mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
                mstore(0x00, 0x00)
                // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                for { let temp := sLength } 1 {} {
                    o := sub(o, 1)
                    mstore8(o, add(48, mod(temp, 10)))
                    temp := div(temp, 10)
                    if iszero(temp) { break }
                }
                let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                mstore(s, sLength) // Restore the length.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  CANONICAL HASH FUNCTIONS                  */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        // The following functions returns the hash of the signature in it's canonicalized format,
        // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
        // If `s` is greater than `N / 2` then it will be converted to `N - s`
        // and the `v` value will be flipped.
        // If the signature has an invalid length, or if `v` is invalid,
        // a uniquely corrupt hash will be returned.
        // These functions are useful for "poor-mans-VRF".
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let l := mload(signature)
                for {} 1 {} {
                    mstore(0x00, mload(add(signature, 0x20))) // `r`.
                    let s := mload(add(signature, 0x40))
                    let v := mload(add(signature, 0x41))
                    if eq(l, 64) {
                        v := add(shr(255, s), 27)
                        s := shr(1, shl(1, s))
                    }
                    if iszero(lt(s, _HALF_N_PLUS_1)) {
                        v := xor(v, 7)
                        s := sub(N, s)
                    }
                    mstore(0x21, v)
                    mstore(0x20, s)
                    result := keccak256(0x00, 0x41)
                    mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                    break
                }
    
                // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                if iszero(lt(sub(l, 64), 2)) {
                    // `bytes4(keccak256("InvalidSignatureLength"))`.
                    result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
                }
            }
        }
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHashCalldata(bytes calldata signature)
            internal
            pure
            returns (bytes32 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                for {} 1 {} {
                    mstore(0x00, calldataload(signature.offset)) // `r`.
                    let s := calldataload(add(signature.offset, 0x20))
                    let v := calldataload(add(signature.offset, 0x21))
                    if eq(signature.length, 64) {
                        v := add(shr(255, s), 27)
                        s := shr(1, shl(1, s))
                    }
                    if iszero(lt(s, _HALF_N_PLUS_1)) {
                        v := xor(v, 7)
                        s := sub(N, s)
                    }
                    mstore(0x21, v)
                    mstore(0x20, s)
                    result := keccak256(0x00, 0x41)
                    mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                    break
                }
                // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                if iszero(lt(sub(signature.length, 64), 2)) {
                    calldatacopy(mload(0x40), signature.offset, signature.length)
                    // `bytes4(keccak256("InvalidSignatureLength"))`.
                    result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
                }
            }
        }
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, r) // `r`.
                let v := add(shr(255, vs), 27)
                let s := shr(1, shl(1, vs))
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
            }
        }
    
        /// @dev Returns the canonical hash of `signature`.
        function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, r) // `r`.
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns an empty calldata bytes.
        function emptySignature() internal pure returns (bytes calldata signature) {
            /// @solidity memory-safe-assembly
            assembly {
                signature.length := 0
            }
        }
    }
    
  • lib/solady/src/utils/MerkleProofLib.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    
    /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
    library MerkleProofLib {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*            MERKLE PROOF VERIFICATION OPERATIONS            */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
            internal
            pure
            returns (bool isValid)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if mload(proof) {
                    // Initialize `offset` to the offset of `proof` elements in memory.
                    let offset := add(proof, 0x20)
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(offset, shl(5, mload(proof)))
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, mload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), mload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root)
            }
        }
    
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
            internal
            pure
            returns (bool isValid)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if proof.length {
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(proof.offset, shl(5, proof.length))
                    // Initialize `offset` to the offset of `proof` in the calldata.
                    let offset := proof.offset
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, calldataload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), calldataload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root)
            }
        }
    
        /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
        /// given `proof` and `flags`.
        ///
        /// Note:
        /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
        ///   will always return false.
        /// - The sum of the lengths of `proof` and `leaves` must never overflow.
        /// - Any non-zero word in the `flags` array is treated as true.
        /// - The memory offset of `proof` must be non-zero
        ///   (i.e. `proof` is not pointing to the scratch space).
        function verifyMultiProof(
            bytes32[] memory proof,
            bytes32 root,
            bytes32[] memory leaves,
            bool[] memory flags
        ) internal pure returns (bool isValid) {
            // Rebuilds the root by consuming and producing values on a queue.
            // The queue starts with the `leaves` array, and goes into a `hashes` array.
            // After the process, the last element on the queue is verified
            // to be equal to the `root`.
            //
            // The `flags` array denotes whether the sibling
            // should be popped from the queue (`flag == true`), or
            // should be popped from the `proof` (`flag == false`).
            /// @solidity memory-safe-assembly
            assembly {
                // Cache the lengths of the arrays.
                let leavesLength := mload(leaves)
                let proofLength := mload(proof)
                let flagsLength := mload(flags)
    
                // Advance the pointers of the arrays to point to the data.
                leaves := add(0x20, leaves)
                proof := add(0x20, proof)
                flags := add(0x20, flags)
    
                // If the number of flags is correct.
                for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                    // For the case where `proof.length + leaves.length == 1`.
                    if iszero(flagsLength) {
                        // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                        isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                        break
                    }
    
                    // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                    let proofEnd := add(proof, shl(5, proofLength))
                    // We can use the free memory space for the queue.
                    // We don't need to allocate, since the queue is temporary.
                    let hashesFront := mload(0x40)
                    // Copy the leaves into the hashes.
                    // Sometimes, a little memory expansion costs less than branching.
                    // Should cost less, even with a high free memory offset of 0x7d00.
                    leavesLength := shl(5, leavesLength)
                    for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                        mstore(add(hashesFront, i), mload(add(leaves, i)))
                    }
                    // Compute the back of the hashes.
                    let hashesBack := add(hashesFront, leavesLength)
                    // This is the end of the memory for the queue.
                    // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                    flagsLength := add(hashesBack, shl(5, flagsLength))
    
                    for {} 1 {} {
                        // Pop from `hashes`.
                        let a := mload(hashesFront)
                        // Pop from `hashes`.
                        let b := mload(add(hashesFront, 0x20))
                        hashesFront := add(hashesFront, 0x40)
    
                        // If the flag is false, load the next proof,
                        // else, pops from the queue.
                        if iszero(mload(flags)) {
                            // Loads the next proof.
                            b := mload(proof)
                            proof := add(proof, 0x20)
                            // Unpop from `hashes`.
                            hashesFront := sub(hashesFront, 0x20)
                        }
    
                        // Advance to the next flag.
                        flags := add(flags, 0x20)
    
                        // Slot of `a` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(a, b))
                        // Hash the scratch space and push the result onto the queue.
                        mstore(scratch, a)
                        mstore(xor(scratch, 0x20), b)
                        mstore(hashesBack, keccak256(0x00, 0x40))
                        hashesBack := add(hashesBack, 0x20)
                        if iszero(lt(hashesBack, flagsLength)) { break }
                    }
                    isValid :=
                        and(
                            // Checks if the last value in the queue is same as the root.
                            eq(mload(sub(hashesBack, 0x20)), root),
                            // And whether all the proofs are used, if required.
                            eq(proofEnd, proof)
                        )
                    break
                }
            }
        }
    
        /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
        /// given `proof` and `flags`.
        ///
        /// Note:
        /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
        ///   will always return false.
        /// - Any non-zero word in the `flags` array is treated as true.
        /// - The calldata offset of `proof` must be non-zero
        ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
        function verifyMultiProofCalldata(
            bytes32[] calldata proof,
            bytes32 root,
            bytes32[] calldata leaves,
            bool[] calldata flags
        ) internal pure returns (bool isValid) {
            // Rebuilds the root by consuming and producing values on a queue.
            // The queue starts with the `leaves` array, and goes into a `hashes` array.
            // After the process, the last element on the queue is verified
            // to be equal to the `root`.
            //
            // The `flags` array denotes whether the sibling
            // should be popped from the queue (`flag == true`), or
            // should be popped from the `proof` (`flag == false`).
            /// @solidity memory-safe-assembly
            assembly {
                // If the number of flags is correct.
                for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                    // For the case where `proof.length + leaves.length == 1`.
                    if iszero(flags.length) {
                        // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                        // forgefmt: disable-next-item
                        isValid := eq(
                            calldataload(
                                xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                            ),
                            root
                        )
                        break
                    }
    
                    // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                    let proofEnd := add(proof.offset, shl(5, proof.length))
                    // We can use the free memory space for the queue.
                    // We don't need to allocate, since the queue is temporary.
                    let hashesFront := mload(0x40)
                    // Copy the leaves into the hashes.
                    // Sometimes, a little memory expansion costs less than branching.
                    // Should cost less, even with a high free memory offset of 0x7d00.
                    calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                    // Compute the back of the hashes.
                    let hashesBack := add(hashesFront, shl(5, leaves.length))
                    // This is the end of the memory for the queue.
                    // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                    flags.length := add(hashesBack, shl(5, flags.length))
    
                    // We don't need to make a copy of `proof.offset` or `flags.offset`,
                    // as they are pass-by-value (this trick may not always save gas).
    
                    for {} 1 {} {
                        // Pop from `hashes`.
                        let a := mload(hashesFront)
                        // Pop from `hashes`.
                        let b := mload(add(hashesFront, 0x20))
                        hashesFront := add(hashesFront, 0x40)
    
                        // If the flag is false, load the next proof,
                        // else, pops from the queue.
                        if iszero(calldataload(flags.offset)) {
                            // Loads the next proof.
                            b := calldataload(proof.offset)
                            proof.offset := add(proof.offset, 0x20)
                            // Unpop from `hashes`.
                            hashesFront := sub(hashesFront, 0x20)
                        }
    
                        // Advance to the next flag offset.
                        flags.offset := add(flags.offset, 0x20)
    
                        // Slot of `a` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(a, b))
                        // Hash the scratch space and push the result onto the queue.
                        mstore(scratch, a)
                        mstore(xor(scratch, 0x20), b)
                        mstore(hashesBack, keccak256(0x00, 0x40))
                        hashesBack := add(hashesBack, 0x20)
                        if iszero(lt(hashesBack, flags.length)) { break }
                    }
                    isValid :=
                        and(
                            // Checks if the last value in the queue is same as the root.
                            eq(mload(sub(hashesBack, 0x20)), root),
                            // And whether all the proofs are used, if required.
                            eq(proofEnd, proof.offset)
                        )
                    break
                }
            }
        }
    
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
    
        /// @dev Returns an empty calldata bytes32 array.
        function emptyProof() internal pure returns (bytes32[] calldata proof) {
            /// @solidity memory-safe-assembly
            assembly {
                proof.length := 0
            }
        }
    
        /// @dev Returns an empty calldata bytes32 array.
        function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
            /// @solidity memory-safe-assembly
            assembly {
                leaves.length := 0
            }
        }
    
        /// @dev Returns an empty calldata bool array.
        function emptyFlags() internal pure returns (bool[] calldata flags) {
            /// @solidity memory-safe-assembly
            assembly {
                flags.length := 0
            }
        }
    }
    
  • src/ICliqueLock.sol
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    
    struct StreamConfig {
        address recipient;
        address revokeAuthority;
        address token;
        uint256 amount;
        uint256 startTime;
        uint256 cliffTime;
        uint256 endTime;
        uint256 startUnlockPercentage;
        uint256 cliffUnlockPercentage;
        uint256 pieceDuration;
        address verifier;
        uint256 id;
    }
    
    /**
     * @dev Stream struct containing all parameters for a token stream
     * @param recipient Address that will receive the streamed tokens
     * @param revokeAuthority Address with permission to revoke the stream
     * @param token Address of the ERC20 token being streamed
     * @param amount Total amount of tokens in the stream
     * @param claimedAmount Amount of tokens already claimed
     * @param startTime Timestamp when the stream begins
     * @param cliffTime Timestamp when the cliff period ends
     * @param endTime Timestamp when the stream ends
     * @param startUnlockPercentage Percentage of tokens unlocked at start (18 decimals)
     * @param cliffUnlockPercentage Percentage of tokens unlocked at cliff (18 decimals)
     * @param pieceDuration Duration of each vesting piece (0 for linear vesting)
     */
    struct Stream {
        address recipient;
        address revokeAuthority;
        address token;
        uint256 amount;
        uint256 claimedAmount;
        uint256 startTime;
        uint256 cliffTime;
        uint256 endTime;
        uint256 startUnlockPercentage;
        uint256 cliffUnlockPercentage;
        uint256 pieceDuration;
    }
    
    struct Badge {
        address verifier;
        uint256 id;
    }
    
    interface IVerifier {
        function ownerOf(uint256 id) external view returns (address);
    }
    
    interface ICliqueLock {
        // EXTERNAL FUNCTIONS - STREAM CREATION
        /**
         * @notice Creates a stream using standard ERC20 approve
         * @dev Requires prior approval of token transfer
         * @param config StreamConfig struct containing the stream parameters
         * @return streamId Unique identifier of the created stream
         */
        function createStream(StreamConfig calldata config) external returns (uint256 streamId);
    
        // EXTERNAL FUNCTIONS - STREAM REVOCATION
        /**
         * @notice Revokes a stream, returning unclaimed tokens to the revokeAuthority
         * @dev Can only be called by the designated revokeAuthority
         * @param streamId ID of the stream to revoke
         */
        function revokeStream(uint256 streamId) external;
    
        /**
         * @notice Returns the stream information for a given streamId
         * @param streamId ID of the stream to query
         * @return stream Stream struct containing the stream information
         */
        function streams(uint256 streamId) external view returns (Stream memory stream);
    }
    
ABI · 59 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "_signer",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_projectAdmin",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_vault",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "AlreadyClaimed",
    "type": "error",
    "inputs": []
  },
  {
    "name": "AlreadyInitialized",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InvalidMerkleProof",
    "type": "error",
    "inputs": [
      {
        "name": "root",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "name": "InvalidMerkleRoot",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InvalidReleasedAmount",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InvalidSignature",
    "type": "error",
    "inputs": []
  },
  {
    "name": "NewOwnerIsZeroAddress",
    "type": "error",
    "inputs": []
  },
  {
    "name": "NoHandoverRequest",
    "type": "error",
    "inputs": []
  },
  {
    "name": "NoNftToClaim",
    "type": "error",
    "inputs": []
  },
  {
    "name": "NotActive",
    "type": "error",
    "inputs": []
  },
  {
    "name": "SafeERC20FailedOperation",
    "type": "error",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "Unauthorized",
    "type": "error",
    "inputs": []
  },
  {
    "name": "WithdrawFailed",
    "type": "error",
    "inputs": []
  },
  {
    "name": "ZeroAddress",
    "type": "error",
    "inputs": []
  },
  {
    "name": "AirdropClaimed",
    "type": "event",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "root",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "FeeSet",
    "type": "event",
    "inputs": [
      {
        "name": "fee",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MerkleConfigurationUpdate",
    "type": "event",
    "inputs": [
      {
        "name": "root",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "configurator",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      }
    ],
    "anonymous": false
  },
  {
    "name": "OwnershipHandoverCanceled",
    "type": "event",
    "inputs": [
      {
        "name": "pendingOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "OwnershipHandoverRequested",
    "type": "event",
    "inputs": [
      {
        "name": "pendingOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "OwnershipTransferred",
    "type": "event",
    "inputs": [
      {
        "name": "oldOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "newOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "RolesUpdated",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "roles",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "VaultSet",
    "type": "event",
    "inputs": [
      {
        "name": "vault",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Withdraw",
    "type": "event",
    "inputs": [
      {
        "name": "to",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "type": "fallback",
    "stateMutability": "payable"
  },
  {
    "name": "PROJECT_ADMIN",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "active",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "cancelOwnershipHandover",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "claim",
    "type": "function",
    "inputs": [
      {
        "name": "_proof",
        "type": "bytes32[]",
        "internalType": "bytes32[]"
      },
      {
        "name": "_signature",
        "type": "bytes",
        "internalType": "bytes"
      },
      {
        "name": "_amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_released",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_onBehalfOf",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_extra",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "claimWithNft",
    "type": "function",
    "inputs": [
      {
        "name": "_nfts",
        "type": "address[]",
        "internalType": "address[]"
      },
      {
        "name": "_tokenIds",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "claimed",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "lastClaimedBlock",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "claimedAmount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "completeOwnershipHandover",
    "type": "function",
    "inputs": [
      {
        "name": "pendingOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "fee",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "grantRoles",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "roles",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "hasAllRoles",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "roles",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "hasAnyRole",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "roles",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "merkleConfiguration",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "nftAllocation",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "nftClaimed",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "owner",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "result",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "ownershipHandoverExpiresAt",
    "type": "function",
    "inputs": [
      {
        "name": "pendingOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "result",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "renounceOwnership",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "renounceRoles",
    "type": "function",
    "inputs": [
      {
        "name": "roles",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "requestOwnershipHandover",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "revokeRoles",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "roles",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "rolesOf",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "roles",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "setClaimRoot",
    "type": "function",
    "inputs": [
      {
        "name": "_claimRoot",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "_configurator",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setFee",
    "type": "function",
    "inputs": [
      {
        "name": "_fee",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setNftAllocation",
    "type": "function",
    "inputs": [
      {
        "name": "_nfts",
        "type": "address[]",
        "internalType": "address[]"
      },
      {
        "name": "_allocations",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setSigner",
    "type": "function",
    "inputs": [
      {
        "name": "_signer",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setVault",
    "type": "function",
    "inputs": [
      {
        "name": "_vault",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "signer",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "toggleActive",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "token",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "transferOwnership",
    "type": "function",
    "inputs": [
      {
        "name": "newOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "vault",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "version",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "string",
        "internalType": "string"
      }
    ],
    "stateMutability": "pure"
  },
  {
    "name": "withdrawFee",
    "type": "function",
    "inputs": [
      {
        "name": "_recipient",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "receive",
    "stateMutability": "payable"
  }
]