ETH Price: $3,425.63 (+2.35%)

Contract

0xa1cF615003464Ab30c1c6A7c926f52788E64077d
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Grant Role211154462024-11-04 16:21:2319 days ago1730737283IN
0xa1cF6150...88E64077d
0 ETH0.000489459.52078773
Set Product URI210306792024-10-23 20:27:4731 days ago1729715267IN
0xa1cF6150...88E64077d
0 ETH0.0016314911.88617834
Set Product URI210306782024-10-23 20:27:3531 days ago1729715255IN
0xa1cF6150...88E64077d
0 ETH0.0017403812.67949781
Set Product URI210306772024-10-23 20:27:2331 days ago1729715243IN
0xa1cF6150...88E64077d
0 ETH0.0016995712.3821555
Set Product Arti...210306762024-10-23 20:27:1131 days ago1729715231IN
0xa1cF6150...88E64077d
0 ETH0.0006196112.67343006
Set Product210306752024-10-23 20:26:5931 days ago1729715219IN
0xa1cF6150...88E64077d
0 ETH0.0006893312.89322458
Set Product Arti...210306742024-10-23 20:26:4731 days ago1729715207IN
0xa1cF6150...88E64077d
0 ETH0.0003774313.01895496
Set Product210306732024-10-23 20:26:3531 days ago1729715195IN
0xa1cF6150...88E64077d
0 ETH0.0004550313.5567618
Set Product Arti...210306722024-10-23 20:26:2331 days ago1729715183IN
0xa1cF6150...88E64077d
0 ETH0.0003869513.34729104
Set Product210306712024-10-23 20:26:1131 days ago1729715171IN
0xa1cF6150...88E64077d
0 ETH0.0004070312.12686806
Set Product Arti...210306592024-10-23 20:23:4731 days ago1729715027IN
0xa1cF6150...88E64077d
0 ETH0.0006136312.55115573
Set Product210306582024-10-23 20:23:3531 days ago1729715015IN
0xa1cF6150...88E64077d
0 ETH0.0006743112.61233084
Set Product Arti...210306572024-10-23 20:23:2331 days ago1729715003IN
0xa1cF6150...88E64077d
0 ETH0.0006050812.37619692
Set Product210306562024-10-23 20:23:1131 days ago1729714991IN
0xa1cF6150...88E64077d
0 ETH0.0006516412.18834388
Set Product URI210157272024-10-21 18:23:4733 days ago1729535027IN
0xa1cF6150...88E64077d
0 ETH0.000552098.967786
Set Product URI209874182024-10-17 19:36:5937 days ago1729193819IN
0xa1cF6150...88E64077d
0 ETH0.0013001919.960685
Set Product URI209873822024-10-17 19:29:4737 days ago1729193387IN
0xa1cF6150...88E64077d
0 ETH0.0011421317.53407444
Set Approval For...209652832024-10-14 17:25:3540 days ago1728926735IN
0xa1cF6150...88E64077d
0 ETH0.0012858227.88906332
Set Product URI209351602024-10-10 12:18:1144 days ago1728562691IN
0xa1cF6150...88E64077d
0 ETH0.0009358714.36763717
Set Product URI208850572024-10-03 12:41:5951 days ago1727959319IN
0xa1cF6150...88E64077d
0 ETH0.000944898.26227247
Set Product URI208850562024-10-03 12:41:4751 days ago1727959307IN
0xa1cF6150...88E64077d
0 ETH0.000937258.19554019
Set Product URI208850552024-10-03 12:41:3551 days ago1727959295IN
0xa1cF6150...88E64077d
0 ETH0.000975848.53291372
Set Product Arti...208850542024-10-03 12:41:2351 days ago1727959283IN
0xa1cF6150...88E64077d
0 ETH0.000245978.49171143
Set Product208850532024-10-03 12:41:1151 days ago1727959271IN
0xa1cF6150...88E64077d
0 ETH0.000275768.2216424
Set Product Arti...208850522024-10-03 12:40:5951 days ago1727959259IN
0xa1cF6150...88E64077d
0 ETH0.000248598.58187232
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SykyArtistToken

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 300 runs

Other Settings:
paris EvmVersion
File 1 of 30 : SykyArtistToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SykyArtistToken
 * @author Syky - Nathan Rempel
 * @notice Powered up token for the future of fashion.
 */

/*
           @@@   @@@@@   @@@@@@@         @@@@    @@@@@@@      @@@@@     @@@@@@@        @@@@
         @@@@      @@@     @@@@@@        @@       @@@@@@       @@        @@@@@@        @@
         @@@@@      @@      @@@@@       @@        @@@@@       @            @@@@@      @@
         @@@@@@      @@      @@@@@     @@         @@@@@     @               @@@@     @@
          @@@@@@      @       @@@@@   @@          @@@@@    @                @@@@@    @
           @@@@@@@             @@@@@ @@           @@@@@  @@@                 @@@@@  @
             @@@@@@             @@@@@@            @@@@@@@@@@@                 @@@@@@
               @@@@@@           @@@@@             @@@@@  @@@@@                 @@@@@
                @@@@@@@         @@@@@             @@@@@   @@@@@                @@@@@
         @        @@@@@@        @@@@@             @@@@@    @@@@@               @@@@@
         @@        @@@@@        @@@@@             @@@@@     @@@@@              @@@@@
         @@@@      @@@@@        @@@@@             @@@@@@     @@@@@@           @@@@@@
         @@@@@@   @@@@         @@@@@@@           @@@@@@@     @@@@@@@          @@@@@@@
*/

import "../base/ProductArtistToken.sol";

string constant DEFAULT_NAME = "SYKYGOM";
string constant DEFAULT_SYMBOL = "SYKYGOM";
uint256 constant DEFAULT_SERIES_OFFSET = 6; //Sets token format to be 123'000001
uint128 constant DEFAULT_ROYALTY_BPS = 1000; //Sets royalty to 10%

contract SykyArtistToken is ProductArtistToken {
    /*//////////////////////////////////////////////////////////////
                            Version Info
    //////////////////////////////////////////////////////////////*/

    string public constant ENV = "MAINNET";
    string public constant VER = "1.0.0";

    /*//////////////////////////////////////////////////////////////
                            Constructor
    //////////////////////////////////////////////////////////////*/

    constructor(
        address defaultAdmin_,
        address royaltyRecipient_
    )
        ProductArtistToken(
            defaultAdmin_,
            DEFAULT_NAME,
            DEFAULT_SYMBOL,
            DEFAULT_SERIES_OFFSET,
            royaltyRecipient_,
            DEFAULT_ROYALTY_BPS
        )
    {}
}

File 2 of 30 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./interface/IERC165.sol";

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

File 3 of 30 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 4 of 30 : IERC2981.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

File 5 of 30 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

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

File 6 of 30 : IERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v3.3.0
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import "./IERC721.sol";
import "./IERC721Metadata.sol";

/**
 * @dev Interface of an ERC721A compliant contract.
 */
interface IERC721A is IERC721, IERC721Metadata {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * The caller cannot approve to their own address.
     */
    error ApproveToCaller();

    /**
     * The caller cannot approve to the current owner.
     */
    error ApprovalToCurrentOwner();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    // Compiler will pack this into a single 256bit word.
    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Keeps track of the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
    }

    // Compiler will pack this into a single 256bit word.
    struct AddressData {
        // Realistically, 2**64-1 is more than enough.
        uint64 balance;
        // Keeps track of mint count with minimal overhead for tokenomics.
        uint64 numberMinted;
        // Keeps track of burn count with minimal overhead for tokenomics.
        uint64 numberBurned;
        // For miscellaneous variable(s) pertaining to the address
        // (e.g. number of whitelist mint slots used).
        // If there are multiple variables, please pack them into a uint64.
        uint64 aux;
    }

    /**
     * @dev Returns the total amount of tokens stored by the contract.
     *
     * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
     */
    function totalSupply() external view returns (uint256);
}

File 7 of 30 : IERC721Metadata.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
/* is ERC721 */
interface IERC721Metadata {
    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external view returns (string memory);

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external view returns (string memory);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns (string memory);
}

File 8 of 30 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

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

File 9 of 30 : ContractMetadata.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "./interface/IContractMetadata.sol";

/**
 *  @title   Contract Metadata
 *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
 *           for you contract.
 *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
 */

abstract contract ContractMetadata is IContractMetadata {
    /// @notice Returns the contract metadata URI.
    string public override contractURI;

    /**
     *  @notice         Lets a contract admin set the URI for contract-level metadata.
     *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
     *                  See {_canSetContractURI}.
     *                  Emits {ContractURIUpdated Event}.
     *
     *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     */
    function setContractURI(string memory _uri) external override {
        if (!_canSetContractURI()) {
            revert("Not authorized");
        }

        _setupContractURI(_uri);
    }

    /// @dev Lets a contract admin set the URI for contract-level metadata.
    function _setupContractURI(string memory _uri) internal {
        string memory prevURI = contractURI;
        contractURI = _uri;

        emit ContractURIUpdated(prevURI, _uri);
    }

    /// @dev Returns whether contract metadata can be set in the given execution context.
    function _canSetContractURI() internal view virtual returns (bool);
}

File 10 of 30 : IContractMetadata.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
 *  for you contract.
 *
 *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
 */

interface IContractMetadata {
    /// @dev Returns the metadata URI of the contract.
    function contractURI() external view returns (string memory);

    /**
     *  @dev Sets contract URI for the storefront-level metadata of the contract.
     *       Only module admin can call this function.
     */
    function setContractURI(string calldata _uri) external;

    /// @dev Emitted when the contract URI is updated.
    event ContractURIUpdated(string prevURI, string newURI);
}

File 11 of 30 : IMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
interface IMulticall {
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     */
    function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}

File 12 of 30 : IOwnable.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
 *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
 *  information about who the contract's owner is.
 */

interface IOwnable {
    /// @dev Returns the owner of the contract.
    function owner() external view returns (address);

    /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
    function setOwner(address _newOwner) external;

    /// @dev Emitted when a new Owner is set.
    event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
}

File 13 of 30 : IPermissions.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IPermissions {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

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

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

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

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

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

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

File 14 of 30 : IRoyalty.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "../../eip/interface/IERC2981.sol";

/**
 *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
 *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
 *  that uses information about royalty fees, if desired.
 *
 *  The `Royalty` contract is ERC2981 compliant.
 */

interface IRoyalty is IERC2981 {
    struct RoyaltyInfo {
        address recipient;
        uint256 bps;
    }

    /// @dev Returns the royalty recipient and fee bps.
    function getDefaultRoyaltyInfo() external view returns (address, uint16);

    /// @dev Lets a module admin update the royalty bps and recipient.
    function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;

    /// @dev Lets a module admin set the royalty recipient for a particular token Id.
    function setRoyaltyInfoForToken(
        uint256 tokenId,
        address recipient,
        uint256 bps
    ) external;

    /// @dev Returns the royalty recipient for a particular token Id.
    function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);

    /// @dev Emitted when royalty info is updated.
    event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);

    /// @dev Emitted when royalty recipient for tokenId is set
    event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
}

File 15 of 30 : Multicall.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "../lib/TWAddress.sol";
import "./interface/IMulticall.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
contract Multicall is IMulticall {
    /**
     *  @notice Receives and executes a batch of function calls on this contract.
     *  @dev Receives and executes a batch of function calls on this contract.
     *
     *  @param data The bytes data that makes up the batch of function calls to execute.
     *  @return results The bytes data that makes up the result of the batch of function calls executed.
     */
    function multicall(bytes[] calldata data) external virtual override returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = TWAddress.functionDelegateCall(address(this), data[i]);
        }
        return results;
    }
}

File 16 of 30 : Ownable.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "./interface/IOwnable.sol";

/**
 *  @title   Ownable
 *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
 *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
 *           information about who the contract's owner is.
 */

abstract contract Ownable is IOwnable {
    /// @dev Owner of the contract (purpose: OpenSea compatibility)
    address private _owner;

    /// @dev Reverts if caller is not the owner.
    modifier onlyOwner() {
        if (msg.sender != _owner) {
            revert("Not authorized");
        }
        _;
    }

    /**
     *  @notice Returns the owner of the contract.
     */
    function owner() public view override returns (address) {
        return _owner;
    }

    /**
     *  @notice Lets an authorized wallet set a new owner for the contract.
     *  @param _newOwner The address to set as the new owner of the contract.
     */
    function setOwner(address _newOwner) external override {
        if (!_canSetOwner()) {
            revert("Not authorized");
        }
        _setupOwner(_newOwner);
    }

    /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
    function _setupOwner(address _newOwner) internal {
        address _prevOwner = _owner;
        _owner = _newOwner;

        emit OwnerUpdated(_prevOwner, _newOwner);
    }

    /// @dev Returns whether owner can be set in the given execution context.
    function _canSetOwner() internal view virtual returns (bool);
}

File 17 of 30 : Permissions.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "./interface/IPermissions.sol";
import "../lib/TWStrings.sol";

/**
 *  @title   Permissions
 *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
 */
contract Permissions is IPermissions {
    /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
    mapping(bytes32 => mapping(address => bool)) private _hasRole;

    /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
    mapping(bytes32 => bytes32) private _getRoleAdmin;

    /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
    modifier onlyRole(bytes32 role) {
        _checkRole(role, msg.sender);
        _;
    }

    /**
     *  @notice         Checks whether an account has a particular role.
     *  @dev            Returns `true` if `account` has been granted `role`.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account for which the role is being checked.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _hasRole[role][account];
    }

    /**
     *  @notice         Checks whether an account has a particular role;
     *                  role restrictions can be swtiched on and off.
     *
     *  @dev            Returns `true` if `account` has been granted `role`.
     *                  Role restrictions can be swtiched on and off:
     *                      - If address(0) has ROLE, then the ROLE restrictions
     *                        don't apply.
     *                      - If address(0) does not have ROLE, then the ROLE
     *                        restrictions will apply.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account for which the role is being checked.
     */
    function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
        if (!_hasRole[role][address(0)]) {
            return _hasRole[role][account];
        }

        return true;
    }

    /**
     *  @notice         Returns the admin role that controls the specified role.
     *  @dev            See {grantRole} and {revokeRole}.
     *                  To change a role's admin, use {_setRoleAdmin}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     */
    function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
        return _getRoleAdmin[role];
    }

    /**
     *  @notice         Grants a role to an account, if not previously granted.
     *  @dev            Caller must have admin role for the `role`.
     *                  Emits {RoleGranted Event}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account to which the role is being granted.
     */
    function grantRole(bytes32 role, address account) public virtual override {
        _checkRole(_getRoleAdmin[role], msg.sender);
        if (_hasRole[role][account]) {
            revert("Can only grant to non holders");
        }
        _setupRole(role, account);
    }

    /**
     *  @notice         Revokes role from an account.
     *  @dev            Caller must have admin role for the `role`.
     *                  Emits {RoleRevoked Event}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account from which the role is being revoked.
     */
    function revokeRole(bytes32 role, address account) public virtual override {
        _checkRole(_getRoleAdmin[role], msg.sender);
        _revokeRole(role, account);
    }

    /**
     *  @notice         Revokes role from the account.
     *  @dev            Caller must have the `role`, with caller being the same as `account`.
     *                  Emits {RoleRevoked Event}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account from which the role is being revoked.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        if (msg.sender != account) {
            revert("Can only renounce for self");
        }
        _revokeRole(role, account);
    }

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

    /// @dev Sets up `role` for `account`
    function _setupRole(bytes32 role, address account) internal virtual {
        _hasRole[role][account] = true;
        emit RoleGranted(role, account, msg.sender);
    }

    /// @dev Revokes `role` from `account`
    function _revokeRole(bytes32 role, address account) internal virtual {
        _checkRole(role, account);
        delete _hasRole[role][account];
        emit RoleRevoked(role, account, msg.sender);
    }

    /// @dev Checks `role` for `account`. Reverts with a message including the required role.
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!_hasRole[role][account]) {
            revert(
                string(
                    abi.encodePacked(
                        "Permissions: account ",
                        TWStrings.toHexString(uint160(account), 20),
                        " is missing role ",
                        TWStrings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /// @dev Checks `role` for `account`. Reverts with a message including the required role.
    function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
        if (!hasRoleWithSwitch(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "Permissions: account ",
                        TWStrings.toHexString(uint160(account), 20),
                        " is missing role ",
                        TWStrings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }
}

File 18 of 30 : Royalty.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "./interface/IRoyalty.sol";

/**
 *  @title   Royalty
 *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
 *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
 *           that uses information about royalty fees, if desired.
 *
 *  @dev     The `Royalty` contract is ERC2981 compliant.
 */

abstract contract Royalty is IRoyalty {
    /// @dev The (default) address that receives all royalty value.
    address private royaltyRecipient;

    /// @dev The (default) % of a sale to take as royalty (in basis points).
    uint16 private royaltyBps;

    /// @dev Token ID => royalty recipient and bps for token
    mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;

    /**
     *  @notice   View royalty info for a given token and sale price.
     *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
     *  @param tokenId          The tokenID of the NFT for which to query royalty info.
     *  @param salePrice        Sale price of the token.
     *
     *  @return receiver        Address of royalty recipient account.
     *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        virtual
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
        receiver = recipient;
        royaltyAmount = (salePrice * bps) / 10_000;
    }

    /**
     *  @notice          View royalty info for a given token.
     *  @dev             Returns royalty recipient and bps for `_tokenId`.
     *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
     */
    function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
        RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];

        return
            royaltyForToken.recipient == address(0)
                ? (royaltyRecipient, uint16(royaltyBps))
                : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
    }

    /**
     *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
     */
    function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
        return (royaltyRecipient, uint16(royaltyBps));
    }

    /**
     *  @notice         Updates default royalty recipient and bps.
     *  @dev            Caller should be authorized to set royalty info.
     *                  See {_canSetRoyaltyInfo}.
     *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
     *
     *  @param _royaltyRecipient   Address to be set as default royalty recipient.
     *  @param _royaltyBps         Updated royalty bps.
     */
    function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
        if (!_canSetRoyaltyInfo()) {
            revert("Not authorized");
        }

        _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
    }

    /// @dev Lets a contract admin update the default royalty recipient and bps.
    function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
        if (_royaltyBps > 10_000) {
            revert("Exceeds max bps");
        }

        royaltyRecipient = _royaltyRecipient;
        royaltyBps = uint16(_royaltyBps);

        emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
    }

    /**
     *  @notice         Updates default royalty recipient and bps for a particular token.
     *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
     *                  See {_canSetRoyaltyInfo}.
     *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
     *
     *  @param _recipient   Address to be set as royalty recipient for given token Id.
     *  @param _bps         Updated royalty bps for the token Id.
     */
    function setRoyaltyInfoForToken(
        uint256 _tokenId,
        address _recipient,
        uint256 _bps
    ) external override {
        if (!_canSetRoyaltyInfo()) {
            revert("Not authorized");
        }

        _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
    }

    /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
    function _setupRoyaltyInfoForToken(
        uint256 _tokenId,
        address _recipient,
        uint256 _bps
    ) internal {
        if (_bps > 10_000) {
            revert("Exceeds max bps");
        }

        royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });

        emit RoyaltyForToken(_tokenId, _recipient, _bps);
    }

    /// @dev Returns whether royalty info can be set in the given execution context.
    function _canSetRoyaltyInfo() internal view virtual returns (bool);
}

File 19 of 30 : 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 20 of 30 : TWAddress.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev Collection of functions related to the address type
 */
