ETH Price: $2,055.73 (+5.31%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Stake232774782025-09-02 19:22:11182 days ago1756840931IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000084840.30954715
Stake232174002025-08-25 10:03:11191 days ago1756116191IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000029380.17922336
Stake232085122025-08-24 4:18:35192 days ago1756009115IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000039130.15388263
Stake232028792025-08-23 9:26:11193 days ago1755941171IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000040810.24191044
Stake231986732025-08-22 19:21:11193 days ago1755890471IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000411841.50247779
Stake231866922025-08-21 3:12:35195 days ago1755745955IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000863512.26536014
Stake231839452025-08-20 18:00:47195 days ago1755712847IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000193930.70750522
Stake231801272025-08-20 5:13:59196 days ago1755666839IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000025470.16471732
Stake231773952025-08-19 20:05:35196 days ago1755633935IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000660551.97134878
Stake231765132025-08-19 17:08:23196 days ago1755623303IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000797452.34645885
Stake231756962025-08-19 14:24:11196 days ago1755613451IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000184651.09434298
Stake231752792025-08-19 13:00:23197 days ago1755608423IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000685181.47334366
Stake231744782025-08-19 10:19:47197 days ago1755598787IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000085010.22255247
Stake231744592025-08-19 10:15:59197 days ago1755598559IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000078830.20637757
Stake231710282025-08-18 22:47:23197 days ago1755557243IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000179650.7200266
Stake231688412025-08-18 15:28:59197 days ago1755530939IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000153610.91037006
Stake231632882025-08-17 20:52:59198 days ago1755463979IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.00059092.15572526
Stake231600842025-08-17 10:08:47199 days ago1755425327IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000556742.18921211
Stake231599002025-08-17 9:31:59199 days ago1755423119IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000041650.2468788
Stake231548582025-08-16 16:40:47199 days ago1755362447IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000167470.9924923
Stake231536792025-08-16 12:44:23200 days ago1755348263IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000030810.18261569
Stake231527212025-08-16 9:31:59200 days ago1755336719IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.00013730.71839801
Stake231502762025-08-16 1:19:23200 days ago1755307163IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000051380.30454527
Stake231481552025-08-15 18:13:47200 days ago1755281627IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.00010050.59561374
Stake231467622025-08-15 13:33:59200 days ago1755264839IN
0xD4e3E7A9...3eaf2e7A4
0 ETH0.000551452.01199403
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
On ERC721Receive...232774782025-09-02 19:22:11182 days ago1756840931
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...232774782025-09-02 19:22:11182 days ago1756840931
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...232774782025-09-02 19:22:11182 days ago1756840931
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...232774782025-09-02 19:22:11182 days ago1756840931
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...232174002025-08-25 10:03:11191 days ago1756116191
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...232174002025-08-25 10:03:11191 days ago1756116191
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...232085122025-08-24 4:18:35192 days ago1756009115
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...232085122025-08-24 4:18:35192 days ago1756009115
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...232085122025-08-24 4:18:35192 days ago1756009115
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...232085122025-08-24 4:18:35192 days ago1756009115
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...232028792025-08-23 9:26:11193 days ago1755941171
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...232028792025-08-23 9:26:11193 days ago1755941171
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231986732025-08-22 19:21:11193 days ago1755890471
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...231986732025-08-22 19:21:11193 days ago1755890471
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231986732025-08-22 19:21:11193 days ago1755890471
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...231986732025-08-22 19:21:11193 days ago1755890471
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231866922025-08-21 3:12:35195 days ago1755745955
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...231866922025-08-21 3:12:35195 days ago1755745955
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231866922025-08-21 3:12:35195 days ago1755745955
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...231866922025-08-21 3:12:35195 days ago1755745955
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231866922025-08-21 3:12:35195 days ago1755745955
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...231866922025-08-21 3:12:35195 days ago1755745955
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231839452025-08-20 18:00:47195 days ago1755712847
0xD4e3E7A9...3eaf2e7A4
0 ETH
Safe Transfer Fr...231839452025-08-20 18:00:47195 days ago1755712847
0xD4e3E7A9...3eaf2e7A4
0 ETH
On ERC721Receive...231839452025-08-20 18:00:47195 days ago1755712847
0xD4e3E7A9...3eaf2e7A4
0 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ERC721VaultLockable

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**
 * @title ERC721VaultLockable
 * @author RebaseRicky
 * @notice A pre-mainnet launch staking vault for Tapout Genesis NFTs that receives and distributes ETH rewards.
 * @dev Users can stake NFTs to earn ETH rewards, which are claimable at any time.
 *      However, NFT withdrawals are disabled by default and can only be enabled by an admin,
 *      incentivizing users to keep their NFTs locked in the vault.
 */
contract ERC721VaultLockable is IERC721Receiver, ReentrancyGuard, AccessControl {
    using FixedPointMathLib for uint256;

    // --- Roles ---
    bytes32 public constant DELEGATE_ROLE = keccak256("DELEGATE_ROLE");

    // --- State Variables ---
    IERC721 public nft;
    address public treasury;

    uint256 public totalSupply;
    uint256 private constant _MULTIPLIER = 1e18;
    uint256 public rewardIndex;

    // The single control flag for the pre-launch lock-in period.
    bool public withdrawalsEnabled;

    // Mappings
    mapping(address => uint256) public balanceOf;
    mapping(address => uint256) private rewardIndexOf;
    mapping(address => uint256) private earned;
    mapping(address => uint256) public claimedRewards;
    mapping(address => uint256[]) private stakedTokenIds;
    mapping(uint256 => address) private stakedTokenOwner;
    mapping(address => mapping(uint256 => uint256)) private tokenIdToIndex;

    // --- Events ---
    event Staked(address indexed user, uint256 indexed tokenId);
    event Unstaked(address indexed user, uint256 indexed tokenId);
    event Claimed(address indexed user, uint256 amount, uint256 totalClaimed);
    event Received(uint256 amount, uint256 rewardIndex);
    event RewardsUpdated(address indexed user, uint256 newEarned, uint256 newIndex);
    event WithdrawalsEnabledSet(bool enabled);
    event TreasuryUpdated(address indexed newTreasury);
    event NFTContractSet(address indexed nftContract);

    // --- Errors ---
    error NotNFTOwner();
    error NFTNotEligibleForStaking();
    error NFTNotStaked();
    error NFTNotStakedByUser();
    error ZeroRewards();
    error TransferFailed();
    error UnauthorizedTransfer();
    error ZeroAddress();
    error EmptyTokenArray();
    error WithdrawalsNotEnabled();
    error AlreadyInitialized();
    error NotInitialized();

    // --- Structs ---
    struct UserInfo {
        uint256 pendingRewards;
        uint256 claimedRewards;
        uint256[] stakedTokenIds;
    }

    // --- Constructor ---
    /// @notice Sets up the vault, initial roles, and treasury. The NFT contract must be set later via initialize().
    /// @param _deployer The address that will be granted the DEFAULT_ADMIN_ROLE and DELEGATE_ROLE.
    /// @param _initialAdmin The address that will be granted the initial DELEGATE role.
    /// @param _treasury The address to receive funds sent to this contract before any NFTs are staked.
    constructor(address _deployer, address _initialAdmin, address _treasury) {
        if (_deployer == address(0) || _initialAdmin == address(0) || _treasury == address(0)) {
            revert ZeroAddress();
        }
        treasury = _treasury;
        emit TreasuryUpdated(_treasury); // <-- ADDED: Emit event for initial treasury

        _grantRole(DEFAULT_ADMIN_ROLE, _deployer);
        _grantRole(DELEGATE_ROLE, _deployer);
        _grantRole(DELEGATE_ROLE, _initialAdmin);

        // Withdrawals are disabled by default for the pre-launch phase.
        withdrawalsEnabled = false;
        emit WithdrawalsEnabledSet(false); // <-- ADDED: Emit event for initial withdrawal state
    }

    // --- Modifiers ---
    modifier isInitialized() {
        if (address(nft) == address(0)) revert NotInitialized();
        _;
    }

    /// @notice Sets the NFT contract address.
    /// @dev This function is critical for the contract's operation and can ONLY be called once by the admin.
    ///      It must be called before any staking or unstaking can occur.
    /// @param _nft The address of the ERC721 contract to be staked.
    function initialize(address _nft) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (address(nft) != address(0)) revert AlreadyInitialized();
        if (_nft == address(0)) revert ZeroAddress();
        nft = IERC721(_nft);
        emit NFTContractSet(_nft);
    }

    // --- Reward Distribution ---
    /// @notice Receives ETH. If NFTs are staked, it distributes rewards. If not, it forwards ETH to the treasury.
    receive() external payable {
        if (totalSupply > 0) {
            uint256 reward = msg.value.mulDiv(_MULTIPLIER, totalSupply);
            rewardIndex += reward;
            emit Received(msg.value, rewardIndex);
        } else {
            (bool success, ) = payable(treasury).call{value: msg.value}("");
            if (!success) revert TransferFailed();
        }
    }

    // --- User-Facing Functions ---

    /// @notice Allows users to stake their NFTs.
    /// @param tokenIds Array of token IDs to stake.
    function stake(uint256[] calldata tokenIds) external nonReentrant isInitialized {

        uint256 length = tokenIds.length;
        if (length == 0) revert EmptyTokenArray();

        _updateRewards(msg.sender);

        balanceOf[msg.sender] += length;
        totalSupply += length;

        address sender = msg.sender;
        uint256[] storage userTokens = stakedTokenIds[sender];

        for (uint256 i = 0; i < length; i++) {
            uint256 tokenId = tokenIds[i];
            nft.safeTransferFrom(sender, address(this), tokenId);
            stakedTokenOwner[tokenId] = sender;
            tokenIdToIndex[sender][tokenId] = userTokens.length;
            userTokens.push(tokenId);

            emit Staked(sender, tokenId);
        }
    }

    /// @notice Allows users to unstake their NFTs, ONLY if withdrawals are enabled by an admin.
    /// @param tokenIds Array of token IDs to unstake.
    function unstake(uint256[] calldata tokenIds) external nonReentrant isInitialized {
        if (!withdrawalsEnabled) revert WithdrawalsNotEnabled();
        uint256 length = tokenIds.length;
        if (length == 0) revert EmptyTokenArray();

        address sender = msg.sender;

        _updateRewards(sender);

        balanceOf[sender] -= length;
        totalSupply -= length;

        for (uint256 i = 0; i < length; i++) {
            uint256 tokenId = tokenIds[i];
            if (stakedTokenOwner[tokenId] != sender) revert NFTNotStakedByUser();
            nft.safeTransferFrom(address(this), sender, tokenId);
            delete stakedTokenOwner[tokenId];
            _removeStakedTokenId(sender, tokenId);

            emit Unstaked(sender, tokenId);
        }
    }

    /// @notice Allows users to claim their staking rewards at any time.
    /// @return The amount of rewards claimed.
    function claim() external nonReentrant returns (uint256) {
        address sender = msg.sender;
        _updateRewards(sender);
        uint256 claimAmount = earned[sender];
        if (claimAmount == 0) revert ZeroRewards();

        earned[sender] = 0;

        (bool success, ) = payable(sender).call{value: claimAmount}("");
        if (!success) {
            // If the transfer fails, the entire transaction reverts,
            // automatically restoring earned[sender] to its original value.
            // The manual reversal logic is not needed.
            revert TransferFailed();
        }

        claimedRewards[sender] += claimAmount;
        emit Claimed(sender, claimAmount, claimedRewards[sender]);
        return claimAmount;
    }

    // --- Admin Functions ---

    /// @notice Enables or disables NFT withdrawals. Only callable by an admin.
    function setWithdrawalsEnabled(bool _enabled) external onlyRole(DELEGATE_ROLE) {
        withdrawalsEnabled = _enabled;
        emit WithdrawalsEnabledSet(_enabled);
    }

    /// @notice Updates the treasury address. Only callable by an admin.
    function setTreasury(address _newTreasury) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (_newTreasury == address(0)) revert ZeroAddress();
        treasury = _newTreasury;
        emit TreasuryUpdated(_newTreasury);
    }

    // --- View Functions ---

    function getUserInfo(address user) public view returns (UserInfo memory) {
        if (user == address(0)) revert ZeroAddress();
        return UserInfo({
            pendingRewards: getPendingRewards(user),
            claimedRewards: claimedRewards[user],
            stakedTokenIds: stakedTokenIds[user]
        });
    }

    function getPendingRewards(address account) public view returns (uint256) {
        return earned[account] + _calculateRewards(account);
    }

    function isTokenStakedByUser(uint256 tokenId, address user) external view returns (bool) {
        return stakedTokenOwner[tokenId] == user;
    }

    // --- Internal & Private Functions ---

    function _calculateRewards(address account) private view returns (uint256) {
        uint256 nftCount = balanceOf[account];
        if (nftCount == 0 || rewardIndex <= rewardIndexOf[account]) return 0;
        uint256 indexDiff = rewardIndex - rewardIndexOf[account];
        return nftCount.mulDiv(indexDiff, _MULTIPLIER);
    }

    function _updateRewards(address account) private {
        uint256 newEarned = _calculateRewards(account);
        if (newEarned > 0) {
            earned[account] += newEarned;
            emit RewardsUpdated(account, earned[account], rewardIndex);
        }
        rewardIndexOf[account] = rewardIndex;
    }

    function _removeStakedTokenId(address user, uint256 tokenId) private {
        uint256[] storage userTokens = stakedTokenIds[user];
        uint256 index = tokenIdToIndex[user][tokenId];
        uint256 lastTokenId = userTokens[userTokens.length - 1];
        userTokens[index] = lastTokenId;
        tokenIdToIndex[user][lastTokenId] = index;
        delete tokenIdToIndex[user][tokenId];
        userTokens.pop();
    }

    // --- ERC721 Receiver ---
    function onERC721Received(address operator, address, uint256, bytes calldata) external isInitialized returns (bytes4) {
        if (msg.sender != address(nft)) revert NFTNotEligibleForStaking();
        if (operator != address(this)) revert UnauthorizedTransfer();
        return this.onERC721Received.selector;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted to signal this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

// 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);
}

