ETH Price: $3,088.28 (-0.48%)
Gas: 3 Gwei

Token

NFiniTi Membership (NFTM)
 

Overview

Max Total Supply

242 NFTM

Holders

192

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Balance
4 NFTM
0x40b6d9400c7ea7fbbe38d851727de2e958795c11
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
NFiniTiMembership

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2024-05-27
*/

// SPDX-License-Identifier: MIT


// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

// File: @openzeppelin/contracts/utils/Address.sol


// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

// File: @openzeppelin/contracts/utils/ReentrancyGuard.sol


// OpenZeppelin Contracts (last updated v5.0.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 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;
    }
}

// File: @openzeppelin/contracts/interfaces/draft-IERC6093.sol


// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// File: @openzeppelin/contracts/utils/math/SignedMath.sol


// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// File: @openzeppelin/contracts/utils/math/Math.sol


// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the 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.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// File: @openzeppelin/contracts/utils/Strings.sol


// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;



/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// File: @openzeppelin/contracts/utils/Context.sol


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

// File: @openzeppelin/contracts/access/Ownable.sol


// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;


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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/access/Ownable2Step.sol


// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;


/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 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);
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol


// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

// File: @openzeppelin/contracts/utils/introspection/ERC165.sol


// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;


/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 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;
    }
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;


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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.20;


/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

// File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;


/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// File: @openzeppelin/contracts/token/ERC721/ERC721.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;