library TWAddress {
    /**
     * @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
     * ====
     *
     * [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.
     *
     * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) 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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 21 of 30 : TWStrings.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev String operations.
 */
library TWStrings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

File 22 of 30 : IProductArtistToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

interface IProductArtistToken {
    /*//////////////////////////////////////////////////////////////
                        Methods
    //////////////////////////////////////////////////////////////*/

    /// @notice Lets an authorized address mint single NFTs to a recipient.
    function mintTo(address _to, uint256 _productId) external;

    function mintTo(address _to, uint256 _productId, bytes memory _data) external;

    function mintTo(
        address _to,
        uint256 _productId,
        string calldata _uri,
        bytes memory _data
    ) external;

    /// @notice Lets an authorized address mint multiple NFTs at once to a recipient.
    function batchMintTo(address _to, uint256 _productId, uint256 _quantity) external;

    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity,
        bytes memory _data
    ) external;

    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity,
        string[] calldata _uris,
        bytes memory _data
    ) external;

    /*//////////////////////////////////////////////////////////////
                        Events
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when all minting is enabled or disabled
    event GlobalMintingRestricted(bool restricted);

    /// @dev Emitted when all transfers are enabled or disabled
    event GlobalTransfersRestricted(bool restricted);

    /// @dev Emitted when all burning is enabled or disabled
    event GlobalBurningRestricted(bool restricted);

    /*//////////////////////////////////////////////////////////////
                        Errors
    //////////////////////////////////////////////////////////////*/

    /// @dev URIs array length for batch mint must match _quantity
    error BatchMintURICountMismatch();

    /// @dev Action requires the manager or admin role
    error ManagerRoleRequired();

    /// @dev Action requires the minting or admin role
    error MintingRoleRequired();

    /// @dev Minting has been disabled via address(0) role
    error GlobalMintingDisabled();

    /// @dev Transfers have been disabled via address(0) role
    error GlobalTransfersDisabled();

    /// @dev Burning has been disabled via address(0) role
    error GlobalBurningDisabled();
}

File 23 of 30 : IProductDataContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

import "../../extension/interface/IProductCreators.sol";

interface IProductDataContract {
    function tieredURI(
        uint256 _tokenId,
        uint256 _productId,
        bool _isUnique
    ) external view returns (string memory);

    function getProductCreators(
        uint256 _productId
    ) external view returns (IProductCreators.CreatorData[] memory);

    function baseURI() external view returns (string memory);

    function setBaseURI(string memory _uri) external returns (string memory);

    function productURI(uint256 _productId) external view returns (string memory);

    function setProductURI(
        uint256 _productId,
        string calldata _uri
    ) external returns (string memory);

    function tokenURI(uint256 _tokenId) external view returns (string memory);

    function setTokenURI(
        uint256 _tokenId,
        string calldata _uri
    ) external returns (string memory);

    function setTokenURIs(
        uint256[] calldata _tokenIds,
        string[] calldata _uris
    ) external returns (string[] memory);

    function setTokenURIs(
        uint256 _startTokenId,
        uint256 _quantity,
        string[] calldata _uri
    ) external returns (string[] memory);

    function runBeforeExtensions(
        uint256 _productId,
        address _from,
        address _to,
        uint256 _startTokenId,
        uint256 _quantity
    ) external;

    function runAfterExtensions(
        uint256 _productId,
        address _from,
        address _to,
        uint256 _startTokenId,
        uint256 _quantity
    ) external;

    error ManagerRoleRequired();
    error CalledFromTokenOnly();
}

File 24 of 30 : ProductArtistToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

import "../eip/ERC721ArtistProduct.sol";
import "./interface/IProductArtistToken.sol";
import "../extension/ProductDataConnector.sol";

import "@thirdweb-dev/contracts/extension/ContractMetadata.sol";
import "@thirdweb-dev/contracts/extension/Multicall.sol";
import "@thirdweb-dev/contracts/extension/Ownable.sol";
import "@thirdweb-dev/contracts/extension/Royalty.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";
import "@thirdweb-dev/contracts/lib/TWStrings.sol";

// ERC-5192 (lock/unlock a token);
// ERC-5192 (recommended for gas efficiency)
// event Locked(uint256 tokenId);
// event Unlocked(uint256 tokenId);
// ERC-4906 metadata update
// event MetadataUpdate(uint256 _tokenId);
// event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
//means the metadata is permanently frozen
//event PermanentURI(string _value, uint256 indexed _id);
// To refresh a whole collection, emit _toTokenId with type(uint256).max
//event PermanentURI(string _value, uint256 indexed _id);

//ERC721Tradable - allow opensea proxy

