POL Price: $0.218431 (-0.71%)
Gas: 31.2 GWei
 

Overview

POL Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 POL

POL Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...695284062025-03-26 15:53:0427 days ago1743004384IN
0xf8173a39...05D284D11
0 POL0.0008364834.17136832
Set Approval For...690548522025-03-14 22:08:0939 days ago1741990089IN
0xf8173a39...05D284D11
0 POL0.0007143729.08321426
Set Approval For...690417672025-03-14 14:24:2939 days ago1741962269IN
0xf8173a39...05D284D11
0 POL0.0033501561.35476445
Set Approval For...670437722025-01-23 10:43:3589 days ago1737629015IN
0xf8173a39...05D284D11
0 POL0.0016211566
Set Approval For...664214562025-01-07 19:40:14105 days ago1736278814IN
0xf8173a39...05D284D11
0 POL0.0011839148.3646662
Set Approval For...663199602025-01-05 4:05:10108 days ago1736049910IN
0xf8173a39...05D284D11
0 POL0.0007035828.64400038
Set Approval For...662656332025-01-03 18:20:54109 days ago1735928454IN
0xf8173a39...05D284D11
0 POL0.0021030485.61845857
Set Approval For...659109442024-12-25 13:57:46118 days ago1735135066IN
0xf8173a39...05D284D11
0 POL0.0014469259.10889782
Set Approval For...656347102024-12-18 13:51:14125 days ago1734529874IN
0xf8173a39...05D284D11
0 POL0.0020881985.01377962
Set Approval For...656347042024-12-18 13:51:02125 days ago1734529862IN
0xf8173a39...05D284D11
0 POL0.0021547988.02638856
Safe Transfer Fr...652192852024-12-08 4:54:45136 days ago1733633685IN
0xf8173a39...05D284D11
0 POL0.055421,000.00000002
Set Approval For...651560952024-12-06 14:11:14137 days ago1733494274IN
0xf8173a39...05D284D11
0 POL0.0017334270.5704719
Set Approval For...649009132024-11-30 5:10:42144 days ago1732943442IN
0xf8173a39...05D284D11
0 POL0.0008105732.9999999
Set Approval For...647043052024-11-25 6:35:15149 days ago1732516515IN
0xf8173a39...05D284D11
0 POL0.0017987232.99255473
Set Approval For...645636832024-11-21 17:53:04152 days ago1732211584IN
0xf8173a39...05D284D11
0 POL0.0009471138.55845457
Set Approval For...645076452024-11-20 8:20:51154 days ago1732090851IN
0xf8173a39...05D284D11
0 POL0.0023584496.3457401
Set Approval For...642503172024-11-13 21:45:41160 days ago1731534341IN
0xf8173a39...05D284D11
0 POL0.0010385730.00022769
Set Approval For...642503172024-11-13 21:45:41160 days ago1731534341IN
0xf8173a39...05D284D11
0 POL0.0016355730.00017101
Set Approval For...642501202024-11-13 21:38:43160 days ago1731533923IN
0xf8173a39...05D284D11
0 POL0.0025546746.78643537
Set Approval For...639899812024-11-07 10:01:01166 days ago1730973661IN
0xf8173a39...05D284D11
0 POL0.0017982832.98447589
Set Approval For...639524672024-11-06 11:15:29167 days ago1730891729IN
0xf8173a39...05D284D11
0 POL0.0038635270.7566987
Set Approval For...634459012024-10-24 20:53:14180 days ago1729803194IN
0xf8173a39...05D284D11
0 POL0.0007745131.53186612
Set Approval For...626363412024-10-04 20:47:15200 days ago1728074835IN
0xf8173a39...05D284D11
0 POL0.0032761860.00000003
Set Approval For...621702902024-09-23 8:23:33212 days ago1727079813IN
0xf8173a39...05D284D11
0 POL0.0007368930.00000005
Set Approval For...617716682024-09-13 10:58:05221 days ago1726225085IN
0xf8173a39...05D284D11
0 POL0.0017456971.07027946
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ERC721Full

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 99999 runs

Other Settings:
paris EvmVersion, MIT license
File 1 of 69 : ERC721Full.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import {IForwarderRegistry} from "./../../../metatx/interfaces/IForwarderRegistry.sol";
import {ITokenMetadataResolver} from "./../../metadata/interfaces/ITokenMetadataResolver.sol";
import {IOperatorFilterRegistry} from "./../../royalty/interfaces/IOperatorFilterRegistry.sol";
import {ERC721WithOperatorFilterer} from "./../ERC721WithOperatorFilterer.sol";
import {ERC721BatchTransferWithOperatorFilterer} from "./../ERC721BatchTransferWithOperatorFilterer.sol";
import {ERC721Metadata} from "./../ERC721Metadata.sol";
import {ERC721Mintable} from "./../ERC721Mintable.sol";
import {ERC721Deliverable} from "./../ERC721Deliverable.sol";
import {ERC2981} from "./../../royalty/ERC2981.sol";
import {ContractOwnership} from "./../../../access/ContractOwnership.sol";
import {TokenRecovery} from "./../../../security/TokenRecovery.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {ForwarderRegistryContextBase} from "./../../../metatx/base/ForwarderRegistryContextBase.sol";
import {ForwarderRegistryContext} from "./../../../metatx/ForwarderRegistryContext.sol";

contract ERC721Full is
    ERC721WithOperatorFilterer,
    ERC721BatchTransferWithOperatorFilterer,
    ERC721Metadata,
    ERC721Mintable,
    ERC721Deliverable,
    ERC2981,
    TokenRecovery,
    ForwarderRegistryContext
{
    constructor(
        string memory tokenName,
        string memory tokenSymbol,
        ITokenMetadataResolver metadataResolver,
        IOperatorFilterRegistry filterRegistry,
        IForwarderRegistry forwarderRegistry
    )
        ContractOwnership(msg.sender)
        ERC721Metadata(tokenName, tokenSymbol, metadataResolver)
        ERC721WithOperatorFilterer(filterRegistry)
        ForwarderRegistryContext(forwarderRegistry)
    {}

    /// @inheritdoc ForwarderRegistryContextBase
    function _msgSender() internal view virtual override(Context, ForwarderRegistryContextBase) returns (address) {
        return ForwarderRegistryContextBase._msgSender();
    }

    /// @inheritdoc ForwarderRegistryContextBase
    function _msgData() internal view virtual override(Context, ForwarderRegistryContextBase) returns (bytes calldata) {
        return ForwarderRegistryContextBase._msgData();
    }
}

File 2 of 69 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

File 3 of 69 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 4 of 69 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 5 of 69 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 6 of 69 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @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, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * 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.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
        }
    }
}

File 7 of 69 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

File 8 of 69 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

File 9 of 69 : CommonErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when trying to transfer tokens without calldata to the contract.
error EtherReceptionDisabled();

/// @notice Thrown when the multiple related arrays have different lengths.
error InconsistentArrayLengths();

/// @notice Thrown when an ETH transfer has failed.
error TransferFailed();

File 10 of 69 : AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {AccessControlBase} from "./base/AccessControlBase.sol";
import {ContractOwnership} from "./ContractOwnership.sol";

/// @title Access control via roles management (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract AccessControl is AccessControlBase, ContractOwnership {

}

File 11 of 69 : ContractOwnership.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ContractOwnershipStorage} from "./libraries/ContractOwnershipStorage.sol";
import {ContractOwnershipBase} from "./base/ContractOwnershipBase.sol";
import {InterfaceDetection} from "./../introspection/InterfaceDetection.sol";

/// @title ERC173 Contract Ownership Standard (immutable version).
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ContractOwnership is ContractOwnershipBase, InterfaceDetection {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @notice Initializes the storage with an initial contract owner.
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner the initial contract owner.
    constructor(address initialOwner) {
        ContractOwnershipStorage.layout().constructorInit(initialOwner);
    }
}

File 12 of 69 : AccessControlBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IAccessControl} from "./../../access/interfaces/IAccessControl.sol";
import {AccessControlStorage} from "./../libraries/AccessControlStorage.sol";
import {ContractOwnershipStorage} from "./../libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title Access control via roles management (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract AccessControlBase is IAccessControl, Context {
    using AccessControlStorage for AccessControlStorage.Layout;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @notice Grants a role to an account.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Emits a {RoleGranted} event if the account did not previously have the role.
    /// @param role The role to grant.
    /// @param account The account to grant the role to.
    function grantRole(bytes32 role, address account) external {
        address operator = _msgSender();
        ContractOwnershipStorage.layout().enforceIsContractOwner(operator);
        AccessControlStorage.layout().grantRole(role, account, operator);
    }

    /// @notice Revokes a role from an account.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Emits a {RoleRevoked} event if the account previously had the role.
    /// @param role The role to revoke.
    /// @param account The account to revoke the role from.
    function revokeRole(bytes32 role, address account) external {
        address operator = _msgSender();
        ContractOwnershipStorage.layout().enforceIsContractOwner(operator);
        AccessControlStorage.layout().revokeRole(role, account, operator);
    }

    /// @inheritdoc IAccessControl
    function renounceRole(bytes32 role) external {
        AccessControlStorage.layout().renounceRole(_msgSender(), role);
    }

    /// @inheritdoc IAccessControl
    function hasRole(bytes32 role, address account) external view returns (bool hasRole_) {
        return AccessControlStorage.layout().hasRole(role, account);
    }
}

File 13 of 69 : ContractOwnershipBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC173} from "./../interfaces/IERC173.sol";
import {ContractOwnershipStorage} from "./../libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC173 Contract Ownership Standard (proxiable version).
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
abstract contract ContractOwnershipBase is IERC173, Context {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @inheritdoc IERC173
    function owner() public view virtual returns (address) {
        return ContractOwnershipStorage.layout().owner();
    }

    /// @inheritdoc IERC173
    function transferOwnership(address newOwner) public virtual {
        ContractOwnershipStorage.layout().transferOwnership(_msgSender(), newOwner);
    }
}

File 14 of 69 : AccessControlErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when an account does not have the required role.
/// @param role The role the caller is missing.
/// @param account The account that was checked.
error NotRoleHolder(bytes32 role, address account);

/// @notice Thrown when an account does not have the required role on a target contract.
/// @param targetContract The contract that was checked.
/// @param role The role that was checked.
/// @param account The account that was checked.
error NotTargetContractRoleHolder(address targetContract, bytes32 role, address account);

File 15 of 69 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when the target contract is actually not a contract.
/// @param targetContract The contract that was checked
error TargetIsNotAContract(address targetContract);

File 16 of 69 : ContractOwnershipErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when an account is not the contract owner but is required to.
/// @param account The account that was checked.
error NotContractOwner(address account);

/// @notice Thrown when an account is not the target contract owner but is required to.
/// @param targetContract The contract that was checked.
/// @param account The account that was checked.
error NotTargetContractOwner(address targetContract, address account);

File 17 of 69 : AccessControlEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Emitted when `role` is granted to `account`.
/// @param role The role that has been granted.
/// @param account The account that has been granted the role.
/// @param operator The account that granted the role.
event RoleGranted(bytes32 role, address account, address operator);

/// @notice Emitted when `role` is revoked from `account`.
/// @param role The role that has been revoked.
/// @param account The account that has been revoked the role.
/// @param operator The account that revoked the role.
event RoleRevoked(bytes32 role, address account, address operator);

File 18 of 69 : ERC173Events.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Emitted when the contract ownership changes.
/// @param previousOwner the previous contract owner.
/// @param newOwner the new contract owner.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

File 19 of 69 : IAccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title Access control via roles management (functions)
interface IAccessControl {
    /// @notice Renounces a role by the sender.
    /// @dev Reverts if `sender` does not have `role`.
    /// @dev Emits a {RoleRevoked} event.
    /// @param role The role to renounce.
    function renounceRole(bytes32 role) external;

    /// @notice Retrieves whether an account has a role.
    /// @param role The role.
    /// @param account The account.
    /// @return hasRole_ Whether `account` has `role`.
    function hasRole(bytes32 role, address account) external view returns (bool hasRole_);
}

File 20 of 69 : IERC173.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC-173 Contract Ownership Standard (functions)
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev Note: the ERC-165 identifier for this interface is 0x7f5828d0
interface IERC173 {
    /// @notice Sets the address of the new contract owner.
    /// @dev Reverts if the sender is not the contract owner.
    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.
    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.
    function transferOwnership(address newOwner) external;

    /// @notice Gets the address of the contract owner.
    /// @return contractOwner The address of the contract owner.
    function owner() external view returns (address contractOwner);
}

File 21 of 69 : AccessControlStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {NotRoleHolder, NotTargetContractRoleHolder} from "./../errors/AccessControlErrors.sol";
import {TargetIsNotAContract} from "./../errors/Common.sol";
import {RoleGranted, RoleRevoked} from "./../events/AccessControlEvents.sol";
import {IAccessControl} from "./../interfaces/IAccessControl.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

library AccessControlStorage {
    using Address for address;
    using AccessControlStorage for AccessControlStorage.Layout;

    struct Layout {
        mapping(bytes32 => mapping(address => bool)) roles;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.access.AccessControl.storage")) - 1);

    /// @notice Grants a role to an account.
    /// @dev Note: Call to this function should be properly access controlled.
    /// @dev Emits a {RoleGranted} event if the account did not previously have the role.
    /// @param role The role to grant.
    /// @param account The account to grant the role to.
    /// @param operator The account requesting the role change.
    function grantRole(Layout storage s, bytes32 role, address account, address operator) internal {
        if (!s.hasRole(role, account)) {
            s.roles[role][account] = true;
            emit RoleGranted(role, account, operator);
        }
    }

    /// @notice Revokes a role from an account.
    /// @dev Note: Call to this function should be properly access controlled.
    /// @dev Emits a {RoleRevoked} event if the account previously had the role.
    /// @param role The role to revoke.
    /// @param account The account to revoke the role from.
    /// @param operator The account requesting the role change.
    function revokeRole(Layout storage s, bytes32 role, address account, address operator) internal {
        if (s.hasRole(role, account)) {
            s.roles[role][account] = false;
            emit RoleRevoked(role, account, operator);
        }
    }

    /// @notice Renounces a role by the sender.
    /// @dev Reverts with {NotRoleHolder} if `sender` does not have `role`.
    /// @dev Emits a {RoleRevoked} event.
    /// @param sender The message sender.
    /// @param role The role to renounce.
    function renounceRole(Layout storage s, address sender, bytes32 role) internal {
        s.enforceHasRole(role, sender);
        s.roles[role][sender] = false;
        emit RoleRevoked(role, sender, sender);
    }

    /// @notice Retrieves whether an account has a role.
    /// @param role The role.
    /// @param account The account.
    /// @return hasRole_ Whether `account` has `role`.
    function hasRole(Layout storage s, bytes32 role, address account) internal view returns (bool hasRole_) {
        return s.roles[role][account];
    }

    /// @notice Checks whether an account has a role in a target contract.
    /// @param targetContract The contract to check.
    /// @param role The role to check.
    /// @param account The account to check.
    /// @return hasTargetContractRole_ Whether `account` has `role` in `targetContract`.
    function hasTargetContractRole(address targetContract, bytes32 role, address account) internal view returns (bool hasTargetContractRole_) {
        if (!targetContract.isContract()) revert TargetIsNotAContract(targetContract);
        return IAccessControl(targetContract).hasRole(role, account);
    }

    /// @notice Ensures that an account has a role.
    /// @dev Reverts with {NotRoleHolder} if `account` does not have `role`.
    /// @param role The role.
    /// @param account The account.
    function enforceHasRole(Layout storage s, bytes32 role, address account) internal view {
        if (!s.hasRole(role, account)) revert NotRoleHolder(role, account);
    }

    /// @notice Enforces that an account has a role in a target contract.
    /// @dev Reverts with {NotTargetContractRoleHolder} if the account does not have the role.
    /// @param targetContract The contract to check.
    /// @param role The role to check.
    /// @param account The account to check.
    function enforceHasTargetContractRole(address targetContract, bytes32 role, address account) internal view {
        if (!hasTargetContractRole(targetContract, role, account)) revert NotTargetContractRoleHolder(targetContract, role, account);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 22 of 69 : ContractOwnershipStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {NotContractOwner, NotTargetContractOwner} from "./../errors/ContractOwnershipErrors.sol";
import {TargetIsNotAContract} from "./../errors/Common.sol";
import {OwnershipTransferred} from "./../events/ERC173Events.sol";
import {IERC173} from "./../interfaces/IERC173.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ProxyInitialization} from "./../../proxy/libraries/ProxyInitialization.sol";
import {InterfaceDetectionStorage} from "./../../introspection/libraries/InterfaceDetectionStorage.sol";

library ContractOwnershipStorage {
    using Address for address;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        address contractOwner;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.access.ContractOwnership.storage")) - 1);
    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.core.access.ContractOwnership.phase")) - 1);

    /// @notice Initializes the storage with an initial contract owner (immutable version).
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner The initial contract owner.
    function constructorInit(Layout storage s, address initialOwner) internal {
        if (initialOwner != address(0)) {
            s.contractOwner = initialOwner;
            emit OwnershipTransferred(address(0), initialOwner);
        }
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC173).interfaceId, true);
    }

    /// @notice Initializes the storage with an initial contract owner (proxied version).
    /// @notice Sets the proxy initialization phase to `1`.
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner The initial contract owner.
    function proxyInit(Layout storage s, address initialOwner) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.constructorInit(initialOwner);
    }

    /// @notice Sets the address of the new contract owner.
    /// @dev Reverts with {NotContractOwner} if `sender` is not the contract owner.
    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.
    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.
    function transferOwnership(Layout storage s, address sender, address newOwner) internal {
        address previousOwner = s.contractOwner;
        if (sender != previousOwner) revert NotContractOwner(sender);
        if (previousOwner != newOwner) {
            s.contractOwner = newOwner;
            emit OwnershipTransferred(previousOwner, newOwner);
        }
    }

    /// @notice Gets the address of the contract owner.
    /// @return contractOwner The address of the contract owner.
    function owner(Layout storage s) internal view returns (address contractOwner) {
        return s.contractOwner;
    }

    /// @notice Checks whether an account is the owner of a target contract.
    /// @param targetContract The contract to check.
    /// @param account The account to check.
    /// @return isTargetContractOwner_ Whether `account` is the owner of `targetContract`.
    function isTargetContractOwner(address targetContract, address account) internal view returns (bool isTargetContractOwner_) {
        if (!targetContract.isContract()) revert TargetIsNotAContract(targetContract);
        return IERC173(targetContract).owner() == account;
    }

    /// @notice Ensures that an account is the contract owner.
    /// @dev Reverts with {NotContractOwner} if `account` is not the contract owner.
    /// @param account The account.
    function enforceIsContractOwner(Layout storage s, address account) internal view {
        if (account != s.contractOwner) revert NotContractOwner(account);
    }

    /// @notice Enforces that an account is the owner of a target contract.
    /// @dev Reverts with {NotTheTargetContractOwner} if the account is not the owner.
    /// @param targetContract The contract to check.
    /// @param account The account to check.
    function enforceIsTargetContractOwner(address targetContract, address account) internal view {
        if (!isTargetContractOwner(targetContract, account)) revert NotTargetContractOwner(targetContract, account);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 23 of 69 : InterfaceDetection.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC165} from "./interfaces/IERC165.sol";
import {InterfaceDetectionStorage} from "./libraries/InterfaceDetectionStorage.sol";

/// @title ERC165 Interface Detection Standard (immutable or proxiable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) or proxied implementation.
abstract contract InterfaceDetection is IERC165 {
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return InterfaceDetectionStorage.layout().supportsInterface(interfaceId);
    }
}