/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    mapping(uint256 tokenId => address) private _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        return _requireOwned(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual {
        _approve(to, tokenId, _msgSender());
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual returns (address) {
        _requireOwned(tokenId);

        return _getApproved(tokenId);
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
    function _getApproved(uint256 tokenId) internal view virtual returns (address) {
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
    }

    /**
     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
     * the `spender` for the specific `tokenId`.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
    function _increaseBalance(address account, uint128 value) internal virtual {
        unchecked {
            _balances[account] += value;
        }
    }

    /**
     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
        address from = _ownerOf(tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(address(0), tokenId, address(0), false);

            unchecked {
                _balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                _balances[to] += 1;
            }
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    /**
     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        _checkOnERC721Received(address(0), to, tokenId, data);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal {
        address previousOwner = _update(address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC721 standard to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId) internal {
        _safeTransfer(from, to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address to, uint256 tokenId, address auth) internal {
        _approve(to, tokenId, auth, true);
    }

    /**
     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        _tokenApprovals[tokenId] = to;
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
     * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    revert ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}

// File: @openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol


// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.20;




/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds enumerability
 * of all the token ids in the contract as well as all token ids owned by each account.
 *
 * CAUTION: `ERC721` extensions that implement custom `balanceOf` logic, such as `ERC721Consecutive`,
 * interfere with enumerability and should not be used together with `ERC721Enumerable`.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens;
    mapping(uint256 tokenId => uint256) private _ownedTokensIndex;

    uint256[] private _allTokens;
    mapping(uint256 tokenId => uint256) private _allTokensIndex;

    /**
     * @dev An `owner`'s token query was out of bounds for `index`.
     *
     * NOTE: The owner being `address(0)` indicates a global out of bounds index.
     */
    error ERC721OutOfBoundsIndex(address owner, uint256 index);

    /**
     * @dev Batch mint is not allowed.
     */
    error ERC721EnumerableForbiddenBatchMint();

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

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
        if (index >= balanceOf(owner)) {
            revert ERC721OutOfBoundsIndex(owner, index);
        }
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual returns (uint256) {
        if (index >= totalSupply()) {
            revert ERC721OutOfBoundsIndex(address(0), index);
        }
        return _allTokens[index];
    }

    /**
     * @dev See {ERC721-_update}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
        address previousOwner = super._update(to, tokenId, auth);

        if (previousOwner == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (previousOwner != to) {
            _removeTokenFromOwnerEnumeration(previousOwner, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (previousOwner != to) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }

        return previousOwner;
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = balanceOf(to) - 1;
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = balanceOf(from);
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }

    /**
     * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
     */
    function _increaseBalance(address account, uint128 amount) internal virtual override {
        if (amount > 0) {
            revert ERC721EnumerableForbiddenBatchMint();
        }
        super._increaseBalance(account, amount);
    }
}

// File: contracts/NFiniTiMembership.sol


pragma solidity ^0.8.24;








/**
 * @title NFiniTi Membership NFT Contract
 *
 * @dev This contract manages the issuance and administration of NFiniTi
 * Membership NFTs.
 *
 * It supports controlled minting of NFTs across different levels, with each
 * NFT representing a tier of membership within the NFiniTi ecosystem.
 *
 * Minting a Membership NFT require a signature-based minting authorization
 *
 */

contract NFiniTiMembership is
    ERC721,
    ReentrancyGuard,
    ERC721Enumerable,
    Ownable2Step
{
    using Address for address;
    using Strings for uint256;
    using ECDSA for bytes32;

    /// @notice event emitted when the contract is paused
    event Paused();

    /// @notice event emitted when the contract is unpaused
    event Unpaused();

    /// @notice event emitted when the funds are withdrawn
    event WithdrawCalled(address indexed to, uint256 amount);

    /// @notice event emitted when the contract base URI is updated
    event BaseURIUpdated(string baseURI);

    /// @notice event emitted when the withdrawal address is updated
    event WithdrawalAddressUpdated(address withdrawalAddress);

    /// @notice event emitted when the authorized signer is updated
    event AuthorizedSignerUpdated(address authorizedSigner);

    /// @notice event emitted when the authorized signer is updated
    event MembershipMinted(address minter, uint256 tokenId);

    /// @notice Address authorized to mint NFTs
    address public authorizedSigner;

    /// @notice Address where funds will be sent upon withdrawal
    address public withdrawalAddress;

    /// @notice Base URI for NFT metadata
    string public baseURI;

    /// @notice variable to store the paused state of the contract
    bool public paused;

    struct LevelInfo {
        uint256 mintCount; // Current number of mints for this level
    }

    /// @dev Array of maximum mints allowed per level, index 0 is level 1
    uint256[] public maxMintsPerLevel;

    /// @notice Mapping of the token levels
    mapping(uint256 level => LevelInfo) public levelInfos;

    // Mapping from token ID to its level
    mapping(uint256 tokenId => uint256 level) private tokenLevels;

    /// @dev Custom errors
    error InvalidLevelError();
    error InvalidPriceError();
    error InvalidRecipientError();
    error InvalidSignatureError();
    error MintLimitReachedError();
    error MintPausedError();
    error NoAddressZeroError();
    error NoFundsToWithdrawError();
    error NotAuthorizedError();

    /**
     * @dev Sets the initial values for the contract
     * @param authorizedSigner_ Address authorized to mint NFTs
     * @param withdrawalAddress_ Address where funds will be withdrawn to
     * @param maxMintsPerLevel_ Array specifying the max NFTs per level
     * @param baseURI_ Base URI for NFT metadata
     */
    constructor(
        address initialOwner_,
        address authorizedSigner_,
        address withdrawalAddress_,
        string memory baseURI_,
        uint256[] memory maxMintsPerLevel_
    ) ERC721("NFiniTi Membership", "NFTM") Ownable(initialOwner_) {
        authorizedSigner = authorizedSigner_;
        withdrawalAddress = withdrawalAddress_;
        baseURI = baseURI_;

        // Set the maximum mints for each level, only 3 levels are supported
        maxMintsPerLevel = maxMintsPerLevel_;
    }

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     * @param tokenId_ The token ID for which to return the URI.
     * @return A string representing the URI for the metadata of the given token ID.
     */
    function tokenURI(
        uint256 tokenId_
    ) public view override returns (string memory) {
        // Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
        _requireOwned(tokenId_);

        return
            string(
                abi.encodePacked(baseURI, "/", tokenId_.toString(), ".json")
            );
    }

    /**
     * @dev Safely mints a new token.
     * Requirements:
     * - `tokenId` must not exist.
     * @param to_ address to which the token will be minted
     * @param tokenId_ token ID to mint
     * @param level_ level of the token to mint
     * @param price_ price of the token to mint
     * @param signature_ signature to validate
     */
    function mint(
        address to_,
        uint256 tokenId_,
        uint256 level_,
        uint256 price_,
        bytes memory signature_
    ) external payable nonReentrant {
        // Revert if contract is paused
        if (paused) {
            revert MintPausedError();
        }

        // Perform the signature validation check
        if (!_isValidSignature(to_, tokenId_, level_, price_, signature_)) {
            revert InvalidSignatureError();
        }
        // If to_ != address(0) and user is not the contract owner check that msg.sender == to_
        if (to_ != address(0) && msg.sender != to_ && msg.sender != owner()) {
            revert InvalidRecipientError();
        }

        // check that the level is valid, must be between 1 and 3
        if (level_ < 1 || level_ > 3) {
            revert InvalidLevelError();
        }

        // check that the mint limit has not been reached for that level
        if (levelInfos[level_].mintCount >= maxMintsPerLevel[level_ - 1]) {
            revert MintLimitReachedError();
        }

        // check that the value sent is the same than the price_ parameter
        if (msg.value != price_) {
            revert InvalidPriceError();
        }

        // Update the mint count for the level
        LevelInfo storage level = levelInfos[level_];
        level.mintCount += 1;

        // Store the level of the minted token
        tokenLevels[tokenId_] = level_;

        // Set the correct address to mint the new token
        address mintTo = to_ == address(0) ? msg.sender : to_;

        emit MembershipMinted(mintTo, tokenId_);

        _safeMint(mintTo, tokenId_);
    }

    /**
     * @dev Returns the highest level of membership for the given wallet address.
     * @param wallet The address to check for membership level.
     * @return The highest membership level of the given wallet address.
     */

    function getHighestMembershipLevel(
        address wallet
    ) external view returns (uint256) {
        uint256 highestLevel = 0;
        uint256 balance = balanceOf(wallet);

        for (uint256 i = 0; i < balance; i++) {
            uint256 tokenId = tokenOfOwnerByIndex(wallet, i);
            uint256 level = getLevel(tokenId);
            if (level > highestLevel) {
                highestLevel = level;
            }
        }

        return highestLevel;
    }

    function getLevel(uint256 tokenId_) public view returns (uint256) {
        // Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
        _requireOwned(tokenId_);
        return tokenLevels[tokenId_];
    }

    /**
     * @dev Updates the address authorized to mint new tokens.
     * @param authorizedSigner_ new authorized signer
     */
    function updateAuthorizedSigner(
        address authorizedSigner_
    ) external onlyOwner {
        if (authorizedSigner_ == address(0)) {
            revert NoAddressZeroError();
        }
        emit AuthorizedSignerUpdated(authorizedSigner_);

        authorizedSigner = authorizedSigner_;
    }

    /**
     * @dev Updates the withdrawal address for funds.
     * @param withdrawalAddress_ new withdrawal address
     */
    function updateWithdrawalAddress(
        address withdrawalAddress_
    ) external onlyOwner {
        if (withdrawalAddress_ == address(0)) {
            revert NoAddressZeroError();
        }
        emit WithdrawalAddressUpdated(withdrawalAddress_);
        withdrawalAddress = withdrawalAddress_;
    }

    /**
     * @dev Updates the base URI for NFT metadata.
     * @param baseURI_ new base URI
     */
    function setBaseURI(string memory baseURI_) external onlyOwner {
        emit BaseURIUpdated(baseURI_);
        baseURI = baseURI_;
    }

    /**
     * @dev Pauses minting
     */
    function pause() external onlyOwner {
        paused = true;
        emit Paused();
    }

    /**
     * @dev Unpauses minting
     */
    function unpause() external onlyOwner {
        paused = false;
        emit Unpaused();
    }

    /**
     * @notice Validates the signature provided
     * @param to_ The address of the recipient.
     * @param tokenId_ The token ID to mint.
     * @param level_ The level of the token to mint.
     * @param price_ The price of the token to mint.
     * @param signature_ The signature to validate.
     * @return Returns true if the signature is valid, otherwise false.
     */
    function _isValidSignature(
        address to_,
        uint256 tokenId_,
        uint256 level_,
        uint256 price_,
        bytes memory signature_
    ) internal view returns (bool) {
        // prefixed message hash
        bytes32 messageHash = keccak256(
            abi.encodePacked(
                "\x19Ethereum Signed Message:\n32",
                keccak256(
                    abi.encodePacked(
                        address(this), // address of the current smart-contract
                        to_,
                        tokenId_,
                        level_,
                        price_
                    )
                )
            )
        );
        // use ECDSA.recover to recover signer address from signature
        address signer = ECDSA.recover(messageHash, signature_);

        return (signer == authorizedSigner);
    }

    /// @notice Transfer pending balance to the withdrawal address.
    function withdraw() external nonReentrant {
        // Only withdrawalAddress is allowed to withdraw funds
        if (msg.sender != withdrawalAddress) {
            revert NotAuthorizedError();
        }
        uint256 balance = address(this).balance;
        if (balance == 0) revert NoFundsToWithdrawError();

        Address.sendValue(payable(withdrawalAddress), balance);
        emit WithdrawCalled(msg.sender, balance);
    }

    // The following functions are overrides required by Solidity.

    function _update(
        address to,
        uint256 tokenId,
        address auth
    ) internal override(ERC721, ERC721Enumerable) returns (address) {
        return super._update(to, tokenId, auth);
    }

    function supportsInterface(
        bytes4 interfaceId
    ) public view override(ERC721, ERC721Enumerable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    function _increaseBalance(
        address account,
        uint128 value
    ) internal override(ERC721, ERC721Enumerable) {
        super._increaseBalance(account, value);
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"initialOwner_","type":"address"},{"internalType":"address","name":"authorizedSigner_","type":"address"},{"internalType":"address","name":"withdrawalAddress_","type":"address"},{"internalType":"string","name":"baseURI_","type":"string"},{"internalType":"uint256[]","name":"maxMintsPerLevel_","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"ERC721EnumerableForbiddenBatchMint","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ERC721OutOfBoundsIndex","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidLevelError","type":"error"},{"inputs":[],"name":"InvalidPriceError","type":"error"},{"inputs":[],"name":"InvalidRecipientError","type":"error"},{"inputs":[],"name":"InvalidSignatureError","type":"error"},{"inputs":[],"name":"MintLimitReachedError","type":"error"},{"inputs":[],"name":"MintPausedError","type":"error"},{"inputs":[],"name":"NoAddressZeroError","type":"error"},{"inputs":[],"name":"NoFundsToWithdrawError","type":"error"},{"inputs":[],"name":"NotAuthorizedError","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"authorizedSigner","type":"address"}],"name":"AuthorizedSignerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"baseURI","type":"string"}],"name":"BaseURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"MembershipMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"withdrawalAddress","type":"address"}],"name":"WithdrawalAddressUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authorizedSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getHighestMembershipLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"getLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"level","type":"uint256"}],"name":"levelInfos","outputs":[{"internalType":"uint256","name":"mintCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"maxMintsPerLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"level_","type":"uint256"},{"internalType":"uint256","name":"price_","type":"uint256"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI_","type":"string"}],"name":"setBaseURI","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":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"authorizedSigner_","type":"address"}],"name":"updateAuthorizedSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"withdrawalAddress_","type":"address"}],"name":"updateWithdrawalAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561000f575f80fd5b5060405161288338038061288383398101604081905261002e916102d1565b846040518060400160405280601281526020017104e46696e695469204d656d626572736869760741b815250604051806040016040528060048152602001634e46544d60e01b815250815f90816100859190610434565b5060016100928282610434565b50506001600655506001600160a01b0381166100c757604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100d08161012c565b50600d80546001600160a01b038087166001600160a01b031992831617909255600e805492861692909116919091179055600f61010d8382610434565b508051610121906011906020840190610199565b5050505050506104f3565b600c80546001600160a01b031916905561014581610148565b50565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b828054828255905f5260205f209081019282156101d2579160200282015b828111156101d25782518255916020019190600101906101b7565b506101de9291506101e2565b5090565b5b808211156101de575f81556001016101e3565b80516001600160a01b038116811461020c575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561024d5761024d610211565b604052919050565b5f82601f830112610264575f80fd5b815160206001600160401b0382111561027f5761027f610211565b8160051b61028e828201610225565b92835284810182019282810190878511156102a7575f80fd5b83870192505b848310156102c6578251825291830191908301906102ad565b979650505050505050565b5f805f805f60a086880312156102e5575f80fd5b6102ee866101f6565b945060206102fd8188016101f6565b945061030b604088016101f6565b60608801519094506001600160401b0380821115610327575f80fd5b818901915089601f83011261033a575f80fd5b81518181111561034c5761034c610211565b61035e601f8201601f19168501610225565b8181528b85838601011115610371575f80fd5b818585018683015e5f91810190940152608089015192945080831115610395575f80fd5b50506103a388828901610255565b9150509295509295909350565b600181811c908216806103c457607f821691505b6020821081036103e257634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561042f57805f5260205f20601f840160051c8101602085101561040d5750805b601f840160051c820191505b8181101561042c575f8155600101610419565b50505b505050565b81516001600160401b0381111561044d5761044d610211565b6104618161045b84546103b0565b846103e8565b602080601f831160018114610494575f841561047d5750858301515b5f19600386901b1c1916600185901b1785556104eb565b5f85815260208120601f198616915b828110156104c2578886015182559484019460019091019084016104a3565b50858210156104df57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b612383806105005f395ff3fe608060405260043610610207575f3560e01c80636c0360eb11610113578063b49c99b81161009d578063c87b56dd1161006d578063c87b56dd146105a7578063e30c3978146105c6578063e985e9c5146105e3578063f2bcd02214610602578063f2fde38b14610621575f80fd5b8063b49c99b814610537578063b88d4fde1461054a578063baef27b814610569578063c771909c14610588575f80fd5b80638456cb59116100e35780638456cb59146104b457806386481d40146104c85780638da5cb5b146104e757806395d89b4114610504578063a22cb46514610518575f80fd5b80636c0360eb1461045957806370a082311461046d578063715018a61461048c57806379ba5097146104a0575f80fd5b80633ccfd60b1161019457806355f804b31161016457806355f804b3146103c45780635c975abb146103e35780635e72fc5d146103fc5780636352211e1461041b578063669ed9351461043a575f80fd5b80633ccfd60b1461035e5780633f4ba83a1461037257806342842e0e146103865780634f6ccce7146103a5575f80fd5b806318160ddd116101da57806318160ddd146102b857806321117695146102d657806323b872dd146102f557806326c0aba2146103145780632f745c591461033f575f80fd5b806301ffc9a71461020b57806306fdde031461023f578063081812fc14610260578063095ea7b314610297575b5f80fd5b348015610216575f80fd5b5061022a610225366004611d7d565b610640565b60405190151581526020015b60405180910390f35b34801561024a575f80fd5b50610253610650565b6040516102369190611dcd565b34801561026b575f80fd5b5061027f61027a366004611ddf565b6106df565b6040516001600160a01b039091168152602001610236565b3480156102a2575f80fd5b506102b66102b1366004611e11565b610706565b005b3480156102c3575f80fd5b506009545b604051908152602001610236565b3480156102e1575f80fd5b506102c86102f0366004611e39565b610715565b348015610300575f80fd5b506102b661030f366004611e52565b610765565b34801561031f575f80fd5b506102c861032e366004611ddf565b60126020525f908152604090205481565b34801561034a575f80fd5b506102c8610359366004611e11565b6107f3565b348015610369575f80fd5b506102b6610856565b34801561037d575f80fd5b506102b6610903565b348015610391575f80fd5b506102b66103a0366004611e52565b61093f565b3480156103b0575f80fd5b506102c86103bf366004611ddf565b61095e565b3480156103cf575f80fd5b506102b66103de366004611f12565b6109b3565b3480156103ee575f80fd5b5060105461022a9060ff1681565b348015610407575f80fd5b506102b6610416366004611e39565b6109fe565b348015610426575f80fd5b5061027f610435366004611ddf565b610a8b565b348015610445575f80fd5b506102b6610454366004611e39565b610a95565b348015610464575f80fd5b50610253610b22565b348015610478575f80fd5b506102c8610487366004611e39565b610bae565b348015610497575f80fd5b506102b6610bf3565b3480156104ab575f80fd5b506102b6610c04565b3480156104bf575f80fd5b506102b6610c48565b3480156104d3575f80fd5b506102c86104e2366004611ddf565b610c87565b3480156104f2575f80fd5b50600b546001600160a01b031661027f565b34801561050f575f80fd5b50610253610ca4565b348015610523575f80fd5b506102b6610532366004611f57565b610cb3565b6102b6610545366004611fae565b610cbe565b348015610555575f80fd5b506102b6610564366004612014565b610ebd565b348015610574575f80fd5b506102c8610583366004611ddf565b610ed4565b348015610593575f80fd5b50600d5461027f906001600160a01b031681565b3480156105b2575f80fd5b506102536105c1366004611ddf565b610ef3565b3480156105d1575f80fd5b50600c546001600160a01b031661027f565b3480156105ee575f80fd5b5061022a6105fd366004612078565b610f31565b34801561060d575f80fd5b50600e5461027f906001600160a01b031681565b34801561062c575f80fd5b506102b661063b366004611e39565b610f5e565b5f61064a82610fcf565b92915050565b60605f805461065e906120a9565b80601f016020809104026020016040519081016040528092919081815260200182805461068a906120a9565b80156106d55780601f106106ac576101008083540402835291602001916106d5565b820191905f5260205f20905b8154815290600101906020018083116106b857829003601f168201915b5050505050905090565b5f6106e982610ff3565b505f828152600460205260409020546001600160a01b031661064a565b61071182823361102b565b5050565b5f808061072184610bae565b90505f5b8181101561075c575f61073886836107f3565b90505f61074482610c87565b905084811115610752578094505b5050600101610725565b50909392505050565b6001600160a01b03821661079357604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f61079f838333611038565b9050836001600160a01b0316816001600160a01b0316146107ed576040516364283d7b60e01b81526001600160a01b038086166004830152602482018490528216604482015260640161078a565b50505050565b5f6107fd83610bae565b821061082e5760405163295f44f760e21b81526001600160a01b03841660048201526024810183905260440161078a565b506001600160a01b03919091165f908152600760209081526040808320938352929052205490565b61085e61104c565b600e546001600160a01b03163314610889576040516379b798cf60e11b815260040160405180910390fd5b475f8190036108ab5760405163e168e91760e01b815260040160405180910390fd5b600e546108c1906001600160a01b031682611076565b60405181815233907f26d04682db6b10c93d5d39df697547e7f39badb6e2927e68323333e1afa795c29060200160405180910390a2506109016001600655565b565b61090b611109565b6010805460ff191690556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d16933905f90a1565b61095983838360405180602001604052805f815250610ebd565b505050565b5f61096860095490565b82106109905760405163295f44f760e21b81525f60048201526024810183905260440161078a565b600982815481106109a3576109a36120e1565b905f5260205f2001549050919050565b6109bb611109565b7f6741b2fc379fad678116fe3d4d4b9a1a184ab53ba36b86ad0fa66340b1ab41ad816040516109ea9190611dcd565b60405180910390a1600f6107118282612139565b610a06611109565b6001600160a01b038116610a2d576040516301e8bbc160e31b815260040160405180910390fd5b6040516001600160a01b03821681527f227cb3c52b593782f40ecef3ba6f526592f2abe6ef6c34128c20b9dddf58ce639060200160405180910390a1600e80546001600160a01b0319166001600160a01b0392909216919091179055565b5f61064a82610ff3565b610a9d611109565b6001600160a01b038116610ac4576040516301e8bbc160e31b815260040160405180910390fd5b6040516001600160a01b03821681527f24bada2c0d54dfcadf1a6b7d7c4528c4219c6fffcce683ca9f39a8441947c63e9060200160405180910390a1600d80546001600160a01b0319166001600160a01b0392909216919091179055565b600f8054610b2f906120a9565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5b906120a9565b8015610ba65780601f10610b7d57610100808354040283529160200191610ba6565b820191905f5260205f20905b815481529060010190602001808311610b8957829003601f168201915b505050505081565b5f6001600160a01b038216610bd8576040516322718ad960e21b81525f600482015260240161078a565b506001600160a01b03165f9081526003602052604090205490565b610bfb611109565b6109015f611136565b600c5433906001600160a01b03168114610c3c5760405163118cdaa760e01b81526001600160a01b038216600482015260240161078a565b610c4581611136565b50565b610c50611109565b6010805460ff191660011790556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e752905f90a1565b5f610c9182610ff3565b50505f9081526013602052604090205490565b60606001805461065e906120a9565b61071133838361114f565b610cc661104c565b60105460ff1615610cea5760405163c1cffd2f60e01b815260040160405180910390fd5b610cf785858585856111ed565b610d1457604051636cb40d4360e11b815260040160405180910390fd5b6001600160a01b03851615801590610d355750336001600160a01b03861614155b8015610d4c5750600b546001600160a01b03163314155b15610d6a57604051637192f5b760e11b815260040160405180910390fd5b6001831080610d795750600383115b15610d97576040516304b3136f60e11b815260040160405180910390fd5b6011610da460018561220d565b81548110610db457610db46120e1565b5f918252602080832090910154858352601290915260409091205410610ded5760405163d17b0b9f60e01b815260040160405180910390fd5b813414610e0d576040516380c00e2560e01b815260040160405180910390fd5b5f83815260126020526040812080549091600191839190610e2f908490612220565b90915550505f8581526013602052604081208590556001600160a01b03871615610e595786610e5b565b335b604080516001600160a01b0383168152602081018990529192507fbab4562ae868d1765ed8452e98bba908a974596c4babc3ada5e90e05e3f433fb910160405180910390a1610eaa81876112b9565b5050610eb66001600655565b5050505050565b610ec8848484610765565b6107ed848484846112d2565b60118181548110610ee3575f80fd5b5f91825260209091200154905081565b6060610efe82610ff3565b50600f610f0a836113f1565b604051602001610f1b929190612233565b6040516020818303038152906040529050919050565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b610f66611109565b600c80546001600160a01b0383166001600160a01b03199091168117909155610f97600b546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f6001600160e01b0319821663780e9d6360e01b148061064a575061064a82611481565b5f818152600260205260408120546001600160a01b03168061064a57604051637e27328960e01b81526004810184905260240161078a565b61095983838360016114d0565b5f6110448484846115d4565b949350505050565b60026006540361106f57604051633ee5aeb560e01b815260040160405180910390fd5b6002600655565b804710156110995760405163cd78605960e01b815230600482015260240161078a565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f81146110e2576040519150601f19603f3d011682016040523d82523d5f602084013e6110e7565b606091505b505090508061095957604051630a12f52160e11b815260040160405180910390fd5b600b546001600160a01b031633146109015760405163118cdaa760e01b815233600482015260240161078a565b600c80546001600160a01b0319169055610c458161169f565b6001600160a01b03821661118157604051630b61174360e31b81526001600160a01b038316600482015260240161078a565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6040516bffffffffffffffffffffffff1930606090811b8216602084015287901b1660348201526048810185905260688101849052608881018390525f90819060a80160408051601f198184030181529082905280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000091830191909152603c820152605c016040516020818303038152906040528051906020012090505f61129c82856116f0565b600d546001600160a01b0390811691161498975050505050505050565b610711828260405180602001604052805f815250611718565b6001600160a01b0383163b156107ed57604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906113149033908890879087906004016122ce565b6020604051808303815f875af192505050801561134e575060408051601f3d908101601f1916820190925261134b9181019061230a565b60015b6113b5573d80801561137b576040519150601f19603f3d011682016040523d82523d5f602084013e611380565b606091505b5080515f036113ad57604051633250574960e11b81526001600160a01b038516600482015260240161078a565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b14610eb657604051633250574960e11b81526001600160a01b038516600482015260240161078a565b60605f6113fd8361172e565b60010190505f8167ffffffffffffffff81111561141c5761141c611e8b565b6040519080825280601f01601f191660200182016040528015611446576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461145057509392505050565b5f6001600160e01b031982166380ac58cd60e01b14806114b157506001600160e01b03198216635b5e139f60e01b145b8061064a57506301ffc9a760e01b6001600160e01b031983161461064a565b80806114e457506001600160a01b03821615155b156115a5575f6114f384610ff3565b90506001600160a01b0383161580159061151f5750826001600160a01b0316816001600160a01b031614155b801561153257506115308184610f31565b155b1561155b5760405163a9fbf51f60e01b81526001600160a01b038416600482015260240161078a565b81156115a35783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f806115e1858585611805565b90506001600160a01b03811661163d5761163884600980545f838152600a60205260408120829055600182018355919091527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0155565b611660565b846001600160a01b0316816001600160a01b0316146116605761166081856118f7565b6001600160a01b03851661167c5761167784611984565b611044565b846001600160a01b0316816001600160a01b031614611044576110448585611a2b565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f805f806116fe8686611a79565b92509250925061170e8282611ac2565b5090949350505050565b6117228383611b7a565b6109595f8484846112d2565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061176c5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310611798576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106117b657662386f26fc10000830492506010015b6305f5e10083106117ce576305f5e100830492506008015b61271083106117e257612710830492506004015b606483106117f4576064830492506002015b600a831061064a5760010192915050565b5f828152600260205260408120546001600160a01b039081169083161561183157611831818486611bdb565b6001600160a01b0381161561186b5761184c5f855f806114d0565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615611899576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b5f61190183610bae565b5f83815260086020526040902054909150808214611952576001600160a01b0384165f9081526007602090815260408083208584528252808320548484528184208190558352600890915290208190555b505f9182526008602090815260408084208490556001600160a01b039094168352600781528383209183525290812055565b6009545f906119959060019061220d565b5f838152600a6020526040812054600980549394509092849081106119bc576119bc6120e1565b905f5260205f200154905080600983815481106119db576119db6120e1565b5f918252602080832090910192909255828152600a90915260408082208490558582528120556009805480611a1257611a12612325565b600190038181905f5260205f20015f9055905550505050565b5f6001611a3784610bae565b611a41919061220d565b6001600160a01b039093165f908152600760209081526040808320868452825280832085905593825260089052919091209190915550565b5f805f8351604103611ab0576020840151604085015160608601515f1a611aa288828585611c3f565b955095509550505050611abb565b505081515f91506002905b9250925092565b5f826003811115611ad557611ad5612339565b03611ade575050565b6001826003811115611af257611af2612339565b03611b105760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611b2457611b24612339565b03611b455760405163fce698f760e01b81526004810182905260240161078a565b6003826003811115611b5957611b59612339565b03610711576040516335e2f38360e21b81526004810182905260240161078a565b6001600160a01b038216611ba357604051633250574960e11b81525f600482015260240161078a565b5f611baf83835f611038565b90506001600160a01b03811615610959576040516339e3563760e11b81525f600482015260240161078a565b611be6838383611d07565b610959576001600160a01b038316611c1457604051637e27328960e01b81526004810182905260240161078a565b60405163177e802f60e01b81526001600160a01b03831660048201526024810182905260440161078a565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611c7857505f91506003905082611cfd565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611cc9573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116611cf457505f925060019150829050611cfd565b92505f91508190505b9450945094915050565b5f6001600160a01b038316158015906110445750826001600160a01b0316846001600160a01b03161480611d405750611d408484610f31565b806110445750505f908152600460205260409020546001600160a01b03908116911614919050565b6001600160e01b031981168114610c45575f80fd5b5f60208284031215611d8d575f80fd5b8135611d9881611d68565b9392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f611d986020830184611d9f565b5f60208284031215611def575f80fd5b5035919050565b80356001600160a01b0381168114611e0c575f80fd5b919050565b5f8060408385031215611e22575f80fd5b611e2b83611df6565b946020939093013593505050565b5f60208284031215611e49575f80fd5b611d9882611df6565b5f805f60608486031215611e64575f80fd5b611e6d84611df6565b9250611e7b60208501611df6565b9150604084013590509250925092565b634e487b7160e01b5f52604160045260245ffd5b5f67ffffffffffffffff80841115611eb957611eb9611e8b565b604051601f8501601f19908116603f01168101908282118183101715611ee157611ee1611e8b565b81604052809350858152868686011115611ef9575f80fd5b858560208301375f602087830101525050509392505050565b5f60208284031215611f22575f80fd5b813567ffffffffffffffff811115611f38575f80fd5b8201601f81018413611f48575f80fd5b61104484823560208401611e9f565b5f8060408385031215611f68575f80fd5b611f7183611df6565b915060208301358015158114611f85575f80fd5b809150509250929050565b5f82601f830112611f9f575f80fd5b611d9883833560208501611e9f565b5f805f805f60a08688031215611fc2575f80fd5b611fcb86611df6565b9450602086013593506040860135925060608601359150608086013567ffffffffffffffff811115611ffb575f80fd5b61200788828901611f90565b9150509295509295909350565b5f805f8060808587031215612027575f80fd5b61203085611df6565b935061203e60208601611df6565b925060408501359150606085013567ffffffffffffffff811115612060575f80fd5b61206c87828801611f90565b91505092959194509250565b5f8060408385031215612089575f80fd5b61209283611df6565b91506120a060208401611df6565b90509250929050565b600181811c908216806120bd57607f821691505b6020821081036120db57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b601f82111561095957805f5260205f20601f840160051c8101602085101561211a5750805b601f840160051c820191505b81811015610eb6575f8155600101612126565b815167ffffffffffffffff81111561215357612153611e8b565b6121678161216184546120a9565b846120f5565b602080601f83116001811461219a575f84156121835750858301515b5f19600386901b1c1916600185901b1785556121f1565b5f85815260208120601f198616915b828110156121c8578886015182559484019460019091019084016121a9565b50858210156121e557878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561064a5761064a6121f9565b8082018082111561064a5761064a6121f9565b5f808454612240816120a9565b60018281168015612258576001811461226d57612299565b60ff1984168752821515830287019450612299565b885f526020805f205f5b858110156122905781548a820152908401908201612277565b50505082870194505b50505050602f60f81b815283518060208601600184015e64173539b7b760d91b91016001810191825260060195945050505050565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f9061230090830184611d9f565b9695505050505050565b5f6020828403121561231a575f80fd5b8151611d9881611d68565b634e487b7160e01b5f52603160045260245ffd5b634e487b7160e01b5f52602160045260245ffdfea2646970667358221220e932a2916064214be22aa725ba4d4b36c0f61d51acb0555a41ca0b3fda292cfd64736f6c63430008190033000000000000000000000000dd8d662e9b077d1fc9fa009c0ffab5d695d00db000000000000000000000000039f0cd0a98f3f700c9a0d5e828799fec4cec1b35000000000000000000000000651d7e02053e77de61247fd331cc102535c6885000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d567737546b474d475259457735454d56316a55335265385838724a4377394a725358516b4751676f7a424c6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000009c400000000000000000000000000000000000000000000000000000000000001f40000000000000000000000000000000000000000000000000000000000000064