contract ProductArtistToken is
    IProductArtistToken,
    ERC721ArtistProduct,
    ContractMetadata,
    ProductDataConnector,
    Multicall,
    Ownable,
    Royalty,
    Permissions
{
    using TWStrings for uint256;
    /*//////////////////////////////////////////////////////////////
                            Roles
    //////////////////////////////////////////////////////////////*/

    bytes32 public constant BURNING_ROLE = keccak256("BURNING_ROLE");
    bytes32 public constant MINTING_ROLE = keccak256("MINTING_ROLE");
    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
    bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");

    /*//////////////////////////////////////////////////////////////
                            Constructor
    //////////////////////////////////////////////////////////////*/

    constructor(
        address defaultAdmin_,
        string memory name_,
        string memory symbol_,
        uint256 productOffset_,
        address royaltyRecipient_,
        uint128 royaltyBps_
    ) ERC721ArtistProduct(name_, symbol_, productOffset_) {
        //sets the owner of the contract (important for OpenSea)
        _setupOwner(defaultAdmin_);
        //sets the admin of the contract to the owner
        _setupRole(DEFAULT_ADMIN_ROLE, defaultAdmin_);
        //enables transfers for all tokens
        _setupRole(TRANSFER_ROLE, address(0));
        _setupRole(MINTING_ROLE, address(0));
        //sets the admin for various roles
        _setRoleAdmin(BURNING_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(MINTING_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(TRANSFER_ROLE, DEFAULT_ADMIN_ROLE);
        //sets the default royalty setup
        _setupDefaultRoyaltyInfo(royaltyRecipient_, royaltyBps_);
    }

    /*//////////////////////////////////////////////////////////////
                            ERC165 Logic
    //////////////////////////////////////////////////////////////*/

    /// @dev See ERC165: https://eips.ethereum.org/EIPS/eip-165
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(ERC721ArtistProduct, IERC165) returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
            interfaceId == 0x49064906 || // ERC165 Interface ID for ERC4906 - MetadataUpdated
            interfaceId == type(IERC2981).interfaceId; // ERC165 ID for ERC2981 - Royalties
    }

    /*//////////////////////////////////////////////////////////////
                        Overriden ERC721 logic
    //////////////////////////////////////////////////////////////*/

    /**
     *  @notice         Returns the metadata URI for an NFT.
     *
     *  @param _tokenId The tokenId of an NFT.
     */
    function tokenURI(
        uint256 _tokenId
    ) public view override(ERC721ArtistProduct) returns (string memory) {
        if (!_exists(_tokenId)) revert URIQueryForNonexistentToken();

        uint256 productId = _getProductId(_tokenId);
        bool isUnique = _getProductUniqueMetadata(productId);
        return _tieredURI(_tokenId, productId, isUnique);
    }

    /*//////////////////////////////////////////////////////////////
                            Minting logic
    //////////////////////////////////////////////////////////////*/

    /**
     *  @notice          Lets an authorized address mint an NFT to a recipient.
     *
     *  @param _to          The recipient of the NFT to mint.
     *  @param _productId   The productId of the NFT to mint.
     */
    function mintTo(address _to, uint256 _productId) external onlyMinter {
        _safeMint(_to, _productId, 1, "");
    }

    /**
     *  @notice          Lets an authorized address mint an NFT to a recipient.
     *
     *  @param _to          The recipient of the NFT to mint.
     *  @param _productId   The productId of the NFT to mint.
     *  @param _data        Data to be passed along during minting.
     */
    function mintTo(
        address _to,
        uint256 _productId,
        bytes memory _data
    ) external onlyMinter {
        _safeMint(_to, _productId, 1, _data);
    }

    /**
     *  @notice          Lets an authorized address mint an NFT to a recipient.
     *
     *  @param _to          The recipient of the NFT to mint.
     *  @param _productId   The productId of the NFT to mint.
     *  @param _uri         The full metadata uri for the token being minted.
     *  @param _data        Data to be passed along during minting.
     */
    function mintTo(
        address _to,
        uint256 _productId,
        string calldata _uri,
        bytes memory _data
    ) external onlyMinter {
        (uint256 startTokenId, ) = _safeMint(_to, _productId, 1, _data);
        _setTokenUri(startTokenId, _uri);
    }

    /**
     *  @notice          Lets an authorized address mint multiple NFTs at once to a recipient.
     *
     *  @param _to          The recipient of the NFT to mint.
     *  @param _productId   The productId of the NFT to mint.
     *  @param _quantity    The number of NFTs to mint.
     */
    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity
    ) external onlyMinter {
        _safeMint(_to, _productId, _quantity);
    }

    /**
     *  @notice          Lets an authorized address mint multiple NFTs at once to a recipient.
     *
     *  @param _to          The recipient of the NFT to mint.
     *  @param _productId   The productId of the NFT to mint.
     *  @param _quantity    The number of NFTs to mint.
     *  @param _data        Data to be passed along during minting.
     */
    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity,
        bytes memory _data
    ) external onlyMinter {
        _safeMint(_to, _productId, _quantity, _data);
    }

    /**
     *  @notice          Lets an authorized address mint multiple NFTs at once to a recipient.
     *
     *  @param _to          The recipient of the NFT to mint.
     *  @param _productId   The productId of the NFT to mint.
     *  @param _quantity    The number of NFTs to mint.
     *  @param _uris        The custom token URIs for each NFT in the batch
     *  @param _data        Data to be passed along during minting.
     */
    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity,
        string[] calldata _uris,
        bytes memory _data
    ) external onlyMinter {
        (uint256 startTokenId, ) = _safeMint(_to, _productId, _quantity, _data);
        _setTokenUris(startTokenId, _quantity, _uris);
    }

    /**
     *  @notice         Lets an owner or approved operator burn the NFT of the given tokenId.
     *  @dev            ERC721A"s `_burn(uint256,bool)` internally checks for token approvals.
     *
     *  @param _tokenId The tokenId of the NFT to burn.
     */
    function burn(uint256 _tokenId) external {
        _burn(_tokenId, true);
    }

    /*//////////////////////////////////////////////////////////////
                        Public getters
    //////////////////////////////////////////////////////////////*/

    /// @notice The total minted tokens among all products
    function totalMinted() external view returns (uint256) {
        return _mintCounter;
    }

    /// @notice The total burned tokens among all products
    function totalBurned() external view returns (uint256) {
        return _burnCounter;
    }

    /// @notice The owner data for a given address
    function getOwnerData(address _owner) external view returns (AddressData memory) {
        return _getAddressData(_owner);
    }

    /// @notice The token data for a given token id
    function getTokenData(
        uint256 _tokenId
    ) external view returns (TokenOwnership memory) {
        return _ownershipOf(_tokenId);
    }

    /// @notice The product offset for token id formation
    function getProductOffset() external view returns (uint256) {
        return _getProductOffset();
    }

    /// @notice The product details for a given product id
    function getProduct(uint256 _productId) external view returns (ProductData memory) {
        return _getProduct(_productId);
    }

    /// @notice The product details for a given product id
    function getProductArtist(uint256 _productId) external view returns (address) {
        return _getArtist(_productId);
    }

    /// @notice The product details for a given token id
    function getProductForToken(
        uint256 _tokenId
    ) external view returns (ProductData memory) {
        return _getProduct(_getProductId(_tokenId));
    }

    /// @notice The product details for a given token id
    function getArtistForToken(uint256 _tokenId) external view returns (address) {
        return _getArtist(_getProductId(_tokenId));
    }

    /// @notice Returns whether a given address is the owner, or approved to transfer an NFT.
    function isApprovedOrOwner(
        address _operator,
        uint256 _tokenId
    ) external view returns (bool isApprovedOrOwnerOf) {
        address owner = ownerOf(_tokenId);
        isApprovedOrOwnerOf = (_operator == owner ||
            isApprovedForAll(owner, _operator) ||
            getApproved(_tokenId) == _operator);
    }

    /*//////////////////////////////////////////////////////////////
                        Admin setters
    //////////////////////////////////////////////////////////////*/

    function setProductURI(
        uint256 _productId,
        string calldata _uri
    ) external override onlyManager {
        _setProductUri(_productId, _uri);

        if (_getProductQuantity(_productId) != 0) {
            emit BatchMetadataUpdate(
                _getMinTokenId(_productId),
                _getMaxTokenId(_productId)
            );
        }
    }

    function setOwnerAux(address owner, uint64 aux) external onlyManager {
        _setAux(owner, aux);
    }

    function setProductIdOffset(uint256 _offset) external onlyManager {
        _setProductOffset(_offset);
    }

    function setProduct(uint256 _productId, uint64 _quantity) external onlyManager {
        _setProductQuantity(_productId, _quantity);
    }

    function setProductArtist(uint256 _productId, address _artist) external onlyManager {
        _setArtist(_productId, _artist);
    }

    function setProductUniqueMetadata(
        uint256 _productId,
        bool _isUnique
    ) external onlyManager {
        _setProductUniqueMetadata(_productId, _isUnique);
    }

    function restrictProductMinting(
        uint256 _productId,
        bool _toRestrict
    ) external onlyManager {
        _setProductLocked(_productId, _toRestrict);
    }

    function restrictProductTransfers(
        uint256 _productId,
        bool _toRestrict
    ) external onlyManager {
        _setProductFrozen(_productId, _toRestrict);
    }

    function restrictProductBurning(
        uint256 _productId,
        bool _toRestrict
    ) external onlyManager {
        _setProductBurnable(_productId, !_toRestrict);
    }

    function restrictMinting(bool _toRestrict) external onlyManager {
        if (_toRestrict) {
            _revokeRole(MINTING_ROLE, address(0));
        } else {
            _setupRole(MINTING_ROLE, address(0));
        }
        emit GlobalMintingRestricted(_toRestrict);
    }

    function restrictTransfers(bool _toRestrict) external onlyManager {
        if (_toRestrict) {
            _revokeRole(TRANSFER_ROLE, address(0));
        } else {
            _setupRole(TRANSFER_ROLE, address(0));
        }
        emit GlobalTransfersRestricted(_toRestrict);
    }

    function restrictBurning(bool _toRestrict) external onlyManager {
        if (_toRestrict) {
            _revokeRole(BURNING_ROLE, address(0));
        } else {
            _setupRole(BURNING_ROLE, address(0));
        }
        emit GlobalBurningRestricted(_toRestrict);
    }

    /*//////////////////////////////////////////////////////////////
                        ERC-721 overrides
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev See {ERC721A-_beforeTokenTransfers}.
     * @notice Product levels are restricted prior to hook
     *         This means even if specific users has a transfer or burn role
     *         The product level setting will override that functionality
     */
    function _beforeTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal override(ERC721ArtistProduct) {
        // Check transfer restrictions
        if (from != address(0) && to != address(0)) {
            //transfers restricted globally, unless from and to both have transfer role
            if (
                !hasRole(TRANSFER_ROLE, address(0)) &&
                !(hasRole(TRANSFER_ROLE, from) && hasRole(TRANSFER_ROLE, to))
            ) revert GlobalTransfersDisabled();
        }

        // Check burn restrictions
        if (from != address(0) && to == address(0)) {
            //burning restricted globally, unless from has burn permission
            if (!hasRoleWithSwitch(BURNING_ROLE, from)) revert GlobalBurningDisabled();
        }

        // Check mint restrictions
        if (from == address(0) && to != address(0)) {
            //minting restricted globally, regardless of operator roles
            if (!hasRole(MINTING_ROLE, address(0))) revert GlobalMintingDisabled();
        }

        _runBeforeExtensions(
            _getProductId(startTokenId),
            from,
            to,
            startTokenId,
            quantity
        );
    }

    /// @dev See {ERC721A-_afterTokenTransfers}.
    function _afterTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal override(ERC721ArtistProduct) {
        _runAfterExtensions(
            _getProductId(startTokenId),
            from,
            to,
            startTokenId,
            quantity
        );
    }

    /*//////////////////////////////////////////////////////////////
                            Modifiers
    //////////////////////////////////////////////////////////////*/

    /// @dev Modifier that checks if an account has admin or product role; reverts otherwise.
    modifier onlyManager() {
        _checkManagerAdmin();
        _;
    }

    /// @dev Modifier that checks if an account has admin or minter role; reverts otherwise.
    modifier onlyMinter() {
        _checkMintingAdmin();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                        Module admin checks
    //////////////////////////////////////////////////////////////*/

    /// @dev Function that checks if an account has admin or product role; reverts otherwise.
    function _checkManagerAdmin() internal view {
        if (
            !hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(MANAGER_ROLE, msg.sender)
        ) {
            revert ManagerRoleRequired();
        }
    }

    /// @dev Function that checks if an account has admin or minter role; reverts otherwise.
    function _checkMintingAdmin() internal view {
        if (
            !hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(MINTING_ROLE, msg.sender)
        ) {
            revert MintingRoleRequired();
        }
    }

    /// @dev Returns whether contract metadata can be set in the given execution context.
    function _canSetContractURI() internal view override returns (bool) {
        return
            hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(MANAGER_ROLE, msg.sender);
    }

    /// @dev Returns whether contract metadata can be set in the given execution context.
    function _canSetData() internal view override returns (bool) {
        return
            hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(MANAGER_ROLE, msg.sender);
    }

    /// @dev Returns whether owner can be set in the given execution context.
    function _canSetOwner() internal view override returns (bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    /// @dev Returns whether royalty info can be set in the given execution context.
    function _canSetRoyaltyInfo() internal view override returns (bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }
}

File 25 of 30 : ERC721ArtistProduct.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

/// @author Syky - Nathan Rempel

import "./interface/IERC721ArtistProduct.sol";

import "@thirdweb-dev/contracts/eip/ERC165.sol";
import "@thirdweb-dev/contracts/eip/interface/IERC721Receiver.sol";
import "@thirdweb-dev/contracts/external-deps/openzeppelin/utils/Context.sol";
import "@thirdweb-dev/contracts/lib/TWStrings.sol";
import "@thirdweb-dev/contracts/lib/TWAddress.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, along with ERC-1155 like functionality of "Product" with mint numbers.
 * Built to optimize for lower gas during batch mints.
 *
 * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 *
 * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
 */
contract ERC721ArtistProduct is Context, ERC165, IERC721ArtistProduct {
    using TWAddress for address;
    using TWStrings for uint256;

    // The product offset factor N to represent product
    // Example: offset of 6 means that token ids will be:
    // product 1 will have the token ids 1000001, 1000002, etc
    // product 69 will have the token ids 69000001, 69000002, etc
    // The offset provides 10 ** N - 1 possible supply

    //enables 9 assets to be minted per product
    uint256 constant PRODUCT_OFFSET_MIN = 1;
    //enables 1e19-1 assets to be minted, fitting uint64 max
    uint256 constant PRODUCT_OFFSET_MAX = 19;
    //stores the 10**N value to save math operations
    uint256 private _productOffset;

    // The number of tokens minted.
    uint256 internal _mintCounter;

    // The number of tokens burned.
    uint256 internal _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
    mapping(uint256 => TokenOwnership) internal _ownerships;

    //Mapping from product ID to product details
    mapping(uint256 => ProductData) private _productData;

    //Mapping from product ID to artist address
    mapping(uint256 => address) private _productArtist;

    //Mapping owner address to product balance
    mapping(address => mapping(uint256 => uint256)) private _productBalance;

    // Mapping owner address to address data
    mapping(address => AddressData) private _addressData;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    constructor(string memory name_, string memory symbol_, uint256 offset_) {
        _name = name_;
        _symbol = symbol_;
        _setProductOffset(offset_);
    }

    /**
     * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
     */
    function totalSupply() public view override returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than _mintCounter times
        unchecked {
            return _mintCounter - _burnCounter;
        }
    }

    /**
     * @dev Burned tokens are calculated here, use _getProductBurned() if you want to count just minted tokens.
     */
    function productSupply(uint256 productId) public view returns (uint256) {
        // Counter underflow is impossible as numberBurned cannot be incremented
        // more than numberMinted times
        ProductData memory product = _productData[productId];
        unchecked {
            return product.numberMinted - product.numberBurned;
        }
    }

    /**
     * Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view returns (uint256) {
        return _mintCounter;
    }

    /**
     * Returns the total amount of tokens burned in the contract.
     */
    function _totalBurned() internal view returns (uint256) {
        return _burnCounter;
    }

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function productBalanceOf(
        address owner,
        uint256 productId
    ) public view returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return _productBalance[owner][productId];
    }

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

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _getAddressData(address owner) internal view returns (AddressData memory) {
        return _addressData[owner];
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return uint256(_addressData[owner].numberMinted);
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return uint256(_addressData[owner].numberBurned);
    }

    /**
     * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return _addressData[owner].aux;
    }

    /**
     * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal {
        _addressData[owner].aux = aux;
    }

    /**
     * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getArtist(uint256 _productId) internal view returns (address) {
        return _productArtist[_productId];
    }

    /**
     * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setArtist(uint256 _productId, address _artist) internal {
        _productArtist[_productId] = _artist;
    }

    /**
     * Gets the product configuration
     */
    function _getProduct(uint256 _productId) internal view returns (ProductData memory) {
        return _productData[_productId];
    }

    /**
     * Gets the product offset
     */
    function _getProductOffset() internal view returns (uint256 _offset) {
        uint256 offset = _productOffset;
        while (offset % 10 == 0) {
            offset /= 10;
            ++_offset;
        }
    }

    /**
     * Sets the product offset if prior to minting
     */
    function _setProductOffset(uint256 _offset) internal {
        if (_mintCounter > 0) revert ProductOffsetAfterMinting();
        if (_offset < PRODUCT_OFFSET_MIN || _offset > PRODUCT_OFFSET_MAX) {
            revert ProductOffsetOutsideRange(
                _offset,
                PRODUCT_OFFSET_MIN,
                PRODUCT_OFFSET_MAX
            );
        }
        _productOffset = 10 ** _offset;
    }

    /**
     * Gets the maximum productId based on the product offset
     */
    function _getProductMaxId() internal view returns (uint256) {
        return type(uint256).max / _productOffset - _productOffset;
    }

    /**
     * Gets the maximum product supply based on the product offset
     */
    function _getProductMaxQuantity() internal view returns (uint64) {
        return uint64(_productOffset - 1);
    }

    /**
     * Gets the product token supply
     */
    function _getProductQuantity(uint256 _productId) internal view returns (uint64) {
        return _productData[_productId].quantity;
    }

    /**
     * Creates a product or modifies the supply
     */
    function _setProductQuantity(uint256 _productId, uint64 _quantity) internal {
        if (_getProductMaxId() < _productId)
            revert ProductIdExceedsOffsetMaximum(_productId, _getProductMaxId());
        if (_getProductMaxQuantity() < _quantity)
            revert ProductQuantityExceedsOffsetMaximum(
                _quantity,
                _getProductMaxQuantity()
            );

        ProductData storage product = _productData[_productId];
        uint64 oldQuantity = product.quantity;
        if (_quantity < product.numberMinted) revert ProductMintsExceedsQuantity();
        product.quantity = _quantity;
        emit ProductQuantityUpdated(_productId, oldQuantity, _quantity);
    }

    /**
     * Gets the product tokens minted
     */
    function _getProductMinted(uint256 _productId) internal view returns (uint64) {
        return _productData[_productId].numberMinted;
    }

    /**
     * Gets the product tokens burn
     */
    function _getProductBurned(uint256 _productId) internal view returns (uint64) {
        return _productData[_productId].numberBurned;
    }

    /**
     * Gets the product locked status (minting)
     */
    function _getProductLocked(uint256 _productId) internal view returns (bool) {
        return _productData[_productId].locked;
    }

    /**
     * Sets the locked condition for a product (minting)
     */
    function _setProductLocked(uint256 _productId, bool _locked) internal {
        ProductData storage product = _productData[_productId];
        if (product.quantity == 0) revert ProductNotConfigured();
        product.locked = _locked;
        emit ProductMintingRestricted(_productId, _locked);
    }

    /**
     * Gets the product frozen status (transfers)
     */
    function _getProductFrozen(uint256 _productId) internal view returns (bool) {
        return _productData[_productId].frozen;
    }

    /**
     * Sets the frozen condition for a product (transfers)
     */
    function _setProductFrozen(uint256 _productId, bool _frozen) internal {
        ProductData storage product = _productData[_productId];
        if (product.quantity == 0) revert ProductNotConfigured();
        product.frozen = _frozen;
        emit ProductTransfersRestricted(_productId, _frozen);
    }

    /**
     * Gets the product burnable status (burning)
     */
    function _getProductBurnable(uint256 _productId) internal view returns (bool) {
        return _productData[_productId].burnable;
    }

    /**
     * Sets the burnable condition for a product (burning)
     */
    function _setProductBurnable(uint256 _productId, bool _burnable) internal {
        ProductData storage product = _productData[_productId];
        if (product.quantity == 0) revert ProductNotConfigured();
        product.burnable = _burnable;
        emit ProductBurningRestricted(_productId, !_burnable);
    }

    /**
     * Gets the product burnable status (burning)
     */
    function _getProductUniqueMetadata(uint256 _productId) internal view returns (bool) {
        return _productData[_productId].uniqueMetadata;
    }

    /**
     * Sets the burnable condition for a product (burning)
     */
    function _setProductUniqueMetadata(uint256 _productId, bool _isUnique) internal {
        ProductData storage product = _productData[_productId];
        if (product.quantity == 0) revert ProductNotConfigured();
        product.uniqueMetadata = _isUnique;
        emit ProductUniqueTokenMetadata(_productId, _isUnique);
    }

    /**
     * Gets the product id for a token
     */
    function _getProductId(uint256 _tokenId) internal view returns (uint256 _productId) {
        if (_tokenId < _productOffset) revert ProductQueryForImpossibleToken();
        _productId = _tokenId / _productOffset;
        if (_productData[_productId].quantity == 0)
            revert ProductQueryForNonexistantToken();
    }

    /**
     * Gets the next token id for product given a quantity
     * Checks that range of tokens wont exceed supply
     */
    function _getNextTokenIds(
        uint256 _productId,
        uint256 _quantity
    ) internal view returns (uint256 startTokenId, uint256 finalTokenId) {
        ProductData memory product = _productData[_productId];
        if (product.quantity == 0) revert ProductNotConfigured();
        if (product.quantity < product.numberMinted + _quantity)
            revert ProductMintsExceedsQuantity();
        startTokenId = _productId * _productOffset + product.numberMinted + 1;
        finalTokenId = startTokenId + _quantity - 1;
    }

    /**
     * Gets the previously minted token id for a product
     */
    function _getPrevTokenId(uint256 _productId) internal view returns (uint256) {
        return _productId * _productOffset + _getProductMinted(_productId);
    }

    /**
     * Gets the first possible token id for a product
     */
    function _getMinTokenId(uint256 _productId) internal view returns (uint256) {
        return _productId * _productOffset + 1;
    }

    /**
     * Gets the last possible token id for a product
     */
    function _getMaxTokenId(uint256 _productId) internal view returns (uint256) {
        return _productId * _productOffset + _getProductQuantity(_productId) + 1;
    }

    /**
     * Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around in the collection over time.
     */
    function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
        uint256 curr = tokenId;
        //this will revert if the product is unconfigured
        uint256 productId = _getProductId(tokenId);
        uint256 minTokenId = _getMinTokenId(productId);

        unchecked {
            if (minTokenId <= curr)
                if (curr <= _getPrevTokenId(productId)) {
                    TokenOwnership memory ownership = _ownerships[curr];
                    if (!ownership.burned) {
                        if (ownership.addr != address(0)) {
                            return ownership;
                        }
                        // Invariant:
                        // There will always be an ownership that has an address and is not burned
                        // before an ownership that does not have an address and is not burned.
                        // Hence, curr will not underflow.
                        while (minTokenId < curr) {
                            curr--;
                            ownership = _ownerships[curr];
                            if (ownership.addr != address(0)) {
                                return ownership;
                            }
                        }
                    }
                }
        }
        revert OwnerQueryForNonexistentToken();
    }

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

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

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

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(
        uint256 tokenId
    ) public view virtual override returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

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

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

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721ArtistProduct.ownerOf(tokenId);
        if (to == owner) revert ApprovalToCurrentOwner();

        if (_msgSender() != owner)
            if (!isApprovedForAll(owner, _msgSender())) {
                revert ApprovalCallerNotOwnerNorApproved();
            }

        _approve(to, tokenId, owner);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view override returns (address) {
        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        if (operator == _msgSender()) revert ApproveToCaller();

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

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

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        _transfer(from, to, tokenId);
    }

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

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        _transfer(from, to, tokenId);
        if (to.isContract())
            if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                revert TransferToNonERC721ReceiverImplementer();
            }
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     */
    function _exists(uint256 tokenId) internal view returns (bool) {
        uint256 productId = _getProductId(tokenId);
        return
            _getMinTokenId(productId) <= tokenId &&
            tokenId <= _getPrevTokenId(productId) &&
            !_ownerships[tokenId].burned;
    }

    /**
     * @dev Equivalent to `_safeMint(to, productId, quantity, '')`.
     */
    function _safeMint(
        address to,
        uint256 productId,
        uint256 quantity
    ) internal returns (uint256 startTokenId, uint256 finalTokenId) {
        return _safeMint(to, productId, quantity, "");
    }

    /**
     * @dev Safely mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement
     *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
     * - `quantity` must be greater than 0.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(
        address to,
        uint256 productId,
        uint256 quantity,
        bytes memory _data
    ) internal returns (uint256 startTokenId, uint256 finalTokenId) {
        uint256 startCount = _mintCounter;

        if (to == address(0)) revert MintToZeroAddress();
        if (quantity == 0) revert MintZeroQuantity();
        if (_getProductLocked(productId)) revert ProductMintingDisabled();

        //this validates against that total minted wont exceed supply
        (startTokenId, finalTokenId) = _getNextTokenIds(productId, quantity);

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are incredibly unrealistic.
        // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
        // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
        unchecked {
            _addressData[to].balance += uint64(quantity);
            _addressData[to].numberMinted += uint64(quantity);

            _ownerships[startTokenId].addr = to;
            _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);

            _productBalance[to][productId] += quantity;

            uint256 currTokenId = startTokenId;

            //increment final token to save on comparison gas in the loops
            finalTokenId++;

            if (to.isContract()) {
                do {
                    if (_productArtist[productId] != address(0)) {
                        emit Transfer(address(0), _productArtist[productId], currTokenId);
                        emit Transfer(_productArtist[productId], to, currTokenId);
                    } else {
                        emit Transfer(address(0), to, currTokenId);
                    }
                    if (
                        !_checkContractOnERC721Received(
                            address(0),
                            to,
                            currTokenId++,
                            _data
                        )
                    ) {
                        revert TransferToNonERC721ReceiverImplementer();
                    }
                } while (currTokenId < finalTokenId);
                // Reentrancy protection
                if (_mintCounter != startCount) revert();
            } else {
                do {
                    if (_productArtist[productId] != address(0)) {
                        emit Transfer(address(0), _productArtist[productId], currTokenId);
                        emit Transfer(_productArtist[productId], to, currTokenId);
                    } else {
                        emit Transfer(address(0), to, currTokenId);
                    }
                    ++currTokenId;
                } while (currTokenId < finalTokenId);
            }

            _mintCounter += quantity;
            _productData[productId].numberMinted += uint64(quantity);
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {Transfer} event.
     */
    function _mint(
        address to,
        uint256 productId,
        uint256 quantity
    ) internal returns (uint256 startTokenId, uint256 finalTokenId) {
        if (to == address(0)) revert MintToZeroAddress();
        if (quantity == 0) revert MintZeroQuantity();
        if (_getProductLocked(productId)) revert ProductMintingDisabled();

        //this validates against that total minted wont exceed supply
        (startTokenId, finalTokenId) = _getNextTokenIds(productId, quantity);

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are incredibly unrealistic.
        // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
        // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
        unchecked {
            _addressData[to].balance += uint64(quantity);
            _addressData[to].numberMinted += uint64(quantity);

            _ownerships[startTokenId].addr = to;
            _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);

            _productBalance[to][productId] += quantity;

            uint256 currTokenId = startTokenId;

            //increment final token to save on comparison gas in the loops
            finalTokenId++;

            do {
                if (_productArtist[productId] != address(0)) {
                    emit Transfer(address(0), _productArtist[productId], currTokenId);
                    emit Transfer(_productArtist[productId], to, currTokenId);
                } else {
                    emit Transfer(address(0), to, currTokenId);
                }
                ++currTokenId;
            } while (currTokenId < finalTokenId);

            _mintCounter += quantity;
            _productData[productId].numberMinted += uint64(quantity);
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) private {
        TokenOwnership memory prevOwnership = _ownershipOf(tokenId);

        if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();

        bool isApprovedOrOwner = (_msgSender() == from ||
            isApprovedForAll(from, _msgSender()) ||
            getApproved(tokenId) == _msgSender());

        if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
        if (to == address(0)) revert TransferToZeroAddress();

        uint256 productId = _getProductId(tokenId);
        if (_getProductFrozen(productId)) revert ProductTransfersDisabled();

        _beforeTokenTransfers(from, to, tokenId, 1);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId, from);

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
        unchecked {
            _addressData[from].balance -= 1;
            _addressData[to].balance += 1;

            _productBalance[from][productId] -= 1;
            _productBalance[to][productId] += 1;

            TokenOwnership storage currSlot = _ownerships[tokenId];
            currSlot.addr = to;
            currSlot.startTimestamp = uint64(block.timestamp);

            // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
            // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.

            TokenOwnership storage nextSlot = _ownerships[tokenId + 1];
            if (nextSlot.addr == address(0)) {
                // This will suffice for checking _exists(nextTokenId),
                // as a burned slot cannot contain the zero address.
                if (tokenId != _getPrevTokenId(productId)) {
                    // This confirms the current token is not the last token in the product
                    nextSlot.addr = from;
                    nextSlot.startTimestamp = prevOwnership.startTimestamp;
                }
            }
        }

        emit Transfer(from, to, tokenId);
        _afterTokenTransfers(from, to, tokenId, 1);
    }

    /**
     * @dev Equivalent to `_burn(tokenId, false)`.
     */
    function _burn(uint256 tokenId) internal virtual {
        _burn(tokenId, false);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
        TokenOwnership memory prevOwnership = _ownershipOf(tokenId);

        address from = prevOwnership.addr;

        if (approvalCheck) {
            bool isApprovedOrOwner = (_msgSender() == from ||
                isApprovedForAll(from, _msgSender()) ||
                getApproved(tokenId) == _msgSender());

            if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
        }

        uint256 productId = _getProductId(tokenId);
        if (!_getProductBurnable(productId)) revert ProductBurningDisabled();

        _beforeTokenTransfers(from, address(0), tokenId, 1);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId, from);

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
        unchecked {
            AddressData storage addressData = _addressData[from];
            addressData.balance -= 1;
            addressData.numberBurned += 1;

            // Keep track of who burned the token, and the timestamp of burning.
            TokenOwnership storage currSlot = _ownerships[tokenId];
            currSlot.addr = from;
            currSlot.startTimestamp = uint64(block.timestamp);
            currSlot.burned = true;

            _productBalance[from][productId] -= 1;

            // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
            // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
            TokenOwnership storage nextSlot = _ownerships[tokenId + 1];
            if (nextSlot.addr == address(0)) {
                // This will suffice for checking _exists(nextTokenId),
                // as a burned slot cannot contain the zero address.
                if (tokenId != _getPrevTokenId(productId)) {
                    // This confirms the current token is not the last token in the product
                    nextSlot.addr = from;
                    nextSlot.startTimestamp = prevOwnership.startTimestamp;
                }
            }
        }

        emit Transfer(from, address(0), tokenId);
        _afterTokenTransfers(from, address(0), tokenId, 1);

        // Overflow not possible, as _burnCounter cannot be exceed _mintCounter times.
        unchecked {
            _burnCounter++;
            _productData[productId].numberBurned++;
        }
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId, address owner) private {
        _tokenApprovals[tokenId] = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkContractOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        try
            IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data)
        returns (bytes4 retval) {
            return retval == IERC721Receiver(to).onERC721Received.selector;
        } catch (bytes memory reason) {
            if (reason.length == 0) {
                revert TransferToNonERC721ReceiverImplementer();
            } else {
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    }

    /**
     * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
     * And also called before burning one token.
     *
     * startTokenId - the first token id to be transferred
     * quantity - the amount to be transferred
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _beforeTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
     * minting.
     * And also called after one token has been burned.
     *
     * startTokenId - the first token id to be transferred
     * quantity - the amount to be transferred
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
     * transferred to `to`.
     * - When `from` is zero, `tokenId` has been minted for `to`.
     * - When `to` is zero, `tokenId` has been burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _afterTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}
}

File 26 of 30 : IERC4906.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

import "@thirdweb-dev/contracts/eip/interface/IERC165.sol";
import "@thirdweb-dev/contracts/eip/interface/IERC721.sol";

/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
    /// @dev This event emits when the metadata of a token is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFT.
    event MetadataUpdate(uint256 _tokenId);

    /// @dev This event emits when the metadata of a range of tokens is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFTs.
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}

File 27 of 30 : IERC721ArtistProduct.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

import "@thirdweb-dev/contracts/eip/interface/IERC721A.sol";

interface IERC721ArtistProduct is IERC721A {
    /*//////////////////////////////////////////////////////////////
                        Structs
    //////////////////////////////////////////////////////////////*/

    // Compiler will pack this into a single 256bit word.
    struct ProductData {
        // Keeps track of quantity, Realistically, 2**64-1 is more than enough.
        uint64 quantity;
        // Keeps track of mint count with minimal overhead for tokenomics.
        uint64 numberMinted;
        // Keeps track of burn count with minimal overhead for tokenomics.
        uint64 numberBurned;
        //Toggles if mints are disabled for this product
        //Defaults to enabled (locked = false)
        bool locked;
        //Toggles if transfers are disabled for this product
        //Defaults to enabled (frozen = false)
        bool frozen;
        //Toggles if burns are enabled for this product
        //Defaults to disabled (burnable = false)
        bool burnable;
        //Toggles if tokens within the product have unique metadata
        //Defaults to non-unique (uniqueMetadata = false)
        bool uniqueMetadata;
    }

    /*//////////////////////////////////////////////////////////////
                        Methods
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Returns the total amount of tokens stored by the contract product.
     *
     * Burned tokens are calculated here, use `_getProductMinted()` if you want to count just minted tokens.
     */
    function productSupply(uint256 productId) external view returns (uint256);

    function productBalanceOf(
        address owner,
        uint256 productId
    ) external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        Events
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when a product is created or has its supply modified
    event ProductQuantityUpdated(
        uint256 indexed productId,
        uint64 indexed oldQuantity,
        uint64 indexed newQuantity
    );

    /// @dev Emitted when specific product mints are enabled or disabled
    event ProductMintingRestricted(uint256 indexed productId, bool indexed isRestricted);

    /// @dev Emitted when specific product transfers are enabled or disabled
    event ProductTransfersRestricted(
        uint256 indexed productId,
        bool indexed isRestricted
    );

    /// @dev Emitted when specific product burns are enabled or disabled
    event ProductBurningRestricted(uint256 indexed productId, bool indexed isRestricted);

    /// @dev Emitted when specific products toggle unique token metadata within product
    event ProductUniqueTokenMetadata(
        uint256 indexed productId,
        bool indexed isUniquePerToken
    );

    /*//////////////////////////////////////////////////////////////
                        Errors
    //////////////////////////////////////////////////////////////*/

    /// @dev The product scale must be set prior to minting.
    error ProductOffsetAfterMinting();

    /// @dev The product scale must be set prior to minting.
    error ProductOffsetOutsideRange(uint256 requested, uint256 minumum, uint256 maximum);

    /// @dev The productId must not exceed the maximum for the scale.
    error ProductIdExceedsOffsetMaximum(uint256 requested, uint256 maximum);

    /// @dev The product quantity must not exceed the maximum for the scale.
    error ProductQuantityExceedsOffsetMaximum(uint256 requested, uint256 maximum);

    /// @dev The product quantity must equal or exceed the number minted.
    error ProductMintsExceedsQuantity();

    /// @dev The product must be configured by setting a quantity.
    error ProductNotConfigured();

    /// @dev The token ID is impossible given the product scale.
    error ProductQueryForImpossibleToken();

    /// @dev The token ID cannot exist given configured product
    error ProductQueryForNonexistantToken();

    /// @dev Minting is disabled for this product
    error ProductMintingDisabled();

    /// @dev Burning are disabled for this product
    error ProductBurningDisabled();

    /// @dev Transfers are disabled for this product
    error ProductTransfersDisabled();
}