File 24 of 69 : InterfaceDetectionErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when setting the illegal interfaceId 0xffffffff.
error IllegalInterfaceId();

File 25 of 69 : IERC165.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC165 Interface Detection Standard.
/// @dev See https://eips.ethereum.org/EIPS/eip-165.
/// @dev Note: The ERC-165 identifier for this interface is 0x01ffc9a7.
interface IERC165 {
    /// @notice Returns whether this contract implements a given interface.
    /// @dev Note: This function call must use less than 30 000 gas.
    /// @param interfaceId the interface identifier to test.
    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.
    function supportsInterface(bytes4 interfaceId) external view returns (bool supported);
}

File 26 of 69 : InterfaceDetectionStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IllegalInterfaceId} from "./../errors/InterfaceDetectionErrors.sol";
import {IERC165} from "./../interfaces/IERC165.sol";

library InterfaceDetectionStorage {
    struct Layout {
        mapping(bytes4 => bool) supportedInterfaces;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.introspection.InterfaceDetection.storage")) - 1);

    bytes4 internal constant ILLEGAL_INTERFACE_ID = 0xffffffff;

    /// @notice Sets or unsets an ERC165 interface.
    /// @dev Revertswith {IllegalInterfaceId} if `interfaceId` is `0xffffffff`.
    /// @param interfaceId the interface identifier.
    /// @param supported True to set the interface, false to unset it.
    function setSupportedInterface(Layout storage s, bytes4 interfaceId, bool supported) internal {
        if (interfaceId == ILLEGAL_INTERFACE_ID) revert IllegalInterfaceId();
        s.supportedInterfaces[interfaceId] = supported;
    }

    /// @notice Returns whether this contract implements a given interface.
    /// @dev Note: This function call must use less than 30 000 gas.
    /// @param interfaceId The interface identifier to test.
    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.
    function supportsInterface(Layout storage s, bytes4 interfaceId) internal view returns (bool supported) {
        if (interfaceId == ILLEGAL_INTERFACE_ID) {
            return false;
        }
        if (interfaceId == type(IERC165).interfaceId) {
            return true;
        }
        return s.supportedInterfaces[interfaceId];
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 27 of 69 : ForwarderRegistryContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IForwarderRegistry} from "./interfaces/IForwarderRegistry.sol";
import {IERC2771} from "./interfaces/IERC2771.sol";
import {ForwarderRegistryContextBase} from "./base/ForwarderRegistryContextBase.sol";

/// @title Meta-Transactions Forwarder Registry Context (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
/// @dev Derived from https://github.com/wighawag/universal-forwarder (MIT licence)
abstract contract ForwarderRegistryContext is ForwarderRegistryContextBase, IERC2771 {
    constructor(IForwarderRegistry forwarderRegistry_) ForwarderRegistryContextBase(forwarderRegistry_) {}

    function forwarderRegistry() external view returns (IForwarderRegistry) {
        return _FORWARDER_REGISTRY;
    }

    /// @inheritdoc IERC2771
    function isTrustedForwarder(address forwarder) external view virtual returns (bool) {
        return forwarder == address(_FORWARDER_REGISTRY);
    }
}

File 28 of 69 : ForwarderRegistryContextBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IForwarderRegistry} from "./../interfaces/IForwarderRegistry.sol";
import {ERC2771Calldata} from "./../libraries/ERC2771Calldata.sol";

/// @title Meta-Transactions Forwarder Registry Context (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Derived from https://github.com/wighawag/universal-forwarder (MIT licence)
abstract contract ForwarderRegistryContextBase {
    IForwarderRegistry internal immutable _FORWARDER_REGISTRY;

    constructor(IForwarderRegistry forwarderRegistry) {
        _FORWARDER_REGISTRY = forwarderRegistry;
    }

    /// @notice Returns the message sender depending on the ForwarderRegistry-based meta-transaction context.
    function _msgSender() internal view virtual returns (address) {
        // Optimised path in case of an EOA-initiated direct tx to the contract or a call from a contract not complying with EIP-2771
        // solhint-disable-next-line avoid-tx-origin
        if (msg.sender == tx.origin || msg.data.length < 24) {
            return msg.sender;
        }

        address sender = ERC2771Calldata.msgSender();

        // Return the EIP-2771 calldata-appended sender address if the message was forwarded by the ForwarderRegistry or an approved forwarder
        if (msg.sender == address(_FORWARDER_REGISTRY) || _FORWARDER_REGISTRY.isApprovedForwarder(sender, msg.sender)) {
            return sender;
        }

        return msg.sender;
    }

    /// @notice Returns the message data depending on the ForwarderRegistry-based meta-transaction context.
    function _msgData() internal view virtual returns (bytes calldata) {
        // Optimised path in case of an EOA-initiated direct tx to the contract or a call from a contract not complying with EIP-2771
        // solhint-disable-next-line avoid-tx-origin
        if (msg.sender == tx.origin || msg.data.length < 24) {
            return msg.data;
        }

        // Return the EIP-2771 calldata (minus the appended sender) if the message was forwarded by the ForwarderRegistry or an approved forwarder
        if (msg.sender == address(_FORWARDER_REGISTRY) || _FORWARDER_REGISTRY.isApprovedForwarder(ERC2771Calldata.msgSender(), msg.sender)) {
            return ERC2771Calldata.msgData();
        }

        return msg.data;
    }
}

File 29 of 69 : IERC2771.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title Secure Protocol for Native Meta Transactions.
/// @dev See https://eips.ethereum.org/EIPS/eip-2771
interface IERC2771 {
    /// @notice Checks whether a forwarder is trusted.
    /// @param forwarder The forwarder to check.
    /// @return isTrusted True if `forwarder` is trusted, false if not.
    function isTrustedForwarder(address forwarder) external view returns (bool isTrusted);
}

File 30 of 69 : IForwarderRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title Universal Meta-Transactions Forwarder Registry.
/// @dev Derived from https://github.com/wighawag/universal-forwarder (MIT licence)
interface IForwarderRegistry {
    /// @notice Checks whether an account is as an approved meta-transaction forwarder for a sender account.
    /// @param sender The sender account.
    /// @param forwarder The forwarder account.
    /// @return isApproved True if `forwarder` is an approved meta-transaction forwarder for `sender`, false otherwise.
    function isApprovedForwarder(address sender, address forwarder) external view returns (bool isApproved);
}

File 31 of 69 : ERC2771Calldata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @dev Derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT licence)
/// @dev See https://eips.ethereum.org/EIPS/eip-2771
library ERC2771Calldata {
    /// @notice Returns the sender address appended at the end of the calldata, as specified in EIP-2771.
    function msgSender() internal pure returns (address sender) {
        assembly {
            sender := shr(96, calldataload(sub(calldatasize(), 20)))
        }
    }

    /// @notice Returns the calldata while omitting the appended sender address, as specified in EIP-2771.
    function msgData() internal pure returns (bytes calldata data) {
        unchecked {
            return msg.data[:msg.data.length - 20];
        }
    }
}

File 32 of 69 : ProxyInitializationErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Emitted when trying to set a phase value that has already been reached.
/// @param currentPhase The current phase.
/// @param newPhase The new phase trying to be set.
error InitializationPhaseAlreadyReached(uint256 currentPhase, uint256 newPhase);

File 33 of 69 : ProxyInitialization.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {InitializationPhaseAlreadyReached} from "./../errors/ProxyInitializationErrors.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";

/// @notice Multiple calls protection for storage-modifying proxy initialization functions.
library ProxyInitialization {
    /// @notice Sets the initialization phase during a storage-modifying proxy initialization function.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if `phase` has been reached already.
    /// @param storageSlot the storage slot where `phase` is stored.
    /// @param phase the initialization phase.
    function setPhase(bytes32 storageSlot, uint256 phase) internal {
        StorageSlot.Uint256Slot storage currentVersion = StorageSlot.getUint256Slot(storageSlot);
        uint256 currentPhase = currentVersion.value;
        if (currentPhase >= phase) revert InitializationPhaseAlreadyReached(currentPhase, phase);
        currentVersion.value = phase;
    }
}

File 34 of 69 : TokenRecovery.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {TokenRecoveryBase} from "./base/TokenRecoveryBase.sol";
import {ContractOwnership} from "./../access/ContractOwnership.sol";

/// @title Recovery mechanism for ETH/ERC20/ERC721 tokens accidentally sent to this contract (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract TokenRecovery is TokenRecoveryBase, ContractOwnership {

}

File 35 of 69 : TokenRecoveryBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {InconsistentArrayLengths} from "./../../CommonErrors.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC721} from "./../../token/ERC721/interfaces/IERC721.sol";
import {ContractOwnershipStorage} from "./../../access/libraries/ContractOwnershipStorage.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

/// @title Recovery mechanism for ETH/ERC20/ERC721 tokens accidentally sent to this contract (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract TokenRecoveryBase is Context {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;
    using SafeERC20 for IERC20;
    using Address for address payable;

    /// @notice Extract ETH tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Note: While contracts can generally prevent accidental ETH transfer by implementating a reverting
    ///  `receive()` function, this can still be bypassed in a `selfdestruct(address)` scenario.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ETH tokens
    ///  so that the extraction is limited to only amounts sent accidentally.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {InconsistentArrayLengths} `accounts` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ETH transfers fails for any reason.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param amounts the list of token amounts to transfer.
    function recoverETH(address payable[] calldata accounts, uint256[] calldata amounts) public virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        uint256 length = accounts.length;
        if (length != amounts.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            accounts[i].sendValue(amounts[i]);
        }
    }

    /// @notice Extract ERC20 tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ERC20 tokens
    ///  so that the extraction is limited to only amounts sent accidentally.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {InconsistentArrayLengths} if `accounts`, `tokens` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ERC20 transfers fails for any reason.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param tokens the list of ERC20 token addresses.
    /// @param amounts the list of token amounts to transfer.
    function recoverERC20s(address[] calldata accounts, IERC20[] calldata tokens, uint256[] calldata amounts) public virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        uint256 length = accounts.length;
        if (length != tokens.length || length != amounts.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            tokens[i].safeTransfer(accounts[i], amounts[i]);
        }
    }

    /// @notice Extract ERC721 tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ERC721 tokens
    ///  so that the extraction is limited to only tokens sent accidentally.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {InconsistentArrayLengths} if `accounts`, `contracts` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ERC721 transfers fails for any reason.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param contracts the list of ERC721 contract addresses.
    /// @param tokenIds the list of token ids to transfer.
    function recoverERC721s(address[] calldata accounts, IERC721[] calldata contracts, uint256[] calldata tokenIds) public virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        uint256 length = accounts.length;
        if (length != contracts.length || length != tokenIds.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            contracts[i].transferFrom(address(this), accounts[i], tokenIds[i]);
        }
    }
}

File 36 of 69 : ERC721BatchTransferWithOperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {ERC721BatchTransferWithOperatorFiltererBase} from "./base/ERC721BatchTransferWithOperatorFiltererBase.sol";

/// @title ERC721 Non-Fungible Token Standard: optional extension: Batch Transfer with Operator Filterer (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721BatchTransferWithOperatorFilterer is ERC721BatchTransferWithOperatorFiltererBase {
    /// @notice Marks the following ERC165 interfaces(s) as supported: ERC721BatchTransfer
    constructor() {
        ERC721Storage.initERC721BatchTransfer();
    }
}

File 37 of 69 : ERC721Deliverable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {ERC721DeliverableBase} from "./base/ERC721DeliverableBase.sol";
import {AccessControl} from "./../../access/AccessControl.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Deliverable (immutable version).
/// @notice ERC721Deliverable implementation where burnt tokens can be minted again.
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721Deliverable is ERC721DeliverableBase, AccessControl {
    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Deliverable.
    constructor() {
        ERC721Storage.initERC721Deliverable();
    }
}

File 38 of 69 : ERC721Metadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ITokenMetadataResolver} from "./../metadata/interfaces/ITokenMetadataResolver.sol";
import {TokenMetadataStorage} from "./../metadata/libraries/TokenMetadataStorage.sol";
import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {ERC721MetadataBase} from "./base/ERC721MetadataBase.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Metadata (immutable version).
/// @notice This contracts uses an external resolver for managing individual tokens metadata.
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721Metadata is ERC721MetadataBase {
    using TokenMetadataStorage for TokenMetadataStorage.Layout;

    /// @notice Marks the following ERC165 interfaces as supported: ERC721Metadata.
    /// @param name The name of the token.
    /// @param symbol The symbol of the token.
    /// @param metadataResolver The address of the metadata resolver contract.
    constructor(string memory name, string memory symbol, ITokenMetadataResolver metadataResolver) {
        TokenMetadataStorage.layout().constructorInit(name, symbol, metadataResolver);
        ERC721Storage.initERC721Metadata();
    }
}

File 39 of 69 : ERC721Mintable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {ERC721MintableBase} from "./base/ERC721MintableBase.sol";
import {AccessControl} from "./../../access/AccessControl.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Mintable (immutable version).
/// @notice ERC721Mintable implementation where burnt tokens can be minted again.
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721Mintable is ERC721MintableBase, AccessControl {
    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Mintable.
    constructor() {
        ERC721Storage.initERC721Mintable();
    }
}

File 40 of 69 : ERC721WithOperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IOperatorFilterRegistry} from "./../royalty/interfaces/IOperatorFilterRegistry.sol";
import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {OperatorFiltererStorage} from "./../royalty/libraries/OperatorFiltererStorage.sol";
import {ERC721WithOperatorFiltererBase} from "./base/ERC721WithOperatorFiltererBase.sol";
import {OperatorFiltererBase} from "./../royalty/base/OperatorFiltererBase.sol";
import {ContractOwnership} from "./../../access/ContractOwnership.sol";

/// @title ERC721 Non-Fungible Token Standard with Operator Filterer (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721WithOperatorFilterer is ERC721WithOperatorFiltererBase, OperatorFiltererBase, ContractOwnership {
    using OperatorFiltererStorage for OperatorFiltererStorage.Layout;

    /// @notice Marks the following ERC165 interfaces as supported: ERC721.
    /// @notice Sets the address that the contract will make OperatorFilter checks against.
    /// @param operatorFilterRegistry The operator filter registry address. When set to the zero address, checks will be bypassed.
    constructor(IOperatorFilterRegistry operatorFilterRegistry) {
        ERC721Storage.init();
        OperatorFiltererStorage.layout().constructorInit(operatorFilterRegistry);
    }
}