Deployed Bytecode

0x608060405260043610610207575f3560e01c80636c0360eb11610113578063b49c99b81161009d578063c87b56dd1161006d578063c87b56dd146105a7578063e30c3978146105c6578063e985e9c5146105e3578063f2bcd02214610602578063f2fde38b14610621575f80fd5b8063b49c99b814610537578063b88d4fde1461054a578063baef27b814610569578063c771909c14610588575f80fd5b80638456cb59116100e35780638456cb59146104b457806386481d40146104c85780638da5cb5b146104e757806395d89b4114610504578063a22cb46514610518575f80fd5b80636c0360eb1461045957806370a082311461046d578063715018a61461048c57806379ba5097146104a0575f80fd5b80633ccfd60b1161019457806355f804b31161016457806355f804b3146103c45780635c975abb146103e35780635e72fc5d146103fc5780636352211e1461041b578063669ed9351461043a575f80fd5b80633ccfd60b1461035e5780633f4ba83a1461037257806342842e0e146103865780634f6ccce7146103a5575f80fd5b806318160ddd116101da57806318160ddd146102b857806321117695146102d657806323b872dd146102f557806326c0aba2146103145780632f745c591461033f575f80fd5b806301ffc9a71461020b57806306fdde031461023f578063081812fc14610260578063095ea7b314610297575b5f80fd5b348015610216575f80fd5b5061022a610225366004611d7d565b610640565b60405190151581526020015b60405180910390f35b34801561024a575f80fd5b50610253610650565b6040516102369190611dcd565b34801561026b575f80fd5b5061027f61027a366004611ddf565b6106df565b6040516001600160a01b039091168152602001610236565b3480156102a2575f80fd5b506102b66102b1366004611e11565b610706565b005b3480156102c3575f80fd5b506009545b604051908152602001610236565b3480156102e1575f80fd5b506102c86102f0366004611e39565b610715565b348015610300575f80fd5b506102b661030f366004611e52565b610765565b34801561031f575f80fd5b506102c861032e366004611ddf565b60126020525f908152604090205481565b34801561034a575f80fd5b506102c8610359366004611e11565b6107f3565b348015610369575f80fd5b506102b6610856565b34801561037d575f80fd5b506102b6610903565b348015610391575f80fd5b506102b66103a0366004611e52565b61093f565b3480156103b0575f80fd5b506102c86103bf366004611ddf565b61095e565b3480156103cf575f80fd5b506102b66103de366004611f12565b6109b3565b3480156103ee575f80fd5b5060105461022a9060ff1681565b348015610407575f80fd5b506102b6610416366004611e39565b6109fe565b348015610426575f80fd5b5061027f610435366004611ddf565b610a8b565b348015610445575f80fd5b506102b6610454366004611e39565b610a95565b348015610464575f80fd5b50610253610b22565b348015610478575f80fd5b506102c8610487366004611e39565b610bae565b348015610497575f80fd5b506102b6610bf3565b3480156104ab575f80fd5b506102b6610c04565b3480156104bf575f80fd5b506102b6610c48565b3480156104d3575f80fd5b506102c86104e2366004611ddf565b610c87565b3480156104f2575f80fd5b50600b546001600160a01b031661027f565b34801561050f575f80fd5b50610253610ca4565b348015610523575f80fd5b506102b6610532366004611f57565b610cb3565b6102b6610545366004611fae565b610cbe565b348015610555575f80fd5b506102b6610564366004612014565b610ebd565b348015610574575f80fd5b506102c8610583366004611ddf565b610ed4565b348015610593575f80fd5b50600d5461027f906001600160a01b031681565b3480156105b2575f80fd5b506102536105c1366004611ddf565b610ef3565b3480156105d1575f80fd5b50600c546001600160a01b031661027f565b3480156105ee575f80fd5b5061022a6105fd366004612078565b610f31565b34801561060d575f80fd5b50600e5461027f906001600160a01b031681565b34801561062c575f80fd5b506102b661063b366004611e39565b610f5e565b5f61064a82610fcf565b92915050565b60605f805461065e906120a9565b80601f016020809104026020016040519081016040528092919081815260200182805461068a906120a9565b80156106d55780601f106106ac576101008083540402835291602001916106d5565b820191905f5260205f20905b8154815290600101906020018083116106b857829003601f168201915b5050505050905090565b5f6106e982610ff3565b505f828152600460205260409020546001600160a01b031661064a565b61071182823361102b565b5050565b5f808061072184610bae565b90505f5b8181101561075c575f61073886836107f3565b90505f61074482610c87565b905084811115610752578094505b5050600101610725565b50909392505050565b6001600160a01b03821661079357604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f61079f838333611038565b9050836001600160a01b0316816001600160a01b0316146107ed576040516364283d7b60e01b81526001600160a01b038086166004830152602482018490528216604482015260640161078a565b50505050565b5f6107fd83610bae565b821061082e5760405163295f44f760e21b81526001600160a01b03841660048201526024810183905260440161078a565b506001600160a01b03919091165f908152600760209081526040808320938352929052205490565b61085e61104c565b600e546001600160a01b03163314610889576040516379b798cf60e11b815260040160405180910390fd5b475f8190036108ab5760405163e168e91760e01b815260040160405180910390fd5b600e546108c1906001600160a01b031682611076565b60405181815233907f26d04682db6b10c93d5d39df697547e7f39badb6e2927e68323333e1afa795c29060200160405180910390a2506109016001600655565b565b61090b611109565b6010805460ff191690556040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d16933905f90a1565b61095983838360405180602001604052805f815250610ebd565b505050565b5f61096860095490565b82106109905760405163295f44f760e21b81525f60048201526024810183905260440161078a565b600982815481106109a3576109a36120e1565b905f5260205f2001549050919050565b6109bb611109565b7f6741b2fc379fad678116fe3d4d4b9a1a184ab53ba36b86ad0fa66340b1ab41ad816040516109ea9190611dcd565b60405180910390a1600f6107118282612139565b610a06611109565b6001600160a01b038116610a2d576040516301e8bbc160e31b815260040160405180910390fd5b6040516001600160a01b03821681527f227cb3c52b593782f40ecef3ba6f526592f2abe6ef6c34128c20b9dddf58ce639060200160405180910390a1600e80546001600160a01b0319166001600160a01b0392909216919091179055565b5f61064a82610ff3565b610a9d611109565b6001600160a01b038116610ac4576040516301e8bbc160e31b815260040160405180910390fd5b6040516001600160a01b03821681527f24bada2c0d54dfcadf1a6b7d7c4528c4219c6fffcce683ca9f39a8441947c63e9060200160405180910390a1600d80546001600160a01b0319166001600160a01b0392909216919091179055565b600f8054610b2f906120a9565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5b906120a9565b8015610ba65780601f10610b7d57610100808354040283529160200191610ba6565b820191905f5260205f20905b815481529060010190602001808311610b8957829003601f168201915b505050505081565b5f6001600160a01b038216610bd8576040516322718ad960e21b81525f600482015260240161078a565b506001600160a01b03165f9081526003602052604090205490565b610bfb611109565b6109015f611136565b600c5433906001600160a01b03168114610c3c5760405163118cdaa760e01b81526001600160a01b038216600482015260240161078a565b610c4581611136565b50565b610c50611109565b6010805460ff191660011790556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e752905f90a1565b5f610c9182610ff3565b50505f9081526013602052604090205490565b60606001805461065e906120a9565b61071133838361114f565b610cc661104c565b60105460ff1615610cea5760405163c1cffd2f60e01b815260040160405180910390fd5b610cf785858585856111ed565b610d1457604051636cb40d4360e11b815260040160405180910390fd5b6001600160a01b03851615801590610d355750336001600160a01b03861614155b8015610d4c5750600b546001600160a01b03163314155b15610d6a57604051637192f5b760e11b815260040160405180910390fd5b6001831080610d795750600383115b15610d97576040516304b3136f60e11b815260040160405180910390fd5b6011610da460018561220d565b81548110610db457610db46120e1565b5f918252602080832090910154858352601290915260409091205410610ded5760405163d17b0b9f60e01b815260040160405180910390fd5b813414610e0d576040516380c00e2560e01b815260040160405180910390fd5b5f83815260126020526040812080549091600191839190610e2f908490612220565b90915550505f8581526013602052604081208590556001600160a01b03871615610e595786610e5b565b335b604080516001600160a01b0383168152602081018990529192507fbab4562ae868d1765ed8452e98bba908a974596c4babc3ada5e90e05e3f433fb910160405180910390a1610eaa81876112b9565b5050610eb66001600655565b5050505050565b610ec8848484610765565b6107ed848484846112d2565b60118181548110610ee3575f80fd5b5f91825260209091200154905081565b6060610efe82610ff3565b50600f610f0a836113f1565b604051602001610f1b929190612233565b6040516020818303038152906040529050919050565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b610f66611109565b600c80546001600160a01b0383166001600160a01b03199091168117909155610f97600b546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f6001600160e01b0319821663780e9d6360e01b148061064a575061064a82611481565b5f818152600260205260408120546001600160a01b03168061064a57604051637e27328960e01b81526004810184905260240161078a565b61095983838360016114d0565b5f6110448484846115d4565b949350505050565b60026006540361106f57604051633ee5aeb560e01b815260040160405180910390fd5b6002600655565b804710156110995760405163cd78605960e01b815230600482015260240161078a565b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f81146110e2576040519150601f19603f3d011682016040523d82523d5f602084013e6110e7565b606091505b505090508061095957604051630a12f52160e11b815260040160405180910390fd5b600b546001600160a01b031633146109015760405163118cdaa760e01b815233600482015260240161078a565b600c80546001600160a01b0319169055610c458161169f565b6001600160a01b03821661118157604051630b61174360e31b81526001600160a01b038316600482015260240161078a565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6040516bffffffffffffffffffffffff1930606090811b8216602084015287901b1660348201526048810185905260688101849052608881018390525f90819060a80160408051601f198184030181529082905280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000091830191909152603c820152605c016040516020818303038152906040528051906020012090505f61129c82856116f0565b600d546001600160a01b0390811691161498975050505050505050565b610711828260405180602001604052805f815250611718565b6001600160a01b0383163b156107ed57604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906113149033908890879087906004016122ce565b6020604051808303815f875af192505050801561134e575060408051601f3d908101601f1916820190925261134b9181019061230a565b60015b6113b5573d80801561137b576040519150601f19603f3d011682016040523d82523d5f602084013e611380565b606091505b5080515f036113ad57604051633250574960e11b81526001600160a01b038516600482015260240161078a565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b14610eb657604051633250574960e11b81526001600160a01b038516600482015260240161078a565b60605f6113fd8361172e565b60010190505f8167ffffffffffffffff81111561141c5761141c611e8b565b6040519080825280601f01601f191660200182016040528015611446576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461145057509392505050565b5f6001600160e01b031982166380ac58cd60e01b14806114b157506001600160e01b03198216635b5e139f60e01b145b8061064a57506301ffc9a760e01b6001600160e01b031983161461064a565b80806114e457506001600160a01b03821615155b156115a5575f6114f384610ff3565b90506001600160a01b0383161580159061151f5750826001600160a01b0316816001600160a01b031614155b801561153257506115308184610f31565b155b1561155b5760405163a9fbf51f60e01b81526001600160a01b038416600482015260240161078a565b81156115a35783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f806115e1858585611805565b90506001600160a01b03811661163d5761163884600980545f838152600a60205260408120829055600182018355919091527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0155565b611660565b846001600160a01b0316816001600160a01b0316146116605761166081856118f7565b6001600160a01b03851661167c5761167784611984565b611044565b846001600160a01b0316816001600160a01b031614611044576110448585611a2b565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f805f806116fe8686611a79565b92509250925061170e8282611ac2565b5090949350505050565b6117228383611b7a565b6109595f8484846112d2565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061176c5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310611798576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106117b657662386f26fc10000830492506010015b6305f5e10083106117ce576305f5e100830492506008015b61271083106117e257612710830492506004015b606483106117f4576064830492506002015b600a831061064a5760010192915050565b5f828152600260205260408120546001600160a01b039081169083161561183157611831818486611bdb565b6001600160a01b0381161561186b5761184c5f855f806114d0565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615611899576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b5f61190183610bae565b5f83815260086020526040902054909150808214611952576001600160a01b0384165f9081526007602090815260408083208584528252808320548484528184208190558352600890915290208190555b505f9182526008602090815260408084208490556001600160a01b039094168352600781528383209183525290812055565b6009545f906119959060019061220d565b5f838152600a6020526040812054600980549394509092849081106119bc576119bc6120e1565b905f5260205f200154905080600983815481106119db576119db6120e1565b5f918252602080832090910192909255828152600a90915260408082208490558582528120556009805480611a1257611a12612325565b600190038181905f5260205f20015f9055905550505050565b5f6001611a3784610bae565b611a41919061220d565b6001600160a01b039093165f908152600760209081526040808320868452825280832085905593825260089052919091209190915550565b5f805f8351604103611ab0576020840151604085015160608601515f1a611aa288828585611c3f565b955095509550505050611abb565b505081515f91506002905b9250925092565b5f826003811115611ad557611ad5612339565b03611ade575050565b6001826003811115611af257611af2612339565b03611b105760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611b2457611b24612339565b03611b455760405163fce698f760e01b81526004810182905260240161078a565b6003826003811115611b5957611b59612339565b03610711576040516335e2f38360e21b81526004810182905260240161078a565b6001600160a01b038216611ba357604051633250574960e11b81525f600482015260240161078a565b5f611baf83835f611038565b90506001600160a01b03811615610959576040516339e3563760e11b81525f600482015260240161078a565b611be6838383611d07565b610959576001600160a01b038316611c1457604051637e27328960e01b81526004810182905260240161078a565b60405163177e802f60e01b81526001600160a01b03831660048201526024810182905260440161078a565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611c7857505f91506003905082611cfd565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611cc9573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116611cf457505f925060019150829050611cfd565b92505f91508190505b9450945094915050565b5f6001600160a01b038316158015906110445750826001600160a01b0316846001600160a01b03161480611d405750611d408484610f31565b806110445750505f908152600460205260409020546001600160a01b03908116911614919050565b6001600160e01b031981168114610c45575f80fd5b5f60208284031215611d8d575f80fd5b8135611d9881611d68565b9392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f611d986020830184611d9f565b5f60208284031215611def575f80fd5b5035919050565b80356001600160a01b0381168114611e0c575f80fd5b919050565b5f8060408385031215611e22575f80fd5b611e2b83611df6565b946020939093013593505050565b5f60208284031215611e49575f80fd5b611d9882611df6565b5f805f60608486031215611e64575f80fd5b611e6d84611df6565b9250611e7b60208501611df6565b9150604084013590509250925092565b634e487b7160e01b5f52604160045260245ffd5b5f67ffffffffffffffff80841115611eb957611eb9611e8b565b604051601f8501601f19908116603f01168101908282118183101715611ee157611ee1611e8b565b81604052809350858152868686011115611ef9575f80fd5b858560208301375f602087830101525050509392505050565b5f60208284031215611f22575f80fd5b813567ffffffffffffffff811115611f38575f80fd5b8201601f81018413611f48575f80fd5b61104484823560208401611e9f565b5f8060408385031215611f68575f80fd5b611f7183611df6565b915060208301358015158114611f85575f80fd5b809150509250929050565b5f82601f830112611f9f575f80fd5b611d9883833560208501611e9f565b5f805f805f60a08688031215611fc2575f80fd5b611fcb86611df6565b9450602086013593506040860135925060608601359150608086013567ffffffffffffffff811115611ffb575f80fd5b61200788828901611f90565b9150509295509295909350565b5f805f8060808587031215612027575f80fd5b61203085611df6565b935061203e60208601611df6565b925060408501359150606085013567ffffffffffffffff811115612060575f80fd5b61206c87828801611f90565b91505092959194509250565b5f8060408385031215612089575f80fd5b61209283611df6565b91506120a060208401611df6565b90509250929050565b600181811c908216806120bd57607f821691505b6020821081036120db57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b601f82111561095957805f5260205f20601f840160051c8101602085101561211a5750805b601f840160051c820191505b81811015610eb6575f8155600101612126565b815167ffffffffffffffff81111561215357612153611e8b565b6121678161216184546120a9565b846120f5565b602080601f83116001811461219a575f84156121835750858301515b5f19600386901b1c1916600185901b1785556121f1565b5f85815260208120601f198616915b828110156121c8578886015182559484019460019091019084016121a9565b50858210156121e557878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561064a5761064a6121f9565b8082018082111561064a5761064a6121f9565b5f808454612240816120a9565b60018281168015612258576001811461226d57612299565b60ff1984168752821515830287019450612299565b885f526020805f205f5b858110156122905781548a820152908401908201612277565b50505082870194505b50505050602f60f81b815283518060208601600184015e64173539b7b760d91b91016001810191825260060195945050505050565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f9061230090830184611d9f565b9695505050505050565b5f6020828403121561231a575f80fd5b8151611d9881611d68565b634e487b7160e01b5f52603160045260245ffd5b634e487b7160e01b5f52602160045260245ffdfea2646970667358221220e932a2916064214be22aa725ba4d4b36c0f61d51acb0555a41ca0b3fda292cfd64736f6c63430008190033

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