File 28 of 30 : IProductCreators.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

interface IProductCreators {
    /*//////////////////////////////////////////////////////////////
                        Structs
    //////////////////////////////////////////////////////////////*/

    struct ProductCreatorData {
        uint256 index;
        mapping(uint256 => CreatorData) creators;
    }

    struct CreatorData {
        address creatorAddress;
        string creatorInfo;
    }

    /*//////////////////////////////////////////////////////////////
                        Methods
    //////////////////////////////////////////////////////////////*/

    /// @dev Returns the base URI of the contract.
    function getProductCreators(
        uint256 _productId
    ) external view returns (CreatorData[] memory);

    /**
     *  @dev Sets base URI for the token metadata of the contract.
     *       Only module admin can call this function.
     */
    function setProductCreators(
        uint256 _productId,
        CreatorData[] calldata _creators
    ) external;

    /*//////////////////////////////////////////////////////////////
                        Events
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when the base URI is updated.
    event ProductCreatorsUpdated(uint256 indexed productId, CreatorData[] creators);

    /*//////////////////////////////////////////////////////////////
                        Errors
    //////////////////////////////////////////////////////////////*/

    /// @dev Module admin is required
    error ProductCreatorNotAuthorized();
}

File 29 of 30 : IProductDataConnector.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

import "./IProductCreators.sol";
import "../../eip/interface/IERC4906.sol";

interface IProductDataConnector is IERC4906 {
    /*//////////////////////////////////////////////////////////////
                        Methods
    //////////////////////////////////////////////////////////////*/

    function setDataContract(address dataContract_) external;

    function getDataContract() external view returns (address);

    function getProductCreators(
        uint256 _productId
    ) external view returns (IProductCreators.CreatorData[] memory);

    function setBaseURI(string calldata _uri) external;

    function setTokenURI(uint256 _tokenId, string calldata _uri) external;

    function setTokenURIs(
        uint256 _tokenId,
        uint256 _quantity,
        string[] calldata _uris
    ) external;

    function setTokenURIs(uint256[] calldata _tokenIds, string[] calldata _uris) external;

    function setProductURI(uint256 _productId, string calldata _uri) external;

    /// @dev Emitted when the base URI is updated.
    event BaseURIUpdated(string prevURI, string newURI);

    /// @dev Emitted when the token URI is updated.
    event TokenURIUpdated(uint256 indexed tokenId, string prevURI, string newURI);

    /// @dev Emitted when the product URI is updated.
    event ProductURIUpdated(uint256 indexed tokenId, string prevURI, string newURI);

    error ProductDataContractNotConfigured();

    error ProductDataNotAuthorized();
}

File 30 of 30 : ProductDataConnector.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

import "./interface/IProductDataConnector.sol";
import "../base/interface/IProductDataContract.sol";