File 41 of 69 : ERC721BatchTransferWithOperatorFiltererBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC721BatchTransfer} from "./../interfaces/IERC721BatchTransfer.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {OperatorFiltererStorage} from "./../../royalty/libraries/OperatorFiltererStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Batch Transfer with Operator Filterer (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC721 (Non-Fungible Token Standard).
abstract contract ERC721BatchTransferWithOperatorFiltererBase is IERC721BatchTransfer, Context {
    using ERC721Storage for ERC721Storage.Layout;
    using OperatorFiltererStorage for OperatorFiltererStorage.Layout;

    /// @inheritdoc IERC721BatchTransfer
    /// @dev Reverts with OperatorNotAllowed if the sender is not `from` and is not allowed by the operator registry.
    function batchTransferFrom(address from, address to, uint256[] calldata tokenIds) external virtual {
        address sender = _msgSender();
        OperatorFiltererStorage.layout().requireAllowedOperatorForTransfer(sender, from);
        ERC721Storage.layout().batchTransferFrom(sender, from, to, tokenIds);
    }
}

File 42 of 69 : ERC721DeliverableBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC721Deliverable} from "./../interfaces/IERC721Deliverable.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {AccessControlStorage} from "./../../../access/libraries/AccessControlStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Deliverable (proxiable version).
/// @notice ERC721Deliverable implementation where burnt tokens can be minted again.
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC721 (Non-Fungible Token Standard).
/// @dev Note: This contract requires AccessControl.
abstract contract ERC721DeliverableBase is IERC721Deliverable, Context {
    using ERC721Storage for ERC721Storage.Layout;
    using AccessControlStorage for AccessControlStorage.Layout;

    // prevent variable name clash with public ERC721MintableBase.MINTER_ROLE
    bytes32 private constant _MINTER_ROLE = "minter";

    /// @inheritdoc IERC721Deliverable
    /// @dev Reverts with {NotRoleHolder} if the sender does not have the 'minter' role.
    function deliver(address[] calldata recipients, uint256[] calldata tokenIds) external virtual {
        AccessControlStorage.layout().enforceHasRole(_MINTER_ROLE, _msgSender());
        ERC721Storage.layout().deliver(recipients, tokenIds);
    }
}

File 43 of 69 : ERC721MetadataBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC721Metadata} from "./../interfaces/IERC721Metadata.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {TokenMetadataStorage} from "./../../metadata/libraries/TokenMetadataStorage.sol";
import {TokenMetadataBase} from "./../../metadata/base/TokenMetadataBase.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Metadata (proxiable version).
/// @notice This contracts uses an external resolver for managing individual tokens metadata.
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC721 (Non-Fungible Token Standard).
abstract contract ERC721MetadataBase is TokenMetadataBase, IERC721Metadata {
    using ERC721Storage for ERC721Storage.Layout;
    using TokenMetadataStorage for TokenMetadataStorage.Layout;

    /// @inheritdoc IERC721Metadata
    function name() public view virtual override(IERC721Metadata, TokenMetadataBase) returns (string memory tokenName) {
        return TokenMetadataBase.name();
    }

    /// @inheritdoc IERC721Metadata
    function symbol() public view virtual override(IERC721Metadata, TokenMetadataBase) returns (string memory tokenSymbol) {
        return TokenMetadataBase.symbol();
    }

    /// @inheritdoc IERC721Metadata
    function tokenURI(uint256 tokenId) external view virtual returns (string memory uri) {
        ERC721Storage.layout().ownerOf(tokenId); // reverts if the token does not exist
        return TokenMetadataStorage.layout().tokenMetadataURI(address(this), tokenId);
    }
}

File 44 of 69 : ERC721MintableBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC721Mintable} from "./../interfaces/IERC721Mintable.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {AccessControlStorage} from "./../../../access/libraries/AccessControlStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Mintable (proxiable version).
/// @notice ERC721Mintable implementation where burnt tokens can be minted again.
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC721 (Non-Fungible Token Standard).
/// @dev Note: This contract requires AccessControl.
abstract contract ERC721MintableBase is IERC721Mintable, Context {
    using ERC721Storage for ERC721Storage.Layout;
    using AccessControlStorage for AccessControlStorage.Layout;

    bytes32 public constant MINTER_ROLE = "minter";

    /// @inheritdoc IERC721Mintable
    /// @dev Reverts with {NotRoleHolder} if the sender does not have the 'minter' role.
    function mint(address to, uint256 tokenId) external virtual {
        AccessControlStorage.layout().enforceHasRole(MINTER_ROLE, _msgSender());
        ERC721Storage.layout().mint(to, tokenId);
    }

    /// @inheritdoc IERC721Mintable
    /// @dev Reverts with {NotRoleHolder} if the sender does not have the 'minter' role.
    function safeMint(address to, uint256 tokenId, bytes calldata data) external virtual {
        AccessControlStorage.layout().enforceHasRole(MINTER_ROLE, _msgSender());
        ERC721Storage.layout().safeMint(_msgSender(), to, tokenId, data);
    }

    /// @inheritdoc IERC721Mintable
    /// @dev Reverts with {NotRoleHolder} if the sender does not have the 'minter' role.
    function batchMint(address to, uint256[] calldata tokenIds) external virtual {
        AccessControlStorage.layout().enforceHasRole(MINTER_ROLE, _msgSender());
        ERC721Storage.layout().batchMint(to, tokenIds);
    }
}

File 45 of 69 : ERC721WithOperatorFiltererBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC721} from "./../interfaces/IERC721.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {OperatorFiltererStorage} from "./../../royalty/libraries/OperatorFiltererStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC721 Non-Fungible Token Standard with Operator Filterer (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
/// @dev Note: This contract requires OperatorFilterer.
abstract contract ERC721WithOperatorFiltererBase is IERC721, Context {
    using ERC721Storage for ERC721Storage.Layout;
    using OperatorFiltererStorage for OperatorFiltererStorage.Layout;

    /// @inheritdoc IERC721
    /// @dev Reverts with {OperatorNotAllowed} if `to` is not the zero address and is not allowed by the operator registry.
    function approve(address to, uint256 tokenId) external virtual {
        if (to != address(0)) {
            OperatorFiltererStorage.layout().requireAllowedOperatorForApproval(to);
        }
        ERC721Storage.layout().approve(_msgSender(), to, tokenId);
    }

    /// @inheritdoc IERC721
    /// @dev Reverts with {OperatorNotAllowed} if `approved` is true and `operator` is not allowed by the operator registry.
    function setApprovalForAll(address operator, bool approved) external virtual {
        if (approved) {
            OperatorFiltererStorage.layout().requireAllowedOperatorForApproval(operator);
        }
        ERC721Storage.layout().setApprovalForAll(_msgSender(), operator, approved);
    }

    /// @inheritdoc IERC721
    /// @dev Reverts with {OperatorNotAllowed} if the sender is not `from` and is not allowed by the operator registry.
    function transferFrom(address from, address to, uint256 tokenId) external {
        address sender = _msgSender();
        OperatorFiltererStorage.layout().requireAllowedOperatorForTransfer(sender, from);
        ERC721Storage.layout().transferFrom(sender, from, to, tokenId);
    }

    /// @inheritdoc IERC721
    /// @dev Reverts with {OperatorNotAllowed} if the sender is not `from` and is not allowed by the operator registry.
    function safeTransferFrom(address from, address to, uint256 tokenId) external virtual {
        address sender = _msgSender();
        OperatorFiltererStorage.layout().requireAllowedOperatorForTransfer(sender, from);
        ERC721Storage.layout().safeTransferFrom(sender, from, to, tokenId);
    }

    /// @inheritdoc IERC721
    /// @dev Reverts with {OperatorNotAllowed} if the sender is not `from` and is not allowed by the operator registry.
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external virtual {
        address sender = _msgSender();
        OperatorFiltererStorage.layout().requireAllowedOperatorForTransfer(sender, from);
        ERC721Storage.layout().safeTransferFrom(sender, from, to, tokenId, data);
    }

    /// @inheritdoc IERC721
    function balanceOf(address owner) external view returns (uint256 balance) {
        return ERC721Storage.layout().balanceOf(owner);
    }

    /// @inheritdoc IERC721
    function ownerOf(uint256 tokenId) external view returns (address tokenOwner) {
        return ERC721Storage.layout().ownerOf(tokenId);
    }

    /// @inheritdoc IERC721
    function getApproved(uint256 tokenId) external view returns (address approved) {
        return ERC721Storage.layout().getApproved(tokenId);
    }

    /// @inheritdoc IERC721
    function isApprovedForAll(address owner, address operator) external view returns (bool approvedForAll) {
        return ERC721Storage.layout().isApprovedForAll(owner, operator);
    }
}

File 46 of 69 : ERC721Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when trying to approve oneself.
/// @param account The account trying to approve itself.
error ERC721SelfApproval(address account);

/// @notice Thrown when trying to approveForAll oneself.
/// @param account The account trying to approveForAll itself.
error ERC721SelfApprovalForAll(address account);

/// @notice Thrown when a sender tries to set a token approval but is neither the owner nor approvedForAll by the owner.
/// @param sender The message sender.
/// @param tokenId The identifier of the token.
error ERC721NonApprovedForApproval(address sender, address owner, uint256 tokenId);

/// @notice Thrown when transferring a token to the zero address.
error ERC721TransferToAddressZero();

/// @notice Thrown when a token does not exist but is required to.
/// @param tokenId The identifier of the token that was checked.
error ERC721NonExistingToken(uint256 tokenId);

/// @notice Thrown when a sender tries to transfer a token but is neither the owner nor approved by the owner.
/// @param sender The message sender.
/// @param tokenId The identifier of the token.
error ERC721NonApprovedForTransfer(address sender, address owner, uint256 tokenId);

/// @notice Thrown when a token is not owned by the expected account.
/// @param account The account that was expected to own the token.
/// @param tokenId The identifier of the token.
error ERC721NonOwnedToken(address account, uint256 tokenId);

/// @notice Thrown when a safe transfer is rejected by the recipient contract.
/// @param recipient The recipient contract.
/// @param tokenId The identifier of the token.
error ERC721SafeTransferRejected(address recipient, uint256 tokenId);

/// @notice Thrown when querying the balance of the zero address.
error ERC721BalanceOfAddressZero();

File 47 of 69 : ERC721MintableErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when minting a token to the zero address.
error ERC721MintToAddressZero();

/// @notice Thrown when minting a token that already exists.
/// @param tokenId The identifier of the token that already exists.
error ERC721ExistingToken(uint256 tokenId);

File 48 of 69 : ERC721MintableOnceErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when minting a token which has been burnt before (MintableOnce implementation).
/// @param tokenId The identifier of the token that has been burnt before.
error ERC721BurntToken(uint256 tokenId);

File 49 of 69 : ERC721Events.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Emitted when a token is transferred.
/// @param from The previous token owner.
/// @param to The new token owner.
/// @param tokenId The transferred token identifier.
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

/// @notice Emitted when a single token approval is set.
/// @param owner The token owner.
/// @param approved The approved address.
/// @param tokenId The approved token identifier.
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

/// @notice Emitted when an approval for all tokens is set or unset.
/// @param owner The tokens owner.
/// @param operator The approved address.
/// @param approved True when then approval is set, false when it is unset.
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

File 50 of 69 : IERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, basic interface (functions).
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev This interface only contains the standard functions. See IERC721Events for the events.
/// @dev Note: The ERC-165 identifier for this interface is 0x80ac58cd.
interface IERC721 {
    /// @notice Sets or unsets an approval to transfer a single token on behalf of its owner.
    /// @dev Note: There can only be one approved address per token at a given time.
    /// @dev Note: A token approval gets reset when this token is transferred, including a self-transfer.
    /// @dev Reverts if `tokenId` does not exist.
    /// @dev Reverts if `to` is the token owner.
    /// @dev Reverts if the sender is not the token owner and has not been approved by the token owner.
    /// @dev Emits an {Approval} event.
    /// @param to The address to approve, or the zero address to remove any existing approval.
    /// @param tokenId The token identifier to give approval for.
    function approve(address to, uint256 tokenId) external;

    /// @notice Sets or unsets an approval to transfer all tokens on behalf of their owner.
    /// @dev Reverts if the sender is the same as `operator`.
    /// @dev Emits an {ApprovalForAll} event.
    /// @param operator The address to approve for all tokens.
    /// @param approved True to set an approval for all tokens, false to unset it.
    function setApprovalForAll(address operator, bool approved) external;

    /// @notice Unsafely transfers the ownership of a token to a recipient.
    /// @dev Note: Usage of this method is discouraged, use `safeTransferFrom` whenever possible.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `from` is not the owner of `tokenId`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits a {Transfer} event.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer. Self-transfers are possible.
    /// @param tokenId The identifier of the token to transfer.
    function transferFrom(address from, address to, uint256 tokenId) external;

    /// @notice Safely transfers the ownership of a token to a recipient.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `from` is not the owner of `tokenId`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts if `to` is a contract and the call to {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /// @notice Safely transfers the ownership of a token to a recipient.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `from` is not the owner of `tokenId`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts if `to` is a contract and the call to {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    /// @param data Optional data to send along to a receiver contract.
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /// @notice Gets the balance of an address.
    /// @dev Reverts if `owner` is the zero address.
    /// @param owner The address to query the balance of.
    /// @return balance The amount owned by the owner.
    function balanceOf(address owner) external view returns (uint256 balance);

    /// @notice Gets the owner of a token.
    /// @dev Reverts if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the owner of.
    /// @return tokenOwner The owner of the token identifier.
    function ownerOf(uint256 tokenId) external view returns (address tokenOwner);

    /// @notice Gets the approved address for a token.
    /// @dev Reverts if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the approval of.
    /// @return approved The approved address for the token identifier, or the zero address if no approval is set.
    function getApproved(uint256 tokenId) external view returns (address approved);

    /// @notice Gets whether an operator is approved for all tokens by an owner.
    /// @param owner The address which gives the approval for all tokens.
    /// @param operator The address which receives the approval for all tokens.
    /// @return approvedForAll Whether the operator is approved for all tokens by the owner.
    function isApprovedForAll(address owner, address operator) external view returns (bool approvedForAll);
}

File 51 of 69 : IERC721BatchTransfer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Batch Transfer.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0xf3993d11.
interface IERC721BatchTransfer {
    /// @notice Unsafely transfers a batch of tokens to a recipient.
    /// @dev Resets the token approval for each of `tokenIds`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits an {IERC721-Transfer} event for each of `tokenIds`.
    /// @param from Current tokens owner.
    /// @param to Address of the new token owner.
    /// @param tokenIds Identifiers of the tokens to transfer.
    function batchTransferFrom(address from, address to, uint256[] calldata tokenIds) external;
}

File 52 of 69 : IERC721Burnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Burnable.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x8b8b4ef5.
interface IERC721Burnable {
    /// @notice Burns a token.
    /// @dev Reverts if `tokenId` is not owned by `from`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits an {IERC721-Transfer} event with `to` set to the zero address.
    /// @param from The current token owner.
    /// @param tokenId The identifier of the token to burn.
    function burnFrom(address from, uint256 tokenId) external;

    /// @notice Burns a batch of tokens.
    /// @dev Reverts if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits an {IERC721-Transfer} event with `to` set to the zero address for each of `tokenIds`.
    /// @param from The current tokens owner.
    /// @param tokenIds The identifiers of the tokens to burn.
    function batchBurnFrom(address from, uint256[] calldata tokenIds) external;
}

File 53 of 69 : IERC721Deliverable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Deliverable.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x9da5e832.
interface IERC721Deliverable {
    /// @notice Unsafely mints tokens to multiple recipients.
    /// @dev Reverts if `recipients` and `tokenIds` have different lengths.
    /// @dev Reverts if one of `recipients` is the zero address.
    /// @dev Reverts if one of `tokenIds` already exists.
    /// @dev Emits an {IERC721-Transfer} event from the zero address for each of `recipients` and `tokenIds`.
    /// @param recipients Addresses of the new tokens owners.
    /// @param tokenIds Identifiers of the tokens to mint.
    function deliver(address[] calldata recipients, uint256[] calldata tokenIds) external;
}

File 54 of 69 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Metadata.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x5b5e139f.
interface IERC721Metadata {
    /// @notice Gets the name of the token. E.g. "My Token".
    /// @return tokenName The name of the token.
    function name() external view returns (string memory tokenName);

    /// @notice Gets the symbol of the token. E.g. "TOK".
    /// @return tokenSymbol The symbol of the token.
    function symbol() external view returns (string memory tokenSymbol);

    /// @notice Gets the metadata URI for a token identifier.
    /// @dev Reverts if `tokenId` does not exist.
    /// @param tokenId The token identifier.
    /// @return uri The metadata URI for the token identifier.
    function tokenURI(uint256 tokenId) external view returns (string memory uri);
}

File 55 of 69 : IERC721Mintable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Mintable.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x8e773e13.
interface IERC721Mintable {
    /// @notice Unsafely mints a token.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `tokenId` already exists.
    /// @dev Emits an {IERC721-Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    function mint(address to, uint256 tokenId) external;

    /// @notice Safely mints a token.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `tokenId` already exists.
    /// @dev Reverts if `to` is a contract and the call to {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits an {IERC721-Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    /// @param data Optional data to pass along to the receiver call.
    function safeMint(address to, uint256 tokenId, bytes calldata data) external;

    /// @notice Unsafely mints a batch of tokens.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if one of `tokenIds` already exists.
    /// @dev Emits an {IERC721-Transfer} event from the zero address for each of `tokenIds`.
    /// @param to Address of the new tokens owner.
    /// @param tokenIds Identifiers of the tokens to mint.
    function batchMint(address to, uint256[] calldata tokenIds) external;
}

File 56 of 69 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC721 Non-Fungible Token Standard, Tokens Receiver.
/// @notice Interface for supporting safe transfers from ERC721 contracts.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x150b7a02.
interface IERC721Receiver {
    /// @notice Handles the receipt of an ERC721 token.
    /// @dev Note: This function is called by an ERC721 contract after a safe transfer.
    /// @dev Note: The ERC721 contract address is always the message sender.
    /// @param operator The initiator of the safe transfer.
    /// @param from The previous token owner.
    /// @param tokenId The token identifier.
    /// @param data Optional additional data with no specified format.
    /// @return magicValue `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` (`0x150b7a02`) to accept, any other value to refuse.
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4 magicValue);
}