File 5 of 10 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC-721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC-721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// 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);
}

File 10 of 10 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant WAD = 1e18;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if gt(x, div(not(0), y)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if iszero(eq(div(z, y), x)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(mul(y, eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    /// Note: This function is an approximation.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

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

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

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

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

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

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

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

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

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

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    /// Note: This function is an approximation. Monotonically increasing.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            (int256 wad, int256 p) = (int256(WAD), x);
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (uint256(w >> 63) == uint256(0)) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == uint256(0)) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != uint256(0));
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c == uint256(0)) return w;
            int256 t = w | 1;
            /// @solidity memory-safe-assembly
            assembly {
                x := sdiv(mul(x, wad), t)
            }
            x = (t * (wad + lnWad(x)));
            /// @solidity memory-safe-assembly
            assembly {
                w := sdiv(x, add(wad, t))
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a * b == x * y`, with full precision.
    function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // 512-bit multiply `[p1 p0] = x * y`.
            // Compute the product mod `2**256` and mod `2**256 - 1`
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that `product = p1 * 2**256 + p0`.

            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`.
            for {} 1 {} {
                // If overflows.
                if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.

                    /*------------------- 512 by 256 division --------------------*/

                    // Make division exact by subtracting the remainder from `[p1 p0]`.
                    let r := mulmod(x, y, d) // Compute remainder using mulmod.
                    let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
                    // Make sure `z` is less than `2**256`. Also prevents `d == 0`.
                    // Placing the check here seems to give more optimal stack operations.
                    if iszero(gt(d, p1)) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    d := div(d, t) // Divide `d` by `t`, which is a power of two.
                    // Invert `d mod 2**256`
                    // Now that `d` is an odd number, it has an inverse
                    // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                    // Compute the inverse by starting with a seed that is correct
                    // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                    let inv := xor(2, mul(3, d))
                    // Now use Newton-Raphson iteration to improve the precision.
                    // Thanks to Hensel's lifting lemma, this also works in modular
                    // arithmetic, doubling the correct bits in each step.
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                    z :=
                        mul(
                            // Divide [p1 p0] by the factors of two.
                            // Shift in bits from `p1` into `p0`. For this we need
                            // to flip `t` such that it is `2**256 / t`.
                            or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                            mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
                        )
                    break
                }
                z := div(z, d)
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
    /// Performs the full 512 bit calculation regardless.
    function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            let mm := mulmod(x, y, not(0))
            let p1 := sub(mm, add(z, lt(mm, z)))
            let t := and(d, sub(0, d))
            let r := mulmod(x, y, d)
            d := div(d, t)
            let inv := xor(2, mul(3, d))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            z :=
                mul(
                    or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                    mul(sub(2, mul(d, inv)), inv)
                )
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        z = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                z := add(z, 1)
                if iszero(z) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
    /// Throws if result overflows a uint256.
    /// Credit to Philogy under MIT license:
    /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
    function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
            for {} 1 {} {
                if iszero(or(iszero(x), eq(div(z, x), y))) {
                    let k := and(n, 0xff) // `n`, cleaned.
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
                    //         |      p1     |      z     |
                    // Before: | p1_0 ¦ p1_1 | z_0  ¦ z_1 |
                    // Final:  |   0  ¦ p1_0 | p1_1 ¦ z_0 |
                    // Check that final `z` doesn't overflow by checking that p1_0 = 0.
                    if iszero(shr(k, p1)) {
                        z := add(shl(sub(256, k), p1), shr(k, z))
                        break
                    }
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
                z := shr(and(n, 0xff), z)
                break
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(z, d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(z, d))), div(z, d))
        }
    }

    /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
    function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
        /// @solidity memory-safe-assembly
        assembly {
            let g := n
            let r := mod(a, n)
            for { let y := 1 } 1 {} {
                let q := div(g, r)
                let t := g
                g := r
                r := sub(t, mul(r, q))
                let u := x
                x := y
                y := sub(u, mul(y, q))
                if iszero(r) { break }
            }
            x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x + y)`.
    function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(0, lt(add(x, y), x)), add(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x * y)`.
    function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `x != 0 ? x : y`, without branching.
    function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != bytes32(0) ? x : y`, without branching.
    function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != address(0) ? x : y`, without branching.
    function coalesce(address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(shl(96, x))))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if x {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

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

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

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

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

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

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

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

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

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

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`, rounded down.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // Makeshift lookup table to nudge the approximate log2 result.
            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
            // Newton-Raphson's.
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            // Round down.
            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
            z = (1 + sqrt(x)) * 10 ** 9;
            z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
        }
        /// @solidity memory-safe-assembly
        assembly {
            z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
            z = (1 + cbrt(x)) * 10 ** 12;
            z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let p := x
            for {} 1 {} {
                if iszero(shr(229, p)) {
                    if iszero(shr(199, p)) {
                        p := mul(p, 100000000000000000) // 10 ** 17.
                        break
                    }
                    p := mul(p, 100000000) // 10 ** 8.
                    break
                }
                if iszero(shr(249, p)) { p := mul(p, 100) }
                break
            }
            let t := mulmod(mul(z, z), z, p)
            z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
        }
    }

    /// @dev Returns `sqrt(x * y)`. Also called the geometric mean.
    function mulSqrt(uint256 x, uint256 y) internal pure returns (uint256 z) {
        if (x == y) return x;
        uint256 p = rawMul(x, y);
        if (y == rawDiv(p, x)) return sqrt(p);
        for (z = saturatingMul(rawAdd(sqrt(x), 1), rawAdd(sqrt(y), 1));; z = avg(z, p)) {
            if ((p = fullMulDivUnchecked(x, y, z)) >= z) break;
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := 1
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for {} x { x := sub(x, 1) } { z := mul(z, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards zero.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        unchecked {
            z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
        internal
        pure
        returns (uint256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        unchecked {
            if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
            return a - fullMulDiv(a - b, t - begin, end - begin);
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
        internal
        pure
        returns (int256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        // forgefmt: disable-next-item
        unchecked {
            if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
                uint256(t - begin), uint256(end - begin)));
            return int256(uint256(a) - fullMulDiv(uint256(a - b),
                uint256(t - begin), uint256(end - begin)));
        }
    }

    /// @dev Returns if `x` is an even number. Some people may need this.
    function isEven(uint256 x) internal pure returns (bool) {
        return x & uint256(1) == uint256(0);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "paris",
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_deployer","type":"address"},{"internalType":"address","name":"_initialAdmin","type":"address"},{"internalType":"address","name":"_treasury","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"EmptyTokenArray","type":"error"},{"inputs":[],"name":"NFTNotEligibleForStaking","type":"error"},{"inputs":[],"name":"NFTNotStaked","type":"error"},{"inputs":[],"name":"NFTNotStakedByUser","type":"error"},{"inputs":[],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"NotNFTOwner","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UnauthorizedTransfer","type":"error"},{"inputs":[],"name":"WithdrawalsNotEnabled","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroRewards","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"}],"name":"NFTContractSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardIndex","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newEarned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newIndex","type":"uint256"}],"name":"RewardsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"WithdrawalsEnabledSet","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getPendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserInfo","outputs":[{"components":[{"internalType":"uint256","name":"pendingRewards","type":"uint256"},{"internalType":"uint256","name":"claimedRewards","type":"uint256"},{"internalType":"uint256[]","name":"stakedTokenIds","type":"uint256[]"}],"internalType":"struct ERC721VaultLockable.UserInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_nft","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"isTokenStakedByUser","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newTreasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setWithdrawalsEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalsEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b5060405161187d38038061187d83398101604081905261002f916101fd565b60016000556001600160a01b038316158061005157506001600160a01b038216155b8061006357506001600160a01b038116155b156100815760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040517f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d190600090a26100d360008461014c565b506100ec60008051602061185d8339815191528461014c565b5061010560008051602061185d8339815191528361014c565b506006805460ff19169055604051600081527fd889a4b5aef69cd23eeb1c54b8a1bff3bcea1ec9ad73240c10c3bad638c2a2d69060200160405180910390a1505050610240565b60008281526001602090815260408083206001600160a01b038516845290915281205460ff166101d75760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016101db565b5060005b92915050565b80516001600160a01b03811681146101f857600080fd5b919050565b60008060006060848603121561021257600080fd5b61021b846101e1565b9250610229602085016101e1565b9150610237604085016101e1565b90509250925092565b61160e8061024f6000396000f3fe60806040526004361061016a5760003560e01c80636386c1c7116100d1578063c4d66de81161008a578063e449f34111610064578063e449f341146105a3578063e9ee2fa9146105c3578063f0f44260146105d9578063f6ed2017146105f957600080fd5b8063c4d66de814610549578063d0dbcd2414610569578063d547741f1461058357600080fd5b80636386c1c71461045957806370a082311461048657806391d14854146104b3578063a217fddf146104d3578063bd834345146104e8578063c0ba241b1461051557600080fd5b80632f2ff15d116101235780632f2ff15d1461037057806336568abe1461039057806347ccca02146103b05780634e71d92d146103e85780635aca7d9b146103fd57806361d027b31461043957600080fd5b806301ffc9a71461026d5780630a179327146102a25780630fbf0a93146102c2578063150b7a02146102e257806318160ddd1461031b578063248a9ca31461033f57600080fd5b3661026857600454156101f0576000610198670de0b6b3a7640000600454346106199092919063ffffffff16565b905080600560008282546101ac9190611339565b90915550506005546040805134815260208101929092527f49b838b1f120c3d2fb431047a22c4c3f41cbebfeec469a48c6d0f94db4b831b5910160405180910390a1005b6003546040516000916001600160a01b03169034908381818185875af1925050503d806000811461023d576040519150601f19603f3d011682016040523d82523d6000602084013e610242565b606091505b5050905080610264576040516312171d8360e31b815260040160405180910390fd5b505b005b600080fd5b34801561027957600080fd5b5061028d61028836600461134c565b61063f565b60405190151581526020015b60405180910390f35b3480156102ae57600080fd5b506102666102bd36600461137d565b610676565b3480156102ce57600080fd5b506102666102dd36600461139f565b6106e8565b3480156102ee57600080fd5b506103026102fd366004611432565b6108c0565b6040516001600160e01b03199091168152602001610299565b34801561032757600080fd5b5061033160045481565b604051908152602001610299565b34801561034b57600080fd5b5061033161035a3660046114cf565b6000908152600160208190526040909120015490565b34801561037c57600080fd5b5061026661038b3660046114e8565b610952565b34801561039c57600080fd5b506102666103ab3660046114e8565b61097e565b3480156103bc57600080fd5b506002546103d0906001600160a01b031681565b6040516001600160a01b039091168152602001610299565b3480156103f457600080fd5b506103316109b6565b34801561040957600080fd5b5061028d6104183660046114e8565b6000918252600c6020526040909120546001600160a01b0390811691161490565b34801561044557600080fd5b506003546103d0906001600160a01b031681565b34801561046557600080fd5b50610479610474366004611514565b610b14565b604051610299919061152f565b34801561049257600080fd5b506103316104a1366004611514565b60076020526000908152604090205481565b3480156104bf57600080fd5b5061028d6104ce3660046114e8565b610bf5565b3480156104df57600080fd5b50610331600081565b3480156104f457600080fd5b50610331610503366004611514565b600a6020526000908152604090205481565b34801561052157600080fd5b506103317f1a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f1581565b34801561055557600080fd5b50610266610564366004611514565b610c20565b34801561057557600080fd5b5060065461028d9060ff1681565b34801561058f57600080fd5b5061026661059e3660046114e8565b610cc6565b3480156105af57600080fd5b506102666105be36600461139f565b610cec565b3480156105cf57600080fd5b5061033160055481565b3480156105e557600080fd5b506102666105f4366004611514565b610efa565b34801561060557600080fd5b50610331610614366004611514565b610f77565b828202831584820484141782026106385763ad251c276000526004601cfd5b0492915050565b60006001600160e01b03198216637965db0b60e01b148061067057506301ffc9a760e01b6001600160e01b03198316145b92915050565b7f1a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f156106a081610fa5565b6006805460ff19168315159081179091556040519081527fd889a4b5aef69cd23eeb1c54b8a1bff3bcea1ec9ad73240c10c3bad638c2a2d69060200160405180910390a15050565b6106f0610fb2565b6002546001600160a01b0316610719576040516321c4e35760e21b815260040160405180910390fd5b80600081900361073c576040516337e4edd960e21b815260040160405180910390fd5b61074533610fdc565b3360009081526007602052604081208054839290610764908490611339565b92505081905550806004600082825461077d9190611339565b9091555050336000818152600b60205260408120905b838110156108ae5760008686838181106107af576107af611599565b600254604051632142170760e11b81526001600160a01b038981166004830152306024830152602093909302949094013560448501819052945016916342842e0e9150606401600060405180830381600087803b15801561080f57600080fd5b505af1158015610823573d6000803e3d6000fd5b5050506000828152600c6020908152604080832080546001600160a01b0319166001600160a01b038a169081179091558754818552600d845282852087865284528285208190556001810189558885529284209092018590555184935090917f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d91a350600101610793565b505050506108bc6001600055565b5050565b6002546000906001600160a01b03166108ec576040516321c4e35760e21b815260040160405180910390fd5b6002546001600160a01b0316331461091757604051638d4ab38960e01b815260040160405180910390fd5b6001600160a01b0386163014610940576040516325cdf54f60e21b815260040160405180910390fd5b50630a85bd0160e11b95945050505050565b6000828152600160208190526040909120015461096e81610fa5565b6109788383611098565b50505050565b6001600160a01b03811633146109a75760405163334bd91960e11b815260040160405180910390fd5b6109b18282611111565b505050565b60006109c0610fb2565b336109ca81610fdc565b6001600160a01b03811660009081526009602052604081205490819003610a045760405163899aaa9d60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600960205260408082208290555190919083908381818185875af1925050503d8060008114610a5e576040519150601f19603f3d011682016040523d82523d6000602084013e610a63565b606091505b5050905080610a85576040516312171d8360e31b815260040160405180910390fd5b6001600160a01b0383166000908152600a602052604081208054849290610aad908490611339565b90915550506001600160a01b0383166000818152600a6020908152604091829020548251868152918201527f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a910160405180910390a250915050610b116001600055565b90565b610b3860405180606001604052806000815260200160008152602001606081525090565b6001600160a01b038216610b5f5760405163d92e233d60e01b815260040160405180910390fd5b6040518060600160405280610b7384610f77565b81526001600160a01b0384166000818152600a602090815260408083205482860152928252600b8152908290208054835181840281018401855281815293909401939091830182828015610be657602002820191906000526020600020905b815481526020019060010190808311610bd2575b50505050508152509050919050565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6000610c2b81610fa5565b6002546001600160a01b031615610c545760405162dc149f60e41b815260040160405180910390fd5b6001600160a01b038216610c7b5760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0384169081179091556040517f5a6a54be6929433a25d1b5103e7dea8d9440b7903216e380344a320ca1cdd85f90600090a25050565b60008281526001602081905260409091200154610ce281610fa5565b6109788383611111565b610cf4610fb2565b6002546001600160a01b0316610d1d576040516321c4e35760e21b815260040160405180910390fd5b60065460ff16610d40576040516309a2a07d60e11b815260040160405180910390fd5b806000819003610d63576040516337e4edd960e21b815260040160405180910390fd5b33610d6d81610fdc565b6001600160a01b03811660009081526007602052604081208054849290610d959084906115af565b925050819055508160046000828254610dae91906115af565b90915550600090505b82811015610eed576000858583818110610dd357610dd3611599565b602090810292909201356000818152600c909352604090922054919250506001600160a01b03848116911614610e1c5760405163ec76d92760e01b815260040160405180910390fd5b600254604051632142170760e11b81523060048201526001600160a01b03858116602483015260448201849052909116906342842e0e90606401600060405180830381600087803b158015610e7057600080fd5b505af1158015610e84573d6000803e3d6000fd5b5050506000828152600c6020526040902080546001600160a01b031916905550610eae838261117e565b60405181906001600160a01b038516907f0f5bb82176feb1b5e747e28471aa92156a04d9f3ab9f45f28e2d704232b93f7590600090a350600101610db7565b5050506108bc6001600055565b6000610f0581610fa5565b6001600160a01b038216610f2c5760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0384169081179091556040517f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d190600090a25050565b6000610f8282611255565b6001600160a01b0383166000908152600960205260409020546106709190611339565b610faf81336112e6565b50565b600260005403610fd557604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b6000610fe782611255565b90508015611078576001600160a01b03821660009081526009602052604081208054839290611017908490611339565b90915550506001600160a01b038216600081815260096020526040908190205460055491517fca40132af767b10947fd7de6fe4074613c5850cc28b8d0aa94adb189bfb716be9261106f928252602082015260400190565b60405180910390a25b506005546001600160a01b03909116600090815260086020526040902055565b60006110a48383610bf5565b6111095760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610670565b506000610670565b600061111d8383610bf5565b156111095760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610670565b6001600160a01b0382166000908152600b60209081526040808320600d835281842085855290925282205481549192909183906111bd906001906115af565b815481106111cd576111cd611599565b90600052602060002001549050808383815481106111ed576111ed611599565b6000918252602080832091909101929092556001600160a01b0387168152600d82526040808220848352909252818120849055858152908120558254839080611238576112386115c2565b600190038181906000526020600020016000905590555050505050565b6001600160a01b03811660009081526007602052604081205480158061129557506001600160a01b03831660009081526008602052604090205460055411155b156112a35750600092915050565b6001600160a01b0383166000908152600860205260408120546005546112c991906115af565b90506112de8282670de0b6b3a7640000610619565b949350505050565b6112f08282610bf5565b6108bc5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561067057610670611323565b60006020828403121561135e57600080fd5b81356001600160e01b03198116811461137657600080fd5b9392505050565b60006020828403121561138f57600080fd5b8135801515811461137657600080fd5b600080602083850312156113b257600080fd5b823567ffffffffffffffff8111156113c957600080fd5b8301601f810185136113da57600080fd5b803567ffffffffffffffff8111156113f157600080fd5b8560208260051b840101111561140657600080fd5b6020919091019590945092505050565b80356001600160a01b038116811461142d57600080fd5b919050565b60008060008060006080868803121561144a57600080fd5b61145386611416565b945061146160208701611416565b935060408601359250606086013567ffffffffffffffff81111561148457600080fd5b8601601f8101881361149557600080fd5b803567ffffffffffffffff8111156114ac57600080fd5b8860208284010111156114be57600080fd5b959894975092955050506020019190565b6000602082840312156114e157600080fd5b5035919050565b600080604083850312156114fb57600080fd5b8235915061150b60208401611416565b90509250929050565b60006020828403121561152657600080fd5b61137682611416565b602081526000608082018351602084015260208401516040840152604084015160608085015281815180845260a086019150602083019350600092505b8083101561158f578351825260208201915060208401935060018301925061156c565b5095945050505050565b634e487b7160e01b600052603260045260246000fd5b8181038181111561067057610670611323565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220c1c13db05cbedfeb6655c613f26160f5202e6d488eebe36d9670c019b35e936f64736f6c634300081a00331a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f150000000000000000000000008ff243a318f02cad7efe5c2d950bd6d6388a2bf2000000000000000000000000ec3475f942ae76014997419fc69c20ce052b889f0000000000000000000000008ff243a318f02cad7efe5c2d950bd6d6388a2bf2

Deployed Bytecode

0x60806040526004361061016a5760003560e01c80636386c1c7116100d1578063c4d66de81161008a578063e449f34111610064578063e449f341146105a3578063e9ee2fa9146105c3578063f0f44260146105d9578063f6ed2017146105f957600080fd5b8063c4d66de814610549578063d0dbcd2414610569578063d547741f1461058357600080fd5b80636386c1c71461045957806370a082311461048657806391d14854146104b3578063a217fddf146104d3578063bd834345146104e8578063c0ba241b1461051557600080fd5b80632f2ff15d116101235780632f2ff15d1461037057806336568abe1461039057806347ccca02146103b05780634e71d92d146103e85780635aca7d9b146103fd57806361d027b31461043957600080fd5b806301ffc9a71461026d5780630a179327146102a25780630fbf0a93146102c2578063150b7a02146102e257806318160ddd1461031b578063248a9ca31461033f57600080fd5b3661026857600454156101f0576000610198670de0b6b3a7640000600454346106199092919063ffffffff16565b905080600560008282546101ac9190611339565b90915550506005546040805134815260208101929092527f49b838b1f120c3d2fb431047a22c4c3f41cbebfeec469a48c6d0f94db4b831b5910160405180910390a1005b6003546040516000916001600160a01b03169034908381818185875af1925050503d806000811461023d576040519150601f19603f3d011682016040523d82523d6000602084013e610242565b606091505b5050905080610264576040516312171d8360e31b815260040160405180910390fd5b505b005b600080fd5b34801561027957600080fd5b5061028d61028836600461134c565b61063f565b60405190151581526020015b60405180910390f35b3480156102ae57600080fd5b506102666102bd36600461137d565b610676565b3480156102ce57600080fd5b506102666102dd36600461139f565b6106e8565b3480156102ee57600080fd5b506103026102fd366004611432565b6108c0565b6040516001600160e01b03199091168152602001610299565b34801561032757600080fd5b5061033160045481565b604051908152602001610299565b34801561034b57600080fd5b5061033161035a3660046114cf565b6000908152600160208190526040909120015490565b34801561037c57600080fd5b5061026661038b3660046114e8565b610952565b34801561039c57600080fd5b506102666103ab3660046114e8565b61097e565b3480156103bc57600080fd5b506002546103d0906001600160a01b031681565b6040516001600160a01b039091168152602001610299565b3480156103f457600080fd5b506103316109b6565b34801561040957600080fd5b5061028d6104183660046114e8565b6000918252600c6020526040909120546001600160a01b0390811691161490565b34801561044557600080fd5b506003546103d0906001600160a01b031681565b34801561046557600080fd5b50610479610474366004611514565b610b14565b604051610299919061152f565b34801561049257600080fd5b506103316104a1366004611514565b60076020526000908152604090205481565b3480156104bf57600080fd5b5061028d6104ce3660046114e8565b610bf5565b3480156104df57600080fd5b50610331600081565b3480156104f457600080fd5b50610331610503366004611514565b600a6020526000908152604090205481565b34801561052157600080fd5b506103317f1a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f1581565b34801561055557600080fd5b50610266610564366004611514565b610c20565b34801561057557600080fd5b5060065461028d9060ff1681565b34801561058f57600080fd5b5061026661059e3660046114e8565b610cc6565b3480156105af57600080fd5b506102666105be36600461139f565b610cec565b3480156105cf57600080fd5b5061033160055481565b3480156105e557600080fd5b506102666105f4366004611514565b610efa565b34801561060557600080fd5b50610331610614366004611514565b610f77565b828202831584820484141782026106385763ad251c276000526004601cfd5b0492915050565b60006001600160e01b03198216637965db0b60e01b148061067057506301ffc9a760e01b6001600160e01b03198316145b92915050565b7f1a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f156106a081610fa5565b6006805460ff19168315159081179091556040519081527fd889a4b5aef69cd23eeb1c54b8a1bff3bcea1ec9ad73240c10c3bad638c2a2d69060200160405180910390a15050565b6106f0610fb2565b6002546001600160a01b0316610719576040516321c4e35760e21b815260040160405180910390fd5b80600081900361073c576040516337e4edd960e21b815260040160405180910390fd5b61074533610fdc565b3360009081526007602052604081208054839290610764908490611339565b92505081905550806004600082825461077d9190611339565b9091555050336000818152600b60205260408120905b838110156108ae5760008686838181106107af576107af611599565b600254604051632142170760e11b81526001600160a01b038981166004830152306024830152602093909302949094013560448501819052945016916342842e0e9150606401600060405180830381600087803b15801561080f57600080fd5b505af1158015610823573d6000803e3d6000fd5b5050506000828152600c6020908152604080832080546001600160a01b0319166001600160a01b038a169081179091558754818552600d845282852087865284528285208190556001810189558885529284209092018590555184935090917f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d91a350600101610793565b505050506108bc6001600055565b5050565b6002546000906001600160a01b03166108ec576040516321c4e35760e21b815260040160405180910390fd5b6002546001600160a01b0316331461091757604051638d4ab38960e01b815260040160405180910390fd5b6001600160a01b0386163014610940576040516325cdf54f60e21b815260040160405180910390fd5b50630a85bd0160e11b95945050505050565b6000828152600160208190526040909120015461096e81610fa5565b6109788383611098565b50505050565b6001600160a01b03811633146109a75760405163334bd91960e11b815260040160405180910390fd5b6109b18282611111565b505050565b60006109c0610fb2565b336109ca81610fdc565b6001600160a01b03811660009081526009602052604081205490819003610a045760405163899aaa9d60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600960205260408082208290555190919083908381818185875af1925050503d8060008114610a5e576040519150601f19603f3d011682016040523d82523d6000602084013e610a63565b606091505b5050905080610a85576040516312171d8360e31b815260040160405180910390fd5b6001600160a01b0383166000908152600a602052604081208054849290610aad908490611339565b90915550506001600160a01b0383166000818152600a6020908152604091829020548251868152918201527f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a910160405180910390a250915050610b116001600055565b90565b610b3860405180606001604052806000815260200160008152602001606081525090565b6001600160a01b038216610b5f5760405163d92e233d60e01b815260040160405180910390fd5b6040518060600160405280610b7384610f77565b81526001600160a01b0384166000818152600a602090815260408083205482860152928252600b8152908290208054835181840281018401855281815293909401939091830182828015610be657602002820191906000526020600020905b815481526020019060010190808311610bd2575b50505050508152509050919050565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6000610c2b81610fa5565b6002546001600160a01b031615610c545760405162dc149f60e41b815260040160405180910390fd5b6001600160a01b038216610c7b5760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0384169081179091556040517f5a6a54be6929433a25d1b5103e7dea8d9440b7903216e380344a320ca1cdd85f90600090a25050565b60008281526001602081905260409091200154610ce281610fa5565b6109788383611111565b610cf4610fb2565b6002546001600160a01b0316610d1d576040516321c4e35760e21b815260040160405180910390fd5b60065460ff16610d40576040516309a2a07d60e11b815260040160405180910390fd5b806000819003610d63576040516337e4edd960e21b815260040160405180910390fd5b33610d6d81610fdc565b6001600160a01b03811660009081526007602052604081208054849290610d959084906115af565b925050819055508160046000828254610dae91906115af565b90915550600090505b82811015610eed576000858583818110610dd357610dd3611599565b602090810292909201356000818152600c909352604090922054919250506001600160a01b03848116911614610e1c5760405163ec76d92760e01b815260040160405180910390fd5b600254604051632142170760e11b81523060048201526001600160a01b03858116602483015260448201849052909116906342842e0e90606401600060405180830381600087803b158015610e7057600080fd5b505af1158015610e84573d6000803e3d6000fd5b5050506000828152600c6020526040902080546001600160a01b031916905550610eae838261117e565b60405181906001600160a01b038516907f0f5bb82176feb1b5e747e28471aa92156a04d9f3ab9f45f28e2d704232b93f7590600090a350600101610db7565b5050506108bc6001600055565b6000610f0581610fa5565b6001600160a01b038216610f2c5760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0384169081179091556040517f7dae230f18360d76a040c81f050aa14eb9d6dc7901b20fc5d855e2a20fe814d190600090a25050565b6000610f8282611255565b6001600160a01b0383166000908152600960205260409020546106709190611339565b610faf81336112e6565b50565b600260005403610fd557604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b6000610fe782611255565b90508015611078576001600160a01b03821660009081526009602052604081208054839290611017908490611339565b90915550506001600160a01b038216600081815260096020526040908190205460055491517fca40132af767b10947fd7de6fe4074613c5850cc28b8d0aa94adb189bfb716be9261106f928252602082015260400190565b60405180910390a25b506005546001600160a01b03909116600090815260086020526040902055565b60006110a48383610bf5565b6111095760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610670565b506000610670565b600061111d8383610bf5565b156111095760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610670565b6001600160a01b0382166000908152600b60209081526040808320600d835281842085855290925282205481549192909183906111bd906001906115af565b815481106111cd576111cd611599565b90600052602060002001549050808383815481106111ed576111ed611599565b6000918252602080832091909101929092556001600160a01b0387168152600d82526040808220848352909252818120849055858152908120558254839080611238576112386115c2565b600190038181906000526020600020016000905590555050505050565b6001600160a01b03811660009081526007602052604081205480158061129557506001600160a01b03831660009081526008602052604090205460055411155b156112a35750600092915050565b6001600160a01b0383166000908152600860205260408120546005546112c991906115af565b90506112de8282670de0b6b3a7640000610619565b949350505050565b6112f08282610bf5565b6108bc5760405163e2517d3f60e01b81526001600160a01b03821660048201526024810183905260440160405180910390fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561067057610670611323565b60006020828403121561135e57600080fd5b81356001600160e01b03198116811461137657600080fd5b9392505050565b60006020828403121561138f57600080fd5b8135801515811461137657600080fd5b600080602083850312156113b257600080fd5b823567ffffffffffffffff8111156113c957600080fd5b8301601f810185136113da57600080fd5b803567ffffffffffffffff8111156113f157600080fd5b8560208260051b840101111561140657600080fd5b6020919091019590945092505050565b80356001600160a01b038116811461142d57600080fd5b919050565b60008060008060006080868803121561144a57600080fd5b61145386611416565b945061146160208701611416565b935060408601359250606086013567ffffffffffffffff81111561148457600080fd5b8601601f8101881361149557600080fd5b803567ffffffffffffffff8111156114ac57600080fd5b8860208284010111156114be57600080fd5b959894975092955050506020019190565b6000602082840312156114e157600080fd5b5035919050565b600080604083850312156114fb57600080fd5b8235915061150b60208401611416565b90509250929050565b60006020828403121561152657600080fd5b61137682611416565b602081526000608082018351602084015260208401516040840152604084015160608085015281815180845260a086019150602083019350600092505b8083101561158f578351825260208201915060208401935060018301925061156c565b5095945050505050565b634e487b7160e01b600052603260045260246000fd5b8181038181111561067057610670611323565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220c1c13db05cbedfeb6655c613f26160f5202e6d488eebe36d9670c019b35e936f64736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000008ff243a318f02cad7efe5c2d950bd6d6388a2bf2000000000000000000000000ec3475f942ae76014997419fc69c20ce052b889f0000000000000000000000008ff243a318f02cad7efe5c2d950bd6d6388a2bf2

-----Decoded View---------------
Arg [0] : _deployer (address): 0x8ff243a318f02CAD7efE5c2d950bD6D6388A2BF2
Arg [1] : _initialAdmin (address): 0xEc3475f942AE76014997419fc69c20cE052B889f
Arg [2] : _treasury (address): 0x8ff243a318f02CAD7efE5c2d950bD6D6388A2BF2

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000008ff243a318f02cad7efe5c2d950bd6d6388a2bf2
Arg [1] : 000000000000000000000000ec3475f942ae76014997419fc69c20ce052b889f
Arg [2] : 0000000000000000000000008ff243a318f02cad7efe5c2d950bd6d6388a2bf2


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.