000000000000000000000000dd8d662e9b077d1fc9fa009c0ffab5d695d00db000000000000000000000000039f0cd0a98f3f700c9a0d5e828799fec4cec1b35000000000000000000000000651d7e02053e77de61247fd331cc102535c6885000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d567737546b474d475259457735454d56316a55335265385838724a4377394a725358516b4751676f7a424c6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000009c400000000000000000000000000000000000000000000000000000000000001f40000000000000000000000000000000000000000000000000000000000000064

-----Decoded View---------------
Arg [0] : initialOwner_ (address): 0xdD8D662E9b077D1fC9fA009C0FFab5D695d00dB0
Arg [1] : authorizedSigner_ (address): 0x39F0cD0A98F3F700C9a0d5E828799FEc4CeC1b35
Arg [2] : withdrawalAddress_ (address): 0x651D7E02053e77DE61247fD331cc102535C68850
Arg [3] : baseURI_ (string): ipfs://QmVw7TkGMGRYEw5EMV1jU3Re8X8rJCw9JrSXQkGQgozBLk
Arg [4] : maxMintsPerLevel_ (uint256[]): 2500,500,100

-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 000000000000000000000000dd8d662e9b077d1fc9fa009c0ffab5d695d00db0
Arg [1] : 00000000000000000000000039f0cd0a98f3f700c9a0d5e828799fec4cec1b35
Arg [2] : 000000000000000000000000651d7e02053e77de61247fd331cc102535c68850
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [6] : 697066733a2f2f516d567737546b474d475259457735454d56316a5533526538
Arg [7] : 5838724a4377394a725358516b4751676f7a424c6b0000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [9] : 00000000000000000000000000000000000000000000000000000000000009c4
Arg [10] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000064