File 57 of 69 : ERC721Storage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

// solhint-disable-next-line max-line-length
import {ERC721SelfApproval, ERC721SelfApprovalForAll, ERC721NonApprovedForApproval, ERC721TransferToAddressZero, ERC721NonExistingToken, ERC721NonApprovedForTransfer, ERC721NonOwnedToken, ERC721SafeTransferRejected, ERC721BalanceOfAddressZero} from "./../errors/ERC721Errors.sol";
import {ERC721MintToAddressZero, ERC721ExistingToken} from "./../errors/ERC721MintableErrors.sol";
import {ERC721BurntToken} from "./../errors/ERC721MintableOnceErrors.sol";
import {InconsistentArrayLengths} from "./../../../CommonErrors.sol";
import {Transfer, Approval, ApprovalForAll} from "./../events/ERC721Events.sol";
import {IERC721} from "./../interfaces/IERC721.sol";
import {IERC721BatchTransfer} from "./../interfaces/IERC721BatchTransfer.sol";
import {IERC721Metadata} from "./../interfaces/IERC721Metadata.sol";
import {IERC721Mintable} from "./../interfaces/IERC721Mintable.sol";
import {IERC721Deliverable} from "./../interfaces/IERC721Deliverable.sol";
import {IERC721Burnable} from "./../interfaces/IERC721Burnable.sol";
import {IERC721Receiver} from "./../interfaces/IERC721Receiver.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {InterfaceDetectionStorage} from "./../../../introspection/libraries/InterfaceDetectionStorage.sol";

library ERC721Storage {
    using Address for address;
    using ERC721Storage for ERC721Storage.Layout;
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        mapping(uint256 => uint256) owners;
        mapping(address => uint256) balances;
        mapping(uint256 => address) approvals;
        mapping(address => mapping(address => bool)) operators;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.ERC721.ERC721.storage")) - 1);

    bytes4 internal constant ERC721_RECEIVED = IERC721Receiver.onERC721Received.selector;

    // Single token approval flag
    // This bit is set in the owner's value to indicate that there is an approval set for this token
    uint256 internal constant TOKEN_APPROVAL_OWNER_FLAG = 1 << 160;

    // Burnt token magic value
    // This magic number is used as the owner's value to indicate that the token has been burnt
    uint256 internal constant BURNT_TOKEN_OWNER_VALUE = 0xdead000000000000000000000000000000000000000000000000000000000000;

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721.
    function init() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721BatchTransfer.
    function initERC721BatchTransfer() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721BatchTransfer).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Metadata.
    function initERC721Metadata() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Metadata).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Mintable.
    function initERC721Mintable() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Mintable).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Deliverable.
    function initERC721Deliverable() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Deliverable).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Burnable.
    function initERC721Burnable() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Burnable).interfaceId, true);
    }

    /// @notice Sets or unsets an approval to transfer a single token on behalf of its owner.
    /// @dev Note: This function implements {ERC721-approve(address,uint256)}.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721SelfApproval} if `to` is the token owner.
    /// @dev Reverts with {ERC721NonApprovedForApproval} if `sender` is not the token owner and has not been approved by the token owner.
    /// @dev Emits an {Approval} event.
    /// @param sender The message sender.
    /// @param to The address to approve, or the zero address to remove any existing approval.
    /// @param tokenId The token identifier to give approval for.
    function approve(Layout storage s, address sender, address to, uint256 tokenId) internal {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        address ownerAddress = _tokenOwner(owner);
        if (to == ownerAddress) revert ERC721SelfApproval(ownerAddress);
        if (!_isOperatable(s, ownerAddress, sender)) revert ERC721NonApprovedForApproval(sender, ownerAddress, tokenId);
        if (to == address(0)) {
            if (_tokenHasApproval(owner)) {
                // remove the approval bit if it is present
                s.owners[tokenId] = uint256(uint160(ownerAddress));
            }
        } else {
            uint256 ownerWithApprovalBit = owner | TOKEN_APPROVAL_OWNER_FLAG;
            if (owner != ownerWithApprovalBit) {
                // add the approval bit if it is not present
                s.owners[tokenId] = ownerWithApprovalBit;
            }
            s.approvals[tokenId] = to;
        }
        emit Approval(ownerAddress, to, tokenId);
    }

    /// @notice Sets or unsets an approval to transfer all tokens on behalf of their owner.
    /// @dev Note: This function implements {ERC721-setApprovalForAll(address,bool)}.
    /// @dev Reverts with {ERC721SelfApprovalForAll} if `sender` is the same as `operator`.
    /// @dev Emits an {ApprovalForAll} event.
    /// @param sender The message sender.
    /// @param operator The address to approve for all tokens.
    /// @param approved True to set an approval for all tokens, false to unset it.
    function setApprovalForAll(Layout storage s, address sender, address operator, bool approved) internal {
        if (operator == sender) revert ERC721SelfApprovalForAll(sender);
        s.operators[sender][operator] = approved;
        emit ApprovalForAll(sender, operator, approved);
    }

    /// @notice Unsafely transfers the ownership of a token to a recipient by a sender.
    /// @dev Note: This function implements {ERC721-transferFrom(address,address,uint256)}.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if `from` is not the owner of `tokenId`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits a {Transfer} event.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    function transferFrom(Layout storage s, address sender, address from, address to, uint256 tokenId) internal {
        if (to == address(0)) revert ERC721TransferToAddressZero();

        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);

        if (!_isOperatable(s, from, sender)) {
            if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
        }

        s.owners[tokenId] = uint256(uint160(to));
        if (from != to) {
            unchecked {
                // cannot underflow as balance is verified through ownership
                --s.balances[from];
                //  cannot overflow as supply cannot overflow
                ++s.balances[to];
            }
        }

        emit Transfer(from, to, tokenId);
    }

    /// @notice Safely transfers the ownership of a token to a recipient by a sender.
    /// @dev Note: This function implements {ERC721-safeTransferFrom(address,address,uint256)}.
    /// @dev Warning: Since a `to` contract can run arbitrary code, developers should be aware of potential re-entrancy attacks.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if `from` is not the owner of `tokenId`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    function safeTransferFrom(Layout storage s, address sender, address from, address to, uint256 tokenId) internal {
        s.transferFrom(sender, from, to, tokenId);
        if (to.isContract()) {
            _callOnERC721Received(sender, from, to, tokenId, "");
        }
    }

    /// @notice Safely transfers the ownership of a token to a recipient by a sender.
    /// @dev Note: This function implements {ERC721-safeTransferFrom(address,address,uint256,bytes)}.
    /// @dev Warning: Since a `to` contract can run arbitrary code, developers should be aware of potential re-entrancy attacks.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if `from` is not the owner of `tokenId`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    /// @param data Optional data to send along to a receiver contract.
    function safeTransferFrom(Layout storage s, address sender, address from, address to, uint256 tokenId, bytes calldata data) internal {
        s.transferFrom(sender, from, to, tokenId);
        if (to.isContract()) {
            _callOnERC721Received(sender, from, to, tokenId, data);
        }
    }

    /// @notice Unsafely transfers a batch of tokens to a recipient by a sender.
    /// @dev Note: This function implements {ERC721BatchTransfer-batchTransferFrom(address,address,uint256[])}.
    /// @dev Resets the token approval for each of `tokenIds`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if one of `tokenIds` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if the sender is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits a {Transfer} event for each of `tokenIds`.
    /// @param sender The message sender.
    /// @param from Current tokens owner.
    /// @param to Address of the new token owner.
    /// @param tokenIds Identifiers of the tokens to transfer.
    function batchTransferFrom(Layout storage s, address sender, address from, address to, uint256[] calldata tokenIds) internal {
        if (to == address(0)) revert ERC721TransferToAddressZero();
        bool operatable = _isOperatable(s, from, sender);

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
            if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);
            if (!operatable) {
                if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
            }
            s.owners[tokenId] = uint256(uint160(to));
            emit Transfer(from, to, tokenId);
        }

        if (from != to && length != 0) {
            unchecked {
                // cannot underflow as balance is verified through ownership
                s.balances[from] -= length;
                // cannot overflow as supply cannot overflow
                s.balances[to] += length;
            }
        }
    }

    /// @notice Unsafely mints a token.
    /// @dev Note: This function implements {ERC721Mintable-mint(address,uint256)}.
    /// @dev Note: Either `mint` or `mintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    function mint(Layout storage s, address to, uint256 tokenId) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();
        if (_tokenExists(s.owners[tokenId])) revert ERC721ExistingToken(tokenId);

        s.owners[tokenId] = uint256(uint160(to));

        unchecked {
            // cannot overflow due to the cost of minting individual tokens
            ++s.balances[to];
        }

        emit Transfer(address(0), to, tokenId);
    }

    /// @notice Safely mints a token.
    /// @dev Note: This function implements {ERC721Mintable-safeMint(address,uint256,bytes)}.
    /// @dev Note: Either `safeMint` or `safeMintOnce` should be used in a given contract, but not both.
    /// @dev Warning: Since a `to` contract can run arbitrary code, developers should be aware of potential re-entrancy attacks.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    /// @param data Optional data to pass along to the receiver call.
    function safeMint(Layout storage s, address sender, address to, uint256 tokenId, bytes memory data) internal {
        s.mint(to, tokenId);
        if (to.isContract()) {
            _callOnERC721Received(sender, address(0), to, tokenId, data);
        }
    }

    /// @notice Unsafely mints a batch of tokens.
    /// @dev Note: This function implements {ERC721Mintable-batchMint(address,uint256[])}.
    /// @dev Note: Either `batchMint` or `batchMintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Emits a {Transfer} event from the zero address for each of `tokenIds`.
    /// @param to Address of the new tokens owner.
    /// @param tokenIds Identifiers of the tokens to mint.
    function batchMint(Layout storage s, address to, uint256[] memory tokenIds) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            if (_tokenExists(s.owners[tokenId])) revert ERC721ExistingToken(tokenId);

            s.owners[tokenId] = uint256(uint160(to));
            emit Transfer(address(0), to, tokenId);
        }

        unchecked {
            s.balances[to] += length;
        }
    }

    /// @notice Unsafely mints tokens to multiple recipients.
    /// @dev Note: This function implements {ERC721Deliverable-deliver(address[],uint256[])}.
    /// @dev Note: Either `deliver` or `deliverOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {InconsistentArrayLengths} if `recipients` and `tokenIds` have different lengths.
    /// @dev Reverts with {ERC721MintToAddressZero} if one of `recipients` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Emits a {Transfer} event from the zero address for each of `recipients` and `tokenIds`.
    /// @param recipients Addresses of the new tokens owners.
    /// @param tokenIds Identifiers of the tokens to mint.
    function deliver(Layout storage s, address[] memory recipients, uint256[] memory tokenIds) internal {
        uint256 length = recipients.length;
        if (length != tokenIds.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            s.mint(recipients[i], tokenIds[i]);
        }
    }

    /// @notice Unsafely mints a token once.
    /// @dev Note: This function implements {ERC721Mintable-mint(address,uint256)}.
    /// @dev Note: Either `mint` or `mintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Reverts with {ERC721BurntToken} if `tokenId` has been previously burnt.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    function mintOnce(Layout storage s, address to, uint256 tokenId) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();

        uint256 owner = s.owners[tokenId];
        if (_tokenExists(owner)) revert ERC721ExistingToken(tokenId);
        if (_tokenWasBurnt(owner)) revert ERC721BurntToken(tokenId);

        s.owners[tokenId] = uint256(uint160(to));

        unchecked {
            // cannot overflow due to the cost of minting individual tokens
            ++s.balances[to];
        }

        emit Transfer(address(0), to, tokenId);
    }

    /// @notice Safely mints a token once.
    /// @dev Note: This function implements {ERC721Mintable-safeMint(address,uint256,bytes)}.
    /// @dev Note: Either `safeMint` or `safeMintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Reverts with {ERC721BurntToken} if `tokenId` has been previously burnt.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    /// @param data Optional data to pass along to the receiver call.
    function safeMintOnce(Layout storage s, address sender, address to, uint256 tokenId, bytes memory data) internal {
        s.mintOnce(to, tokenId);
        if (to.isContract()) {
            _callOnERC721Received(sender, address(0), to, tokenId, data);
        }
    }

    /// @notice Unsafely mints a batch of tokens once.
    /// @dev Note: This function implements {ERC721Mintable-batchMint(address,uint256[])}.
    /// @dev Note: Either `batchMint` or `batchMintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Reverts with {ERC721BurntToken} if one of `tokenIds` has been previously burnt.
    /// @dev Emits a {Transfer} event from the zero address for each of `tokenIds`.
    /// @param to Address of the new tokens owner.
    /// @param tokenIds Identifiers of the tokens to mint.
    function batchMintOnce(Layout storage s, address to, uint256[] memory tokenIds) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (_tokenExists(owner)) revert ERC721ExistingToken(tokenId);
            if (_tokenWasBurnt(owner)) revert ERC721BurntToken(tokenId);

            s.owners[tokenId] = uint256(uint160(to));

            emit Transfer(address(0), to, tokenId);
        }

        unchecked {
            s.balances[to] += length;
        }
    }

    /// @notice Unsafely mints tokens to multiple recipients once.
    /// @dev Note: This function implements {ERC721Deliverable-deliver(address[],uint256[])}.
    /// @dev Note: Either `deliver` or `deliverOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {InconsistentArrayLengths} if `recipients` and `tokenIds` have different lengths.
    /// @dev Reverts with {ERC721MintToAddressZero} if one of `recipients` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Reverts with {ERC721BurntToken} if one of `tokenIds` has been previously burnt.
    /// @dev Emits a {Transfer} event from the zero address for each of `recipients` and `tokenIds`.
    /// @param recipients Addresses of the new tokens owners.
    /// @param tokenIds Identifiers of the tokens to mint.
    function deliverOnce(Layout storage s, address[] memory recipients, uint256[] memory tokenIds) internal {
        uint256 length = recipients.length;
        if (length != tokenIds.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            address to = recipients[i];
            if (to == address(0)) revert ERC721MintToAddressZero();

            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (_tokenExists(owner)) revert ERC721ExistingToken(tokenId);
            if (_tokenWasBurnt(owner)) revert ERC721BurntToken(tokenId);

            s.owners[tokenId] = uint256(uint160(to));
            unchecked {
                ++s.balances[to];
            }

            emit Transfer(address(0), to, tokenId);
        }
    }

    /// @notice Burns a token by a sender.
    /// @dev Note: This function implements {ERC721Burnable-burnFrom(address,uint256)}.
    /// @dev Reverts with {ERC721NonOwnedToken} if `tokenId` is not owned by `from`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits a {Transfer} event with `to` set to the zero address.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param tokenId The identifier of the token to burn.
    function burnFrom(Layout storage s, address sender, address from, uint256 tokenId) internal {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);

        if (!_isOperatable(s, from, sender)) {
            if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
        }

        s.owners[tokenId] = BURNT_TOKEN_OWNER_VALUE;

        unchecked {
            // cannot underflow as balance is verified through TOKEN ownership
            --s.balances[from];
        }
        emit Transfer(from, address(0), tokenId);
    }

    /// @notice Burns a batch of tokens by a sender.
    /// @dev Note: This function implements {ERC721Burnable-batchBurnFrom(address,uint256[])}.
    /// @dev Reverts with {ERC721NonOwnedToken} if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits a {Transfer} event with `to` set to the zero address for each of `tokenIds`.
    /// @param sender The message sender.
    /// @param from The current tokens owner.
    /// @param tokenIds The identifiers of the tokens to burn.
    function batchBurnFrom(Layout storage s, address sender, address from, uint256[] calldata tokenIds) internal {
        bool operatable = _isOperatable(s, from, sender);

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
            if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);
            if (!operatable) {
                if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
            }
            s.owners[tokenId] = BURNT_TOKEN_OWNER_VALUE;
            emit Transfer(from, address(0), tokenId);
        }

        if (length != 0) {
            unchecked {
                s.balances[from] -= length;
            }
        }
    }

    /// @notice Gets the balance of an address.
    /// @dev Note: This function implements {ERC721-balanceOf(address)}.
    /// @dev Reverts with {ERC721BalanceOfAddressZero} if `owner` is the zero address.
    /// @param owner The address to query the balance of.
    /// @return balance The amount owned by the owner.
    function balanceOf(Layout storage s, address owner) internal view returns (uint256 balance) {
        if (owner == address(0)) revert ERC721BalanceOfAddressZero();
        return s.balances[owner];
    }

    /// @notice Gets the owner of a token.
    /// @dev Note: This function implements {ERC721-ownerOf(uint256)}.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the owner of.
    /// @return tokenOwner The owner of the token.
    function ownerOf(Layout storage s, uint256 tokenId) internal view returns (address tokenOwner) {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        return _tokenOwner(owner);
    }

    /// @notice Gets the approved address for a token.
    /// @dev Note: This function implements {ERC721-getApproved(uint256)}.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the approval of.
    /// @return approved The approved address for the token identifier, or the zero address if no approval is set.
    function getApproved(Layout storage s, uint256 tokenId) internal view returns (address approved) {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        if (_tokenHasApproval(owner)) {
            return s.approvals[tokenId];
        } else {
            return address(0);
        }
    }

    /// @notice Gets whether an operator is approved for all tokens by an owner.
    /// @dev Note: This function implements {ERC721-isApprovedForAll(address,address)}.
    /// @param owner The address which gives the approval for all tokens.
    /// @param operator The address which receives the approval for all tokens.
    /// @return approvedForAll Whether the operator is approved for all tokens by the owner.
    function isApprovedForAll(Layout storage s, address owner, address operator) internal view returns (bool approvedForAll) {
        return s.operators[owner][operator];
    }

    /// @notice Gets whether a token was burnt.
    /// @param tokenId The token identifier.
    /// @return tokenWasBurnt Whether the token was burnt.
    function wasBurnt(Layout storage s, uint256 tokenId) internal view returns (bool tokenWasBurnt) {
        return _tokenWasBurnt(s.owners[tokenId]);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }

    /// @notice Calls {IERC721Receiver-onERC721Received} on a target contract.
    /// @dev Reverts with {ERC721SafeTransferRejected} if the call to the target fails, reverts or is rejected.
    /// @param sender The message sender.
    /// @param from Previous token owner.
    /// @param to New token owner.
    /// @param tokenId Identifier of the token transferred.
    /// @param data Optional data to send along with the receiver contract call.
    function _callOnERC721Received(address sender, address from, address to, uint256 tokenId, bytes memory data) private {
        if (IERC721Receiver(to).onERC721Received(sender, from, tokenId, data) != ERC721_RECEIVED) revert ERC721SafeTransferRejected(to, tokenId);
    }

    /// @notice Returns whether an account is authorised to make a transfer on behalf of an owner.
    /// @param owner The token owner.
    /// @param account The account to check the operatability of.
    /// @return operatable True if `account` is `owner` or is an operator for `owner`, false otherwise.
    function _isOperatable(Layout storage s, address owner, address account) private view returns (bool operatable) {
        return (owner == account) || s.operators[owner][account];
    }

    function _tokenOwner(uint256 owner) private pure returns (address tokenOwner) {
        return address(uint160(owner));
    }

    function _tokenExists(uint256 owner) private pure returns (bool tokenExists) {
        return uint160(owner) != 0;
    }

    function _tokenWasBurnt(uint256 owner) private pure returns (bool tokenWasBurnt) {
        return owner == BURNT_TOKEN_OWNER_VALUE;
    }

    function _tokenHasApproval(uint256 owner) private pure returns (bool tokenHasApproval) {
        return owner & TOKEN_APPROVAL_OWNER_FLAG != 0;
    }
}