abstract contract ProductDataConnector is IProductDataConnector {
    address private _dataContract;

    /*//////////////////////////////////////////////////////////////
                        Public getters
    //////////////////////////////////////////////////////////////*/

    function getDataContract() external view virtual returns (address) {
        return _dataContract;
    }

    function getProductCreators(
        uint256 _productId
    ) external view virtual returns (IProductCreators.CreatorData[] memory) {
        if (_dataContract == address(0)) return new IProductCreators.CreatorData[](0);
        return IProductDataContract(_dataContract).getProductCreators(_productId);
    }

    /*//////////////////////////////////////////////////////////////
                        Admin setters
    //////////////////////////////////////////////////////////////*/

    function setDataContract(address dataContract_) external virtual onlyData {
        _dataContract = dataContract_;
    }

    function setBaseURI(string calldata _uri) external virtual onlyData {
        _setBaseUri(_uri);
    }

    function setTokenURI(
        uint256 _tokenId,
        string calldata _uri
    ) external virtual onlyData {
        _setTokenUri(_tokenId, _uri);
    }

    function setTokenURIs(
        uint256 _tokenId,
        uint256 _quantity,
        string[] calldata _uris
    ) external virtual onlyData {
        _setTokenUris(_tokenId, _quantity, _uris);
    }

    function setTokenURIs(
        uint256[] calldata _tokenIds,
        string[] calldata _uris
    ) external virtual onlyData {
        _setTokenUris(_tokenIds, _uris);
    }

    function setProductURI(
        uint256 _productId,
        string calldata _uri
    ) external virtual onlyData {
        _setProductUri(_productId, _uri);
    }

    /*//////////////////////////////////////////////////////////////
                            Modifiers
    //////////////////////////////////////////////////////////////*/

    /// @dev Modifier that checks if the data contract has been configured
    modifier onlyConfigured() {
        _notConfigured();
        _;
    }

    /// @dev Modifier that checks module admin permissions
    modifier onlyData() {
        _checkAuth();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    function _checkAuth() internal virtual {
        if (!_canSetData()) revert ProductDataNotAuthorized();
    }

    /// @dev Returns whether product data can be set in the given execution context.
    function _canSetData() internal view virtual returns (bool);

    function _notConfigured() internal virtual {
        if (_dataContract == address(0)) revert ProductDataContractNotConfigured();
    }

    function _tieredURI(
        uint256 _tokenId,
        uint256 _productId,
        bool _isUnique
    ) internal view returns (string memory) {
        if (_dataContract == address(0)) return "";
        return
            IProductDataContract(_dataContract).tieredURI(
                _tokenId,
                _productId,
                _isUnique
            );
    }

    function _setBaseUri(string memory _uri) internal onlyConfigured {
        string memory prevURI = IProductDataContract(_dataContract).setBaseURI(_uri);
        emit BaseURIUpdated(prevURI, _uri);
    }

    function _setTokenUri(uint256 _tokenId, string memory _uri) internal onlyConfigured {
        string memory prevURI = IProductDataContract(_dataContract).setTokenURI(
            _tokenId,
            _uri
        );
        emit TokenURIUpdated(_tokenId, prevURI, _uri);
        emit MetadataUpdate(_tokenId);
    }

    function _setTokenUris(
        uint256 _tokenId,
        uint256 _quantity,
        string[] memory _uris
    ) internal onlyConfigured {
        string[] memory _prevURIs = IProductDataContract(_dataContract).setTokenURIs(
            _tokenId,
            _quantity,
            _uris
        );

        unchecked {
            for (uint i; i < _quantity; ) {
                emit TokenURIUpdated(_tokenId + i, _prevURIs[i], _uris[i]);
                ++i;
            }
            emit BatchMetadataUpdate(_tokenId, _tokenId + _quantity - 1);
        }
    }

    function _setTokenUris(
        uint256[] memory _tokenIds,
        string[] memory _uris
    ) internal onlyConfigured {
        string[] memory _prevURIs = IProductDataContract(_dataContract).setTokenURIs(
            _tokenIds,
            _uris
        );

        uint256 length = _prevURIs.length;
        for (uint i; i < length; ) {
            emit TokenURIUpdated(_tokenIds[i], _prevURIs[i], _uris[i]);
            emit MetadataUpdate(_tokenIds[i]);
            unchecked {
                ++i;
            }
        }
    }

    function _setProductUri(
        uint256 _productId,
        string memory _uri
    ) internal onlyConfigured {
        string memory prevURI = IProductDataContract(_dataContract).setProductURI(
            _productId,
            _uri
        );
        emit ProductURIUpdated(_productId, prevURI, _uri);
    }

    function _runBeforeExtensions(
        uint256 _productId,
        address _from,
        address _to,
        uint256 _startTokenId,
        uint256 _quantity
    ) internal {
        if (_dataContract == address(0)) return;
        IProductDataContract(_dataContract).runBeforeExtensions(
            _productId,
            _from,
            _to,
            _startTokenId,
            _quantity
        );
    }

    function _runAfterExtensions(
        uint256 _productId,
        address _from,
        address _to,
        uint256 _startTokenId,
        uint256 _quantity
    ) internal {
        if (_dataContract == address(0)) return;
        IProductDataContract(_dataContract).runAfterExtensions(
            _productId,
            _from,
            _to,
            _startTokenId,
            _quantity
        );
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"defaultAdmin_","type":"address"},{"internalType":"address","name":"royaltyRecipient_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BatchMintURICountMismatch","type":"error"},{"inputs":[],"name":"GlobalBurningDisabled","type":"error"},{"inputs":[],"name":"GlobalMintingDisabled","type":"error"},{"inputs":[],"name":"GlobalTransfersDisabled","type":"error"},{"inputs":[],"name":"ManagerRoleRequired","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"MintingRoleRequired","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ProductBurningDisabled","type":"error"},{"inputs":[],"name":"ProductDataContractNotConfigured","type":"error"},{"inputs":[],"name":"ProductDataNotAuthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"name":"ProductIdExceedsOffsetMaximum","type":"error"},{"inputs":[],"name":"ProductMintingDisabled","type":"error"},{"inputs":[],"name":"ProductMintsExceedsQuantity","type":"error"},{"inputs":[],"name":"ProductNotConfigured","type":"error"},{"inputs":[],"name":"ProductOffsetAfterMinting","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"minumum","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"name":"ProductOffsetOutsideRange","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"name":"ProductQuantityExceedsOffsetMaximum","type":"error"},{"inputs":[],"name":"ProductQueryForImpossibleToken","type":"error"},{"inputs":[],"name":"ProductQueryForNonexistantToken","type":"error"},{"inputs":[],"name":"ProductTransfersDisabled","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"prevURI","type":"string"},{"indexed":false,"internalType":"string","name":"newURI","type":"string"}],"name":"BaseURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"prevURI","type":"string"},{"indexed":false,"internalType":"string","name":"newURI","type":"string"}],"name":"ContractURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newRoyaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"newRoyaltyBps","type":"uint256"}],"name":"DefaultRoyalty","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"restricted","type":"bool"}],"name":"GlobalBurningRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"restricted","type":"bool"}],"name":"GlobalMintingRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"restricted","type":"bool"}],"name":"GlobalTransfersRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prevOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":true,"internalType":"bool","name":"isRestricted","type":"bool"}],"name":"ProductBurningRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":true,"internalType":"bool","name":"isRestricted","type":"bool"}],"name":"ProductMintingRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"oldQuantity","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"newQuantity","type":"uint64"}],"name":"ProductQuantityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":true,"internalType":"bool","name":"isRestricted","type":"bool"}],"name":"ProductTransfersRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"prevURI","type":"string"},{"indexed":false,"internalType":"string","name":"newURI","type":"string"}],"name":"ProductURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"productId","type":"uint256"},{"indexed":true,"internalType":"bool","name":"isUniquePerToken","type":"bool"}],"name":"ProductUniqueTokenMetadata","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"royaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"royaltyBps","type":"uint256"}],"name":"RoyaltyForToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"prevURI","type":"string"},{"indexed":false,"internalType":"string","name":"newURI","type":"string"}],"name":"TokenURIUpdated","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":"BURNING_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ENV","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTING_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VER","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint256","name":"_quantity","type":"uint256"}],"name":"batchMintTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint256","name":"_quantity","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"batchMintTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint256","name":"_quantity","type":"uint256"},{"internalType":"string[]","name":"_uris","type":"string[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"batchMintTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getArtistForToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDataContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDefaultRoyaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getOwnerData","outputs":[{"components":[{"internalType":"uint64","name":"balance","type":"uint64"},{"internalType":"uint64","name":"numberMinted","type":"uint64"},{"internalType":"uint64","name":"numberBurned","type":"uint64"},{"internalType":"uint64","name":"aux","type":"uint64"}],"internalType":"struct IERC721A.AddressData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"}],"name":"getProduct","outputs":[{"components":[{"internalType":"uint64","name":"quantity","type":"uint64"},{"internalType":"uint64","name":"numberMinted","type":"uint64"},{"internalType":"uint64","name":"numberBurned","type":"uint64"},{"internalType":"bool","name":"locked","type":"bool"},{"internalType":"bool","name":"frozen","type":"bool"},{"internalType":"bool","name":"burnable","type":"bool"},{"internalType":"bool","name":"uniqueMetadata","type":"bool"}],"internalType":"struct IERC721ArtistProduct.ProductData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"}],"name":"getProductArtist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"}],"name":"getProductCreators","outputs":[{"components":[{"internalType":"address","name":"creatorAddress","type":"address"},{"internalType":"string","name":"creatorInfo","type":"string"}],"internalType":"struct IProductCreators.CreatorData[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getProductForToken","outputs":[{"components":[{"internalType":"uint64","name":"quantity","type":"uint64"},{"internalType":"uint64","name":"numberMinted","type":"uint64"},{"internalType":"uint64","name":"numberBurned","type":"uint64"},{"internalType":"bool","name":"locked","type":"bool"},{"internalType":"bool","name":"frozen","type":"bool"},{"internalType":"bool","name":"burnable","type":"bool"},{"internalType":"bool","name":"uniqueMetadata","type":"bool"}],"internalType":"struct IERC721ArtistProduct.ProductData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProductOffset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getRoyaltyInfoForToken","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getTokenData","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"}],"internalType":"struct IERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRoleWithSwitch","outputs":[{"internalType":"bool","name":"","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":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"isApprovedOrOwnerOf","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"}],"name":"mintTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"string","name":"_uri","type":"string"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"mintTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"mintTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"productId","type":"uint256"}],"name":"productBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"productId","type":"uint256"}],"name":"productSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_toRestrict","type":"bool"}],"name":"restrictBurning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_toRestrict","type":"bool"}],"name":"restrictMinting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"bool","name":"_toRestrict","type":"bool"}],"name":"restrictProductBurning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"bool","name":"_toRestrict","type":"bool"}],"name":"restrictProductMinting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"bool","name":"_toRestrict","type":"bool"}],"name":"restrictProductTransfers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_toRestrict","type":"bool"}],"name":"restrictTransfers","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":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setContractURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"dataContract_","type":"address"}],"name":"setDataContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_royaltyRecipient","type":"address"},{"internalType":"uint256","name":"_royaltyBps","type":"uint256"}],"name":"setDefaultRoyaltyInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint64","name":"aux","type":"uint64"}],"name":"setOwnerAux","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint64","name":"_quantity","type":"uint64"}],"name":"setProduct","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"address","name":"_artist","type":"address"}],"name":"setProductArtist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_offset","type":"uint256"}],"name":"setProductIdOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"string","name":"_uri","type":"string"}],"name":"setProductURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_productId","type":"uint256"},{"internalType":"bool","name":"_isUnique","type":"bool"}],"name":"setProductUniqueMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_bps","type":"uint256"}],"name":"setRoyaltyInfoForToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"string","name":"_uri","type":"string"}],"name":"setTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"string[]","name":"_uris","type":"string[]"}],"name":"setTokenURIs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_quantity","type":"uint256"},{"internalType":"string[]","name":"_uris","type":"string[]"}],"name":"setTokenURIs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBurned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162005926380380620059268339810160408190526200003491620003fc565b60408051808201825260078082526653594b59474f4d60c81b602080840182905284518086019095529184529083015283916006846103e884848460036200007d8482620004d9565b5060046200008c8382620004d9565b506200009881620001c8565b505050620000ac866200024260201b60201c565b620000b960008762000294565b620000e67f8502233096d909befbda0999bb8ea2f3a6be3c138b9fbf003752a4c8bce86f6c600062000294565b6200010260008051602062005906833981519152600062000294565b6200012f7f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b6000620002ef565b6200014b600080516020620059068339815191526000620002ef565b620001787f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b086000620002ef565b620001a57f8502233096d909befbda0999bb8ea2f3a6be3c138b9fbf003752a4c8bce86f6c6000620002ef565b620001ba826001600160801b03831662000337565b5050505050505050620006cf565b60015415620001ea576040516346464cc560e11b815260040160405180910390fd5b6001811080620001fa5750601381115b156200022f57604051637043fcb160e11b81526004810182905260016024820152601360448201526064015b60405180910390fd5b6200023c81600a620006ba565b60005550565b600e80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a35050565b60008281526011602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b600082815260126020526040808220805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6127108111156200037d5760405162461bcd60e51b815260206004820152600f60248201526e45786365656473206d61782062707360881b604482015260640162000226565b600f80546001600160a01b0384166001600160b01b03199091168117600160a01b61ffff851602179091556040518281527f90d7ec04bcb8978719414f82e52e4cb651db41d0e6f8cea6118c2191e6183adb9060200160405180910390a25050565b80516001600160a01b0381168114620003f757600080fd5b919050565b600080604083850312156200041057600080fd5b6200041b83620003df565b91506200042b60208401620003df565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200045f57607f821691505b6020821081036200048057634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620004d457600081815260208120601f850160051c81016020861015620004af5750805b601f850160051c820191505b81811015620004d057828155600101620004bb565b5050505b505050565b81516001600160401b03811115620004f557620004f562000434565b6200050d816200050684546200044a565b8462000486565b602080601f8311600181146200054557600084156200052c5750858301515b600019600386901b1c1916600185901b178555620004d0565b600085815260208120601f198616915b82811015620005765788860151825594840194600190910190840162000555565b5085821015620005955787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620005fc578160001904821115620005e057620005e0620005a5565b80851615620005ee57918102915b93841c9390800290620005c0565b509250929050565b6000826200061557506001620006b4565b816200062457506000620006b4565b81600181146200063d5760028114620006485762000668565b6001915050620006b4565b60ff8411156200065c576200065c620005a5565b50506001821b620006b4565b5060208310610133831016604e8410600b84101617156200068d575081810a620006b4565b620006998383620005bb565b8060001904821115620006b057620006b0620005a5565b0290505b92915050565b6000620006c8838362000604565b9392505050565b61522780620006df6000396000f3fe608060405234801561001057600080fd5b50600436106104495760003560e01c806382a819aa11610241578063c24c41c31161013b578063db3e4c84116100c3578063e8a3d48511610087578063e8a3d48514610b0e578063e985e9c514610b16578063ec87621c14610b52578063ef8593e714610b79578063f642ef5b14610c1d57600080fd5b8063db3e4c8414610a6c578063dfdbb20214610a7f578063e2a0c07814610a92578063e6b962a914610aa5578063e6ff9f9d14610afb57600080fd5b8063d52eff531161010a578063d52eff5314610a0b578063d547741f14610a1e578063d666c4f314610a31578063d6b465ec14610a44578063d89135cd14610a6457600080fd5b8063c24c41c3146109bf578063c4766a05146109d2578063c87b56dd146109e5578063d403bf86146109f857600080fd5b8063a22cb465116101c9578063ac9650d81161018d578063ac9650d814610912578063b09afec114610932578063b24f2d391461097b578063b88d4fde14610999578063b9db15b4146109ac57600080fd5b8063a22cb465146108c9578063a2309ff8146108dc578063a32fa5b3146108e4578063a34563b2146108f7578063a44bbf08146108ff57600080fd5b806395d89b411161021057806395d89b41146108805780639b639da7146108885780639bcf7a151461089b5780639fc1b85f146108ae578063a217fddf146108c157600080fd5b806382a819aa146108365780638da5cb5b1461084957806391d148541461085a578063938e3d7b1461086d57600080fd5b806330fa1cc3116103525780634cc157df116102da5780636d12a0531161029e5780636d12a053146107c457806370a08231146107d757806373fa563f146107ea57806375380153146107fd5780637c6285a11461081057600080fd5b80634cc157df1461072f57806355f804b314610764578063600dd5ea146107775780636352211e1461078a578063658e9a781461079d57600080fd5b80634231144e116103215780634231144e146106d057806342842e0e146106e357806342966c68146106f6578063430c208114610709578063449a52f81461071c57600080fd5b806330fa1cc31461060457806336568abe1461068457806338d44a1b146106975780633c4de40f146106ac57600080fd5b806318fb4427116103d557806324e0fbaa116103a457806324e0fbaa146105885780632a55205a146105995780632a9b503d146105cb5780632c7ef077146105de5780632f2ff15d146105f157600080fd5b806318fb44271461052d578063206b60f91461054057806323b872dd14610555578063248a9ca31461056857600080fd5b8063095ea7b31161041c578063095ea7b3146104cb57806313af4035146104de578063162094c4146104f157806316f0fe611461050457806318160ddd1461051757600080fd5b806301ffc9a71461044e57806303959bb71461047657806306fdde031461048b578063081812fc146104a0575b600080fd5b61046161045c366004614075565b610c30565b60405190151581526020015b60405180910390f35b6104896104843660046140a7565b610cb8565b005b610493610ce2565b60405161046d9190614114565b6104b36104ae366004614127565b610d74565b6040516001600160a01b03909116815260200161046d565b6104896104d9366004614140565b610db8565b6104896104ec3660046140a7565b610e3e565b6104896104ff3660046141b4565b610e94565b61048961051236600461420f565b610edc565b600254600154035b60405190815260200161046d565b61048961053b36600461422a565b610f7e565b61051f60008051602061516b83398151915281565b61048961056336600461425a565b610fb5565b61051f610576366004614127565b60009081526012602052604090205490565b600d546001600160a01b03166104b3565b6105ac6105a736600461429b565b610fc0565b604080516001600160a01b03909316835260208301919091520161046d565b6104896105d93660046142bd565b610ffd565b6104896105ec3660046142e9565b61100f565b6104896105ff36600461422a565b611029565b610617610612366004614127565b6110bf565b60405161046d9190600060e0820190506001600160401b038084511683528060208501511660208401528060408501511660408401525060608301511515606083015260808301511515608083015260a0830151151560a083015260c0830151151560c083015292915050565b61048961069236600461422a565b611109565b61051f60008051602061518b83398151915281565b610493604051806040016040528060058152602001640312e302e360dc1b81525081565b6104896106de366004614409565b61116b565b6104896106f136600461425a565b611187565b610489610704366004614127565b6111a2565b610461610717366004614140565b6111ad565b61048961072a366004614140565b61122c565b61074261073d366004614127565b611256565b604080516001600160a01b03909316835261ffff90911660208301520161046d565b61048961077236600461446b565b6112c1565b610489610785366004614140565b611308565b6104b3610798366004614127565b611357565b61051f7f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b81565b61051f6107d2366004614140565b611369565b61051f6107e53660046140a7565b6113bb565b6104896107f836600461420f565b611409565b61048961080b366004614127565b611480565b610493604051806040016040528060078152602001661350525393915560ca1b81525081565b6104b3610844366004614127565b611491565b600e546001600160a01b03166104b3565b61046161086836600461422a565b6114ba565b61048961087b3660046144ac565b6114e5565b610493611533565b6104896108963660046142bd565b611542565b6104896108a93660046144e0565b611554565b6104b36108bc366004614127565b6115a4565b61051f600081565b6104896108d7366004614507565b6115c1565b60015461051f565b6104616108f236600461422a565b611656565b61051f6116ac565b61048961090d36600461454a565b6116bb565b6109256109203660046145b1565b6116cd565b60405161046d91906145e6565b610945610940366004614127565b6117c1565b6040805182516001600160a01b031681526020808401516001600160401b0316908201529181015115159082015260600161046d565b600f546001600160a01b03811690600160a01b900461ffff16610742565b6104896109a7366004614648565b6117e7565b6106176109ba366004614127565b61182b565b6104896109cd3660046142bd565b61186d565b6104896109e036600461469b565b61187f565b6104936109f3366004614127565b6118d9565b610489610a063660046141b4565b611933565b610489610a19366004614720565b6119e9565b610489610a2c36600461422a565b611a3c565b610489610a3f36600461474c565b611a55565b610a57610a52366004614127565b611a8b565b60405161046d91906147d9565b60025461051f565b610489610a7a366004614856565b611b53565b610489610a8d3660046148c1565b611ba3565b610489610aa0366004614919565b611bb8565b610ab8610ab33660046140a7565b611bcf565b60405161046d919081516001600160401b039081168252602080840151821690830152604080840151821690830152606092830151169181019190915260800190565b610489610b093660046142bd565b611c71565b610493611c84565b610461610b2436600461495f565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205460ff1690565b61051f7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b61051f610b87366004614127565b600090815260066020908152604091829020825160e08101845290546001600160401b038082168352600160401b82048116938301849052600160801b8204811694830185905260ff600160c01b8304811615156060850152600160c81b8304811615156080850152600160d01b83048116151560a0850152600160d81b909204909116151560c0909201919091529190031690565b610489610c2b36600461420f565b611d12565b60006301ffc9a760e01b6001600160e01b031983161480610c6157506380ac58cd60e01b6001600160e01b03198316145b80610c7c5750635b5e139f60e01b6001600160e01b03198316145b80610c975750632483248360e11b6001600160e01b03198316145b80610cb257506001600160e01b0319821663152a902d60e11b145b92915050565b610cc0611d89565b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b606060038054610cf19061498d565b80601f0160208091040260200160405190810160405280929190818152602001828054610d1d9061498d565b8015610d6a5780601f10610d3f57610100808354040283529160200191610d6a565b820191906000526020600020905b815481529060010190602001808311610d4d57829003601f168201915b5050505050905090565b6000610d7f82611db0565b610d9c576040516333d1c03960e21b815260040160405180910390fd5b506000908152600a60205260409020546001600160a01b031690565b6000610dc382611357565b9050806001600160a01b0316836001600160a01b031603610df75760405163250fdee360e21b815260040160405180910390fd5b336001600160a01b03821614610e2e57610e118133610b24565b610e2e576040516367d9dca160e11b815260040160405180910390fd5b610e39838383611e07565b505050565b610e46611e63565b610e885760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b60448201526064015b60405180910390fd5b610e9181611e6f565b50565b610e9c611d89565b610e398383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611ec192505050565b610ee4611faf565b8015610f1a57610f157f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b600061200c565b610f45565b610f457f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b600061206e565b60405181151581527f4e74b41658d1dc9d9bcc9cd965d9ec1f1c3af066c38fcf4c19d2d23e433fe5db906020015b60405180910390a150565b610f86611faf565b600082815260076020526040902080546001600160a01b0319166001600160a01b0383161790555050565b5050565b610e398383836120c9565b600080600080610fcf86611256565b90945084925061ffff169050612710610fe882876149dd565b610ff29190614a0a565b925050509250929050565b611005611faf565b610fb18282612330565b6110176123b6565b611022838383612400565b5050505050565b600082815260126020526040902054611042903361242a565b60008281526011602090815260408083206001600160a01b038516845290915290205460ff16156110b55760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c646572730000006044820152606401610e7f565b610fb1828261206e565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152610cb2611104836124aa565b61251c565b336001600160a01b038216146111615760405162461bcd60e51b815260206004820152601a60248201527f43616e206f6e6c792072656e6f756e636520666f722073656c660000000000006044820152606401610e7f565b610fb1828261200c565b6111736123b6565b61117f848484846125e5565b505050505050565b610e39838383604051806020016040528060008152506117e7565b610e91816001612958565b6000806111b983611357565b9050806001600160a01b0316846001600160a01b0316148061120057506001600160a01b038082166000908152600b602090815260408083209388168352929052205460ff165b806112245750836001600160a01b031661121984610d74565b6001600160a01b0316145b949350505050565b6112346123b6565b61125082826001604051806020016040528060008152506125e5565b50505050565b6000818152601060209081526040808320815180830190925280546001600160a01b03168083526001909101549282019290925282911561129d57805160208201516112b7565b600f546001600160a01b03811690600160a01b900461ffff165b9250925050915091565b6112c9611d89565b610fb182828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612bcd92505050565b611310611e63565b61134d5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610e7f565b610fb18282612c8c565b600061136282612d41565b5192915050565b60006001600160a01b038316611392576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b03919091166000908152600860209081526040808320938352929052205490565b60006001600160a01b0382166113e4576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b03166000908152600960205260409020546001600160401b031690565b611411611faf565b80156114355761143060008051602061516b833981519152600061200c565b61144e565b61144e60008051602061516b833981519152600061206e565b60405181151581527f981199ac28f5875ea88aaa8f834dc187f9f263ede96642b78a367cedf8940c6d90602001610f73565b611488611faf565b610e9181612e93565b6000610cb261149f836124aa565b6000908152600760205260409020546001600160a01b031690565b60009182526011602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6114ed612f03565b61152a5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610e7f565b610e9181612f3f565b606060048054610cf19061498d565b61154a611faf565b610fb1828261300f565b61155c611e63565b6115995760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610e7f565b610e39838383613095565b6000818152600760205260408120546001600160a01b0316610cb2565b336001600160a01b038316036115ea5760405163b06307db60e01b815260040160405180910390fd5b336000818152600b602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b600082815260116020908152604080832083805290915281205460ff166116a3575060008281526011602090815260408083206001600160a01b038516845290915290205460ff16610cb2565b50600192915050565b60006116b661315e565b905090565b6116c3611faf565b610fb18282613196565b6060816001600160401b038111156116e7576116e761431e565b60405190808252806020026020018201604052801561171a57816020015b60608152602001906001900390816117055790505b50905060005b828110156117ba5761178a3085858481811061173e5761173e614a1e565b90506020028101906117509190614a34565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506132bf92505050565b82828151811061179c5761179c614a1e565b602002602001018190525080806117b290614a7a565b915050611720565b5092915050565b6040805160608101825260008082526020820181905291810191909152610cb282612d41565b6117f28484846120c9565b6001600160a01b0383163b156112505761180e848484846132e4565b611250576040516368d2bf6b60e11b815260040160405180910390fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152610cb28261251c565b611875611faf565b610fb182826133cf565b6118876123b6565b600061189686866001856125e5565b50905061117f8185858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611ec192505050565b60606118e482611db0565b61190157604051630a14c4b560e41b815260040160405180910390fd5b600061190c836124aa565b600081815260066020526040902054909150600160d81b900460ff16611224848383613455565b61193b611faf565b61197b8383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506134fd92505050565b6000838152600660205260409020546001600160401b031615610e39577f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c6119c2846135c0565b6119cb856135d9565b604080519283526020830191909152015b60405180910390a1505050565b6119f1611faf565b6001600160a01b0382166000908152600960205260409020805477ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b6001600160401b038416021790555050565b600082815260126020526040902054611161903361242a565b611a5d6123b6565b6000611a6b878787856125e5565b509050611a828186611a7d8688614ab6565b613612565b50505050505050565b600d546060906001600160a01b0316611ae25760408051600080825260208201909252906117ba565b604080518082019091526000815260606020820152815260200190600190039081611ab45790505092915050565b600d546040516335ad197b60e21b8152600481018490526001600160a01b039091169063d6b465ec90602401600060405180830381865afa158015611b2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cb29190810190614b6e565b611b5b611d89565b611250848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250611b9e9250859150869050614ab6565b61375e565b611bab6123b6565b61102283836001846125e5565b611bc0611d89565b6112508484611a7d8486614ab6565b60408051608080820183526000808352602080840182905283850182905260608085018390528551808501875283815280830184905280870184905281018390526001600160a01b0387168352600982529185902085519384018652546001600160401b038082168552600160401b8204811692850192909252600160801b8104821695840195909552600160c01b9094049093169281019290925290610cb2565b611c79611faf565b610fb18282156138cf565b600c8054611c919061498d565b80601f0160208091040260200160405190810160405280929190818152602001828054611cbd9061498d565b8015611d0a5780601f10611cdf57610100808354040283529160200191611d0a565b820191906000526020600020905b815481529060010190602001808311611ced57829003601f168201915b505050505081565b611d1a611faf565b8015611d3e57611d3960008051602061518b833981519152600061200c565b611d57565b611d5760008051602061518b833981519152600061206e565b60405181151581527f59772cb8eece8db33486761b15a977d3ab990b1233039bf6320b59c71db9f01b90602001610f73565b611d91612f03565b611dae5760405163a6a3237960e01b815260040160405180910390fd5b565b600080611dbc836124aa565b905082611dc8826135c0565b11158015611dde5750611dda81613956565b8311155b8015611e005750600083815260056020526040902054600160e01b900460ff16155b9392505050565b6000828152600a602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60006116b681336114ba565b600e80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a35050565b611ec9613996565b600d54604051630588253160e21b81526000916001600160a01b03169063162094c490611efc9086908690600401614c65565b6000604051808303816000875af1158015611f1b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f439190810190614c7e565b9050827f31b096c6ff64a74a686605caa11c1eb1e36d3ab8079b1e117b5074d70d63ac7b8284604051611f77929190614cb2565b60405180910390a26040518381527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7906020016119dc565b611fba6000336114ba565b158015611fee5750611fec7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08336114ba565b155b15611dae576040516349349c6360e11b815260040160405180910390fd5b612016828261242a565b60008281526011602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60008281526011602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60006120d482612d41565b9050836001600160a01b031681600001516001600160a01b03161461210b5760405162a1148160e81b815260040160405180910390fd5b6000336001600160a01b038616148061212957506121298533610b24565b8061214457503361213984610d74565b6001600160a01b0316145b90508061216457604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b03841661218b57604051633a954ecd60e21b815260040160405180910390fd5b6000612196846124aa565b600081815260066020526040902054909150600160c81b900460ff16156121d05760405163742f32a560e01b815260040160405180910390fd5b6121dd86868660016139bf565b6121e960008588611e07565b6001600160a01b03808716600081815260096020908152604080832080546000196001600160401b038083168201811667ffffffffffffffff19938416179093558c88168087528487208054808616600190810187169190951617905596865260088086528487208a885286528487208054909201909155868652845282852088865284528285208054820190558a85526005909352818420805442909216600160a01b026001600160e01b031990921690951717845590880182529020805491929091166122f2576122bb83613956565b86146122f257805460208601516001600160401b0316600160a01b026001600160e01b03199091166001600160a01b038a16171781555b505083856001600160a01b0316876001600160a01b03166000805160206151d283398151915260405160405180910390a461117f8686866001613b36565b6000828152600660205260408120805490916001600160401b03909116900361236c57604051630ef5d16d60e41b815260040160405180910390fd5b805460ff60c81b1916600160c81b83151590810291909117825560405184907fbc2c3bac56ffb0a6a8a5c2e741c77a517437ccbf0db17c96bad80558cf5e5a6c90600090a3505050565b6123c16000336114ba565b1580156123e357506123e160008051602061518b833981519152336114ba565b155b15611dae57604051620c907560e91b815260040160405180910390fd5b60008061241e858585604051806020016040528060008152506125e5565b91509150935093915050565b60008281526011602090815260408083206001600160a01b038516845290915290205460ff16610fb157612468816001600160a01b03166014613b4b565b612473836020613b4b565b604051602001612484929190614ce0565b60408051601f198184030181529082905262461bcd60e51b8252610e7f91600401614114565b600080548210156124ce57604051631852cb5560e11b815260040160405180910390fd5b6000546124db9083614a0a565b6000818152600660205260408120549192506001600160401b0390911690036125175760405163d42cd7a760e01b815260040160405180910390fd5b919050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915250600090815260066020908152604091829020825160e08101845290546001600160401b038082168352600160401b8204811693830193909352600160801b81049092169281019290925260ff600160c01b8204811615156060840152600160c81b8204811615156080840152600160d01b82048116151560a0840152600160d81b90910416151560c082015290565b60015460009081906001600160a01b03871661261357604051622e076360e81b815260040160405180910390fd5b846000036126345760405163b562e8dd60e01b815260040160405180910390fd5b600086815260066020526040902054600160c01b900460ff161561266b5760405163507c917f60e01b815260040160405180910390fd5b6126758686613ce6565b909350915061268760008885886139bf565b6001600160a01b038716600081815260096020908152604080832080546fffffffffffffffffffffffffffffffff1981166001600160401b038083168d018116918217600160401b67ffffffffffffffff1990941690921783900481168d018116909202179091558784526005835281842080546001600160e01b0319168617600160a01b4290931692909202919091179055838352600882528083208a845290915290208054870190556001929092019183903b1561283d575b6000878152600760205260409020546001600160a01b0316156127cc5760008781526007602052604080822054905183926001600160a01b0390921691906000805160206151d2833981519152908290a460008781526007602052604080822054905183926001600160a01b03808d169316916000805160206151d28339815191529190a46127f3565b60405181906001600160a01b038a16906000906000805160206151d2833981519152908290a45b61280660008983806001019450886132e4565b612823576040516368d2bf6b60e11b815260040160405180910390fd5b82811061274257816001541461283857600080fd5b6128f9565b6000878152600760205260409020546001600160a01b0316156128c75760008781526007602052604080822054905183926001600160a01b0390921691906000805160206151d2833981519152908290a460008781526007602052604080822054905183926001600160a01b03808d169316916000805160206151d28339815191529190a46128ee565b60405181906001600160a01b038a16906000906000805160206151d2833981519152908290a45b60010182811061283d575b506001805486019055600086815260066020526040812080546001600160401b03600160401b80830482168a01909116026fffffffffffffffff00000000000000001990911617905561294e90888588613b36565b5094509492505050565b600061296383612d41565b805190915082156129c9576000336001600160a01b038316148061298c575061298c8233610b24565b806129a757503361299c86610d74565b6001600160a01b0316145b9050806129c757604051632ce44b5f60e11b815260040160405180910390fd5b505b60006129d4856124aa565b600081815260066020526040902054909150600160d01b900460ff16612a0d576040516325ba5f8760e11b815260040160405180910390fd5b612a1b8260008760016139bf565b612a2760008684611e07565b6001600160a01b0380831660008181526009602090815260408083208054600160801b6000196001600160401b038084168201811667ffffffffffffffff198516811784900482166001908101831690940277ffffffffffffffff0000000000000000ffffffffffffffff19909516179390931784558d87526005808752858820805460ff60e01b1942909616600160a01b026001600160e01b03199091168b171794909416600160e01b178455978752600886528487208a88528652848720805490910190558c0185529490925290912080549193909116612b4457612b0d84613956565b8814612b4457805460208701516001600160401b0316600160a01b026001600160e01b03199091166001600160a01b038716171781555b50506040518691506000906001600160a01b038516906000805160206151d2833981519152908390a4612b7b826000876001613b36565b60028054600190810190915560009182526006602052604090912080546001600160401b03600160801b80830482169094011690920267ffffffffffffffff60801b1990921691909117905550505050565b612bd5613996565b600d546040516355f804b360e01b81526000916001600160a01b0316906355f804b390612c06908590600401614114565b6000604051808303816000875af1158015612c25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612c4d9190810190614c7e565b90507f309b29ded109b9e28fb9885757b3e0096eb75c51d23aa4635d68bcd569f6adc18183604051612c80929190614cb2565b60405180910390a15050565b612710811115612cd05760405162461bcd60e51b815260206004820152600f60248201526e45786365656473206d61782062707360881b6044820152606401610e7f565b600f80546001600160a01b03841675ffffffffffffffffffffffffffffffffffffffffffff199091168117600160a01b61ffff851602179091556040518281527f90d7ec04bcb8978719414f82e52e4cb651db41d0e6f8cea6118c2191e6183adb9060200160405180910390a25050565b6040805160608101825260008082526020820181905291810191909152816000612d6a826124aa565b90506000612d77826135c0565b9050828111612e7a57612d8982613956565b8311612e7a57600083815260056020908152604091829020825160608101845290546001600160a01b0381168252600160a01b81046001600160401b031692820192909252600160e01b90910460ff16151591810182905290612e785780516001600160a01b031615612dff5795945050505050565b83821015612e785750600019909201600081815260056020908152604091829020825160608101845290546001600160a01b0381168083526001600160401b03600160a01b8304169383019390935260ff600160e01b9091041615159281019290925291939115612e735795945050505050565b612dff565b505b604051636f96cda160e11b815260040160405180910390fd5b60015415612eb4576040516346464cc560e11b815260040160405180910390fd5b6001811080612ec35750601381115b15612ef257604051637043fcb160e11b8152600481018290526001602482015260136044820152606401610e7f565b612efd81600a614e39565b60005550565b6000612f0f81336114ba565b806116b657506116b67f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08336114ba565b6000600c8054612f4e9061498d565b80601f0160208091040260200160405190810160405280929190818152602001828054612f7a9061498d565b8015612fc75780601f10612f9c57610100808354040283529160200191612fc7565b820191906000526020600020905b815481529060010190602001808311612faa57829003601f168201915b5050505050905081600c9081612fdd9190614e8b565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a168183604051612c80929190614cb2565b6000828152600660205260408120805490916001600160401b03909116900361304b57604051630ef5d16d60e41b815260040160405180910390fd5b805460ff60d81b1916600160d81b83151590810291909117825560405184907f8ae563703bfd9dee13e2840d7b2da05c9571f9999decd1af9bc3c4f1abc7f2a390600090a3505050565b6127108111156130d95760405162461bcd60e51b815260206004820152600f60248201526e45786365656473206d61782062707360881b6044820152606401610e7f565b6040805180820182526001600160a01b038481168083526020808401868152600089815260108352869020945185546001600160a01b031916941693909317845591516001909301929092559151838152909185917f7365cf4122f072a3365c20d54eff9b38d73c096c28e1892ec8f5b0e403a0f12d910160405180910390a3505050565b600080545b61316e600a82614f4a565b60000361319257613180600a82614a0a565b905061318b82614a7a565b9150613163565b5090565b8161319f613e29565b10156131d057816131ae613e29565b60405163083b64a960e21b815260048101929092526024820152604401610e7f565b806001600160401b03166131e2613e43565b6001600160401b0316101561322657806131fa613e43565b60405163e3e9ed1b60e01b81526001600160401b03928316600482015291166024820152604401610e7f565b600082815260066020526040902080546001600160401b0380821691600160401b90048116908416101561326d5760405163e1e4381d60e01b815260040160405180910390fd5b815467ffffffffffffffff19166001600160401b0384811691821784556040519083169086907f5ec1a4766b8551c0899e95a6c0a09ba3e6e3e52e7bc6e8c0841055f30dc4a2a990600090a450505050565b6060611e0083836040518060600160405280602781526020016151ab60279139613e54565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a0290613319903390899088908890600401614f5e565b6020604051808303816000875af1925050508015613354575060408051601f3d908101601f1916820190925261335191810190614f90565b60015b6133b2573d808015613382576040519150601f19603f3d011682016040523d82523d6000602084013e613387565b606091505b5080516000036133aa576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050949350505050565b6000828152600660205260408120805490916001600160401b03909116900361340b57604051630ef5d16d60e41b815260040160405180910390fd5b805460ff60c01b1916600160c01b83151590810291909117825560405184907fc0d0f1d4af8c1176ce7fa98e2590105a56689ecab8f7d722e51ade782c460ee890600090a3505050565b600d546060906001600160a01b031661347d5750604080516020810190915260008152611e00565b600d5460405163af7b3c0560e01b8152600481018690526024810185905283151560448201526001600160a01b039091169063af7b3c0590606401600060405180830381865afa1580156134d5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112249190810190614c7e565b613505613996565b600d54604051636a01dfc360e11b81526000916001600160a01b03169063d403bf86906135389086908690600401614c65565b6000604051808303816000875af1158015613557573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261357f9190810190614c7e565b9050827fc8e7cab76fbea9aac4c8cff92b0a463b810237de68fa454ab9272e17137a37b182846040516135b3929190614cb2565b60405180910390a2505050565b600080546135ce90836149dd565b610cb2906001614fad565b6000818152600660205260408120546001600160401b03166001600160401b03166000548361360891906149dd565b6135ce9190614fad565b61361a613996565b600d54604051631c54180f60e31b81526000916001600160a01b03169063e2a0c0789061364f90879087908790600401615015565b6000604051808303816000875af115801561366e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136969190810190615034565b905060005b83811015613719578085017f31b096c6ff64a74a686605caa11c1eb1e36d3ab8079b1e117b5074d70d63ac7b8383815181106136d9576136d9614a1e565b60200260200101518584815181106136f3576136f3614a1e565b6020026020010151604051613709929190614cb2565b60405180910390a260010161369b565b50604080518581526000198587010160208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c910160405180910390a150505050565b613766613996565b600d546040516336cf932160e21b81526000916001600160a01b03169063db3e4c849061379990869086906004016150d7565b6000604051808303816000875af11580156137b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526137e09190810190615034565b805190915060005b818110156110225784818151811061380257613802614a1e565b60200260200101517f31b096c6ff64a74a686605caa11c1eb1e36d3ab8079b1e117b5074d70d63ac7b84838151811061383d5761383d614a1e565b602002602001015186848151811061385757613857614a1e565b602002602001015160405161386d929190614cb2565b60405180910390a27ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce78582815181106138a8576138a8614a1e565b60200260200101516040516138bf91815260200190565b60405180910390a16001016137e8565b6000828152600660205260408120805490916001600160401b03909116900361390b57604051630ef5d16d60e41b815260040160405180910390fd5b805482158015600160d01b0260ff60d01b1990921691909117825560405184907f8c3f451ed4bc4c4399ba519f3f203f8b1fbd25f432b8936ed35293771b64c6cd90600090a3505050565b600081815260066020526040812054600160401b90046001600160401b03166001600160401b03166000548361398c91906149dd565b610cb29190614fad565b600d546001600160a01b0316611dae57604051631643afd160e01b815260040160405180910390fd5b6001600160a01b038416158015906139df57506001600160a01b03831615155b15613a5c576139fd60008051602061516b83398151915260006114ba565b158015613a3e5750613a1d60008051602061516b833981519152856114ba565b8015613a3c5750613a3c60008051602061516b833981519152846114ba565b155b15613a5c5760405163cfed632160e01b815260040160405180910390fd5b6001600160a01b03841615801590613a7b57506001600160a01b038316155b15613ac757613aaa7f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b85611656565b613ac7576040516339ba107760e11b815260040160405180910390fd5b6001600160a01b038416158015613ae657506001600160a01b03831615155b15613b2157613b0460008051602061518b83398151915260006114ba565b613b215760405163789534d760e11b815260040160405180910390fd5b611250613b2d836124aa565b85858585613f31565b611250613b42836124aa565b85858585613fc6565b60606000613b5a8360026149dd565b613b65906002614fad565b6001600160401b03811115613b7c57613b7c61431e565b6040519080825280601f01601f191660200182016040528015613ba6576020820181803683370190505b509050600360fc1b81600081518110613bc157613bc1614a1e565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110613bf057613bf0614a1e565b60200101906001600160f81b031916908160001a9053506000613c148460026149dd565b613c1f906001614fad565b90505b6001811115613c97576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613c5357613c53614a1e565b1a60f81b828281518110613c6957613c69614a1e565b60200101906001600160f81b031916908160001a90535060049490941c93613c9081615124565b9050613c22565b508315611e005760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610e7f565b6000828152600660209081526040808320815160e08101835290546001600160401b03808216808452600160401b8304821695840195909552600160801b8204169282019290925260ff600160c01b8304811615156060830152600160c81b8304811615156080830152600160d01b83048116151560a0830152600160d81b909204909116151560c082015282918203613d9357604051630ef5d16d60e41b815260040160405180910390fd5b8381602001516001600160401b0316613dac9190614fad565b81516001600160401b03161015613dd65760405163e1e4381d60e01b815260040160405180910390fd5b80602001516001600160401b031660005486613df291906149dd565b613dfc9190614fad565b613e07906001614fad565b92506001613e158585614fad565b613e1f919061513b565b9150509250929050565b60008054613e3981600019614a0a565b6116b6919061513b565b600060016000546116b6919061513b565b60606001600160a01b0384163b613ebc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610e7f565b600080856001600160a01b031685604051613ed7919061514e565b600060405180830381855af49150503d8060008114613f12576040519150601f19603f3d011682016040523d82523d6000602084013e613f17565b606091505b5091509150613f27828286614026565b9695505050505050565b600d546001600160a01b03161561102257600d54604051633bcd465f60e21b8152600481018790526001600160a01b038681166024830152858116604483015260648201859052608482018490529091169063ef35197c9060a4015b600060405180830381600087803b158015613fa757600080fd5b505af1158015613fbb573d6000803e3d6000fd5b505050505050505050565b600d546001600160a01b03161561102257600d546040516331ee986560e01b8152600481018790526001600160a01b03868116602483015285811660448301526064820185905260848201849052909116906331ee98659060a401613f8d565b60608315614035575081611e00565b8251156140455782518084602001fd5b8160405162461bcd60e51b8152600401610e7f9190614114565b6001600160e01b031981168114610e9157600080fd5b60006020828403121561408757600080fd5b8135611e008161405f565b6001600160a01b0381168114610e9157600080fd5b6000602082840312156140b957600080fd5b8135611e0081614092565b60005b838110156140df5781810151838201526020016140c7565b50506000910152565b600081518084526141008160208601602086016140c4565b601f01601f19169290920160200192915050565b602081526000611e0060208301846140e8565b60006020828403121561413957600080fd5b5035919050565b6000806040838503121561415357600080fd5b823561415e81614092565b946020939093013593505050565b60008083601f84011261417e57600080fd5b5081356001600160401b0381111561419557600080fd5b6020830191508360208285010111156141ad57600080fd5b9250929050565b6000806000604084860312156141c957600080fd5b8335925060208401356001600160401b038111156141e657600080fd5b6141f28682870161416c565b9497909650939450505050565b8035801515811461251757600080fd5b60006020828403121561422157600080fd5b611e00826141ff565b6000806040838503121561423d57600080fd5b82359150602083013561424f81614092565b809150509250929050565b60008060006060848603121561426f57600080fd5b833561427a81614092565b9250602084013561428a81614092565b929592945050506040919091013590565b600080604083850312156142ae57600080fd5b50508035926020909101359150565b600080604083850312156142d057600080fd5b823591506142e0602084016141ff565b90509250929050565b6000806000606084860312156142fe57600080fd5b833561430981614092565b95602085013595506040909401359392505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156143565761435661431e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156143845761438461431e565b604052919050565b60006001600160401b038211156143a5576143a561431e565b50601f01601f191660200190565b600082601f8301126143c457600080fd5b81356143d76143d28261438c565b61435c565b8181528460208386010111156143ec57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561441f57600080fd5b843561442a81614092565b9350602085013592506040850135915060608501356001600160401b0381111561445357600080fd5b61445f878288016143b3565b91505092959194509250565b6000806020838503121561447e57600080fd5b82356001600160401b0381111561449457600080fd5b6144a08582860161416c565b90969095509350505050565b6000602082840312156144be57600080fd5b81356001600160401b038111156144d457600080fd5b611224848285016143b3565b6000806000606084860312156144f557600080fd5b83359250602084013561428a81614092565b6000806040838503121561451a57600080fd5b823561452581614092565b91506142e0602084016141ff565b80356001600160401b038116811461251757600080fd5b6000806040838503121561455d57600080fd5b823591506142e060208401614533565b60008083601f84011261457f57600080fd5b5081356001600160401b0381111561459657600080fd5b6020830191508360208260051b85010111156141ad57600080fd5b600080602083850312156145c457600080fd5b82356001600160401b038111156145da57600080fd5b6144a08582860161456d565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561463b57603f198886030184526146298583516140e8565b9450928501929085019060010161460d565b5092979650505050505050565b6000806000806080858703121561465e57600080fd5b843561466981614092565b9350602085013561467981614092565b92506040850135915060608501356001600160401b0381111561445357600080fd5b6000806000806000608086880312156146b357600080fd5b85356146be81614092565b94506020860135935060408601356001600160401b03808211156146e157600080fd5b6146ed89838a0161416c565b9095509350606088013591508082111561470657600080fd5b50614713888289016143b3565b9150509295509295909350565b6000806040838503121561473357600080fd5b823561473e81614092565b91506142e060208401614533565b60008060008060008060a0878903121561476557600080fd5b863561477081614092565b9550602087013594506040870135935060608701356001600160401b038082111561479a57600080fd5b6147a68a838b0161456d565b909550935060808901359150808211156147bf57600080fd5b506147cc89828a016143b3565b9150509295509295509295565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561484857888303603f19018552815180516001600160a01b03168452870151878401879052614835878501826140e8565b9588019593505090860190600101614800565b509098975050505050505050565b6000806000806040858703121561486c57600080fd5b84356001600160401b038082111561488357600080fd5b61488f8883890161456d565b909650945060208701359150808211156148a857600080fd5b506148b58782880161456d565b95989497509550505050565b6000806000606084860312156148d657600080fd5b83356148e181614092565b92506020840135915060408401356001600160401b0381111561490357600080fd5b61490f868287016143b3565b9150509250925092565b6000806000806060858703121561492f57600080fd5b843593506020850135925060408501356001600160401b0381111561495357600080fd5b6148b58782880161456d565b6000806040838503121561497257600080fd5b823561497d81614092565b9150602083013561424f81614092565b600181811c908216806149a157607f821691505b6020821081036149c157634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610cb257610cb26149c7565b634e487b7160e01b600052601260045260246000fd5b600082614a1957614a196149f4565b500490565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614a4b57600080fd5b8301803591506001600160401b03821115614a6557600080fd5b6020019150368190038213156141ad57600080fd5b600060018201614a8c57614a8c6149c7565b5060010190565b60006001600160401b03821115614aac57614aac61431e565b5060051b60200190565b6000614ac46143d284614a93565b80848252602080830192508560051b850136811115614ae257600080fd5b855b81811015614b1d5780356001600160401b03811115614b035760008081fd5b614b0f36828a016143b3565b865250938201938201614ae4565b50919695505050505050565b600082601f830112614b3a57600080fd5b8151614b486143d28261438c565b818152846020838601011115614b5d57600080fd5b6112248260208301602087016140c4565b60006020808385031215614b8157600080fd5b82516001600160401b0380821115614b9857600080fd5b818501915085601f830112614bac57600080fd5b8151614bba6143d282614a93565b81815260059190911b83018401908481019088831115614bd957600080fd5b8585015b83811015614c5857805185811115614bf55760008081fd5b86016040818c03601f1901811315614c0d5760008081fd5b614c15614334565b89830151614c2281614092565b8152908201519087821115614c375760008081fd5b614c458d8b84860101614b29565b818b015285525050918601918601614bdd565b5098975050505050505050565b82815260406020820152600061122460408301846140e8565b600060208284031215614c9057600080fd5b81516001600160401b03811115614ca657600080fd5b61122484828501614b29565b604081526000614cc560408301856140e8565b8281036020840152614cd781856140e8565b95945050505050565b7f5065726d697373696f6e733a206163636f756e74200000000000000000000000815260008351614d188160158501602088016140c4565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351614d498160268401602088016140c4565b01602601949350505050565b600181815b80851115614d90578160001904821115614d7657614d766149c7565b80851615614d8357918102915b93841c9390800290614d5a565b509250929050565b600082614da757506001610cb2565b81614db457506000610cb2565b8160018114614dca5760028114614dd457614df0565b6001915050610cb2565b60ff841115614de557614de56149c7565b50506001821b610cb2565b5060208310610133831016604e8410600b8410161715614e13575081810a610cb2565b614e1d8383614d55565b8060001904821115614e3157614e316149c7565b029392505050565b6000611e008383614d98565b601f821115610e3957600081815260208120601f850160051c81016020861015614e6c5750805b601f850160051c820191505b8181101561117f57828155600101614e78565b81516001600160401b03811115614ea457614ea461431e565b614eb881614eb2845461498d565b84614e45565b602080601f831160018114614eed5760008415614ed55750858301515b600019600386901b1c1916600185901b17855561117f565b600085815260208120601f198616915b82811015614f1c57888601518255948401946001909101908401614efd565b5085821015614f3a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600082614f5957614f596149f4565b500690565b60006001600160a01b03808716835280861660208401525083604083015260806060830152613f2760808301846140e8565b600060208284031215614fa257600080fd5b8151611e008161405f565b80820180821115610cb257610cb26149c7565b600081518084526020808501808196508360051b8101915082860160005b85811015615008578284038952614ff68483516140e8565b98850198935090840190600101614fde565b5091979650505050505050565b838152826020820152606060408201526000614cd76060830184614fc0565b6000602080838503121561504757600080fd5b82516001600160401b038082111561505e57600080fd5b818501915085601f83011261507257600080fd5b81516150806143d282614a93565b81815260059190911b8301840190848101908883111561509f57600080fd5b8585015b83811015614c58578051858111156150bb5760008081fd5b6150c98b89838a0101614b29565b8452509186019186016150a3565b604080825283519082018190526000906020906060840190828701845b82811015615110578151845292840192908401906001016150f4565b50505083810382850152613f278186614fc0565b600081615133576151336149c7565b506000190190565b81810381811115610cb257610cb26149c7565b600082516151608184602087016140c4565b919091019291505056fe8502233096d909befbda0999bb8ea2f3a6be3c138b9fbf003752a4c8bce86f6ca4ec463140e04cceb4c413e3f5471bfb51926664fe46e1ac1de7401783fc6a9a416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220acf4ce396720f6563f72b66e192c3060be49b5727f8fde4bfdbea0d01d96407a64736f6c63430008140033a4ec463140e04cceb4c413e3f5471bfb51926664fe46e1ac1de7401783fc6a9a000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104495760003560e01c806382a819aa11610241578063c24c41c31161013b578063db3e4c84116100c3578063e8a3d48511610087578063e8a3d48514610b0e578063e985e9c514610b16578063ec87621c14610b52578063ef8593e714610b79578063f642ef5b14610c1d57600080fd5b8063db3e4c8414610a6c578063dfdbb20214610a7f578063e2a0c07814610a92578063e6b962a914610aa5578063e6ff9f9d14610afb57600080fd5b8063d52eff531161010a578063d52eff5314610a0b578063d547741f14610a1e578063d666c4f314610a31578063d6b465ec14610a44578063d89135cd14610a6457600080fd5b8063c24c41c3146109bf578063c4766a05146109d2578063c87b56dd146109e5578063d403bf86146109f857600080fd5b8063a22cb465116101c9578063ac9650d81161018d578063ac9650d814610912578063b09afec114610932578063b24f2d391461097b578063b88d4fde14610999578063b9db15b4146109ac57600080fd5b8063a22cb465146108c9578063a2309ff8146108dc578063a32fa5b3146108e4578063a34563b2146108f7578063a44bbf08146108ff57600080fd5b806395d89b411161021057806395d89b41146108805780639b639da7146108885780639bcf7a151461089b5780639fc1b85f146108ae578063a217fddf146108c157600080fd5b806382a819aa146108365780638da5cb5b1461084957806391d148541461085a578063938e3d7b1461086d57600080fd5b806330fa1cc3116103525780634cc157df116102da5780636d12a0531161029e5780636d12a053146107c457806370a08231146107d757806373fa563f146107ea57806375380153146107fd5780637c6285a11461081057600080fd5b80634cc157df1461072f57806355f804b314610764578063600dd5ea146107775780636352211e1461078a578063658e9a781461079d57600080fd5b80634231144e116103215780634231144e146106d057806342842e0e146106e357806342966c68146106f6578063430c208114610709578063449a52f81461071c57600080fd5b806330fa1cc31461060457806336568abe1461068457806338d44a1b146106975780633c4de40f146106ac57600080fd5b806318fb4427116103d557806324e0fbaa116103a457806324e0fbaa146105885780632a55205a146105995780632a9b503d146105cb5780632c7ef077146105de5780632f2ff15d146105f157600080fd5b806318fb44271461052d578063206b60f91461054057806323b872dd14610555578063248a9ca31461056857600080fd5b8063095ea7b31161041c578063095ea7b3146104cb57806313af4035146104de578063162094c4146104f157806316f0fe611461050457806318160ddd1461051757600080fd5b806301ffc9a71461044e57806303959bb71461047657806306fdde031461048b578063081812fc146104a0575b600080fd5b61046161045c366004614075565b610c30565b60405190151581526020015b60405180910390f35b6104896104843660046140a7565b610cb8565b005b610493610ce2565b60405161046d9190614114565b6104b36104ae366004614127565b610d74565b6040516001600160a01b03909116815260200161046d565b6104896104d9366004614140565b610db8565b6104896104ec3660046140a7565b610e3e565b6104896104ff3660046141b4565b610e94565b61048961051236600461420f565b610edc565b600254600154035b60405190815260200161046d565b61048961053b36600461422a565b610f7e565b61051f60008051602061516b83398151915281565b61048961056336600461425a565b610fb5565b61051f610576366004614127565b60009081526012602052604090205490565b600d546001600160a01b03166104b3565b6105ac6105a736600461429b565b610fc0565b604080516001600160a01b03909316835260208301919091520161046d565b6104896105d93660046142bd565b610ffd565b6104896105ec3660046142e9565b61100f565b6104896105ff36600461422a565b611029565b610617610612366004614127565b6110bf565b60405161046d9190600060e0820190506001600160401b038084511683528060208501511660208401528060408501511660408401525060608301511515606083015260808301511515608083015260a0830151151560a083015260c0830151151560c083015292915050565b61048961069236600461422a565b611109565b61051f60008051602061518b83398151915281565b610493604051806040016040528060058152602001640312e302e360dc1b81525081565b6104896106de366004614409565b61116b565b6104896106f136600461425a565b611187565b610489610704366004614127565b6111a2565b610461610717366004614140565b6111ad565b61048961072a366004614140565b61122c565b61074261073d366004614127565b611256565b604080516001600160a01b03909316835261ffff90911660208301520161046d565b61048961077236600461446b565b6112c1565b610489610785366004614140565b611308565b6104b3610798366004614127565b611357565b61051f7f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b81565b61051f6107d2366004614140565b611369565b61051f6107e53660046140a7565b6113bb565b6104896107f836600461420f565b611409565b61048961080b366004614127565b611480565b610493604051806040016040528060078152602001661350525393915560ca1b81525081565b6104b3610844366004614127565b611491565b600e546001600160a01b03166104b3565b61046161086836600461422a565b6114ba565b61048961087b3660046144ac565b6114e5565b610493611533565b6104896108963660046142bd565b611542565b6104896108a93660046144e0565b611554565b6104b36108bc366004614127565b6115a4565b61051f600081565b6104896108d7366004614507565b6115c1565b60015461051f565b6104616108f236600461422a565b611656565b61051f6116ac565b61048961090d36600461454a565b6116bb565b6109256109203660046145b1565b6116cd565b60405161046d91906145e6565b610945610940366004614127565b6117c1565b6040805182516001600160a01b031681526020808401516001600160401b0316908201529181015115159082015260600161046d565b600f546001600160a01b03811690600160a01b900461ffff16610742565b6104896109a7366004614648565b6117e7565b6106176109ba366004614127565b61182b565b6104896109cd3660046142bd565b61186d565b6104896109e036600461469b565b61187f565b6104936109f3366004614127565b6118d9565b610489610a063660046141b4565b611933565b610489610a19366004614720565b6119e9565b610489610a2c36600461422a565b611a3c565b610489610a3f36600461474c565b611a55565b610a57610a52366004614127565b611a8b565b60405161046d91906147d9565b60025461051f565b610489610a7a366004614856565b611b53565b610489610a8d3660046148c1565b611ba3565b610489610aa0366004614919565b611bb8565b610ab8610ab33660046140a7565b611bcf565b60405161046d919081516001600160401b039081168252602080840151821690830152604080840151821690830152606092830151169181019190915260800190565b610489610b093660046142bd565b611c71565b610493611c84565b610461610b2436600461495f565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205460ff1690565b61051f7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b61051f610b87366004614127565b600090815260066020908152604091829020825160e08101845290546001600160401b038082168352600160401b82048116938301849052600160801b8204811694830185905260ff600160c01b8304811615156060850152600160c81b8304811615156080850152600160d01b83048116151560a0850152600160d81b909204909116151560c0909201919091529190031690565b610489610c2b36600461420f565b611d12565b60006301ffc9a760e01b6001600160e01b031983161480610c6157506380ac58cd60e01b6001600160e01b03198316145b80610c7c5750635b5e139f60e01b6001600160e01b03198316145b80610c975750632483248360e11b6001600160e01b03198316145b80610cb257506001600160e01b0319821663152a902d60e11b145b92915050565b610cc0611d89565b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b606060038054610cf19061498d565b80601f0160208091040260200160405190810160405280929190818152602001828054610d1d9061498d565b8015610d6a5780601f10610d3f57610100808354040283529160200191610d6a565b820191906000526020600020905b815481529060010190602001808311610d4d57829003601f168201915b5050505050905090565b6000610d7f82611db0565b610d9c576040516333d1c03960e21b815260040160405180910390fd5b506000908152600a60205260409020546001600160a01b031690565b6000610dc382611357565b9050806001600160a01b0316836001600160a01b031603610df75760405163250fdee360e21b815260040160405180910390fd5b336001600160a01b03821614610e2e57610e118133610b24565b610e2e576040516367d9dca160e11b815260040160405180910390fd5b610e39838383611e07565b505050565b610e46611e63565b610e885760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b60448201526064015b60405180910390fd5b610e9181611e6f565b50565b610e9c611d89565b610e398383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611ec192505050565b610ee4611faf565b8015610f1a57610f157f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b600061200c565b610f45565b610f457f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b600061206e565b60405181151581527f4e74b41658d1dc9d9bcc9cd965d9ec1f1c3af066c38fcf4c19d2d23e433fe5db906020015b60405180910390a150565b610f86611faf565b600082815260076020526040902080546001600160a01b0319166001600160a01b0383161790555050565b5050565b610e398383836120c9565b600080600080610fcf86611256565b90945084925061ffff169050612710610fe882876149dd565b610ff29190614a0a565b925050509250929050565b611005611faf565b610fb18282612330565b6110176123b6565b611022838383612400565b5050505050565b600082815260126020526040902054611042903361242a565b60008281526011602090815260408083206001600160a01b038516845290915290205460ff16156110b55760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c646572730000006044820152606401610e7f565b610fb1828261206e565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152610cb2611104836124aa565b61251c565b336001600160a01b038216146111615760405162461bcd60e51b815260206004820152601a60248201527f43616e206f6e6c792072656e6f756e636520666f722073656c660000000000006044820152606401610e7f565b610fb1828261200c565b6111736123b6565b61117f848484846125e5565b505050505050565b610e39838383604051806020016040528060008152506117e7565b610e91816001612958565b6000806111b983611357565b9050806001600160a01b0316846001600160a01b0316148061120057506001600160a01b038082166000908152600b602090815260408083209388168352929052205460ff165b806112245750836001600160a01b031661121984610d74565b6001600160a01b0316145b949350505050565b6112346123b6565b61125082826001604051806020016040528060008152506125e5565b50505050565b6000818152601060209081526040808320815180830190925280546001600160a01b03168083526001909101549282019290925282911561129d57805160208201516112b7565b600f546001600160a01b03811690600160a01b900461ffff165b9250925050915091565b6112c9611d89565b610fb182828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612bcd92505050565b611310611e63565b61134d5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610e7f565b610fb18282612c8c565b600061136282612d41565b5192915050565b60006001600160a01b038316611392576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b03919091166000908152600860209081526040808320938352929052205490565b60006001600160a01b0382166113e4576040516323d3ad8160e21b815260040160405180910390fd5b506001600160a01b03166000908152600960205260409020546001600160401b031690565b611411611faf565b80156114355761143060008051602061516b833981519152600061200c565b61144e565b61144e60008051602061516b833981519152600061206e565b60405181151581527f981199ac28f5875ea88aaa8f834dc187f9f263ede96642b78a367cedf8940c6d90602001610f73565b611488611faf565b610e9181612e93565b6000610cb261149f836124aa565b6000908152600760205260409020546001600160a01b031690565b60009182526011602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6114ed612f03565b61152a5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610e7f565b610e9181612f3f565b606060048054610cf19061498d565b61154a611faf565b610fb1828261300f565b61155c611e63565b6115995760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610e7f565b610e39838383613095565b6000818152600760205260408120546001600160a01b0316610cb2565b336001600160a01b038316036115ea5760405163b06307db60e01b815260040160405180910390fd5b336000818152600b602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b600082815260116020908152604080832083805290915281205460ff166116a3575060008281526011602090815260408083206001600160a01b038516845290915290205460ff16610cb2565b50600192915050565b60006116b661315e565b905090565b6116c3611faf565b610fb18282613196565b6060816001600160401b038111156116e7576116e761431e565b60405190808252806020026020018201604052801561171a57816020015b60608152602001906001900390816117055790505b50905060005b828110156117ba5761178a3085858481811061173e5761173e614a1e565b90506020028101906117509190614a34565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506132bf92505050565b82828151811061179c5761179c614a1e565b602002602001018190525080806117b290614a7a565b915050611720565b5092915050565b6040805160608101825260008082526020820181905291810191909152610cb282612d41565b6117f28484846120c9565b6001600160a01b0383163b156112505761180e848484846132e4565b611250576040516368d2bf6b60e11b815260040160405180910390fd5b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152610cb28261251c565b611875611faf565b610fb182826133cf565b6118876123b6565b600061189686866001856125e5565b50905061117f8185858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611ec192505050565b60606118e482611db0565b61190157604051630a14c4b560e41b815260040160405180910390fd5b600061190c836124aa565b600081815260066020526040902054909150600160d81b900460ff16611224848383613455565b61193b611faf565b61197b8383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506134fd92505050565b6000838152600660205260409020546001600160401b031615610e39577f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c6119c2846135c0565b6119cb856135d9565b604080519283526020830191909152015b60405180910390a1505050565b6119f1611faf565b6001600160a01b0382166000908152600960205260409020805477ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b6001600160401b038416021790555050565b600082815260126020526040902054611161903361242a565b611a5d6123b6565b6000611a6b878787856125e5565b509050611a828186611a7d8688614ab6565b613612565b50505050505050565b600d546060906001600160a01b0316611ae25760408051600080825260208201909252906117ba565b604080518082019091526000815260606020820152815260200190600190039081611ab45790505092915050565b600d546040516335ad197b60e21b8152600481018490526001600160a01b039091169063d6b465ec90602401600060405180830381865afa158015611b2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610cb29190810190614b6e565b611b5b611d89565b611250848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250611b9e9250859150869050614ab6565b61375e565b611bab6123b6565b61102283836001846125e5565b611bc0611d89565b6112508484611a7d8486614ab6565b60408051608080820183526000808352602080840182905283850182905260608085018390528551808501875283815280830184905280870184905281018390526001600160a01b0387168352600982529185902085519384018652546001600160401b038082168552600160401b8204811692850192909252600160801b8104821695840195909552600160c01b9094049093169281019290925290610cb2565b611c79611faf565b610fb18282156138cf565b600c8054611c919061498d565b80601f0160208091040260200160405190810160405280929190818152602001828054611cbd9061498d565b8015611d0a5780601f10611cdf57610100808354040283529160200191611d0a565b820191906000526020600020905b815481529060010190602001808311611ced57829003601f168201915b505050505081565b611d1a611faf565b8015611d3e57611d3960008051602061518b833981519152600061200c565b611d57565b611d5760008051602061518b833981519152600061206e565b60405181151581527f59772cb8eece8db33486761b15a977d3ab990b1233039bf6320b59c71db9f01b90602001610f73565b611d91612f03565b611dae5760405163a6a3237960e01b815260040160405180910390fd5b565b600080611dbc836124aa565b905082611dc8826135c0565b11158015611dde5750611dda81613956565b8311155b8015611e005750600083815260056020526040902054600160e01b900460ff16155b9392505050565b6000828152600a602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60006116b681336114ba565b600e80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7690600090a35050565b611ec9613996565b600d54604051630588253160e21b81526000916001600160a01b03169063162094c490611efc9086908690600401614c65565b6000604051808303816000875af1158015611f1b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f439190810190614c7e565b9050827f31b096c6ff64a74a686605caa11c1eb1e36d3ab8079b1e117b5074d70d63ac7b8284604051611f77929190614cb2565b60405180910390a26040518381527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7906020016119dc565b611fba6000336114ba565b158015611fee5750611fec7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08336114ba565b155b15611dae576040516349349c6360e11b815260040160405180910390fd5b612016828261242a565b60008281526011602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60008281526011602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60006120d482612d41565b9050836001600160a01b031681600001516001600160a01b03161461210b5760405162a1148160e81b815260040160405180910390fd5b6000336001600160a01b038616148061212957506121298533610b24565b8061214457503361213984610d74565b6001600160a01b0316145b90508061216457604051632ce44b5f60e11b815260040160405180910390fd5b6001600160a01b03841661218b57604051633a954ecd60e21b815260040160405180910390fd5b6000612196846124aa565b600081815260066020526040902054909150600160c81b900460ff16156121d05760405163742f32a560e01b815260040160405180910390fd5b6121dd86868660016139bf565b6121e960008588611e07565b6001600160a01b03808716600081815260096020908152604080832080546000196001600160401b038083168201811667ffffffffffffffff19938416179093558c88168087528487208054808616600190810187169190951617905596865260088086528487208a885286528487208054909201909155868652845282852088865284528285208054820190558a85526005909352818420805442909216600160a01b026001600160e01b031990921690951717845590880182529020805491929091166122f2576122bb83613956565b86146122f257805460208601516001600160401b0316600160a01b026001600160e01b03199091166001600160a01b038a16171781555b505083856001600160a01b0316876001600160a01b03166000805160206151d283398151915260405160405180910390a461117f8686866001613b36565b6000828152600660205260408120805490916001600160401b03909116900361236c57604051630ef5d16d60e41b815260040160405180910390fd5b805460ff60c81b1916600160c81b83151590810291909117825560405184907fbc2c3bac56ffb0a6a8a5c2e741c77a517437ccbf0db17c96bad80558cf5e5a6c90600090a3505050565b6123c16000336114ba565b1580156123e357506123e160008051602061518b833981519152336114ba565b155b15611dae57604051620c907560e91b815260040160405180910390fd5b60008061241e858585604051806020016040528060008152506125e5565b91509150935093915050565b60008281526011602090815260408083206001600160a01b038516845290915290205460ff16610fb157612468816001600160a01b03166014613b4b565b612473836020613b4b565b604051602001612484929190614ce0565b60408051601f198184030181529082905262461bcd60e51b8252610e7f91600401614114565b600080548210156124ce57604051631852cb5560e11b815260040160405180910390fd5b6000546124db9083614a0a565b6000818152600660205260408120549192506001600160401b0390911690036125175760405163d42cd7a760e01b815260040160405180910390fd5b919050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915250600090815260066020908152604091829020825160e08101845290546001600160401b038082168352600160401b8204811693830193909352600160801b81049092169281019290925260ff600160c01b8204811615156060840152600160c81b8204811615156080840152600160d01b82048116151560a0840152600160d81b90910416151560c082015290565b60015460009081906001600160a01b03871661261357604051622e076360e81b815260040160405180910390fd5b846000036126345760405163b562e8dd60e01b815260040160405180910390fd5b600086815260066020526040902054600160c01b900460ff161561266b5760405163507c917f60e01b815260040160405180910390fd5b6126758686613ce6565b909350915061268760008885886139bf565b6001600160a01b038716600081815260096020908152604080832080546fffffffffffffffffffffffffffffffff1981166001600160401b038083168d018116918217600160401b67ffffffffffffffff1990941690921783900481168d018116909202179091558784526005835281842080546001600160e01b0319168617600160a01b4290931692909202919091179055838352600882528083208a845290915290208054870190556001929092019183903b1561283d575b6000878152600760205260409020546001600160a01b0316156127cc5760008781526007602052604080822054905183926001600160a01b0390921691906000805160206151d2833981519152908290a460008781526007602052604080822054905183926001600160a01b03808d169316916000805160206151d28339815191529190a46127f3565b60405181906001600160a01b038a16906000906000805160206151d2833981519152908290a45b61280660008983806001019450886132e4565b612823576040516368d2bf6b60e11b815260040160405180910390fd5b82811061274257816001541461283857600080fd5b6128f9565b6000878152600760205260409020546001600160a01b0316156128c75760008781526007602052604080822054905183926001600160a01b0390921691906000805160206151d2833981519152908290a460008781526007602052604080822054905183926001600160a01b03808d169316916000805160206151d28339815191529190a46128ee565b60405181906001600160a01b038a16906000906000805160206151d2833981519152908290a45b60010182811061283d575b506001805486019055600086815260066020526040812080546001600160401b03600160401b80830482168a01909116026fffffffffffffffff00000000000000001990911617905561294e90888588613b36565b5094509492505050565b600061296383612d41565b805190915082156129c9576000336001600160a01b038316148061298c575061298c8233610b24565b806129a757503361299c86610d74565b6001600160a01b0316145b9050806129c757604051632ce44b5f60e11b815260040160405180910390fd5b505b60006129d4856124aa565b600081815260066020526040902054909150600160d01b900460ff16612a0d576040516325ba5f8760e11b815260040160405180910390fd5b612a1b8260008760016139bf565b612a2760008684611e07565b6001600160a01b0380831660008181526009602090815260408083208054600160801b6000196001600160401b038084168201811667ffffffffffffffff198516811784900482166001908101831690940277ffffffffffffffff0000000000000000ffffffffffffffff19909516179390931784558d87526005808752858820805460ff60e01b1942909616600160a01b026001600160e01b03199091168b171794909416600160e01b178455978752600886528487208a88528652848720805490910190558c0185529490925290912080549193909116612b4457612b0d84613956565b8814612b4457805460208701516001600160401b0316600160a01b026001600160e01b03199091166001600160a01b038716171781555b50506040518691506000906001600160a01b038516906000805160206151d2833981519152908390a4612b7b826000876001613b36565b60028054600190810190915560009182526006602052604090912080546001600160401b03600160801b80830482169094011690920267ffffffffffffffff60801b1990921691909117905550505050565b612bd5613996565b600d546040516355f804b360e01b81526000916001600160a01b0316906355f804b390612c06908590600401614114565b6000604051808303816000875af1158015612c25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612c4d9190810190614c7e565b90507f309b29ded109b9e28fb9885757b3e0096eb75c51d23aa4635d68bcd569f6adc18183604051612c80929190614cb2565b60405180910390a15050565b612710811115612cd05760405162461bcd60e51b815260206004820152600f60248201526e45786365656473206d61782062707360881b6044820152606401610e7f565b600f80546001600160a01b03841675ffffffffffffffffffffffffffffffffffffffffffff199091168117600160a01b61ffff851602179091556040518281527f90d7ec04bcb8978719414f82e52e4cb651db41d0e6f8cea6118c2191e6183adb9060200160405180910390a25050565b6040805160608101825260008082526020820181905291810191909152816000612d6a826124aa565b90506000612d77826135c0565b9050828111612e7a57612d8982613956565b8311612e7a57600083815260056020908152604091829020825160608101845290546001600160a01b0381168252600160a01b81046001600160401b031692820192909252600160e01b90910460ff16151591810182905290612e785780516001600160a01b031615612dff5795945050505050565b83821015612e785750600019909201600081815260056020908152604091829020825160608101845290546001600160a01b0381168083526001600160401b03600160a01b8304169383019390935260ff600160e01b9091041615159281019290925291939115612e735795945050505050565b612dff565b505b604051636f96cda160e11b815260040160405180910390fd5b60015415612eb4576040516346464cc560e11b815260040160405180910390fd5b6001811080612ec35750601381115b15612ef257604051637043fcb160e11b8152600481018290526001602482015260136044820152606401610e7f565b612efd81600a614e39565b60005550565b6000612f0f81336114ba565b806116b657506116b67f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08336114ba565b6000600c8054612f4e9061498d565b80601f0160208091040260200160405190810160405280929190818152602001828054612f7a9061498d565b8015612fc75780601f10612f9c57610100808354040283529160200191612fc7565b820191906000526020600020905b815481529060010190602001808311612faa57829003601f168201915b5050505050905081600c9081612fdd9190614e8b565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a168183604051612c80929190614cb2565b6000828152600660205260408120805490916001600160401b03909116900361304b57604051630ef5d16d60e41b815260040160405180910390fd5b805460ff60d81b1916600160d81b83151590810291909117825560405184907f8ae563703bfd9dee13e2840d7b2da05c9571f9999decd1af9bc3c4f1abc7f2a390600090a3505050565b6127108111156130d95760405162461bcd60e51b815260206004820152600f60248201526e45786365656473206d61782062707360881b6044820152606401610e7f565b6040805180820182526001600160a01b038481168083526020808401868152600089815260108352869020945185546001600160a01b031916941693909317845591516001909301929092559151838152909185917f7365cf4122f072a3365c20d54eff9b38d73c096c28e1892ec8f5b0e403a0f12d910160405180910390a3505050565b600080545b61316e600a82614f4a565b60000361319257613180600a82614a0a565b905061318b82614a7a565b9150613163565b5090565b8161319f613e29565b10156131d057816131ae613e29565b60405163083b64a960e21b815260048101929092526024820152604401610e7f565b806001600160401b03166131e2613e43565b6001600160401b0316101561322657806131fa613e43565b60405163e3e9ed1b60e01b81526001600160401b03928316600482015291166024820152604401610e7f565b600082815260066020526040902080546001600160401b0380821691600160401b90048116908416101561326d5760405163e1e4381d60e01b815260040160405180910390fd5b815467ffffffffffffffff19166001600160401b0384811691821784556040519083169086907f5ec1a4766b8551c0899e95a6c0a09ba3e6e3e52e7bc6e8c0841055f30dc4a2a990600090a450505050565b6060611e0083836040518060600160405280602781526020016151ab60279139613e54565b604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a0290613319903390899088908890600401614f5e565b6020604051808303816000875af1925050508015613354575060408051601f3d908101601f1916820190925261335191810190614f90565b60015b6133b2573d808015613382576040519150601f19603f3d011682016040523d82523d6000602084013e613387565b606091505b5080516000036133aa576040516368d2bf6b60e11b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050949350505050565b6000828152600660205260408120805490916001600160401b03909116900361340b57604051630ef5d16d60e41b815260040160405180910390fd5b805460ff60c01b1916600160c01b83151590810291909117825560405184907fc0d0f1d4af8c1176ce7fa98e2590105a56689ecab8f7d722e51ade782c460ee890600090a3505050565b600d546060906001600160a01b031661347d5750604080516020810190915260008152611e00565b600d5460405163af7b3c0560e01b8152600481018690526024810185905283151560448201526001600160a01b039091169063af7b3c0590606401600060405180830381865afa1580156134d5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112249190810190614c7e565b613505613996565b600d54604051636a01dfc360e11b81526000916001600160a01b03169063d403bf86906135389086908690600401614c65565b6000604051808303816000875af1158015613557573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261357f9190810190614c7e565b9050827fc8e7cab76fbea9aac4c8cff92b0a463b810237de68fa454ab9272e17137a37b182846040516135b3929190614cb2565b60405180910390a2505050565b600080546135ce90836149dd565b610cb2906001614fad565b6000818152600660205260408120546001600160401b03166001600160401b03166000548361360891906149dd565b6135ce9190614fad565b61361a613996565b600d54604051631c54180f60e31b81526000916001600160a01b03169063e2a0c0789061364f90879087908790600401615015565b6000604051808303816000875af115801561366e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136969190810190615034565b905060005b83811015613719578085017f31b096c6ff64a74a686605caa11c1eb1e36d3ab8079b1e117b5074d70d63ac7b8383815181106136d9576136d9614a1e565b60200260200101518584815181106136f3576136f3614a1e565b6020026020010151604051613709929190614cb2565b60405180910390a260010161369b565b50604080518581526000198587010160208201527f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c910160405180910390a150505050565b613766613996565b600d546040516336cf932160e21b81526000916001600160a01b03169063db3e4c849061379990869086906004016150d7565b6000604051808303816000875af11580156137b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526137e09190810190615034565b805190915060005b818110156110225784818151811061380257613802614a1e565b60200260200101517f31b096c6ff64a74a686605caa11c1eb1e36d3ab8079b1e117b5074d70d63ac7b84838151811061383d5761383d614a1e565b602002602001015186848151811061385757613857614a1e565b602002602001015160405161386d929190614cb2565b60405180910390a27ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce78582815181106138a8576138a8614a1e565b60200260200101516040516138bf91815260200190565b60405180910390a16001016137e8565b6000828152600660205260408120805490916001600160401b03909116900361390b57604051630ef5d16d60e41b815260040160405180910390fd5b805482158015600160d01b0260ff60d01b1990921691909117825560405184907f8c3f451ed4bc4c4399ba519f3f203f8b1fbd25f432b8936ed35293771b64c6cd90600090a3505050565b600081815260066020526040812054600160401b90046001600160401b03166001600160401b03166000548361398c91906149dd565b610cb29190614fad565b600d546001600160a01b0316611dae57604051631643afd160e01b815260040160405180910390fd5b6001600160a01b038416158015906139df57506001600160a01b03831615155b15613a5c576139fd60008051602061516b83398151915260006114ba565b158015613a3e5750613a1d60008051602061516b833981519152856114ba565b8015613a3c5750613a3c60008051602061516b833981519152846114ba565b155b15613a5c5760405163cfed632160e01b815260040160405180910390fd5b6001600160a01b03841615801590613a7b57506001600160a01b038316155b15613ac757613aaa7f37444b304786b5c32393c28deaf291b97319224d78730cf67fefea49f4d4007b85611656565b613ac7576040516339ba107760e11b815260040160405180910390fd5b6001600160a01b038416158015613ae657506001600160a01b03831615155b15613b2157613b0460008051602061518b83398151915260006114ba565b613b215760405163789534d760e11b815260040160405180910390fd5b611250613b2d836124aa565b85858585613f31565b611250613b42836124aa565b85858585613fc6565b60606000613b5a8360026149dd565b613b65906002614fad565b6001600160401b03811115613b7c57613b7c61431e565b6040519080825280601f01601f191660200182016040528015613ba6576020820181803683370190505b509050600360fc1b81600081518110613bc157613bc1614a1e565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110613bf057613bf0614a1e565b60200101906001600160f81b031916908160001a9053506000613c148460026149dd565b613c1f906001614fad565b90505b6001811115613c97576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613c5357613c53614a1e565b1a60f81b828281518110613c6957613c69614a1e565b60200101906001600160f81b031916908160001a90535060049490941c93613c9081615124565b9050613c22565b508315611e005760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610e7f565b6000828152600660209081526040808320815160e08101835290546001600160401b03808216808452600160401b8304821695840195909552600160801b8204169282019290925260ff600160c01b8304811615156060830152600160c81b8304811615156080830152600160d01b83048116151560a0830152600160d81b909204909116151560c082015282918203613d9357604051630ef5d16d60e41b815260040160405180910390fd5b8381602001516001600160401b0316613dac9190614fad565b81516001600160401b03161015613dd65760405163e1e4381d60e01b815260040160405180910390fd5b80602001516001600160401b031660005486613df291906149dd565b613dfc9190614fad565b613e07906001614fad565b92506001613e158585614fad565b613e1f919061513b565b9150509250929050565b60008054613e3981600019614a0a565b6116b6919061513b565b600060016000546116b6919061513b565b60606001600160a01b0384163b613ebc5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610e7f565b600080856001600160a01b031685604051613ed7919061514e565b600060405180830381855af49150503d8060008114613f12576040519150601f19603f3d011682016040523d82523d6000602084013e613f17565b606091505b5091509150613f27828286614026565b9695505050505050565b600d546001600160a01b03161561102257600d54604051633bcd465f60e21b8152600481018790526001600160a01b038681166024830152858116604483015260648201859052608482018490529091169063ef35197c9060a4015b600060405180830381600087803b158015613fa757600080fd5b505af1158015613fbb573d6000803e3d6000fd5b505050505050505050565b600d546001600160a01b03161561102257600d546040516331ee986560e01b8152600481018790526001600160a01b03868116602483015285811660448301526064820185905260848201849052909116906331ee98659060a401613f8d565b60608315614035575081611e00565b8251156140455782518084602001fd5b8160405162461bcd60e51b8152600401610e7f9190614114565b6001600160e01b031981168114610e9157600080fd5b60006020828403121561408757600080fd5b8135611e008161405f565b6001600160a01b0381168114610e9157600080fd5b6000602082840312156140b957600080fd5b8135611e0081614092565b60005b838110156140df5781810151838201526020016140c7565b50506000910152565b600081518084526141008160208601602086016140c4565b601f01601f19169290920160200192915050565b602081526000611e0060208301846140e8565b60006020828403121561413957600080fd5b5035919050565b6000806040838503121561415357600080fd5b823561415e81614092565b946020939093013593505050565b60008083601f84011261417e57600080fd5b5081356001600160401b0381111561419557600080fd5b6020830191508360208285010111156141ad57600080fd5b9250929050565b6000806000604084860312156141c957600080fd5b8335925060208401356001600160401b038111156141e657600080fd5b6141f28682870161416c565b9497909650939450505050565b8035801515811461251757600080fd5b60006020828403121561422157600080fd5b611e00826141ff565b6000806040838503121561423d57600080fd5b82359150602083013561424f81614092565b809150509250929050565b60008060006060848603121561426f57600080fd5b833561427a81614092565b9250602084013561428a81614092565b929592945050506040919091013590565b600080604083850312156142ae57600080fd5b50508035926020909101359150565b600080604083850312156142d057600080fd5b823591506142e0602084016141ff565b90509250929050565b6000806000606084860312156142fe57600080fd5b833561430981614092565b95602085013595506040909401359392505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156143565761435661431e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156143845761438461431e565b604052919050565b60006001600160401b038211156143a5576143a561431e565b50601f01601f191660200190565b600082601f8301126143c457600080fd5b81356143d76143d28261438c565b61435c565b8181528460208386010111156143ec57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561441f57600080fd5b843561442a81614092565b9350602085013592506040850135915060608501356001600160401b0381111561445357600080fd5b61445f878288016143b3565b91505092959194509250565b6000806020838503121561447e57600080fd5b82356001600160401b0381111561449457600080fd5b6144a08582860161416c565b90969095509350505050565b6000602082840312156144be57600080fd5b81356001600160401b038111156144d457600080fd5b611224848285016143b3565b6000806000606084860312156144f557600080fd5b83359250602084013561428a81614092565b6000806040838503121561451a57600080fd5b823561452581614092565b91506142e0602084016141ff565b80356001600160401b038116811461251757600080fd5b6000806040838503121561455d57600080fd5b823591506142e060208401614533565b60008083601f84011261457f57600080fd5b5081356001600160401b0381111561459657600080fd5b6020830191508360208260051b85010111156141ad57600080fd5b600080602083850312156145c457600080fd5b82356001600160401b038111156145da57600080fd5b6144a08582860161456d565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561463b57603f198886030184526146298583516140e8565b9450928501929085019060010161460d565b5092979650505050505050565b6000806000806080858703121561465e57600080fd5b843561466981614092565b9350602085013561467981614092565b92506040850135915060608501356001600160401b0381111561445357600080fd5b6000806000806000608086880312156146b357600080fd5b85356146be81614092565b94506020860135935060408601356001600160401b03808211156146e157600080fd5b6146ed89838a0161416c565b9095509350606088013591508082111561470657600080fd5b50614713888289016143b3565b9150509295509295909350565b6000806040838503121561473357600080fd5b823561473e81614092565b91506142e060208401614533565b60008060008060008060a0878903121561476557600080fd5b863561477081614092565b9550602087013594506040870135935060608701356001600160401b038082111561479a57600080fd5b6147a68a838b0161456d565b909550935060808901359150808211156147bf57600080fd5b506147cc89828a016143b3565b9150509295509295509295565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561484857888303603f19018552815180516001600160a01b03168452870151878401879052614835878501826140e8565b9588019593505090860190600101614800565b509098975050505050505050565b6000806000806040858703121561486c57600080fd5b84356001600160401b038082111561488357600080fd5b61488f8883890161456d565b909650945060208701359150808211156148a857600080fd5b506148b58782880161456d565b95989497509550505050565b6000806000606084860312156148d657600080fd5b83356148e181614092565b92506020840135915060408401356001600160401b0381111561490357600080fd5b61490f868287016143b3565b9150509250925092565b6000806000806060858703121561492f57600080fd5b843593506020850135925060408501356001600160401b0381111561495357600080fd5b6148b58782880161456d565b6000806040838503121561497257600080fd5b823561497d81614092565b9150602083013561424f81614092565b600181811c908216806149a157607f821691505b6020821081036149c157634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610cb257610cb26149c7565b634e487b7160e01b600052601260045260246000fd5b600082614a1957614a196149f4565b500490565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614a4b57600080fd5b8301803591506001600160401b03821115614a6557600080fd5b6020019150368190038213156141ad57600080fd5b600060018201614a8c57614a8c6149c7565b5060010190565b60006001600160401b03821115614aac57614aac61431e565b5060051b60200190565b6000614ac46143d284614a93565b80848252602080830192508560051b850136811115614ae257600080fd5b855b81811015614b1d5780356001600160401b03811115614b035760008081fd5b614b0f36828a016143b3565b865250938201938201614ae4565b50919695505050505050565b600082601f830112614b3a57600080fd5b8151614b486143d28261438c565b818152846020838601011115614b5d57600080fd5b6112248260208301602087016140c4565b60006020808385031215614b8157600080fd5b82516001600160401b0380821115614b9857600080fd5b818501915085601f830112614bac57600080fd5b8151614bba6143d282614a93565b81815260059190911b83018401908481019088831115614bd957600080fd5b8585015b83811015614c5857805185811115614bf55760008081fd5b86016040818c03601f1901811315614c0d5760008081fd5b614c15614334565b89830151614c2281614092565b8152908201519087821115614c375760008081fd5b614c458d8b84860101614b29565b818b015285525050918601918601614bdd565b5098975050505050505050565b82815260406020820152600061122460408301846140e8565b600060208284031215614c9057600080fd5b81516001600160401b03811115614ca657600080fd5b61122484828501614b29565b604081526000614cc560408301856140e8565b8281036020840152614cd781856140e8565b95945050505050565b7f5065726d697373696f6e733a206163636f756e74200000000000000000000000815260008351614d188160158501602088016140c4565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351614d498160268401602088016140c4565b01602601949350505050565b600181815b80851115614d90578160001904821115614d7657614d766149c7565b80851615614d8357918102915b93841c9390800290614d5a565b509250929050565b600082614da757506001610cb2565b81614db457506000610cb2565b8160018114614dca5760028114614dd457614df0565b6001915050610cb2565b60ff841115614de557614de56149c7565b50506001821b610cb2565b5060208310610133831016604e8410600b8410161715614e13575081810a610cb2565b614e1d8383614d55565b8060001904821115614e3157614e316149c7565b029392505050565b6000611e008383614d98565b601f821115610e3957600081815260208120601f850160051c81016020861015614e6c5750805b601f850160051c820191505b8181101561117f57828155600101614e78565b81516001600160401b03811115614ea457614ea461431e565b614eb881614eb2845461498d565b84614e45565b602080601f831160018114614eed5760008415614ed55750858301515b600019600386901b1c1916600185901b17855561117f565b600085815260208120601f198616915b82811015614f1c57888601518255948401946001909101908401614efd565b5085821015614f3a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600082614f5957614f596149f4565b500690565b60006001600160a01b03808716835280861660208401525083604083015260806060830152613f2760808301846140e8565b600060208284031215614fa257600080fd5b8151611e008161405f565b80820180821115610cb257610cb26149c7565b600081518084526020808501808196508360051b8101915082860160005b85811015615008578284038952614ff68483516140e8565b98850198935090840190600101614fde565b5091979650505050505050565b838152826020820152606060408201526000614cd76060830184614fc0565b6000602080838503121561504757600080fd5b82516001600160401b038082111561505e57600080fd5b818501915085601f83011261507257600080fd5b81516150806143d282614a93565b81815260059190911b8301840190848101908883111561509f57600080fd5b8585015b83811015614c58578051858111156150bb5760008081fd5b6150c98b89838a0101614b29565b8452509186019186016150a3565b604080825283519082018190526000906020906060840190828701845b82811015615110578151845292840192908401906001016150f4565b50505083810382850152613f278186614fc0565b600081615133576151336149c7565b506000190190565b81810381811115610cb257610cb26149c7565b600082516151608184602087016140c4565b919091019291505056fe8502233096d909befbda0999bb8ea2f3a6be3c138b9fbf003752a4c8bce86f6ca4ec463140e04cceb4c413e3f5471bfb51926664fe46e1ac1de7401783fc6a9a416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220acf4ce396720f6563f72b66e192c3060be49b5727f8fde4bfdbea0d01d96407a64736f6c63430008140033

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

000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49

-----Decoded View---------------
Arg [0] : defaultAdmin_ (address): 0xf9E30Ba8Df802EeF5A0fb239d59dee05f18B2e49
Arg [1] : royaltyRecipient_ (address): 0xf9E30Ba8Df802EeF5A0fb239d59dee05f18B2e49

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49
Arg [1] : 000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49


Block Transaction Difficulty 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
Loading...
Loading
[ Download: CSV Export  ]

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