Deployed Bytecode Sourcemap

86242:10707:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;96567:187;;;;;;;;;;-1:-1:-1;96567:187:0;;;;;:::i;:::-;;:::i;:::-;;;565:14:1;;558:22;540:41;;528:2;513:18;96567:187:0;;;;;;;;62662:91;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;63834:158::-;;;;;;;;;;-1:-1:-1;63834:158:0;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;1460:32:1;;;1442:51;;1430:2;1415:18;63834:158:0;1296:203:1;63653:115:0;;;;;;;;;;-1:-1:-1;63653:115:0;;;;;:::i;:::-;;:::i;:::-;;80418:104;;;;;;;;;;-1:-1:-1;80497:10:0;:17;80418:104;;;2087:25:1;;;2075:2;2060:18;80418:104:0;1941:177:1;92228:488:0;;;;;;;;;;-1:-1:-1;92228:488:0;;;;;:::i;:::-;;:::i;64503:588::-;;;;;;;;;;-1:-1:-1;64503:588:0;;;;;:::i;:::-;;:::i;87890:53::-;;;;;;;;;;-1:-1:-1;87890:53:0;;;;;:::i;:::-;;;;;;;;;;;;;;80082:260;;;;;;;;;;-1:-1:-1;80082:260:0;;;;;:::i;:::-;;:::i;95824:443::-;;;;;;;;;;;;;:::i;94350:97::-;;;;;;;;;;;;;:::i;65162:134::-;;;;;;;;;;-1:-1:-1;65162:134:0;;;;;:::i;:::-;;:::i;80599:231::-;;;;;;;;;;-1:-1:-1;80599:231:0;;;;;:::i;:::-;;:::i;94008:140::-;;;;;;;;;;-1:-1:-1;94008:140:0;;;;;:::i;:::-;;:::i;87598:18::-;;;;;;;;;;-1:-1:-1;87598:18:0;;;;;;;;93578:315;;;;;;;;;;-1:-1:-1;93578:315:0;;;;;:::i;:::-;;:::i;62475:120::-;;;;;;;;;;-1:-1:-1;62475:120:0;;;;;:::i;:::-;;:::i;93130:310::-;;;;;;;;;;-1:-1:-1;93130:310:0;;;;;:::i;:::-;;:::i;87500:21::-;;;;;;;;;;;;;:::i;62200:213::-;;;;;;;;;;-1:-1:-1;62200:213:0;;;;;:::i;:::-;;:::i;47835:103::-;;;;;;;;;;;;;:::i;50472:235::-;;;;;;;;;;;;;:::i;94202:92::-;;;;;;;;;;;;;:::i;92724:261::-;;;;;;;;;;-1:-1:-1;92724:261:0;;;;;:::i;:::-;;:::i;47160:87::-;;;;;;;;;;-1:-1:-1;47233:6:0;;-1:-1:-1;;;;;47233:6:0;47160:87;;62822:95;;;;;;;;;;;;;:::i;64064:146::-;;;;;;;;;;-1:-1:-1;64064:146:0;;;;;:::i;:::-;;:::i;90272:1706::-;;;;;;:::i;:::-;;:::i;65367:211::-;;;;;;;;;;-1:-1:-1;65367:211:0;;;;;:::i;:::-;;:::i;87803:33::-;;;;;;;;;;-1:-1:-1;87803:33:0;;;;;:::i;:::-;;:::i;87310:31::-;;;;;;;;;;-1:-1:-1;87310:31:0;;;;-1:-1:-1;;;;;87310:31:0;;;89515:386;;;;;;;;;;-1:-1:-1;89515:386:0;;;;;:::i;:::-;;:::i;49560:101::-;;;;;;;;;;-1:-1:-1;49640:13:0;;-1:-1:-1;;;;;49640:13:0;49560:101;;64281:155;;;;;;;;;;-1:-1:-1;64281:155:0;;;;;:::i;:::-;;:::i;87416:32::-;;;;;;;;;;-1:-1:-1;87416:32:0;;;;-1:-1:-1;;;;;87416:32:0;;;49860:181;;;;;;;;;;-1:-1:-1;49860:181:0;;;;;:::i;:::-;;:::i;96567:187::-;96686:4;96710:36;96734:11;96710:23;:36::i;:::-;96703:43;96567:187;-1:-1:-1;;96567:187:0:o;62662:91::-;62707:13;62740:5;62733:12;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;62662:91;:::o;63834:158::-;63901:7;63921:22;63935:7;63921:13;:22::i;:::-;-1:-1:-1;66410:7:0;66437:24;;;:15;:24;;;;;;-1:-1:-1;;;;;66437:24:0;63963:21;66340:129;63653:115;63725:35;63734:2;63738:7;45249:10;63725:8;:35::i;:::-;63653:115;;:::o;92228:488::-;92318:7;;;92391:17;92401:6;92391:9;:17::i;:::-;92373:35;;92426:9;92421:256;92445:7;92441:1;:11;92421:256;;;92474:15;92492:30;92512:6;92520:1;92492:19;:30::i;:::-;92474:48;;92537:13;92553:17;92562:7;92553:8;:17::i;:::-;92537:33;;92597:12;92589:5;:20;92585:81;;;92645:5;92630:20;;92585:81;-1:-1:-1;;92454:3:0;;92421:256;;;-1:-1:-1;92696:12:0;;92228:488;-1:-1:-1;;;92228:488:0:o;64503:588::-;-1:-1:-1;;;;;64598:16:0;;64594:89;;64638:33;;-1:-1:-1;;;64638:33:0;;64668:1;64638:33;;;1442:51:1;1415:18;;64638:33:0;;;;;;;;64594:89;64904:21;64928:34;64936:2;64940:7;45249:10;64928:7;:34::i;:::-;64904:58;;64994:4;-1:-1:-1;;;;;64977:21:0;:13;-1:-1:-1;;;;;64977:21:0;;64973:111;;65022:50;;-1:-1:-1;;;65022:50:0;;-1:-1:-1;;;;;6505:15:1;;;65022:50:0;;;6487:34:1;6537:18;;;6530:34;;;6600:15;;6580:18;;;6573:43;6422:18;;65022:50:0;6247:375:1;64973:111:0;64583:508;64503:588;;;:::o;80082:260::-;80170:7;80203:16;80213:5;80203:9;:16::i;:::-;80194:5;:25;80190:101;;80243:36;;-1:-1:-1;;;80243:36:0;;-1:-1:-1;;;;;6819:32:1;;80243:36:0;;;6801:51:1;6868:18;;;6861:34;;;6774:18;;80243:36:0;6627:274:1;80190:101:0;-1:-1:-1;;;;;;80308:19:0;;;;;;;;:12;:19;;;;;;;;:26;;;;;;;;;80082:260::o;95824:443::-;16799:21;:19;:21::i;:::-;95959:17:::1;::::0;-1:-1:-1;;;;;95959:17:0::1;95945:10;:31;95941:91;;96000:20;;-1:-1:-1::0;;;96000:20:0::1;;;;;;;;;;;95941:91;96060:21;96042:15;96096:12:::0;;;96092:49:::1;;96117:24;;-1:-1:-1::0;;;96117:24:0::1;;;;;;;;;;;96092:49;96180:17;::::0;96154:54:::1;::::0;-1:-1:-1;;;;;96180:17:0::1;96200:7:::0;96154:17:::1;:54::i;:::-;96224:35;::::0;2087:25:1;;;96239:10:0::1;::::0;96224:35:::1;::::0;2075:2:1;2060:18;96224:35:0::1;;;;;;;95866:401;16843:20:::0;16134:1;17385:7;:21;17202:212;16843:20;95824:443::o;94350:97::-;47046:13;:11;:13::i;:::-;94399:6:::1;:14:::0;;-1:-1:-1;;94399:14:0::1;::::0;;94429:10:::1;::::0;::::1;::::0;94408:5:::1;::::0;94429:10:::1;94350:97::o:0;65162:134::-;65249:39;65266:4;65272:2;65276:7;65249:39;;;;;;;;;;;;:16;:39::i;:::-;65162:134;;;:::o;80599:231::-;80665:7;80698:13;80497:10;:17;;80418:104;80698:13;80689:5;:22;80685:103;;80735:41;;-1:-1:-1;;;80735:41:0;;80766:1;80735:41;;;6801:51:1;6868:18;;;6861:34;;;6774:18;;80735:41:0;6627:274:1;80685:103:0;80805:10;80816:5;80805:17;;;;;;;;:::i;:::-;;;;;;;;;80798:24;;80599:231;;;:::o;94008:140::-;47046:13;:11;:13::i;:::-;94087:24:::1;94102:8;94087:24;;;;;;:::i;:::-;;;;;;;;94122:7;:18;94132:8:::0;94122:7;:18:::1;:::i;93578:315::-:0;47046:13;:11;:13::i;:::-;-1:-1:-1;;;;;93689:32:0;::::1;93685:92;;93745:20;;-1:-1:-1::0;;;93745:20:0::1;;;;;;;;;;;93685:92;93792:44;::::0;-1:-1:-1;;;;;1460:32:1;;1442:51;;93792:44:0::1;::::0;1430:2:1;1415:18;93792:44:0::1;;;;;;;93847:17;:38:::0;;-1:-1:-1;;;;;;93847:38:0::1;-1:-1:-1::0;;;;;93847:38:0;;;::::1;::::0;;;::::1;::::0;;93578:315::o;62475:120::-;62538:7;62565:22;62579:7;62565:13;:22::i;93130:310::-;47046:13;:11;:13::i;:::-;-1:-1:-1;;;;;93239:31:0;::::1;93235:91;;93294:20;;-1:-1:-1::0;;;93294:20:0::1;;;;;;;;;;;93235:91;93341:42;::::0;-1:-1:-1;;;;;1460:32:1;;1442:51;;93341:42:0::1;::::0;1430:2:1;1415:18;93341:42:0::1;;;;;;;93396:16;:36:::0;;-1:-1:-1;;;;;;93396:36:0::1;-1:-1:-1::0;;;;;93396:36:0;;;::::1;::::0;;;::::1;::::0;;93130:310::o;87500:21::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;62200:213::-;62263:7;-1:-1:-1;;;;;62287:19:0;;62283:89;;62330:30;;-1:-1:-1;;;62330:30:0;;62357:1;62330:30;;;1442:51:1;1415:18;;62330:30:0;1296:203:1;62283:89:0;-1:-1:-1;;;;;;62389:16:0;;;;;:9;:16;;;;;;;62200:213::o;47835:103::-;47046:13;:11;:13::i;:::-;47900:30:::1;47927:1;47900:18;:30::i;50472:235::-:0;49640:13;;45249:10;;-1:-1:-1;;;;;49640:13:0;50569:24;;50565:98;;50617:34;;-1:-1:-1;;;50617:34:0;;-1:-1:-1;;;;;1460:32:1;;50617:34:0;;;1442:51:1;1415:18;;50617:34:0;1296:203:1;50565:98:0;50673:26;50692:6;50673:18;:26::i;:::-;50514:193;50472:235::o;94202:92::-;47046:13;:11;:13::i;:::-;94249:6:::1;:13:::0;;-1:-1:-1;;94249:13:0::1;94258:4;94249:13;::::0;;94278:8:::1;::::0;::::1;::::0;94249:6:::1;::::0;94278:8:::1;94202:92::o:0;92724:261::-;92781:7;92915:23;92929:8;92915:13;:23::i;:::-;-1:-1:-1;;92956:21:0;;;;:11;:21;;;;;;;92724:261::o;62822:95::-;62869:13;62902:7;62895:14;;;;;:::i;64064:146::-;64150:52;45249:10;64183:8;64193;64150:18;:52::i;90272:1706::-;16799:21;:19;:21::i;:::-;90512:6:::1;::::0;::::1;;90508:63;;;90542:17;;-1:-1:-1::0;;;90542:17:0::1;;;;;;;;;;;90508:63;90639:60;90657:3;90662:8;90672:6;90680;90688:10;90639:17;:60::i;:::-;90634:124;;90723:23;;-1:-1:-1::0;;;90723:23:0::1;;;;;;;;;;;90634:124;-1:-1:-1::0;;;;;90869:17:0;::::1;::::0;;::::1;::::0;:38:::1;;-1:-1:-1::0;90890:10:0::1;-1:-1:-1::0;;;;;90890:17:0;::::1;;;90869:38;:63;;;;-1:-1:-1::0;47233:6:0;;-1:-1:-1;;;;;47233:6:0;90911:10:::1;:21;;90869:63;90865:126;;;90956:23;;-1:-1:-1::0;;;90956:23:0::1;;;;;;;;;;;90865:126;91083:1;91074:6;:10;:24;;;;91097:1;91088:6;:10;91074:24;91070:83;;;91122:19;;-1:-1:-1::0;;;91122:19:0::1;;;;;;;;;;;91070:83;91275:16;91292:10;91301:1;91292:6:::0;:10:::1;:::i;:::-;91275:28;;;;;;;;:::i;:::-;;::::0;;;::::1;::::0;;;;;::::1;::::0;91243:18;;;:10:::1;:18:::0;;;;;;;:28;:60:::1;91239:123;;91327:23;;-1:-1:-1::0;;;91327:23:0::1;;;;;;;;;;;91239:123;91467:6;91454:9;:19;91450:78;;91497:19;;-1:-1:-1::0;;;91497:19:0::1;;;;;;;;;;;91450:78;91588:23;91614:18:::0;;;:10:::1;:18;::::0;;;;91643:20;;91614:18;;91662:1:::1;::::0;91614:18;;91588:23;91643:20:::1;::::0;91662:1;;91643:20:::1;:::i;:::-;::::0;;;-1:-1:-1;;91724:21:0::1;::::0;;;:11:::1;:21;::::0;;;;:30;;;-1:-1:-1;;;;;91842:17:0;::::1;::::0;:36:::1;;91875:3;91842:36;;;91862:10;91842:36;91896:34;::::0;;-1:-1:-1;;;;;6819:32:1;;6801:51;;6883:2;6868:18;;6861:34;;;91825:53:0;;-1:-1:-1;91896:34:0::1;::::0;6774:18:1;91896:34:0::1;;;;;;;91943:27;91953:6;91961:8;91943:9;:27::i;:::-;90456:1522;;16843:20:::0;16134:1;17385:7;:21;17202:212;16843:20;90272:1706;;;;;:::o;65367:211::-;65481:31;65494:4;65500:2;65504:7;65481:12;:31::i;:::-;65523:47;65546:4;65552:2;65556:7;65565:4;65523:22;:47::i;87803:33::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;87803:33:0;:::o;89515:386::-;89597:13;89737:23;89751:8;89737:13;:23::i;:::-;;89835:7;89849:19;:8;:17;:19::i;:::-;89818:60;;;;;;;;;:::i;:::-;;;;;;;;;;;;;89773:120;;89515:386;;;:::o;64281:155::-;-1:-1:-1;;;;;64393:25:0;;;64369:4;64393:25;;;:18;:25;;;;;;;;:35;;;;;;;;;;;;;;;64281:155::o;49860:181::-;47046:13;:11;:13::i;:::-;49950::::1;:24:::0;;-1:-1:-1;;;;;49950:24:0;::::1;-1:-1:-1::0;;;;;;49950:24:0;;::::1;::::0;::::1;::::0;;;50015:7:::1;47233:6:::0;;-1:-1:-1;;;;;47233:6:0;;47160:87;50015:7:::1;-1:-1:-1::0;;;;;49990:43:0::1;;;;;;;;;;;49860:181:::0;:::o;79774:224::-;79876:4;-1:-1:-1;;;;;;79900:50:0;;-1:-1:-1;;;79900:50:0;;:90;;;79954:36;79978:11;79954:23;:36::i;76809:247::-;76872:7;66195:16;;;:7;:16;;;;;;-1:-1:-1;;;;;66195:16:0;;76936:90;;76983:31;;-1:-1:-1;;;76983:31:0;;;;;2087:25:1;;;2060:18;;76983:31:0;1941:177:1;75041:122:0;75122:33;75131:2;75135:7;75144:4;75150;75122:8;:33::i;96345:214::-;96492:7;96519:32;96533:2;96537:7;96546:4;96519:13;:32::i;:::-;96512:39;96345:214;-1:-1:-1;;;;96345:214:0:o;16879:315::-;16177:1;17008:7;;:18;17004:88;;17050:30;;-1:-1:-1;;;17050:30:0;;;;;;;;;;;17004:88;16177:1;17169:7;:17;16879:315::o;9505:340::-;9615:6;9591:21;:30;9587:111;;;9645:41;;-1:-1:-1;;;9645:41:0;;9680:4;9645:41;;;1442:51:1;1415:18;;9645:41:0;1296:203:1;9587:111:0;9711:12;9729:9;-1:-1:-1;;;;;9729:14:0;9751:6;9729:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9710:52;;;9778:7;9773:65;;9809:17;;-1:-1:-1;;;9809:17:0;;;;;;;;;;;47325:166;47233:6;;-1:-1:-1;;;;;47233:6:0;45249:10;47385:23;47381:103;;47432:40;;-1:-1:-1;;;47432:40:0;;45249:10;47432:40;;;1442:51:1;1415:18;;47432:40:0;1296:203:1;50231:156:0;50321:13;50314:20;;-1:-1:-1;;;;;;50314:20:0;;;50345:34;50370:8;50345:24;:34::i;76248:318::-;-1:-1:-1;;;;;76356:22:0;;76352:93;;76402:31;;-1:-1:-1;;;76402:31:0;;-1:-1:-1;;;;;1460:32:1;;76402:31:0;;;1442:51:1;1415:18;;76402:31:0;1296:203:1;76352:93:0;-1:-1:-1;;;;;76455:25:0;;;;;;;:18;:25;;;;;;;;:35;;;;;;;;;;;;;:46;;-1:-1:-1;;76455:46:0;;;;;;;;;;76517:41;;540::1;;;76517::0;;513:18:1;76517:41:0;;;;;;;76248:318;;;:::o;94851:896::-;95258:251;;-1:-1:-1;;95309:4:0;11551:2:1;11547:15;;;11543:24;;95258:251:0;;;11531:37:1;11602:15;;;11598:24;11584:12;;;11577:46;11639:12;;;11632:28;;;11676:12;;;11669:28;;;11713:13;;;11706:29;;;95041:4:0;;;;11751:13:1;;95258:251:0;;;-1:-1:-1;;95258:251:0;;;;;;;;;;95226:302;;95258:251;95226:302;;;;12017:66:1;95138:405:0;;;12005:79:1;;;;12100:12;;;12093:28;12137:12;;95138:405:0;;;;;;;;;;;;95114:440;;;;;;95092:462;;95636:14;95653:38;95667:11;95680:10;95653:13;:38::i;:::-;95722:16;;-1:-1:-1;;;;;95722:16:0;;;95712:26;;;;94851:896;-1:-1:-1;;;;;;;;94851:896:0:o;71160:102::-;71228:26;71238:2;71242:7;71228:26;;;;;;;;;;;;:9;:26::i;77606:799::-;-1:-1:-1;;;;;77723:14:0;;;:18;77719:679;;77762:71;;-1:-1:-1;;;77762:71:0;;-1:-1:-1;;;;;77762:36:0;;;;;:71;;45249:10;;77813:4;;77819:7;;77828:4;;77762:71;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;-1:-1:-1;77762:71:0;;;;;;;;-1:-1:-1;;77762:71:0;;;;;;;;;;;;:::i;:::-;;;77758:629;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;78076:6;:13;78093:1;78076:18;78072:300;;78126:25;;-1:-1:-1;;;78126:25:0;;-1:-1:-1;;;;;1460:32:1;;78126:25:0;;;1442:51:1;1415:18;;78126:25:0;1296:203:1;78072:300:0;78322:6;78316:13;78307:6;78303:2;78299:15;78292:38;77758:629;-1:-1:-1;;;;;;77881:51:0;;-1:-1:-1;;;77881:51:0;77877:132;;77964:25;;-1:-1:-1;;;77964:25:0;;-1:-1:-1;;;;;1460:32:1;;77964:25:0;;;1442:51:1;1415:18;;77964:25:0;1296:203:1;41939:718:0;41995:13;42046:14;42063:17;42074:5;42063:10;:17::i;:::-;42083:1;42063:21;42046:38;;42099:20;42133:6;42122:18;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;42122:18:0;-1:-1:-1;42099:41:0;-1:-1:-1;42264:28:0;;;42280:2;42264:28;42321:290;-1:-1:-1;;42353:5:0;-1:-1:-1;;;42490:2:0;42479:14;;42474:32;42353:5;42461:46;42553:2;42544:11;;;-1:-1:-1;42574:21:0;42321:290;42574:21;-1:-1:-1;42632:6:0;41939:718;-1:-1:-1;;;41939:718:0:o;61831:305::-;61933:4;-1:-1:-1;;;;;;61970:40:0;;-1:-1:-1;;;61970:40:0;;:105;;-1:-1:-1;;;;;;;62027:48:0;;-1:-1:-1;;;62027:48:0;61970:105;:158;;;-1:-1:-1;;;;;;;;;;53591:40:0;;;62092:36;53491:148;75351:678;75513:9;:31;;;-1:-1:-1;;;;;;75526:18:0;;;;75513:31;75509:471;;;75561:13;75577:22;75591:7;75577:13;:22::i;:::-;75561:38;-1:-1:-1;;;;;;75730:18:0;;;;;;:35;;;75761:4;-1:-1:-1;;;;;75752:13:0;:5;-1:-1:-1;;;;;75752:13:0;;;75730:35;:69;;;;;75770:29;75787:5;75794:4;75770:16;:29::i;:::-;75769:30;75730:69;75726:144;;;75827:27;;-1:-1:-1;;;75827:27:0;;-1:-1:-1;;;;;1460:32:1;;75827:27:0;;;1442:51:1;1415:18;;75827:27:0;1296:203:1;75726:144:0;75890:9;75886:83;;;75945:7;75941:2;-1:-1:-1;;;;;75925:28:0;75934:5;-1:-1:-1;;;;;75925:28:0;;;;;;;;;;;75886:83;75546:434;75509:471;-1:-1:-1;;75992:24:0;;;;:15;:24;;;;;:29;;-1:-1:-1;;;;;;75992:29:0;-1:-1:-1;;;;;75992:29:0;;;;;;;;;;75351:678::o;80891:640::-;80986:7;81006:21;81030:32;81044:2;81048:7;81057:4;81030:13;:32::i;:::-;81006:56;-1:-1:-1;;;;;;81079:27:0;;81075:214;;81123:40;81155:7;82355:10;:17;;82328:24;;;;:15;:24;;;;;:44;;;82383:24;;;;;;;;;;;;82251:164;81123:40;81075:214;;;81202:2;-1:-1:-1;;;;;81185:19:0;:13;-1:-1:-1;;;;;81185:19:0;;81181:108;;81221:56;81254:13;81269:7;81221:32;:56::i;:::-;-1:-1:-1;;;;;81303:16:0;;81299:192;;81336:45;81373:7;81336:36;:45::i;:::-;81299:192;;;81420:2;-1:-1:-1;;;;;81403:19:0;:13;-1:-1:-1;;;;;81403:19:0;;81399:92;;81439:40;81467:2;81471:7;81439:27;:40::i;48473:191::-;48566:6;;;-1:-1:-1;;;;;48583:17:0;;;-1:-1:-1;;;;;;48583:17:0;;;;;;;48616:40;;48566:6;;;48583:17;48566:6;;48616:40;;48547:16;;48616:40;48536:128;48473:191;:::o;3794:259::-;3872:7;3893:17;3912:18;3932:16;3952:27;3963:4;3969:9;3952:10;:27::i;:::-;3892:87;;;;;;3990:28;4002:5;4009:8;3990:11;:28::i;:::-;-1:-1:-1;4036:9:0;;3794:259;-1:-1:-1;;;;3794:259:0:o;71489:185::-;71584:18;71590:2;71594:7;71584:5;:18::i;:::-;71613:53;71644:1;71648:2;71652:7;71661:4;71613:22;:53::i;38343:948::-;38396:7;;-1:-1:-1;;;38474:17:0;;38470:106;;-1:-1:-1;;;38512:17:0;;;-1:-1:-1;38558:2:0;38548:12;38470:106;38603:8;38594:5;:17;38590:106;;38641:8;38632:17;;;-1:-1:-1;38678:2:0;38668:12;38590:106;38723:8;38714:5;:17;38710:106;;38761:8;38752:17;;;-1:-1:-1;38798:2:0;38788:12;38710:106;38843:7;38834:5;:16;38830:103;;38880:7;38871:16;;;-1:-1:-1;38916:1:0;38906:11;38830:103;38960:7;38951:5;:16;38947:103;;38997:7;38988:16;;;-1:-1:-1;39033:1:0;39023:11;38947:103;39077:7;39068:5;:16;39064:103;;39114:7;39105:16;;;-1:-1:-1;39150:1:0;39140:11;39064:103;39194:7;39185:5;:16;39181:68;;39232:1;39222:11;39277:6;38343:948;-1:-1:-1;;38343:948:0:o;69302:824::-;69388:7;66195:16;;;:7;:16;;;;;;-1:-1:-1;;;;;66195:16:0;;;;69503:18;;;69499:88;;69538:37;69555:4;69561;69567:7;69538:16;:37::i;:::-;-1:-1:-1;;;;;69634:18:0;;;69630:263;;69752:48;69769:1;69773:7;69790:1;69794:5;69752:8;:48::i;:::-;-1:-1:-1;;;;;69846:15:0;;;;;;:9;:15;;;;;:20;;-1:-1:-1;;69846:20:0;;;69630:263;-1:-1:-1;;;;;69909:16:0;;;69905:111;;-1:-1:-1;;;;;69971:13:0;;;;;;:9;:13;;;;;:18;;69988:1;69971:18;;;69905:111;70028:16;;;;:7;:16;;;;;;:21;;-1:-1:-1;;;;;;70028:21:0;-1:-1:-1;;;;;70028:21:0;;;;;;;;;70067:27;;70028:16;;70067:27;;;;;;;70114:4;69302:824;-1:-1:-1;;;;69302:824:0:o;83042:977::-;83308:22;83333:15;83343:4;83333:9;:15::i;:::-;83359:18;83380:26;;;:17;:26;;;;;;83308:40;;-1:-1:-1;83513:28:0;;;83509:328;;-1:-1:-1;;;;;83580:18:0;;83558:19;83580:18;;;:12;:18;;;;;;;;:34;;;;;;;;;83631:30;;;;;;:44;;;83748:30;;:17;:30;;;;;:43;;;83509:328;-1:-1:-1;83933:26:0;;;;:17;:26;;;;;;;;83926:33;;;-1:-1:-1;;;;;83977:18:0;;;;;:12;:18;;;;;:34;;;;;;;83970:41;83042:977::o;84314:1079::-;84592:10;:17;84567:22;;84592:21;;84612:1;;84592:21;:::i;:::-;84624:18;84645:24;;;:15;:24;;;;;;85018:10;:26;;84567:46;;-1:-1:-1;84645:24:0;;84567:46;;85018:26;;;;;;:::i;:::-;;;;;;;;;84996:48;;85082:11;85057:10;85068;85057:22;;;;;;;;:::i;:::-;;;;;;;;;;;;:36;;;;85162:28;;;:15;:28;;;;;;;:41;;;85334:24;;;;;85327:31;85369:10;:16;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;84385:1008;;;84314:1079;:::o;81832:218::-;81917:14;81950:1;81934:13;81944:2;81934:9;:13::i;:::-;:17;;;;:::i;:::-;-1:-1:-1;;;;;81962:16:0;;;;;;;:12;:16;;;;;;;;:24;;;;;;;;:34;;;82007:26;;;:17;:26;;;;;;:35;;;;-1:-1:-1;81832:218:0:o;2188:783::-;2269:7;2278:12;2292:7;2316:9;:16;2336:2;2316:22;2312:652;;2660:4;2645:20;;2639:27;2710:4;2695:20;;2689:27;2768:4;2753:20;;2747:27;2355:9;2739:36;2811:25;2822:4;2739:36;2639:27;2689;2811:10;:25::i;:::-;2804:32;;;;;;;;;;;2312:652;-1:-1:-1;;2934:16:0;;2885:1;;-1:-1:-1;2889:35:0;;2312:652;2188:783;;;;;:::o;7362:542::-;7458:20;7449:5;:29;;;;;;;;:::i;:::-;;7445:452;;7362:542;;:::o;7445:452::-;7556:29;7547:5;:38;;;;;;;;:::i;:::-;;7543:354;;7609:23;;-1:-1:-1;;;7609:23:0;;;;;;;;;;;7543:354;7663:35;7654:5;:44;;;;;;;;:::i;:::-;;7650:247;;7722:46;;-1:-1:-1;;;7722:46:0;;;;;2087:25:1;;;2060:18;;7722:46:0;1941:177:1;7650:247:0;7799:30;7790:5;:39;;;;;;;;:::i;:::-;;7786:111;;7853:32;;-1:-1:-1;;;7853:32:0;;;;;2087:25:1;;;2060:18;;7853:32:0;1941:177:1;70462:335:0;-1:-1:-1;;;;;70530:16:0;;70526:89;;70570:33;;-1:-1:-1;;;70570:33:0;;70600:1;70570:33;;;1442:51:1;1415:18;;70570:33:0;1296:203:1;70526:89:0;70625:21;70649:32;70657:2;70661:7;70678:1;70649:7;:32::i;:::-;70625:56;-1:-1:-1;;;;;;70696:27:0;;;70692:98;;70747:31;;-1:-1:-1;;;70747:31:0;;70775:1;70747:31;;;1442:51:1;1415:18;;70747:31:0;1296:203:1;67509:376:0;67622:38;67636:5;67643:7;67652;67622:13;:38::i;:::-;67617:261;;-1:-1:-1;;;;;67681:19:0;;67677:190;;67728:31;;-1:-1:-1;;;67728:31:0;;;;;2087:25:1;;;2060:18;;67728:31:0;1941:177:1;67677:190:0;67807:44;;-1:-1:-1;;;67807:44:0;;-1:-1:-1;;;;;6819:32:1;;67807:44:0;;;6801:51:1;6868:18;;;6861:34;;;6774:18;;67807:44:0;6627:274:1;5265:1556:0;5396:7;;;6339:66;6326:79;;6322:166;;;-1:-1:-1;6438:1:0;;-1:-1:-1;6442:30:0;;-1:-1:-1;6474:1:0;6422:54;;6322:166;6602:24;;;6585:14;6602:24;;;;;;;;;13713:25:1;;;13786:4;13774:17;;13754:18;;;13747:45;;;;13808:18;;;13801:34;;;13851:18;;;13844:34;;;6602:24:0;;13685:19:1;;6602:24:0;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;6602:24:0;;-1:-1:-1;;6602:24:0;;;-1:-1:-1;;;;;;;6641:20:0;;6637:115;;-1:-1:-1;6694:1:0;;-1:-1:-1;6698:29:0;;-1:-1:-1;6694:1:0;;-1:-1:-1;6678:62:0;;6637:115;6772:6;-1:-1:-1;6780:20:0;;-1:-1:-1;6780:20:0;;-1:-1:-1;5265:1556:0;;;;;;;;;:::o;66789:276::-;66892:4;-1:-1:-1;;;;;66929:21:0;;;;;;:128;;;66977:7;-1:-1:-1;;;;;66968:16:0;:5;-1:-1:-1;;;;;66968:16:0;;:52;;;;66988:32;67005:5;67012:7;66988:16;:32::i;:::-;66968:88;;;-1:-1:-1;;66410:7:0;66437:24;;;:15;:24;;;;;;-1:-1:-1;;;;;66437:24:0;;;67024:32;;;;;-1:-1:-1;66789:276:0:o;14:131:1:-;-1:-1:-1;;;;;;88:32:1;;78:43;;68:71;;135:1;132;125:12;150:245;208:6;261:2;249:9;240:7;236:23;232:32;229:52;;;277:1;274;267:12;229:52;316:9;303:23;335:30;359:5;335:30;:::i;:::-;384:5;150:245;-1:-1:-1;;;150:245:1:o;592:289::-;634:3;672:5;666:12;699:6;694:3;687:19;755:6;748:4;741:5;737:16;730:4;725:3;721:14;715:47;807:1;800:4;791:6;786:3;782:16;778:27;771:38;870:4;863:2;859:7;854:2;846:6;842:15;838:29;833:3;829:39;825:50;818:57;;;592:289;;;;:::o;886:220::-;1035:2;1024:9;1017:21;998:4;1055:45;1096:2;1085:9;1081:18;1073:6;1055:45;:::i;1111:180::-;1170:6;1223:2;1211:9;1202:7;1198:23;1194:32;1191:52;;;1239:1;1236;1229:12;1191:52;-1:-1:-1;1262:23:1;;1111:180;-1:-1:-1;1111:180:1:o;1504:173::-;1572:20;;-1:-1:-1;;;;;1621:31:1;;1611:42;;1601:70;;1667:1;1664;1657:12;1601:70;1504:173;;;:::o;1682:254::-;1750:6;1758;1811:2;1799:9;1790:7;1786:23;1782:32;1779:52;;;1827:1;1824;1817:12;1779:52;1850:29;1869:9;1850:29;:::i;:::-;1840:39;1926:2;1911:18;;;;1898:32;;-1:-1:-1;;;1682:254:1:o;2123:186::-;2182:6;2235:2;2223:9;2214:7;2210:23;2206:32;2203:52;;;2251:1;2248;2241:12;2203:52;2274:29;2293:9;2274:29;:::i;2314:328::-;2391:6;2399;2407;2460:2;2448:9;2439:7;2435:23;2431:32;2428:52;;;2476:1;2473;2466:12;2428:52;2499:29;2518:9;2499:29;:::i;:::-;2489:39;;2547:38;2581:2;2570:9;2566:18;2547:38;:::i;:::-;2537:48;;2632:2;2621:9;2617:18;2604:32;2594:42;;2314:328;;;;;:::o;2647:127::-;2708:10;2703:3;2699:20;2696:1;2689:31;2739:4;2736:1;2729:15;2763:4;2760:1;2753:15;2779:632;2844:5;2874:18;2915:2;2907:6;2904:14;2901:40;;;2921:18;;:::i;:::-;2996:2;2990:9;2964:2;3050:15;;-1:-1:-1;;3046:24:1;;;3072:2;3042:33;3038:42;3026:55;;;3096:18;;;3116:22;;;3093:46;3090:72;;;3142:18;;:::i;:::-;3182:10;3178:2;3171:22;3211:6;3202:15;;3241:6;3233;3226:22;3281:3;3272:6;3267:3;3263:16;3260:25;3257:45;;;3298:1;3295;3288:12;3257:45;3348:6;3343:3;3336:4;3328:6;3324:17;3311:44;3403:1;3396:4;3387:6;3379;3375:19;3371:30;3364:41;;;;2779:632;;;;;:::o;3416:451::-;3485:6;3538:2;3526:9;3517:7;3513:23;3509:32;3506:52;;;3554:1;3551;3544:12;3506:52;3594:9;3581:23;3627:18;3619:6;3616:30;3613:50;;;3659:1;3656;3649:12;3613:50;3682:22;;3735:4;3727:13;;3723:27;-1:-1:-1;3713:55:1;;3764:1;3761;3754:12;3713:55;3787:74;3853:7;3848:2;3835:16;3830:2;3826;3822:11;3787:74;:::i;3872:347::-;3937:6;3945;3998:2;3986:9;3977:7;3973:23;3969:32;3966:52;;;4014:1;4011;4004:12;3966:52;4037:29;4056:9;4037:29;:::i;:::-;4027:39;;4116:2;4105:9;4101:18;4088:32;4163:5;4156:13;4149:21;4142:5;4139:32;4129:60;;4185:1;4182;4175:12;4129:60;4208:5;4198:15;;;3872:347;;;;;:::o;4224:221::-;4266:5;4319:3;4312:4;4304:6;4300:17;4296:27;4286:55;;4337:1;4334;4327:12;4286:55;4359:80;4435:3;4426:6;4413:20;4406:4;4398:6;4394:17;4359:80;:::i;4450:600::-;4554:6;4562;4570;4578;4586;4639:3;4627:9;4618:7;4614:23;4610:33;4607:53;;;4656:1;4653;4646:12;4607:53;4679:29;4698:9;4679:29;:::i;:::-;4669:39;;4755:2;4744:9;4740:18;4727:32;4717:42;;4806:2;4795:9;4791:18;4778:32;4768:42;;4857:2;4846:9;4842:18;4829:32;4819:42;;4912:3;4901:9;4897:19;4884:33;4940:18;4932:6;4929:30;4926:50;;;4972:1;4969;4962:12;4926:50;4995:49;5036:7;5027:6;5016:9;5012:22;4995:49;:::i;:::-;4985:59;;;4450:600;;;;;;;;:::o;5055:537::-;5150:6;5158;5166;5174;5227:3;5215:9;5206:7;5202:23;5198:33;5195:53;;;5244:1;5241;5234:12;5195:53;5267:29;5286:9;5267:29;:::i;:::-;5257:39;;5315:38;5349:2;5338:9;5334:18;5315:38;:::i;:::-;5305:48;;5400:2;5389:9;5385:18;5372:32;5362:42;;5455:2;5444:9;5440:18;5427:32;5482:18;5474:6;5471:30;5468:50;;;5514:1;5511;5504:12;5468:50;5537:49;5578:7;5569:6;5558:9;5554:22;5537:49;:::i;:::-;5527:59;;;5055:537;;;;;;;:::o;5597:260::-;5665:6;5673;5726:2;5714:9;5705:7;5701:23;5697:32;5694:52;;;5742:1;5739;5732:12;5694:52;5765:29;5784:9;5765:29;:::i;:::-;5755:39;;5813:38;5847:2;5836:9;5832:18;5813:38;:::i;:::-;5803:48;;5597:260;;;;;:::o;5862:380::-;5941:1;5937:12;;;;5984;;;6005:61;;6059:4;6051:6;6047:17;6037:27;;6005:61;6112:2;6104:6;6101:14;6081:18;6078:38;6075:161;;6158:10;6153:3;6149:20;6146:1;6139:31;6193:4;6190:1;6183:15;6221:4;6218:1;6211:15;6075:161;;5862:380;;;:::o;6906:127::-;6967:10;6962:3;6958:20;6955:1;6948:31;6998:4;6995:1;6988:15;7022:4;7019:1;7012:15;7164:518;7266:2;7261:3;7258:11;7255:421;;;7302:5;7299:1;7292:16;7346:4;7343:1;7333:18;7416:2;7404:10;7400:19;7397:1;7393:27;7387:4;7383:38;7452:4;7440:10;7437:20;7434:47;;;-1:-1:-1;7475:4:1;7434:47;7530:2;7525:3;7521:12;7518:1;7514:20;7508:4;7504:31;7494:41;;7585:81;7603:2;7596:5;7593:13;7585:81;;;7662:1;7648:16;;7629:1;7618:13;7585:81;;7858:1345;7984:3;7978:10;8011:18;8003:6;8000:30;7997:56;;;8033:18;;:::i;:::-;8062:97;8152:6;8112:38;8144:4;8138:11;8112:38;:::i;:::-;8106:4;8062:97;:::i;:::-;8214:4;;8271:2;8260:14;;8288:1;8283:663;;;;8990:1;9007:6;9004:89;;;-1:-1:-1;9059:19:1;;;9053:26;9004:89;-1:-1:-1;;7815:1:1;7811:11;;;7807:24;7803:29;7793:40;7839:1;7835:11;;;7790:57;9106:81;;8253:944;;8283:663;7111:1;7104:14;;;7148:4;7135:18;;-1:-1:-1;;8319:20:1;;;8437:236;8451:7;8448:1;8445:14;8437:236;;;8540:19;;;8534:26;8519:42;;8632:27;;;;8600:1;8588:14;;;;8467:19;;8437:236;;;8441:3;8701:6;8692:7;8689:19;8686:201;;;8762:19;;;8756:26;-1:-1:-1;;8845:1:1;8841:14;;;8857:3;8837:24;8833:37;8829:42;8814:58;8799:74;;8686:201;;;8933:1;8924:6;8921:1;8917:14;8913:22;8907:4;8900:36;8253:944;;;;;7858:1345;;:::o;9208:127::-;9269:10;9264:3;9260:20;9257:1;9250:31;9300:4;9297:1;9290:15;9324:4;9321:1;9314:15;9340:128;9407:9;;;9428:11;;;9425:37;;;9442:18;;:::i;9473:125::-;9538:9;;;9559:10;;;9556:36;;;9572:18;;:::i;9721:1304::-;10099:3;10128:1;10161:6;10155:13;10191:36;10217:9;10191:36;:::i;:::-;10246:1;10263:17;;;10289:133;;;;10436:1;10431:358;;;;10256:533;;10289:133;-1:-1:-1;;10322:24:1;;10310:37;;10395:14;;10388:22;10376:35;;10367:45;;;-1:-1:-1;10289:133:1;;10431:358;10462:6;10459:1;10452:17;10492:4;10537;10534:1;10524:18;10564:1;10578:165;10592:6;10589:1;10586:13;10578:165;;;10670:14;;10657:11;;;10650:35;10713:16;;;;10607:10;;10578:165;;;10582:3;;;10772:6;10767:3;10763:16;10756:23;;10256:533;;;;;-1:-1:-1;;;10805:3:1;10798:16;10845:6;10839:13;10899:8;10892:4;10884:6;10880:17;10876:1;10871:3;10867:11;10861:47;-1:-1:-1;;;10931:18:1;;10951:1;10927:26;;9663:20;;;9699:11;;10984:35;9721:1304;-1:-1:-1;;;;;9721:1304:1:o;12160:489::-;-1:-1:-1;;;;;12429:15:1;;;12411:34;;12481:15;;12476:2;12461:18;;12454:43;12528:2;12513:18;;12506:34;;;12576:3;12571:2;12556:18;;12549:31;;;12354:4;;12597:46;;12623:19;;12615:6;12597:46;:::i;:::-;12589:54;12160:489;-1:-1:-1;;;;;;12160:489:1:o;12654:249::-;12723:6;12776:2;12764:9;12755:7;12751:23;12747:32;12744:52;;;12792:1;12789;12782:12;12744:52;12824:9;12818:16;12843:30;12867:5;12843:30;:::i;13040:127::-;13101:10;13096:3;13092:20;13089:1;13082:31;13132:4;13129:1;13122:15;13156:4;13153:1;13146:15;13172:127;13233:10;13228:3;13224:20;13221:1;13214:31;13264:4;13261:1;13254:15;13288:4;13285:1;13278:15

Swarm Source

ipfs://e932a2916064214be22aa725ba4d4b36c0f61d51acb0555a41ca0b3fda292cfd
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.