File 58 of 69 : TokenMetadataBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ITokenMetadataResolver} from "./../interfaces/ITokenMetadataResolver.sol";
import {TokenMetadataStorage} from "./../libraries/TokenMetadataStorage.sol";

/// @title TokenMetadataBase (proxiable version).
/// @notice Provides metadata management for token contracts (ERC721/ERC1155) which uses an external resolver for managing individual tokens metadata.
/// @dev This contract is to be used via inheritance in a proxied implementation.
abstract contract TokenMetadataBase {
    using TokenMetadataStorage for TokenMetadataStorage.Layout;

    /// @notice Gets the token name. E.g. "My Token".
    /// @return tokenName The token name.
    function name() public view virtual returns (string memory tokenName) {
        return TokenMetadataStorage.layout().name();
    }

    /// @notice Gets the token symbol. E.g. "TOK".
    /// @return tokenSymbol The token symbol.
    function symbol() public view virtual returns (string memory tokenSymbol) {
        return TokenMetadataStorage.layout().symbol();
    }

    /// @notice Gets the token metadata resolver address.
    /// @return tokenMetadataResolver The token metadata resolver address.
    function metadataResolver() external view virtual returns (ITokenMetadataResolver tokenMetadataResolver) {
        return TokenMetadataStorage.layout().metadataResolver();
    }
}

File 59 of 69 : ITokenMetadataResolver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ITokenMetadataResolver
/// @notice Interface for Token Metadata Resolvers.
interface ITokenMetadataResolver {
    /// @notice Gets the token metadata URI for a token.
    /// @param tokenContract The token contract for which to retrieve the token URI.
    /// @param tokenId The token identifier.
    /// @return tokenURI The token metadata URI.
    function tokenMetadataURI(address tokenContract, uint256 tokenId) external view returns (string memory tokenURI);
}

File 60 of 69 : TokenMetadataStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ITokenMetadataResolver} from "./../interfaces/ITokenMetadataResolver.sol";
import {ProxyInitialization} from "./../../../proxy/libraries/ProxyInitialization.sol";

library TokenMetadataStorage {
    struct Layout {
        string tokenName;
        string tokenSymbol;
        ITokenMetadataResolver tokenMetadataResolver;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.metadata.TokenMetadata.storage")) - 1);
    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.token.metadata.TokenMetadata.phase")) - 1);

    /// @notice Initializes the metadata storage (immutable version).
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @param tokenName The token name.
    /// @param tokenSymbol The token symbol.
    /// @param tokenMetadataResolver The address of the metadata resolver contract.
    function constructorInit(
        Layout storage s,
        string memory tokenName,
        string memory tokenSymbol,
        ITokenMetadataResolver tokenMetadataResolver
    ) internal {
        s.tokenName = tokenName;
        s.tokenSymbol = tokenSymbol;
        s.tokenMetadataResolver = tokenMetadataResolver;
    }

    /// @notice Initializes the metadata storage (proxied version).
    /// @notice Sets the proxy initialization phase to `1`.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @param tokenName The token name.
    /// @param tokenSymbol The token symbol.
    /// @param tokenMetadataResolver The address of the metadata resolver contract.
    function proxyInit(
        Layout storage s,
        string calldata tokenName,
        string calldata tokenSymbol,
        ITokenMetadataResolver tokenMetadataResolver
    ) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.tokenName = tokenName;
        s.tokenSymbol = tokenSymbol;
        s.tokenMetadataResolver = tokenMetadataResolver;
    }

    /// @notice Gets the name of the token.
    /// @return tokenName The name of the token contract.
    function name(Layout storage s) internal view returns (string memory tokenName) {
        return s.tokenName;
    }

    /// @notice Gets the symbol of the token.
    /// @return tokenSymbol The symbol of the token contract.
    function symbol(Layout storage s) internal view returns (string memory tokenSymbol) {
        return s.tokenSymbol;
    }

    /// @notice Gets the address of the token metadata resolver.
    /// @return tokenMetadataResolver The address of the token metadata resolver.
    function metadataResolver(Layout storage s) internal view returns (ITokenMetadataResolver tokenMetadataResolver) {
        return s.tokenMetadataResolver;
    }

    /// @notice Gets the token metadata URI retieved from the metadata resolver contract.
    /// @param tokenContract The address of the token contract.
    /// @param tokenId The ID of the token.
    function tokenMetadataURI(Layout storage s, address tokenContract, uint256 tokenId) internal view returns (string memory) {
        return s.tokenMetadataResolver.tokenMetadataURI(tokenContract, tokenId);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 61 of 69 : ERC2981.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ERC2981Storage} from "./libraries/ERC2981Storage.sol";
import {ERC2981Base} from "./base/ERC2981Base.sol";
import {ContractOwnership} from "./../../access/ContractOwnership.sol";

/// @title ERC2981 NFT Royalty Standard (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC2981 is ERC2981Base, ContractOwnership {
    /// @notice Marks the following ERC165 interface(s) as supported: ERC2981.
    constructor() {
        ERC2981Storage.init();
    }
}

File 62 of 69 : ERC2981Base.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC2981} from "./../interfaces/IERC2981.sol";
import {ERC2981Storage} from "./../libraries/ERC2981Storage.sol";
import {ContractOwnershipStorage} from "./../../../access/libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC2981 NFT Royalty Standard (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract ERC2981Base is Context, IERC2981 {
    using ERC2981Storage for ERC2981Storage.Layout;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    uint256 public constant ROYALTY_FEE_DENOMINATOR = ERC2981Storage.ROYALTY_FEE_DENOMINATOR;

    /// @notice Sets the royalty percentage.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {IncorrectRoyaltyPercentage} if `percentage` is above 100% (> FEE_DENOMINATOR).
    /// @param percentage The new percentage to set. For example 50000 sets 50% royalty.
    function setRoyaltyPercentage(uint256 percentage) external {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        ERC2981Storage.layout().setRoyaltyPercentage(percentage);
    }

    /// @notice Sets the royalty receiver.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {IncorrectRoyaltyReceiver} if `receiver` is the zero address.
    /// @param receiver The new receiver to set.
    function setRoyaltyReceiver(address receiver) external {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        ERC2981Storage.layout().setRoyaltyReceiver(receiver);
    }

    /// @inheritdoc IERC2981
    function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount) {
        return ERC2981Storage.layout().royaltyInfo(tokenId, salePrice);
    }
}

File 63 of 69 : OperatorFiltererBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IOperatorFilterRegistry} from "./../interfaces/IOperatorFilterRegistry.sol";
import {OperatorFiltererStorage} from "./../libraries/OperatorFiltererStorage.sol";
import {ContractOwnershipStorage} from "./../../../access/libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title Operator Filterer for token contracts (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract OperatorFiltererBase is Context {
    using OperatorFiltererStorage for OperatorFiltererStorage.Layout;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @notice Updates the address that the contract will make OperatorFilter checks against.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @param registry The new operator filter registry address. When set to the zero address, checks will be bypassed.
    function updateOperatorFilterRegistry(IOperatorFilterRegistry registry) external {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        OperatorFiltererStorage.layout().updateOperatorFilterRegistry(registry);
    }

    /// @notice Gets the operator filter registry address.
    function operatorFilterRegistry() external view returns (IOperatorFilterRegistry) {
        return OperatorFiltererStorage.layout().operatorFilterRegistry();
    }
}

File 64 of 69 : ERC2981Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when setting a royalty percentage that is above 100% (> FEE_DENOMINATOR).
/// @param percentage The royalty percentage that was attempted to be set.
error ERC2981IncorrectRoyaltyPercentage(uint256 percentage);

/// @notice Thrown when setting a royalty receiver that is the zero address.
error ERC2981IncorrectRoyaltyReceiver();

File 65 of 69 : OperatorFiltererErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @notice Thrown when transferring to or giving approval to a non-authorized operator.
/// @param operator The address that is not authorized.
error OperatorNotAllowed(address operator);

File 66 of 69 : IERC2981.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

/// @title ERC2981 NFT Royalty Standard.
/// @dev See https://eips.ethereum.org/EIPS/eip-2981
/// @dev Note: The ERC-165 identifier for this interface is 0x2a55205a.
interface IERC2981 {
    /// @notice Called with the sale price to determine how much royalty is owed and to whom.
    /// @param tokenId The NFT asset queried for royalty information
    /// @param salePrice The sale price of the NFT asset specified by `tokenId`
    /// @return receiver Address of who should be sent the royalty payment
    /// @return royaltyAmount The royalty payment amount for `salePrice`
    function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount);
}

File 67 of 69 : IOperatorFilterRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

interface IOperatorFilterRegistry {
    function isOperatorAllowed(address registrant, address operator) external view returns (bool);

    function register(address registrant) external;

    function registerAndSubscribe(address registrant, address subscription) external;

    function registerAndCopyEntries(address registrant, address registrantToCopy) external;

    function unregister(address addr) external;

    function updateOperator(address registrant, address operator, bool filtered) external;

    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;

    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;

    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;

    function subscribe(address registrant, address registrantToSubscribe) external;

    function unsubscribe(address registrant, bool copyExistingEntries) external;

    function subscriptionOf(address addr) external returns (address registrant);

    function subscribers(address registrant) external returns (address[] memory);

    function subscriberAt(address registrant, uint256 index) external returns (address);

    function copyEntriesOf(address registrant, address registrantToCopy) external;

    function isOperatorFiltered(address registrant, address operator) external returns (bool);

    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);

    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);

    function filteredOperators(address addr) external returns (address[] memory);

    function filteredCodeHashes(address addr) external returns (bytes32[] memory);

    function filteredOperatorAt(address registrant, uint256 index) external returns (address);

    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);

    function isRegistered(address addr) external returns (bool);

    function codeHashOf(address addr) external returns (bytes32);
}

File 68 of 69 : ERC2981Storage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ERC2981IncorrectRoyaltyReceiver, ERC2981IncorrectRoyaltyPercentage} from "./../errors/ERC2981Errors.sol";
import {IERC2981} from "./../interfaces/IERC2981.sol";
import {InterfaceDetectionStorage} from "./../../../introspection/libraries/InterfaceDetectionStorage.sol";

library ERC2981Storage {
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        address royaltyReceiver;
        uint96 royaltyPercentage;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.royalty.ERC2981.storage")) - 1);

    uint256 internal constant ROYALTY_FEE_DENOMINATOR = 100000;

    /// @notice Marks the following ERC165 interface(s) as supported: ERC2981.
    function init() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC2981).interfaceId, true);
    }

    /// @notice Sets the royalty percentage.
    /// @dev Reverts with {ERC2981IncorrectRoyaltyPercentage} if `percentage` is above 100% (> FEE_DENOMINATOR).
    /// @param percentage The new percentage to set. For example 50000 sets 50% royalty.
    function setRoyaltyPercentage(Layout storage s, uint256 percentage) internal {
        if (percentage > ROYALTY_FEE_DENOMINATOR) {
            revert ERC2981IncorrectRoyaltyPercentage(percentage);
        }
        s.royaltyPercentage = uint96(percentage);
    }

    /// @notice Sets the royalty receiver.
    /// @dev Reverts with {ERC2981IncorrectRoyaltyReceiver} if `receiver` is the zero address.
    /// @param receiver The new receiver to set.
    function setRoyaltyReceiver(Layout storage s, address receiver) internal {
        if (receiver == address(0)) {
            revert ERC2981IncorrectRoyaltyReceiver();
        }
        s.royaltyReceiver = receiver;
    }

    /// @notice Called with the sale price to determine how much royalty is owed and to whom.
    // / @param tokenId The NFT asset queried for royalty information
    /// @param salePrice The sale price of the NFT asset specified by `tokenId`
    /// @return receiver Address of who should be sent the royalty payment
    /// @return royaltyAmount The royalty payment amount for `salePrice`
    function royaltyInfo(Layout storage s, uint256, uint256 salePrice) internal view returns (address receiver, uint256 royaltyAmount) {
        receiver = s.royaltyReceiver;
        uint256 royaltyPercentage = s.royaltyPercentage;
        if (salePrice == 0 || royaltyPercentage == 0) {
            royaltyAmount = 0;
        } else {
            if (salePrice < ROYALTY_FEE_DENOMINATOR) {
                royaltyAmount = (salePrice * royaltyPercentage) / ROYALTY_FEE_DENOMINATOR;
            } else {
                royaltyAmount = (salePrice / ROYALTY_FEE_DENOMINATOR) * royaltyPercentage;
            }
        }
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 69 of 69 : OperatorFiltererStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {OperatorNotAllowed} from "./../errors/OperatorFiltererErrors.sol";
import {IOperatorFilterRegistry} from "./../interfaces/IOperatorFilterRegistry.sol";
import {ProxyInitialization} from "./../../../proxy/libraries/ProxyInitialization.sol";

library OperatorFiltererStorage {
    using OperatorFiltererStorage for OperatorFiltererStorage.Layout;

    struct Layout {
        IOperatorFilterRegistry registry;
    }

    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.token.royalty.OperatorFilterer.phase")) - 1);
    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.royalty.OperatorFilterer.storage")) - 1);

    /// @notice Sets the address that the contract will make OperatorFilter checks against.
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @param registry The operator filter registry address. When set to the zero address, checks will be bypassed.
    function constructorInit(Layout storage s, IOperatorFilterRegistry registry) internal {
        s.registry = registry;
    }

    /// @notice Sets the address that the contract will make OperatorFilter checks against.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @param registry The operator filter registry address. When set to the zero address, checks will be bypassed.
    function proxyInit(Layout storage s, IOperatorFilterRegistry registry) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.constructorInit(registry);
    }

    /// @notice Updates the address that the contract will make OperatorFilter checks against.
    /// @param registry The new operator filter registry address. When set to the zero address, checks will be bypassed.
    function updateOperatorFilterRegistry(Layout storage s, IOperatorFilterRegistry registry) internal {
        s.registry = registry;
    }

    /// @dev Reverts with {OperatorNotAllowed} if `sender` is not `from` and is not allowed by a valid operator registry.
    function requireAllowedOperatorForTransfer(Layout storage s, address sender, address from) internal view {
        // Allow spending tokens from addresses with balance
        // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred from an EOA.
        if (sender != from) {
            _checkFilterOperator(s, sender);
        }
    }

    /// @dev Reverts with {OperatorNotAllowed} if `sender` is not allowed by a valid operator registry.
    function requireAllowedOperatorForApproval(Layout storage s, address operator) internal view {
        _checkFilterOperator(s, operator);
    }

    function operatorFilterRegistry(Layout storage s) internal view returns (IOperatorFilterRegistry) {
        return s.registry;
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }

    function _checkFilterOperator(Layout storage s, address operator) private view {
        IOperatorFilterRegistry registry = s.registry;
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(registry) != address(0) && address(registry).code.length > 0) {
            if (!registry.isOperatorAllowed(address(this), operator)) {
                revert OperatorNotAllowed(operator);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"contract ITokenMetadataResolver","name":"metadataResolver","type":"address"},{"internalType":"contract IOperatorFilterRegistry","name":"filterRegistry","type":"address"},{"internalType":"contract IForwarderRegistry","name":"forwarderRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"ERC2981IncorrectRoyaltyPercentage","type":"error"},{"inputs":[],"name":"ERC2981IncorrectRoyaltyReceiver","type":"error"},{"inputs":[],"name":"ERC721BalanceOfAddressZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721ExistingToken","type":"error"},{"inputs":[],"name":"ERC721MintToAddressZero","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonApprovedForApproval","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonApprovedForTransfer","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonExistingToken","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonOwnedToken","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721SafeTransferRejected","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ERC721SelfApproval","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ERC721SelfApprovalForAll","type":"error"},{"inputs":[],"name":"ERC721TransferToAddressZero","type":"error"},{"inputs":[],"name":"IllegalInterfaceId","type":"error"},{"inputs":[],"name":"InconsistentArrayLengths","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NotContractOwner","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"NotRoleHolder","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","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":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"RoleRevoked","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"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"batchMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"batchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"deliver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forwarderRegistry","outputs":[{"internalType":"contract IForwarderRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"approved","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"hasRole_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"approvedForAll","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataResolver","outputs":[{"internalType":"contract ITokenMetadataResolver","name":"tokenMetadataResolver","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"tokenName","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operatorFilterRegistry","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"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":"tokenOwner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"recoverERC20s","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"contract IERC721[]","name":"contracts","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"recoverERC721s","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"recoverETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeMint","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":"uint256","name":"percentage","type":"uint256"}],"name":"setRoyaltyPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"setRoyaltyReceiver","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":"tokenSymbol","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"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":[{"internalType":"contract IOperatorFilterRegistry","name":"registry","type":"address"}],"name":"updateOperatorFilterRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b5060405162003f8d38038062003f8d833981016040819052620000349162000446565b8080868686863362000051816200004a620000f3565b9062000129565b506200005c620001a4565b62000089816200006b620001be565b9081546001600160a01b0319166001600160a01b0391909116179055565b5062000094620001ee565b620000ae838383620000a562000206565b92919062000236565b620000b86200027d565b50620000c691505062000295565b620000d0620002ad565b620000da620002c5565b6001600160a01b03166080525062000671945050505050565b6000806200012360017fc9ed16f33ab3a66c84bfd83099ccb2a8845871e2e1c1928f63797152f0fd54cd620004f2565b92915050565b6001600160a01b03811615620001805781546001600160a01b0319166001600160a01b03821690811783556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35b620001a06307f5828d60e41b600162000198620002d8565b919062000308565b5050565b620001bc6380ac58cd60e01b600162000198620002d8565b565b6000806200012360017f609b85bcafa81ecfaf3ff62cdde2c6c9082a68dbe4922f07399c706bdeb7cd31620004f2565b620001bc63f3993d1160e01b600162000198620002d8565b6000806200012360017f7ea68fd2dcf1c056b94db6a0a537aa80d26fa9ab1eacd85da1ba0b61b7e7a8a5620004f2565b83620002438482620005a5565b5060018401620002548382620005a5565b5060029390930180546001600160a01b0319166001600160a01b03909416939093179092555050565b620001bc635b5e139f60e01b600162000198620002d8565b620001bc638e773e1360e01b600162000198620002d8565b620001bc634ed2f41960e11b600162000198620002d8565b620001bc63152a902d60e11b6001620001985b6000806200012360017fca9d3e17f264b0f3984e2634e94adb37fa3e6a8103f06aeae6fa59e21c769f5e620004f2565b600160e01b6001600160e01b031983160162000337576040516372c683bb60e01b815260040160405180910390fd5b6001600160e01b03199190911660009081526020929092526040909120805460ff1916911515919091179055565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200038d57600080fd5b81516001600160401b0380821115620003aa57620003aa62000365565b604051601f8301601f19908116603f01168101908282118183101715620003d557620003d562000365565b8160405283815260209250866020858801011115620003f357600080fd5b600091505b83821015620004175785820183015181830184015290820190620003f8565b6000602085830101528094505050505092915050565b6001600160a01b03811681146200044357600080fd5b50565b600080600080600060a086880312156200045f57600080fd5b85516001600160401b03808211156200047757600080fd5b6200048589838a016200037b565b965060208801519150808211156200049c57600080fd5b50620004ab888289016200037b565b9450506040860151620004be816200042d565b6060870151909350620004d1816200042d565b6080870151909250620004e4816200042d565b809150509295509295909350565b818103818111156200012357634e487b7160e01b600052601160045260246000fd5b600181811c908216806200052957607f821691505b6020821081036200054a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620005a0576000816000526020600020601f850160051c810160208610156200057b5750805b601f850160051c820191505b818110156200059c5782815560010162000587565b5050505b505050565b81516001600160401b03811115620005c157620005c162000365565b620005d981620005d2845462000514565b8462000550565b602080601f831160018114620006115760008415620005f85750858301515b600019600386901b1c1916600185901b1785556200059c565b600085815260208120601f198616915b82811015620006425788860151825594840194600190910190840162000621565b5085821015620006615787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6080516138eb620006a26000396000818161036d015281816103ed01528181612ba70152612c2f01526138eb6000f3fe608060405234801561001057600080fd5b50600436106102775760003560e01c80638bb9c5bf11610160578063b88d4fde116100d8578063e1a8bf2c1161008c578063f2fde38b11610071578063f2fde38b146105a6578063f3993d11146105b9578063f7ba94bd146105cc57600080fd5b8063e1a8bf2c14610589578063e985e9c51461059357600080fd5b8063c87b56dd116100bd578063c87b56dd1461053c578063d53913931461054f578063d547741f1461057657600080fd5b8063b88d4fde14610516578063c3666c361461052957600080fd5b806395d89b411161012f578063a0c76f6211610114578063a0c76f62146104f3578063a22cb465146104fb578063b0ccc31e1461050e57600080fd5b806395d89b41146104d85780639da5e832146104e057600080fd5b80638bb9c5bf146104975780638da5cb5b146104aa5780638dc251e3146104b257806391d14854146104c557600080fd5b806340c10f19116101f357806361ba27da116101c257806370a08231116101a757806370a082311461045057806373c8a958146104715780638832e6e31461048457600080fd5b806361ba27da1461042a5780636352211e1461043d57600080fd5b806340c10f19146103a457806342842e0e146103b75780634684d7e9146103ca578063572b6c05146103dd57600080fd5b8063114ba8ee1161024a5780632a55205a1161022f5780632a55205a1461032c5780632b4c9f161461036b5780632f2ff15d1461039157600080fd5b8063114ba8ee1461030657806323b872dd1461031957600080fd5b806301ffc9a71461027c57806306fdde03146102a4578063081812fc146102b9578063095ea7b3146102f1575b600080fd5b61028f61028a3660046130f4565b6105df565b60405190151581526020015b60405180910390f35b6102ac6105f9565b60405161029b919061317f565b6102cc6102c7366004613192565b610608565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161029b565b6103046102ff3660046131cd565b61061c565b005b6103046103143660046131f9565b61066b565b610304610327366004613216565b6106d6565b61033f61033a366004613257565b610714565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091520161029b565b7f00000000000000000000000000000000000000000000000000000000000000006102cc565b61030461039f366004613279565b610737565b6103046103b23660046131cd565b61076a565b6103046103c5366004613216565b6107b9565b6103046103d83660046132ee565b6107ea565b61028f6103eb3660046131f9565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b610304610438366004613192565b61085e565b6102cc61044b366004613192565b61087b565b61046361045e3660046131f9565b61088f565b60405190815260200161029b565b61030461047f366004613343565b6108a3565b61030461049236600461341f565b610999565b6103046104a5366004613192565b610a1b565b6102cc610a36565b6103046104c03660046131f9565b610a5d565b61028f6104d3366004613279565b610a7a565b6102ac610ac5565b6103046104ee36600461347b565b610acf565b6102cc610b73565b6103046105093660046134e9565b610b9d565b6102cc610bcc565b610304610524366004613517565b610bd9565b610304610537366004613343565b610c16565b6102ac61054a366004613192565b610d8e565b6104637f6d696e746572000000000000000000000000000000000000000000000000000081565b610304610584366004613279565b610db1565b610463620186a081565b61028f6105a136600461358a565b610ddf565b6103046105b43660046131f9565b610e2d565b6103046105c73660046135b8565b610e48565b6103046105da36600461347b565b610e82565b60006105f3826105ed610f39565b90610f67565b92915050565b6060610603611041565b905090565b60006105f382610616611053565b90611081565b73ffffffffffffffffffffffffffffffffffffffff82161561064a5761064a8261064461113a565b90611168565b610667610655611172565b838361065f611053565b92919061117c565b5050565b610684610676611172565b61067e6113f7565b90611425565b6106d38161069061113a565b9081547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff91909116179055565b50565b60006106e0611172565b90506106f681856106ef61113a565b91906114d2565b61070e81858585610705611053565b9392919061150f565b50505050565b60008061072b84846107246117ed565b919061181b565b915091505b9250929050565b6000610741611172565b905061074f8161067e6113f7565b61076583838361075d6118c5565b9291906118f3565b505050565b6107a57f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b61079e6118c5565b91906119c9565b61066782826107b2611053565b9190611a4f565b60006107c3611172565b90506107d281856106ef61113a565b61070e818585856107e1611053565b93929190611b69565b6108167f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b610765838383808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506108579250611053915050565b9190611baf565b610869610676611172565b6106d3816108756117ed565b90611d27565b60006105f382610889611053565b90611dad565b60006105f38261089d611053565b90611e16565b6108ae610676611172565b8483811415806108be5750808214155b156108f5576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561098f5761098788888381811061091557610915613611565b905060200201602081019061092a91906131f9565b85858481811061093c5761093c613611565b9050602002013588888581811061095557610955613611565b905060200201602081019061096a91906131f9565b73ffffffffffffffffffffffffffffffffffffffff169190611e92565b6001016108f8565b5050505050505050565b6109c57f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b61070e6109d0611172565b858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610a129250611053915050565b93929190611f1f565b6106d3610a26611172565b82610a2f6118c5565b9190611f55565b6000610603610a436113f7565b5473ffffffffffffffffffffffffffffffffffffffff1690565b610a68610676611172565b6106d381610a746117ed565b90611ff9565b6000610abe8383610a896118c5565b60009283526020908152604080842073ffffffffffffffffffffffffffffffffffffffff909316845291905290205460ff1690565b9392505050565b6060610603612046565b610afb7f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b61070e84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250610b6c9250611053915050565b9190612058565b6000610603610b806120ee565b6002015473ffffffffffffffffffffffffffffffffffffffff1690565b8015610baf57610baf8261064461113a565b610667610bba611172565b8383610bc4611053565b92919061211c565b6000610603610a4361113a565b6000610be3611172565b9050610bf281876106ef61113a565b610c0e818787878787610c03611053565b959493929190612234565b505050505050565b610c21610676611172565b848381141580610c315750808214155b15610c68576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561098f57858582818110610c8557610c85613611565b9050602002016020810190610c9a91906131f9565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd308a8a85818110610cc857610cc8613611565b9050602002016020810190610cdd91906131f9565b878786818110610cef57610cef613611565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b158015610d6b57600080fd5b505af1158015610d7f573d6000803e3d6000fd5b50505050806001019050610c6b565b6060610d9c82610889611053565b506105f33083610daa6120ee565b91906122aa565b6000610dbb611172565b9050610dc98161067e6113f7565b610765838383610dd76118c5565b92919061236a565b6000610abe8383610dee611053565b919073ffffffffffffffffffffffffffffffffffffffff9182166000908152600393909301602090815260408085209290931684525290205460ff1690565b6106d3610e38611172565b82610e416113f7565b9190612434565b6000610e52611172565b9050610e6181866106ef61113a565b610e7b8186868686610e71611053565b9493929190612548565b5050505050565b610e8d610676611172565b82818114610ec7576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610c0e57610f31848483818110610ee757610ee7613611565b90506020020135878784818110610f0057610f00613611565b9050602002016020810190610f1591906131f9565b73ffffffffffffffffffffffffffffffffffffffff169061284d565b600101610eca565b6000806105f360017fca9d3e17f264b0f3984e2634e94adb37fa3e6a8103f06aeae6fa59e21c769f5e61366f565b60007c01000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610fb7575060006105f3565b7ffe003659000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601611008575060016105f3565b507fffffffff00000000000000000000000000000000000000000000000000000000166000908152602091909152604090205460ff1690565b606061060361104e6120ee565b6129a7565b6000806105f360017fddf3ee18ae6d688373c219468f8be446e7ae82215f4779d821c7ea5e8c13c0c161366f565b60008181526020839052604081205473ffffffffffffffffffffffffffffffffffffffff81166110e5576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018490526024015b60405180910390fd5b74010000000000000000000000000000000000000000811615611130575050600081815260028301602052604090205473ffffffffffffffffffffffffffffffffffffffff166105f3565b60009150506105f3565b6000806105f360017f609b85bcafa81ecfaf3ff62cdde2c6c9082a68dbe4922f07399c706bdeb7cd3161366f565b6106678282612a3d565b6000610603612b64565b60008181526020859052604090205473ffffffffffffffffffffffffffffffffffffffff81166111db576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018390526024016110dc565b8073ffffffffffffffffffffffffffffffffffffffff80821690851603611246576040517fb7fdc01000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016110dc565b611251868287612cab565b6112ae576040517fc7563a1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808716600483015282166024820152604481018490526064016110dc565b73ffffffffffffffffffffffffffffffffffffffff8416611313577401000000000000000000000000000000000000000082161561130e57600083815260208790526040902073ffffffffffffffffffffffffffffffffffffffff821690555b611394565b7401000000000000000000000000000000000000000082178083146113445760008481526020889052604090208190555b506000838152600287016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86161790555b828473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050505050565b6000806105f360017fc9ed16f33ab3a66c84bfd83099ccb2a8845871e2e1c1928f63797152f0fd54cd61366f565b815473ffffffffffffffffffffffffffffffffffffffff828116911614610667576040517f2ef4875e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016110dc565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff91909116179055565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610765576107658383612a3d565b73ffffffffffffffffffffffffffffffffffffffff821661155c576040517f86c8706700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526020869052604090205473ffffffffffffffffffffffffffffffffffffffff81166115bb576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018390526024016110dc565b73ffffffffffffffffffffffffffffffffffffffff84168173ffffffffffffffffffffffffffffffffffffffff161461163f576040517f14dbb53600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018390526044016110dc565b61164a868587612cab565b6116f957740100000000000000000000000000000000000000008116158061169b5750600082815260028701602052604090205473ffffffffffffffffffffffffffffffffffffffff868116911614155b156116f9576040517fa4a3018b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808716600483015285166024820152604481018390526064016110dc565b600082815260208790526040902073ffffffffffffffffffffffffffffffffffffffff8085169182905585161461178a5773ffffffffffffffffffffffffffffffffffffffff80851660009081526001808901602052604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559286168252919020805490910190555b818373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050505050565b6000806105f360017f2c0cf10337caabbd02dcf226f05f5fd19a0919a41a8df8958c39b8000782685861366f565b825473ffffffffffffffffffffffffffffffffffffffff8116906000907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16831580611869575080155b1561187757600091506118bc565b620186a08410156118a157620186a06118908286613682565b61189a9190613699565b91506118bc565b806118af620186a086613699565b6118b99190613682565b91505b50935093915050565b6000806105f360017fc8827d3282af6f37b64c3e9e6f3ac9df286ab0bb0fccd6f8661bf19adb368b2361366f565b60008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915290205460ff1661070e5760008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8681168086529184529382902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055815187815292830152918316918101919091527f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d906060015b60405180910390a150505050565b60008281526020848152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610765576040517f7aa728820000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff821660248201526044016110dc565b73ffffffffffffffffffffffffffffffffffffffff8216611a9c576040517f7851b9cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526020849052604090205473ffffffffffffffffffffffffffffffffffffffff1615611afb576040517f753eceac000000000000000000000000000000000000000000000000000000008152600481018290526024016110dc565b60008181526020848152604080832073ffffffffffffffffffffffffffffffffffffffff86169081905580845260018088019093528184208054909301909255518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4505050565b611b76858585858561150f565b73ffffffffffffffffffffffffffffffffffffffff82163b15610e7b57610e7b8484848460405180602001604052806000815250612d20565b73ffffffffffffffffffffffffffffffffffffffff8216611bfc576040517f7851b9cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160005b81811015611cf2576000838281518110611c1d57611c1d613611565b60200260200101519050611c5b8660000160008381526020019081526020016000205473ffffffffffffffffffffffffffffffffffffffff16151590565b15611c95576040517f753eceac000000000000000000000000000000000000000000000000000000008152600481018290526024016110dc565b60008181526020879052604080822073ffffffffffffffffffffffffffffffffffffffff88169081905590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600101611c01565b5073ffffffffffffffffffffffffffffffffffffffff9092166000908152600190930160205250604090912080549091019055565b620186a0811115611d67576040517fac040408000000000000000000000000000000000000000000000000000000008152600481018290526024016110dc565b81546bffffffffffffffffffffffff909116740100000000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff909116179055565b60008181526020839052604081205473ffffffffffffffffffffffffffffffffffffffff8116611e0c576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018490526024016110dc565b805b949350505050565b600073ffffffffffffffffffffffffffffffffffffffff8216611e65576040517faefbce4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff166000908152600191909101602052604090205490565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610765908490612e35565b611f2a858484611a4f565b73ffffffffffffffffffffffffffffffffffffffff83163b15610e7b57610e7b846000858585612d20565b611f608382846119c9565b60008181526020848152604080832073ffffffffffffffffffffffffffffffffffffffff86168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905580518481529182018390528101919091527ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9060600160405180910390a1505050565b73ffffffffffffffffffffffffffffffffffffffff8116611490576040517f16de0c8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60606106036120536120ee565b612f44565b815181518114612094576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e7b576120e68482815181106120b4576120b4613611565b60200260200101518483815181106120ce576120ce613611565b602002602001015187611a4f9092919063ffffffff16565b600101612097565b6000806105f360017f7ea68fd2dcf1c056b94db6a0a537aa80d26fa9ab1eacd85da1ba0b61b7e7a8a561366f565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603612199576040517fe4215c6a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016110dc565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260038701602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b612241878787878761150f565b73ffffffffffffffffffffffffffffffffffffffff84163b156122a1576122a18686868686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d2092505050565b50505050505050565b60028301546040517ff724dad700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015260248201849052606092169063f724dad790604401600060405180830381865afa158015612324573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611e0e9190810190613703565b60008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915290205460ff161561070e5760008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8681168086529184529382902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055815187815292830152918316918101919091527ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b906060016119bb565b825473ffffffffffffffffffffffffffffffffffffffff90811690831681146124a1576040517f2ef4875e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016110dc565b8173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461070e5783547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182178655604051908316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350505050565b73ffffffffffffffffffffffffffffffffffffffff8316612595576040517f86c8706700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006125a2878688612cab565b90508160005b818110156127c65760008585838181106125c4576125c4613611565b602090810292909201356000818152928d90526040909220549192505073ffffffffffffffffffffffffffffffffffffffff8116612631576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018390526024016110dc565b73ffffffffffffffffffffffffffffffffffffffff89168173ffffffffffffffffffffffffffffffffffffffff16146126b5576040517f14dbb53600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a166004820152602481018390526044016110dc565b846127655774010000000000000000000000000000000000000000811615806127075750600082815260028c01602052604090205473ffffffffffffffffffffffffffffffffffffffff8b8116911614155b15612765576040517fa4a3018b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808c1660048301528a166024820152604481018390526064016110dc565b600082815260208c9052604080822073ffffffffffffffffffffffffffffffffffffffff808c16918290559151859391928d16917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450506001016125a8565b508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415801561280257508015155b1561098f5773ffffffffffffffffffffffffffffffffffffffff808716600090815260018a016020526040808220805485900390559187168152208054820190555050505050505050565b804710156128b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016110dc565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114612911576040519150601f19603f3d011682016040523d82523d6000602084013e612916565b606091505b5050905080610765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016110dc565b60608160000180546129b8906137c3565b80601f01602080910402602001604051908101604052809291908181526020018280546129e4906137c3565b8015612a315780601f10612a0657610100808354040283529160200191612a31565b820191906000526020600020905b815481529060010190602001808311612a1457829003601f168201915b50505050509050919050565b815473ffffffffffffffffffffffffffffffffffffffff168015801590612a7b575060008173ffffffffffffffffffffffffffffffffffffffff163b115b15610765576040517fc617113400000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015282169063c617113490604401602060405180830381865afa158015612af2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b169190613816565b610765576040517fede71dcc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024016110dc565b600033321480612b745750601836105b15612b7e57503390565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec36013560601c7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16331480612c9a57506040517f8929a8ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523360248301527f00000000000000000000000000000000000000000000000000000000000000001690638929a8ca90604401602060405180830381865afa158015612c76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9a9190613816565b15612ca457919050565b3391505090565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480611e0e57505073ffffffffffffffffffffffffffffffffffffffff9182166000908152600393909301602090815260408085209290931684525290205460ff1690565b6040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612d7a908990899088908890600401613833565b6020604051808303816000875af1158015612d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbd919061387c565b7fffffffff000000000000000000000000000000000000000000000000000000001614610e7b576040517fc215a7a100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018390526044016110dc565b6000612e97826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612f559092919063ffffffff16565b9050805160001480612eb8575080806020019051810190612eb89190613816565b610765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016110dc565b60608160010180546129b8906137c3565b6060611e0e8484600085856000808673ffffffffffffffffffffffffffffffffffffffff168587604051612f899190613899565b60006040518083038185875af1925050503d8060008114612fc6576040519150601f19603f3d011682016040523d82523d6000602084013e612fcb565b606091505b5091509150612fdc87838387612fe7565b979650505050505050565b6060831561307d5782516000036130765773ffffffffffffffffffffffffffffffffffffffff85163b613076576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110dc565b5081611e0e565b611e0e83838151156130925781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110dc919061317f565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146106d357600080fd5b60006020828403121561310657600080fd5b8135610abe816130c6565b60005b8381101561312c578181015183820152602001613114565b50506000910152565b6000815180845261314d816020860160208601613111565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610abe6020830184613135565b6000602082840312156131a457600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146106d357600080fd5b600080604083850312156131e057600080fd5b82356131eb816131ab565b946020939093013593505050565b60006020828403121561320b57600080fd5b8135610abe816131ab565b60008060006060848603121561322b57600080fd5b8335613236816131ab565b92506020840135613246816131ab565b929592945050506040919091013590565b6000806040838503121561326a57600080fd5b50508035926020909101359150565b6000806040838503121561328c57600080fd5b82359150602083013561329e816131ab565b809150509250929050565b60008083601f8401126132bb57600080fd5b50813567ffffffffffffffff8111156132d357600080fd5b6020830191508360208260051b850101111561073057600080fd5b60008060006040848603121561330357600080fd5b833561330e816131ab565b9250602084013567ffffffffffffffff81111561332a57600080fd5b613336868287016132a9565b9497909650939450505050565b6000806000806000806060878903121561335c57600080fd5b863567ffffffffffffffff8082111561337457600080fd5b6133808a838b016132a9565b9098509650602089013591508082111561339957600080fd5b6133a58a838b016132a9565b909650945060408901359150808211156133be57600080fd5b506133cb89828a016132a9565b979a9699509497509295939492505050565b60008083601f8401126133ef57600080fd5b50813567ffffffffffffffff81111561340757600080fd5b60208301915083602082850101111561073057600080fd5b6000806000806060858703121561343557600080fd5b8435613440816131ab565b935060208501359250604085013567ffffffffffffffff81111561346357600080fd5b61346f878288016133dd565b95989497509550505050565b6000806000806040858703121561349157600080fd5b843567ffffffffffffffff808211156134a957600080fd5b6134b5888389016132a9565b909650945060208701359150808211156134ce57600080fd5b5061346f878288016132a9565b80151581146106d357600080fd5b600080604083850312156134fc57600080fd5b8235613507816131ab565b9150602083013561329e816134db565b60008060008060006080868803121561352f57600080fd5b853561353a816131ab565b9450602086013561354a816131ab565b935060408601359250606086013567ffffffffffffffff81111561356d57600080fd5b613579888289016133dd565b969995985093965092949392505050565b6000806040838503121561359d57600080fd5b82356135a8816131ab565b9150602083013561329e816131ab565b600080600080606085870312156135ce57600080fd5b84356135d9816131ab565b935060208501356135e9816131ab565b9250604085013567ffffffffffffffff81111561360557600080fd5b61346f878288016132a9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f3576105f3613640565b80820281158282048414176105f3576105f3613640565b6000826136cf577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561371557600080fd5b815167ffffffffffffffff8082111561372d57600080fd5b818401915084601f83011261374157600080fd5b815181811115613753576137536136d4565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715613799576137996136d4565b816040528281528760208487010111156137b257600080fd5b612fdc836020830160208801613111565b600181811c908216806137d757607f821691505b602082108103613810577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60006020828403121561382857600080fd5b8151610abe816134db565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526138726080830184613135565b9695505050505050565b60006020828403121561388e57600080fd5b8151610abe816130c6565b600082516138ab818460208701613111565b919091019291505056fea26469706673582212208f76727b2352cb155e35798ed65ac892a63b76f9ab032c13b7c182c0bed7b71864736f6c6343000816003300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000224b212198b212e0be24c7d7ead0b8ca5f443e55000000000000000000000000000000000000aaeb6d7670e522a718067333cd4e0000000000000000000000003f547f87251710f70109ae0409d461b27070969300000000000000000000000000000000000000000000000000000000000000224f70656e2043616d707573205075626c6973686572204e465420536561736f6e2032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f435f504e46545f533200000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102775760003560e01c80638bb9c5bf11610160578063b88d4fde116100d8578063e1a8bf2c1161008c578063f2fde38b11610071578063f2fde38b146105a6578063f3993d11146105b9578063f7ba94bd146105cc57600080fd5b8063e1a8bf2c14610589578063e985e9c51461059357600080fd5b8063c87b56dd116100bd578063c87b56dd1461053c578063d53913931461054f578063d547741f1461057657600080fd5b8063b88d4fde14610516578063c3666c361461052957600080fd5b806395d89b411161012f578063a0c76f6211610114578063a0c76f62146104f3578063a22cb465146104fb578063b0ccc31e1461050e57600080fd5b806395d89b41146104d85780639da5e832146104e057600080fd5b80638bb9c5bf146104975780638da5cb5b146104aa5780638dc251e3146104b257806391d14854146104c557600080fd5b806340c10f19116101f357806361ba27da116101c257806370a08231116101a757806370a082311461045057806373c8a958146104715780638832e6e31461048457600080fd5b806361ba27da1461042a5780636352211e1461043d57600080fd5b806340c10f19146103a457806342842e0e146103b75780634684d7e9146103ca578063572b6c05146103dd57600080fd5b8063114ba8ee1161024a5780632a55205a1161022f5780632a55205a1461032c5780632b4c9f161461036b5780632f2ff15d1461039157600080fd5b8063114ba8ee1461030657806323b872dd1461031957600080fd5b806301ffc9a71461027c57806306fdde03146102a4578063081812fc146102b9578063095ea7b3146102f1575b600080fd5b61028f61028a3660046130f4565b6105df565b60405190151581526020015b60405180910390f35b6102ac6105f9565b60405161029b919061317f565b6102cc6102c7366004613192565b610608565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161029b565b6103046102ff3660046131cd565b61061c565b005b6103046103143660046131f9565b61066b565b610304610327366004613216565b6106d6565b61033f61033a366004613257565b610714565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260208301919091520161029b565b7f0000000000000000000000003f547f87251710f70109ae0409d461b2707096936102cc565b61030461039f366004613279565b610737565b6103046103b23660046131cd565b61076a565b6103046103c5366004613216565b6107b9565b6103046103d83660046132ee565b6107ea565b61028f6103eb3660046131f9565b7f0000000000000000000000003f547f87251710f70109ae0409d461b27070969373ffffffffffffffffffffffffffffffffffffffff90811691161490565b610304610438366004613192565b61085e565b6102cc61044b366004613192565b61087b565b61046361045e3660046131f9565b61088f565b60405190815260200161029b565b61030461047f366004613343565b6108a3565b61030461049236600461341f565b610999565b6103046104a5366004613192565b610a1b565b6102cc610a36565b6103046104c03660046131f9565b610a5d565b61028f6104d3366004613279565b610a7a565b6102ac610ac5565b6103046104ee36600461347b565b610acf565b6102cc610b73565b6103046105093660046134e9565b610b9d565b6102cc610bcc565b610304610524366004613517565b610bd9565b610304610537366004613343565b610c16565b6102ac61054a366004613192565b610d8e565b6104637f6d696e746572000000000000000000000000000000000000000000000000000081565b610304610584366004613279565b610db1565b610463620186a081565b61028f6105a136600461358a565b610ddf565b6103046105b43660046131f9565b610e2d565b6103046105c73660046135b8565b610e48565b6103046105da36600461347b565b610e82565b60006105f3826105ed610f39565b90610f67565b92915050565b6060610603611041565b905090565b60006105f382610616611053565b90611081565b73ffffffffffffffffffffffffffffffffffffffff82161561064a5761064a8261064461113a565b90611168565b610667610655611172565b838361065f611053565b92919061117c565b5050565b610684610676611172565b61067e6113f7565b90611425565b6106d38161069061113a565b9081547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff91909116179055565b50565b60006106e0611172565b90506106f681856106ef61113a565b91906114d2565b61070e81858585610705611053565b9392919061150f565b50505050565b60008061072b84846107246117ed565b919061181b565b915091505b9250929050565b6000610741611172565b905061074f8161067e6113f7565b61076583838361075d6118c5565b9291906118f3565b505050565b6107a57f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b61079e6118c5565b91906119c9565b61066782826107b2611053565b9190611a4f565b60006107c3611172565b90506107d281856106ef61113a565b61070e818585856107e1611053565b93929190611b69565b6108167f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b610765838383808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506108579250611053915050565b9190611baf565b610869610676611172565b6106d3816108756117ed565b90611d27565b60006105f382610889611053565b90611dad565b60006105f38261089d611053565b90611e16565b6108ae610676611172565b8483811415806108be5750808214155b156108f5576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561098f5761098788888381811061091557610915613611565b905060200201602081019061092a91906131f9565b85858481811061093c5761093c613611565b9050602002013588888581811061095557610955613611565b905060200201602081019061096a91906131f9565b73ffffffffffffffffffffffffffffffffffffffff169190611e92565b6001016108f8565b5050505050505050565b6109c57f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b61070e6109d0611172565b858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610a129250611053915050565b93929190611f1f565b6106d3610a26611172565b82610a2f6118c5565b9190611f55565b6000610603610a436113f7565b5473ffffffffffffffffffffffffffffffffffffffff1690565b610a68610676611172565b6106d381610a746117ed565b90611ff9565b6000610abe8383610a896118c5565b60009283526020908152604080842073ffffffffffffffffffffffffffffffffffffffff909316845291905290205460ff1690565b9392505050565b6060610603612046565b610afb7f6d696e7465720000000000000000000000000000000000000000000000000000610796611172565b61070e84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250610b6c9250611053915050565b9190612058565b6000610603610b806120ee565b6002015473ffffffffffffffffffffffffffffffffffffffff1690565b8015610baf57610baf8261064461113a565b610667610bba611172565b8383610bc4611053565b92919061211c565b6000610603610a4361113a565b6000610be3611172565b9050610bf281876106ef61113a565b610c0e818787878787610c03611053565b959493929190612234565b505050505050565b610c21610676611172565b848381141580610c315750808214155b15610c68576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561098f57858582818110610c8557610c85613611565b9050602002016020810190610c9a91906131f9565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd308a8a85818110610cc857610cc8613611565b9050602002016020810190610cdd91906131f9565b878786818110610cef57610cef613611565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b158015610d6b57600080fd5b505af1158015610d7f573d6000803e3d6000fd5b50505050806001019050610c6b565b6060610d9c82610889611053565b506105f33083610daa6120ee565b91906122aa565b6000610dbb611172565b9050610dc98161067e6113f7565b610765838383610dd76118c5565b92919061236a565b6000610abe8383610dee611053565b919073ffffffffffffffffffffffffffffffffffffffff9182166000908152600393909301602090815260408085209290931684525290205460ff1690565b6106d3610e38611172565b82610e416113f7565b9190612434565b6000610e52611172565b9050610e6181866106ef61113a565b610e7b8186868686610e71611053565b9493929190612548565b5050505050565b610e8d610676611172565b82818114610ec7576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610c0e57610f31848483818110610ee757610ee7613611565b90506020020135878784818110610f0057610f00613611565b9050602002016020810190610f1591906131f9565b73ffffffffffffffffffffffffffffffffffffffff169061284d565b600101610eca565b6000806105f360017fca9d3e17f264b0f3984e2634e94adb37fa3e6a8103f06aeae6fa59e21c769f5e61366f565b60007c01000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610fb7575060006105f3565b7ffe003659000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601611008575060016105f3565b507fffffffff00000000000000000000000000000000000000000000000000000000166000908152602091909152604090205460ff1690565b606061060361104e6120ee565b6129a7565b6000806105f360017fddf3ee18ae6d688373c219468f8be446e7ae82215f4779d821c7ea5e8c13c0c161366f565b60008181526020839052604081205473ffffffffffffffffffffffffffffffffffffffff81166110e5576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018490526024015b60405180910390fd5b74010000000000000000000000000000000000000000811615611130575050600081815260028301602052604090205473ffffffffffffffffffffffffffffffffffffffff166105f3565b60009150506105f3565b6000806105f360017f609b85bcafa81ecfaf3ff62cdde2c6c9082a68dbe4922f07399c706bdeb7cd3161366f565b6106678282612a3d565b6000610603612b64565b60008181526020859052604090205473ffffffffffffffffffffffffffffffffffffffff81166111db576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018390526024016110dc565b8073ffffffffffffffffffffffffffffffffffffffff80821690851603611246576040517fb7fdc01000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016110dc565b611251868287612cab565b6112ae576040517fc7563a1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808716600483015282166024820152604481018490526064016110dc565b73ffffffffffffffffffffffffffffffffffffffff8416611313577401000000000000000000000000000000000000000082161561130e57600083815260208790526040902073ffffffffffffffffffffffffffffffffffffffff821690555b611394565b7401000000000000000000000000000000000000000082178083146113445760008481526020889052604090208190555b506000838152600287016020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86161790555b828473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050505050565b6000806105f360017fc9ed16f33ab3a66c84bfd83099ccb2a8845871e2e1c1928f63797152f0fd54cd61366f565b815473ffffffffffffffffffffffffffffffffffffffff828116911614610667576040517f2ef4875e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016110dc565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff91909116179055565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610765576107658383612a3d565b73ffffffffffffffffffffffffffffffffffffffff821661155c576040517f86c8706700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526020869052604090205473ffffffffffffffffffffffffffffffffffffffff81166115bb576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018390526024016110dc565b73ffffffffffffffffffffffffffffffffffffffff84168173ffffffffffffffffffffffffffffffffffffffff161461163f576040517f14dbb53600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018390526044016110dc565b61164a868587612cab565b6116f957740100000000000000000000000000000000000000008116158061169b5750600082815260028701602052604090205473ffffffffffffffffffffffffffffffffffffffff868116911614155b156116f9576040517fa4a3018b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808716600483015285166024820152604481018390526064016110dc565b600082815260208790526040902073ffffffffffffffffffffffffffffffffffffffff8085169182905585161461178a5773ffffffffffffffffffffffffffffffffffffffff80851660009081526001808901602052604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559286168252919020805490910190555b818373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050505050565b6000806105f360017f2c0cf10337caabbd02dcf226f05f5fd19a0919a41a8df8958c39b8000782685861366f565b825473ffffffffffffffffffffffffffffffffffffffff8116906000907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16831580611869575080155b1561187757600091506118bc565b620186a08410156118a157620186a06118908286613682565b61189a9190613699565b91506118bc565b806118af620186a086613699565b6118b99190613682565b91505b50935093915050565b6000806105f360017fc8827d3282af6f37b64c3e9e6f3ac9df286ab0bb0fccd6f8661bf19adb368b2361366f565b60008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915290205460ff1661070e5760008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8681168086529184529382902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055815187815292830152918316918101919091527f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d906060015b60405180910390a150505050565b60008281526020848152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610765576040517f7aa728820000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff821660248201526044016110dc565b73ffffffffffffffffffffffffffffffffffffffff8216611a9c576040517f7851b9cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526020849052604090205473ffffffffffffffffffffffffffffffffffffffff1615611afb576040517f753eceac000000000000000000000000000000000000000000000000000000008152600481018290526024016110dc565b60008181526020848152604080832073ffffffffffffffffffffffffffffffffffffffff86169081905580845260018088019093528184208054909301909255518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4505050565b611b76858585858561150f565b73ffffffffffffffffffffffffffffffffffffffff82163b15610e7b57610e7b8484848460405180602001604052806000815250612d20565b73ffffffffffffffffffffffffffffffffffffffff8216611bfc576040517f7851b9cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160005b81811015611cf2576000838281518110611c1d57611c1d613611565b60200260200101519050611c5b8660000160008381526020019081526020016000205473ffffffffffffffffffffffffffffffffffffffff16151590565b15611c95576040517f753eceac000000000000000000000000000000000000000000000000000000008152600481018290526024016110dc565b60008181526020879052604080822073ffffffffffffffffffffffffffffffffffffffff88169081905590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600101611c01565b5073ffffffffffffffffffffffffffffffffffffffff9092166000908152600190930160205250604090912080549091019055565b620186a0811115611d67576040517fac040408000000000000000000000000000000000000000000000000000000008152600481018290526024016110dc565b81546bffffffffffffffffffffffff909116740100000000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff909116179055565b60008181526020839052604081205473ffffffffffffffffffffffffffffffffffffffff8116611e0c576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018490526024016110dc565b805b949350505050565b600073ffffffffffffffffffffffffffffffffffffffff8216611e65576040517faefbce4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5073ffffffffffffffffffffffffffffffffffffffff166000908152600191909101602052604090205490565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610765908490612e35565b611f2a858484611a4f565b73ffffffffffffffffffffffffffffffffffffffff83163b15610e7b57610e7b846000858585612d20565b611f608382846119c9565b60008181526020848152604080832073ffffffffffffffffffffffffffffffffffffffff86168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905580518481529182018390528101919091527ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9060600160405180910390a1505050565b73ffffffffffffffffffffffffffffffffffffffff8116611490576040517f16de0c8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60606106036120536120ee565b612f44565b815181518114612094576040517f6582533600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610e7b576120e68482815181106120b4576120b4613611565b60200260200101518483815181106120ce576120ce613611565b602002602001015187611a4f9092919063ffffffff16565b600101612097565b6000806105f360017f7ea68fd2dcf1c056b94db6a0a537aa80d26fa9ab1eacd85da1ba0b61b7e7a8a561366f565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603612199576040517fe4215c6a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016110dc565b73ffffffffffffffffffffffffffffffffffffffff838116600081815260038701602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b612241878787878761150f565b73ffffffffffffffffffffffffffffffffffffffff84163b156122a1576122a18686868686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d2092505050565b50505050505050565b60028301546040517ff724dad700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015260248201849052606092169063f724dad790604401600060405180830381865afa158015612324573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611e0e9190810190613703565b60008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915290205460ff161561070e5760008381526020858152604080832073ffffffffffffffffffffffffffffffffffffffff8681168086529184529382902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055815187815292830152918316918101919091527ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b906060016119bb565b825473ffffffffffffffffffffffffffffffffffffffff90811690831681146124a1576040517f2ef4875e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016110dc565b8173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461070e5783547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182178655604051908316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350505050565b73ffffffffffffffffffffffffffffffffffffffff8316612595576040517f86c8706700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006125a2878688612cab565b90508160005b818110156127c65760008585838181106125c4576125c4613611565b602090810292909201356000818152928d90526040909220549192505073ffffffffffffffffffffffffffffffffffffffff8116612631576040517fdd560475000000000000000000000000000000000000000000000000000000008152600481018390526024016110dc565b73ffffffffffffffffffffffffffffffffffffffff89168173ffffffffffffffffffffffffffffffffffffffff16146126b5576040517f14dbb53600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a166004820152602481018390526044016110dc565b846127655774010000000000000000000000000000000000000000811615806127075750600082815260028c01602052604090205473ffffffffffffffffffffffffffffffffffffffff8b8116911614155b15612765576040517fa4a3018b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808c1660048301528a166024820152604481018390526064016110dc565b600082815260208c9052604080822073ffffffffffffffffffffffffffffffffffffffff808c16918290559151859391928d16917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450506001016125a8565b508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415801561280257508015155b1561098f5773ffffffffffffffffffffffffffffffffffffffff808716600090815260018a016020526040808220805485900390559187168152208054820190555050505050505050565b804710156128b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016110dc565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d8060008114612911576040519150601f19603f3d011682016040523d82523d6000602084013e612916565b606091505b5050905080610765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016110dc565b60608160000180546129b8906137c3565b80601f01602080910402602001604051908101604052809291908181526020018280546129e4906137c3565b8015612a315780601f10612a0657610100808354040283529160200191612a31565b820191906000526020600020905b815481529060010190602001808311612a1457829003601f168201915b50505050509050919050565b815473ffffffffffffffffffffffffffffffffffffffff168015801590612a7b575060008173ffffffffffffffffffffffffffffffffffffffff163b115b15610765576040517fc617113400000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015282169063c617113490604401602060405180830381865afa158015612af2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b169190613816565b610765576040517fede71dcc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024016110dc565b600033321480612b745750601836105b15612b7e57503390565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec36013560601c7f0000000000000000000000003f547f87251710f70109ae0409d461b27070969373ffffffffffffffffffffffffffffffffffffffff16331480612c9a57506040517f8929a8ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523360248301527f0000000000000000000000003f547f87251710f70109ae0409d461b2707096931690638929a8ca90604401602060405180830381865afa158015612c76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9a9190613816565b15612ca457919050565b3391505090565b60008173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480611e0e57505073ffffffffffffffffffffffffffffffffffffffff9182166000908152600393909301602090815260408085209290931684525290205460ff1690565b6040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612d7a908990899088908890600401613833565b6020604051808303816000875af1158015612d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbd919061387c565b7fffffffff000000000000000000000000000000000000000000000000000000001614610e7b576040517fc215a7a100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018390526044016110dc565b6000612e97826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612f559092919063ffffffff16565b9050805160001480612eb8575080806020019051810190612eb89190613816565b610765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016110dc565b60608160010180546129b8906137c3565b6060611e0e8484600085856000808673ffffffffffffffffffffffffffffffffffffffff168587604051612f899190613899565b60006040518083038185875af1925050503d8060008114612fc6576040519150601f19603f3d011682016040523d82523d6000602084013e612fcb565b606091505b5091509150612fdc87838387612fe7565b979650505050505050565b6060831561307d5782516000036130765773ffffffffffffffffffffffffffffffffffffffff85163b613076576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110dc565b5081611e0e565b611e0e83838151156130925781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110dc919061317f565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146106d357600080fd5b60006020828403121561310657600080fd5b8135610abe816130c6565b60005b8381101561312c578181015183820152602001613114565b50506000910152565b6000815180845261314d816020860160208601613111565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610abe6020830184613135565b6000602082840312156131a457600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146106d357600080fd5b600080604083850312156131e057600080fd5b82356131eb816131ab565b946020939093013593505050565b60006020828403121561320b57600080fd5b8135610abe816131ab565b60008060006060848603121561322b57600080fd5b8335613236816131ab565b92506020840135613246816131ab565b929592945050506040919091013590565b6000806040838503121561326a57600080fd5b50508035926020909101359150565b6000806040838503121561328c57600080fd5b82359150602083013561329e816131ab565b809150509250929050565b60008083601f8401126132bb57600080fd5b50813567ffffffffffffffff8111156132d357600080fd5b6020830191508360208260051b850101111561073057600080fd5b60008060006040848603121561330357600080fd5b833561330e816131ab565b9250602084013567ffffffffffffffff81111561332a57600080fd5b613336868287016132a9565b9497909650939450505050565b6000806000806000806060878903121561335c57600080fd5b863567ffffffffffffffff8082111561337457600080fd5b6133808a838b016132a9565b9098509650602089013591508082111561339957600080fd5b6133a58a838b016132a9565b909650945060408901359150808211156133be57600080fd5b506133cb89828a016132a9565b979a9699509497509295939492505050565b60008083601f8401126133ef57600080fd5b50813567ffffffffffffffff81111561340757600080fd5b60208301915083602082850101111561073057600080fd5b6000806000806060858703121561343557600080fd5b8435613440816131ab565b935060208501359250604085013567ffffffffffffffff81111561346357600080fd5b61346f878288016133dd565b95989497509550505050565b6000806000806040858703121561349157600080fd5b843567ffffffffffffffff808211156134a957600080fd5b6134b5888389016132a9565b909650945060208701359150808211156134ce57600080fd5b5061346f878288016132a9565b80151581146106d357600080fd5b600080604083850312156134fc57600080fd5b8235613507816131ab565b9150602083013561329e816134db565b60008060008060006080868803121561352f57600080fd5b853561353a816131ab565b9450602086013561354a816131ab565b935060408601359250606086013567ffffffffffffffff81111561356d57600080fd5b613579888289016133dd565b969995985093965092949392505050565b6000806040838503121561359d57600080fd5b82356135a8816131ab565b9150602083013561329e816131ab565b600080600080606085870312156135ce57600080fd5b84356135d9816131ab565b935060208501356135e9816131ab565b9250604085013567ffffffffffffffff81111561360557600080fd5b61346f878288016132a9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f3576105f3613640565b80820281158282048414176105f3576105f3613640565b6000826136cf577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561371557600080fd5b815167ffffffffffffffff8082111561372d57600080fd5b818401915084601f83011261374157600080fd5b815181811115613753576137536136d4565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715613799576137996136d4565b816040528281528760208487010111156137b257600080fd5b612fdc836020830160208801613111565b600181811c908216806137d757607f821691505b602082108103613810577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60006020828403121561382857600080fd5b8151610abe816134db565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526138726080830184613135565b9695505050505050565b60006020828403121561388e57600080fd5b8151610abe816130c6565b600082516138ab818460208701613111565b919091019291505056fea26469706673582212208f76727b2352cb155e35798ed65ac892a63b76f9ab032c13b7c182c0bed7b71864736f6c63430008160033

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

00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000224b212198b212e0be24c7d7ead0b8ca5f443e55000000000000000000000000000000000000aaeb6d7670e522a718067333cd4e0000000000000000000000003f547f87251710f70109ae0409d461b27070969300000000000000000000000000000000000000000000000000000000000000224f70656e2043616d707573205075626c6973686572204e465420536561736f6e2032000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f435f504e46545f533200000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : tokenName (string): Open Campus Publisher NFT Season 2
Arg [1] : tokenSymbol (string): OC_PNFT_S2
Arg [2] : metadataResolver (address): 0x224b212198B212E0bE24c7D7EaD0B8cA5F443E55
Arg [3] : filterRegistry (address): 0x000000000000AAeB6D7670E522A718067333cd4E
Arg [4] : forwarderRegistry (address): 0x3f547F87251710F70109Ae0409d461b270709693

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [2] : 000000000000000000000000224b212198b212e0be24c7d7ead0b8ca5f443e55
Arg [3] : 000000000000000000000000000000000000aaeb6d7670e522a718067333cd4e
Arg [4] : 0000000000000000000000003f547f87251710f70109ae0409d461b270709693
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000022
Arg [6] : 4f70656e2043616d707573205075626c6973686572204e465420536561736f6e
Arg [7] : 2032000000000000000000000000000000000000000000000000000000000000
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [9] : 4f435f504e46545f533200000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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