GLMR Price: $0.021118 (-4.05%)

Contract

0x95492EdCC1D373E236E368973285Ad47D56D07b6

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Set Approval For...136833282025-12-13 1:55:3043 days ago1765590930IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0065433932.25
Set Approval For...134100542025-11-20 2:31:1266 days ago1763605872IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0065433932.25
Set Approval For...134099652025-11-20 2:20:4266 days ago1763605242IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0065433932.25
Set Approval For...134099642025-11-20 2:20:3666 days ago1763605236IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0065433932.25
Set Approval For...128536012025-10-05 13:33:18111 days ago1759671198IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0066868533
Set Approval For...105540942025-04-21 17:35:18278 days ago1745256918IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0133351633
Set Approval For...98038882025-02-27 17:01:18331 days ago1740675678IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.01263231.25
Set Approval For...94935642025-02-05 20:08:48353 days ago1738786128IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0133224933
Set Approval For...94935522025-02-05 20:07:36353 days ago1738786056IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.0133224933
Safe Transfer Fr...93211162025-01-24 16:08:06365 days ago1737734886IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.051904125
Safe Transfer Fr...93211112025-01-24 16:07:36365 days ago1737734856IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.051904125
Safe Transfer Fr...93211082025-01-24 16:07:18365 days ago1737734838IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.051904125
Safe Transfer Fr...93210922025-01-24 16:05:42365 days ago1737734742IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.051904125
Safe Transfer Fr...93210892025-01-24 16:05:24365 days ago1737734724IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.051904125
Set Approval For...89059822024-12-26 7:40:30395 days ago1735198830IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.050528125
Set Approval For...88998562024-12-25 21:22:06395 days ago1735161726IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.050528125
Set Approval For...88101082024-12-19 13:33:42401 days ago1734615222IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.050528125
Set Approval For...86261362024-12-05 21:35:18415 days ago1733434518IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.05086771126
Set Approval For...82566992024-11-09 18:01:30441 days ago1731175290IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.050528125
Set Approval For...76570902024-09-28 11:23:12483 days ago1727522592IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.050528125
Set Approval For...76570862024-09-28 11:22:48483 days ago1727522568IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.050528125
Set Approval For...72359812024-08-29 19:08:24513 days ago1724958504IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.012632125
Set Approval For...68703882024-08-04 2:25:36539 days ago1722738336IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.01564384155
Set Approval For...68530152024-08-02 21:03:06540 days ago1722632586IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.012632125
Set Approval For...67111442024-07-23 21:17:36550 days ago1721769456IN
Evrloot: EVRLOOT_FISHING Token
0 GLMR0.01705683169
View all transactions

View more zero value Internal Transactions in Advanced View mode

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EvrFishRMRKEquippable

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 175 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.19;

import "@rmrk-team/evm-contracts/contracts/RMRK/equippable/RMRKEquippable.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/extension/RMRKRoyalties.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/utils/RMRKCollectionMetadata.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/access/Ownable.sol";
import "./Extensions/IndexedItemPools.sol";
import "./IIndexedInitData.sol";
import "./Extensions/AssetAttributes.sol";

error RMRKMintUnderpriced();
error RMRKMintZero();
error BadInputLengths();

/**
 * @title EvrSoulsRMRKEquippable
 * @author Evrloot Team based on implementations provided by RMRK team
 * @notice Evrloot Loot (Nestable Equippable)
 * @dev This contract is used to mint and manage the Evrloot Loot, across multiple released minting cycles and in-game mints.
 */
contract EvrFishRMRKEquippable is
    RMRKCollectionMetadata,
    RMRKRoyalties,
    RMRKEquippable,
    IndexedItemPools,
    Ownable,
    IIndexedInitData,
    AssetAttributes
{
    uint256 private _totalAssets;

    address private _identifyContract;

    /**
     * @notice Used to initialize the smart contract.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     * @param collectionMetadata_ The collection metadata URI
     * @param royaltyRecipient The address to which royalties are sent
     * @param royaltyPercentageBps The royalty percentage in basis points
     * @param initData The `InitData` struct containing additional initialization data
     */
    constructor(
        string memory name_,
        string memory symbol_,
        string memory collectionMetadata_,
        address royaltyRecipient,
        uint256 royaltyPercentageBps, //in basis points
        IndexedInitData memory initData
    )
        IndexedItemPools(initData.poolIds, initData.tokenURI)
        RMRKCollectionMetadata(collectionMetadata_)
        RMRKRoyalties(royaltyRecipient, royaltyPercentageBps)
        RMRKEquippable(name_, symbol_)
    {}

    /**
     * @notice Permits contract owner to specify a metadata URI for a provided itemId
     * @dev Can only be called by the owner or a contributor.
     * @dev This is used to configure new categories of Fish (i.e. rewards) from Evrloot Missions and Expeditions
     *      and is not intended for subsequent metadata updates.
     * @param poolId_ The poolId for which to set the token URI
     * @param tokenURI_ The token URI to set
     */
    function setPoolTokenURI(
        uint16 poolId_,
        string memory tokenURI_
    ) public onlyOwnerOrContributor {
        _setPoolTokenURI(poolId_, tokenURI_);
    }

    /**
     * @notice Used to mint the desired number of tokens to the specified address.
     * @dev The `data` value of the `_safeMint` method is set to an empty value.
     * @dev Can only be called by the owner or a contributor.
     * @param to Address to which to mint the token
     * @param numToMint Number of tokens to mint
     * @param itemId_ Index of the token URI to use for the minted tokens
     */
    function mint(
        address to,
        uint256 numToMint,
        uint16 itemId_
    ) public payable onlyOwnerOrContributor {
        (uint256 nextToken, uint256 totalSupplyOffset) = _preMint(numToMint);

        for (uint256 i = nextToken; i < totalSupplyOffset; ) {
            _safeMint(to, i, "");
            _setItemId(i, itemId_);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Used to calculate the token IDs of tokens to be minted.
     * @param numToMint Amount of tokens to be minted
     * @return uint256 The ID of the first token to be minted in the current minting cycle
     */
    function _preMint(uint256 numToMint) private returns (uint256, uint256) {
        if (numToMint == uint256(0)) revert RMRKMintZero();

        uint256 nextToken = _totalSupply + 1;
        unchecked {
            _totalSupply += numToMint;
        }
        uint256 totalSupplyOffset = _totalSupply + 1;

        return (nextToken, totalSupplyOffset);
    }

    /**
     * @notice Used to mint the desired number of tokens with assets to the specified address.
     * @dev Can only be called by the owner or a contributor.
     * @param to Address to which to mint the token
     * @param numToMint Number of tokens to mint
     * @param itemId_ Index of the token URI to use for the minted tokens (on-chain nft categorical id)
     * @param assets Array of asset IDs to add to each token (auto accepted)
     */
    function mintWithAssets(
        address to,
        uint256 numToMint,
        uint16[] memory itemId_,
        uint64[][] memory assets
    ) public onlyOwnerOrContributor {
        if (assets.length != numToMint) revert BadInputLengths();
        if (itemId_.length != numToMint) revert BadInputLengths();

        (uint256 nextToken, uint256 totalSupplyOffset) = _preMint(numToMint);
        for (uint256 i = nextToken; i < totalSupplyOffset; ) {
            _safeMint(to, i, "");
            if (itemId_[i - nextToken] > 0)
                _setItemId(i, itemId_[i - nextToken]);
            for (uint256 j = 0; j < assets[i - nextToken].length; ) {
                _addAssetToToken(i, assets[i - nextToken][j], 0);
                _acceptAsset(i, 0, assets[i - nextToken][j]);
                unchecked {
                    ++j;
                }
            }
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Used to mint a desired number of child tokens to a given parent token.
     * @dev The `data` value of the `_safeMint` method is set to an empty value.
     * @dev Can only be called while the open sale is open.
     * @param to Address of the collection smart contract of the token into which to mint the child token
     * @param numToMint Number of tokens to mint
     * @param destinationId ID of the token into which to mint the new child token
     */
    function nestMint(
        address to,
        uint256 numToMint,
        uint256 destinationId,
        uint16 itemId_
    ) public onlyOwnerOrContributor returns (uint256) {
        (uint256 nextToken, uint256 totalSupplyOffset) = _preMint(numToMint);

        for (uint256 i = nextToken; i < totalSupplyOffset; ) {
            _nestMint(to, i, destinationId, "");
            _setItemId(i, itemId_);
            unchecked {
                ++i;
            }
        }
        return nextToken;
    }

    /** @notice Used to mint new tokens to souls, with preloaded and accepted assets
     * @dev This is useful for rewards minting.
     * @param to Address to which to mint the token
     * @param numToMint Number of tokens to mint
     * @param destinationId ID of the token into which to mint the new child token
     * @param assets Array of asset IDs to add to the token (auto accepted)
     * @param itemId_ Index of the token URI to use for the minted tokens (on-chain nft categorical id)
     */
    function nestMintWithAssets(
        address to,
        uint256 numToMint,
        uint256 destinationId,
        uint64[] memory assets,
        uint16 itemId_
    ) public onlyOwnerOrContributor returns (uint256) {
        (uint256 nextToken, uint256 totalSupplyOffset) = _preMint(numToMint);

        for (uint256 i = nextToken; i < totalSupplyOffset; ) {
            _nestMint(to, i, destinationId, "");
            _setItemId(i, itemId_);
            for (uint256 j = 0; j < assets.length; j++) {
                _addAssetToToken(i, assets[j], 0);
                _acceptAsset(i, 0, assets[j]);
            }

            unchecked {
                ++i;
            }
        }
        return nextToken;
    }

    /**
     * @inheritdoc IERC6059
     */
    function burn(
        uint256 tokenId,
        uint256 maxChildrenBurns
    ) public override onlyApprovedOrOwner(tokenId) returns (uint256) {
        _decrementSupply();
        return _burn(tokenId, maxChildrenBurns);
    }

    /**
     * @notice Used to add an asset to a token.
     * @dev If the given asset is already added to the token, the execution will be reverted.
     * @dev If the asset ID is invalid, the execution will be reverted.
     * @dev If the token already has the maximum amount of pending assets (128), the execution will be
     *  reverted.
     * @dev If the asset is being added by the current root owner of the token, the asset will be automatically
     *  accepted.
     * @param tokenId ID of the token to add the asset to
     * @param assetId ID of the asset to add to the token
     * @param replacesAssetWithId ID of the asset to replace from the token's list of active assets
     */
    function addAssetToToken(
        uint256 tokenId,
        uint64 assetId,
        uint64 replacesAssetWithId,
        uint16 setsItemId // used with Identify() to set the item id
    ) public onlyOwnerOrContributor {
        _addAssetToToken(tokenId, assetId, replacesAssetWithId);

        _acceptAsset(tokenId, _pendingAssets[tokenId].length - 1, assetId);

        if (setsItemId > 0) _setItemId(tokenId, setsItemId);
    }

    /**
     * @notice Used to add multiple assets to one or multiple tokens.
     * @dev If any given asset is already added to the token, the execution will be reverted.
     * @dev If any asset ID is invalid, the execution will be reverted.
     * @dev If any token already has the maximum amount of pending assets (128), the execution will be
     *  reverted.
     * @dev If any asset is being added by the current root owner of the token, the asset will be automatically
     *  accepted.
     * @param tokenIds IDs of the token to add the asset to
     * @param assetIds IDs of the asset to add to the token
     * @param replacesAssetWithIds IDs of the asset to replace from the token's list of active assets
     */

    function batchAddAssetsToTokens(
        uint256[] memory tokenIds,
        uint64[] memory assetIds,
        uint64[] memory replacesAssetWithIds,
        uint16[] memory setsItemId // used with Identify() to set the item id
    ) public onlyOwnerOrContributor {
        for (uint256 i = 0; i < tokenIds.length; ) {
            _addAssetToToken(tokenIds[i], assetIds[i], replacesAssetWithIds[i]);

            _acceptAsset(
                tokenIds[i],
                _pendingAssets[tokenIds[i]].length - 1,
                assetIds[i]
            );

            if (setsItemId[i] != 0) _setItemId(tokenIds[i], setsItemId[i]);

            unchecked {
                ++i;
            }
        }
        return;
    }

    /**
     * @notice Used to add an equippable asset entry.
     * @dev The ID of the asset is automatically assigned to be the next available asset ID.
     * @param equippableGroupId ID of the equippable group
     * @param catalogAddress Address of the `Catalog` smart contract this asset belongs to
     * @param metadataURI Metadata URI of the asset
     * @param partIds An array of IDs of fixed and slot parts to be included in the asset
     * @return uint256 The total number of assets after this asset has been added
     */
    function addEquippableAssetEntry(
        uint64 equippableGroupId,
        address catalogAddress,
        string memory metadataURI,
        uint64[] calldata partIds,
        uint16[] calldata attributes
    ) public onlyOwnerOrContributor returns (uint256) {
        unchecked {
            _totalAssets += 1;
        }
        _addAssetEntry(
            uint64(_totalAssets),
            equippableGroupId,
            catalogAddress,
            metadataURI,
            partIds
        );
        _setAttributes(_totalAssets, attributes);
        return _totalAssets;
    }

    /**
     * @notice Used to add a asset entry.
     * @dev The ID of the asset is automatically assigned to be the next available asset ID.
     * @param metadataURI Metadata URI of the asset
     */
    function addAssetEntry(
        string memory metadataURI
    ) public onlyOwnerOrContributor returns (uint256) {
        unchecked {
            _totalAssets += 1;
        }
        _addAssetEntry(uint64(_totalAssets), metadataURI);
        return _totalAssets;
    }

    /**
     * @notice Used to declare that the assets belonging to a given `equippableGroupId` are equippable into the `Slot`
     *  associated with the `partId` of the collection at the specified `parentAddress`
     * @param equippableGroupId ID of the equippable group
     * @param parentAddress Address of the parent into which the equippable group can be equipped into
     * @param partId ID of the `Slot` that the items belonging to the equippable group can be equipped into
     */
    function setValidParentForEquippableGroup(
        uint64 equippableGroupId,
        address parentAddress,
        uint64 partId
    ) public onlyOwnerOrContributor {
        _setValidParentForEquippableGroup(
            equippableGroupId,
            parentAddress,
            partId
        );
    }

    /**
     * @notice Used to retrieve the total number of assets.
     * @return The total number of assets
     */
    function totalAssets() public view returns (uint256) {
        return _totalAssets;
    }

    /**
     * @notice Updates royalty recipient address
     * @param newRoyaltyRecipient Address of the new royalty recipient
     */
    function updateRoyaltyRecipient(
        address newRoyaltyRecipient
    ) public override onlyOwner {
        _setRoyaltyRecipient(newRoyaltyRecipient);
    }

    /**
     *@notice Sets the mission contract address.
     *   @dev That is used for auto acceptance of assets added to tokens.
     */
    function setIdentifyContract(address identifyContract) public onlyOwner {
        _identifyContract = identifyContract;
    }

    /**
     * @notice Used to retrieve the maximum supply of the collection.
     * @return uint256 The maximum supply of tokens in the collection
     */
    /*
    function maxSupply() public view returns (uint256) {
        return _maxSupply;
    }
    */
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/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 paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

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

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

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

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

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

File 5 of 28 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (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 `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://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 functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/Context.sol";
import "../library/RMRKErrors.sol";

/**
 * @title Ownable
 * @author RMRK team
 * @notice A minimal ownable smart contractf or owner and contributors.
 * @dev This smart contract is based on "openzeppelin's access/Ownable.sol".
 */
contract Ownable is Context {
    address private _owner;
    mapping(address => uint256) private _contributors;

    /**
     * @notice Used to anounce the transfer of ownership.
     * @param previousOwner Address of the account that transferred their ownership role
     * @param newOwner Address of the account receiving the ownership role
     */
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @notice Event that signifies that an address was granted contributor role or that the permission has been
     *  revoked.
     * @dev This can only be triggered by a current owner, so there is no need to include that information in the event.
     * @param contributor Address of the account that had contributor role status updated
     * @param isContributor A boolean value signifying whether the role has been granted (`true`) or revoked (`false`)
     */
    event ContributorUpdate(address indexed contributor, bool isContributor);

    /**
     * @dev Reverts if called by any account other than the owner or an approved contributor.
     */
    modifier onlyOwnerOrContributor() {
        _onlyOwnerOrContributor();
        _;
    }

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

    /**
     * @dev Initializes the contract by setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

    /**
     * @notice Leaves the contract without owner. Functions using the `onlyOwner` modifier will be disabled.
     * @dev Can only be called by the current owner.
     * @dev Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is
     *  only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

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

    /**
     * @notice Transfers ownership of the contract to a new owner.
     * @dev Internal function without access restriction.
     * @dev Emits ***OwnershipTransferred*** event.
     * @param newOwner Address of the new owner's account
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @notice Adds or removes a contributor to the smart contract.
     * @dev Can only be called by the owner.
     * @dev Emits ***ContributorUpdate*** event.
     * @param contributor Address of the contributor's account
     * @param grantRole A boolean value signifying whether the contributor role is being granted (`true`) or revoked
     *  (`false`)
     */
    function manageContributor(
        address contributor,
        bool grantRole
    ) external onlyOwner {
        if (contributor == address(0)) revert RMRKNewContributorIsZeroAddress();
        grantRole
            ? _contributors[contributor] = 1
            : _contributors[contributor] = 0;
        emit ContributorUpdate(contributor, grantRole);
    }

    /**
     * @notice Used to check if the address is one of the contributors.
     * @param contributor Address of the contributor whose status we are checking
     * @return Boolean value indicating whether the address is a contributor or not
     */
    function isContributor(address contributor) public view returns (bool) {
        return _contributors[contributor] == 1;
    }

    /**
     * @notice Used to verify that the caller is either the owner or a contributor.
     * @dev If the caller is not the owner or a contributor, the execution will be reverted.
     */
    function _onlyOwnerOrContributor() private view {
        if (owner() != _msgSender() && !isContributor(_msgSender()))
            revert RMRKNotOwnerOrContributor();
    }

    /**
     * @notice Used to verify that the caller is the owner.
     * @dev If the caller is not the owner, the execution will be reverted.
     */
    function _onlyOwner() private view {
        if (owner() != _msgSender()) revert RMRKNotOwner();
    }
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @title IRMRKCatalog
 * @author RMRK team
 * @notice An interface Catalog for RMRK equippable module.
 */
interface IRMRKCatalog is IERC165 {
    /**
     * @notice Event to announce addition of a new part.
     * @dev It is emitted when a new part is added.
     * @param partId ID of the part that was added
     * @param itemType Enum value specifying whether the part is `None`, `Slot` and `Fixed`
     * @param zIndex An uint specifying the z value of the part. It is used to specify the depth which the part should
     *  be rendered at
     * @param equippableAddresses An array of addresses that can equip this part
     * @param metadataURI The metadata URI of the part
     */
    event AddedPart(
        uint64 indexed partId,
        ItemType indexed itemType,
        uint8 zIndex,
        address[] equippableAddresses,
        string metadataURI
    );

    /**
     * @notice Event to announce new equippables to the part.
     * @dev It is emitted when new addresses are marked as equippable for `partId`.
     * @param partId ID of the part that had new equippable addresses added
     * @param equippableAddresses An array of the new addresses that can equip this part
     */
    event AddedEquippables(
        uint64 indexed partId,
        address[] equippableAddresses
    );

    /**
     * @notice Event to announce the overriding of equippable addresses of the part.
     * @dev It is emitted when the existing list of addresses marked as equippable for `partId` is overwritten by a new one.
     * @param partId ID of the part whose list of equippable addresses was overwritten
     * @param equippableAddresses The new, full, list of addresses that can equip this part
     */
    event SetEquippables(uint64 indexed partId, address[] equippableAddresses);

    /**
     * @notice Event to announce that a given part can be equipped by any address.
     * @dev It is emitted when a given part is marked as equippable by any.
     * @param partId ID of the part marked as equippable by any address
     */
    event SetEquippableToAll(uint64 indexed partId);

    /**
     * @notice Used to define a type of the item. Possible values are `None`, `Slot` or `Fixed`.
     * @dev Used for fixed and slot parts.
     */
    enum ItemType {
        None,
        Slot,
        Fixed
    }

    /**
     * @notice The integral structure of a standard RMRK catalog item defining it.
     * @dev Requires a minimum of 3 storage slots per catalog item, equivalent to roughly 60,000 gas as of Berlin hard
     *  fork (April 14, 2021), though 5-7 storage slots is more realistic, given the standard length of an IPFS URI.
     *  This will result in between 25,000,000 and 35,000,000 gas per 250 assets--the maximum block size of Ethereum
     *  mainnet is 30M at peak usage.
     * @return itemType The item type of the part
     * @return z The z value of the part defining how it should be rendered when presenting the full NFT
     * @return equippable The array of addresses allowed to be equipped in this part
     * @return metadataURI The metadata URI of the part
     */
    struct Part {
        ItemType itemType; //1 byte
        uint8 z; //1 byte
        address[] equippable; //n Collections that can be equipped into this slot
        string metadataURI; //n bytes 32+
    }

    /**
     * @notice The structure used to add a new `Part`.
     * @dev The part is added with specified ID, so you have to make sure that you are using an unused `partId`,
     *  otherwise the addition of the part vill be reverted.
     * @dev The full `IntakeStruct` looks like this:
     *  [
     *          partID,
     *      [
     *          itemType,
     *          z,
     *          [
     *               permittedCollectionAddress0,
     *               permittedCollectionAddress1,
     *               permittedCollectionAddress2
     *           ],
     *           metadataURI
     *       ]
     *   ]
     * @return partId ID to be assigned to the `Part`
     * @return part A `Part` to be added
     */
    struct IntakeStruct {
        uint64 partId;
        Part part;
    }

    /**
     * @notice Used to return the metadata URI of the associated Catalog.
     * @return Catalog metadata URI
     */
    function getMetadataURI() external view returns (string memory);

    /**
     * @notice Used to return the `itemType` of the associated Catalog
     * @return `itemType` of the associated Catalog
     */
    function getType() external view returns (string memory);

    /**
     * @notice Used to check whether the given address is allowed to equip the desired `Part`.
     * @dev Returns true if a collection may equip asset with `partId`.
     * @param partId The ID of the part that we are checking
     * @param targetAddress The address that we are checking for whether the part can be equipped into it or not
     * @return The status indicating whether the `targetAddress` can be equipped into `Part` with `partId` or not
     */
    function checkIsEquippable(
        uint64 partId,
        address targetAddress
    ) external view returns (bool);

    /**
     * @notice Used to check if the part is equippable by all addresses.
     * @dev Returns true if part is equippable to all.
     * @param partId ID of the part that we are checking
     * @return The status indicating whether the part with `partId` can be equipped by any address or not
     */
    function checkIsEquippableToAll(uint64 partId) external view returns (bool);

    /**
     * @notice Used to retrieve a `Part` with id `partId`
     * @param partId ID of the part that we are retrieving
     * @return The `Part` struct associated with given `partId`
     */
    function getPart(uint64 partId) external view returns (Part memory);

    /**
     * @notice Used to retrieve multiple parts at the same time.
     * @param partIds An array of part IDs that we want to retrieve
     * @return An array of `Part` structs associated with given `partIds`
     */
    function getParts(
        uint64[] memory partIds
    ) external view returns (Part[] memory);
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title IRMRKCore
 * @author RMRK team
 * @notice Interface smart contract for RMRK core module.
 */
interface IRMRKCore {
    /**
     * @notice Used to retrieve the collection name.
     * @return Name of the collection
     */
    function name() external view returns (string memory);

    /**
     * @notice Used to retrieve the collection symbol.
     * @return Symbol of the collection
     */
    function symbol() external view returns (string memory);
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "./IRMRKCore.sol";

/**
 * @title RMRKCore
 * @author RMRK team
 * @notice Smart contract of the RMRK core module.
 * @dev This is currently just a passthrough contract which allows for granular editing of base-level ERC721 functions.
 */
contract RMRKCore is IRMRKCore {
    /**
     * @notice Version of the @rmrk-team/evm-contracts package
     * @return Version identifier of the smart contract
     */
    string public constant VERSION = "1.2.1";
    bytes4 public constant RMRK_INTERFACE = 0x524D524B; // "RMRK" in ASCII hex

    /**
     * @notice Used to initialize the smart contract.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /// Token name
    string private _name;

    /// Token symbol
    string private _symbol;

    /**
     * @notice Used to retrieve the collection name.
     * @return Name of the collection
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @notice Used to retrieve the collection symbol.
     * @return Symbol of the collection
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @notice Hook that is called before any token transfer. This includes minting and burning.
     * @dev 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 to `to`.
     *  - When `to` is zero, ``from``'s `tokenId` will be burned.
     *  - `from` and `to` are never zero at the same time.
     *
     *  To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token is being transferred
     * @param to Address to which the token is being transferred
     * @param tokenId ID of the token being transferred
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @notice Hook that is called after any transfer of tokens. This includes minting and burning.
     * @dev Calling conditions:
     *
     *  - When `from` and `to` are both non-zero.
     *  - `from` and `to` are never zero at the same time.
     *
     *  To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token has been transferred
     * @param to Address to which the token has been transferred
     * @param tokenId ID of the token that has been transferred
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "../multiasset/IERC5773.sol";

/**
 * @title IERC6220
 * @author RMRK team
 * @notice Interface smart contract of the RMRK equippable module.
 */
interface IERC6220 is IERC5773 {
    /**
     * @notice Used to store the core structure of the `Equippable` RMRK lego.
     * @return assetId The ID of the asset equipping a child
     * @return childAssetId The ID of the asset used as equipment
     * @return childId The ID of token that is equipped
     * @return childEquippableAddress Address of the collection to which the child asset belongs to
     */
    struct Equipment {
        uint64 assetId;
        uint64 childAssetId;
        uint256 childId;
        address childEquippableAddress;
    }

    /**
     * @notice Used to provide a struct for inputing equip data.
     * @dev Only used for input and not storage of data.
     * @return tokenId ID of the token we are managing
     * @return childIndex Index of a child in the list of token's active children
     * @return assetId ID of the asset that we are equipping into
     * @return slotPartId ID of the slot part that we are using to equip
     * @return childAssetId ID of the asset that we are equipping
     */
    struct IntakeEquip {
        uint256 tokenId;
        uint256 childIndex;
        uint64 assetId;
        uint64 slotPartId;
        uint64 childAssetId;
    }

    /**
     * @notice Used to notify listeners that a child's asset has been equipped into one of its parent assets.
     * @param tokenId ID of the token that had an asset equipped
     * @param assetId ID of the asset associated with the token we are equipping into
     * @param slotPartId ID of the slot we are using to equip
     * @param childId ID of the child token we are equipping into the slot
     * @param childAddress Address of the child token's collection
     * @param childAssetId ID of the asset associated with the token we are equipping
     */
    event ChildAssetEquipped(
        uint256 indexed tokenId,
        uint64 indexed assetId,
        uint64 indexed slotPartId,
        uint256 childId,
        address childAddress,
        uint64 childAssetId
    );

    /**
     * @notice Used to notify listeners that a child's asset has been unequipped from one of its parent assets.
     * @param tokenId ID of the token that had an asset unequipped
     * @param assetId ID of the asset associated with the token we are unequipping out of
     * @param slotPartId ID of the slot we are unequipping from
     * @param childId ID of the token being unequipped
     * @param childAddress Address of the collection that a token that is being unequipped belongs to
     * @param childAssetId ID of the asset associated with the token we are unequipping
     */
    event ChildAssetUnequipped(
        uint256 indexed tokenId,
        uint64 indexed assetId,
        uint64 indexed slotPartId,
        uint256 childId,
        address childAddress,
        uint64 childAssetId
    );

    /**
     * @notice Used to notify listeners that the assets belonging to a `equippableGroupId` have been marked as
     *  equippable into a given slot and parent
     * @param equippableGroupId ID of the equippable group being marked as equippable into the slot associated with
     *  `slotPartId` of the `parentAddress` collection
     * @param slotPartId ID of the slot part of the catalog into which the parts belonging to the equippable group
     *  associated with `equippableGroupId` can be equipped
     * @param parentAddress Address of the collection into which the parts belonging to `equippableGroupId` can be
     *  equipped
     */
    event ValidParentEquippableGroupIdSet(
        uint64 indexed equippableGroupId,
        uint64 indexed slotPartId,
        address parentAddress
    );

    /**
     * @notice Used to equip a child into a token.
     * @dev The `IntakeEquip` stuct contains the following data:
     *  [
     *      tokenId,
     *      childIndex,
     *      assetId,
     *      slotPartId,
     *      childAssetId
     *  ]
     * @param data An `IntakeEquip` struct specifying the equip data
     */
    function equip(IntakeEquip memory data) external;

    /**
     * @notice Used to unequip child from parent token.
     * @dev This can only be called by the owner of the token or by an account that has been granted permission to
     *  manage the given token by the current owner.
     * @param tokenId ID of the parent from which the child is being unequipped
     * @param assetId ID of the parent's asset that contains the `Slot` into which the child is equipped
     * @param slotPartId ID of the `Slot` from which to unequip the child
     */
    function unequip(
        uint256 tokenId,
        uint64 assetId,
        uint64 slotPartId
    ) external;

    /**
     * @notice Used to check whether the token has a given child equipped.
     * @dev This is used to prevent from transferring a child that is equipped.
     * @param tokenId ID of the parent token for which we are querying for
     * @param childAddress Address of the child token's smart contract
     * @param childId ID of the child token
     * @return A boolean value indicating whether the child token is equipped into the given token or not
     */
    function isChildEquipped(
        uint256 tokenId,
        address childAddress,
        uint256 childId
    ) external view returns (bool);

    /**
     * @notice Used to verify whether a token can be equipped into a given parent's slot.
     * @param parent Address of the parent token's smart contract
     * @param tokenId ID of the token we want to equip
     * @param assetId ID of the asset associated with the token we want to equip
     * @param slotId ID of the slot that we want to equip the token into
     * @return A boolean indicating whether the token with the given asset can be equipped into the desired slot
     */
    function canTokenBeEquippedWithAssetIntoSlot(
        address parent,
        uint256 tokenId,
        uint64 assetId,
        uint64 slotId
    ) external view returns (bool);

    /**
     * @notice Used to get the Equipment object equipped into the specified slot of the desired token.
     * @dev The `Equipment` struct consists of the following data:
     *  [
     *      assetId,
     *      childAssetId,
     *      childId,
     *      childEquippableAddress
     *  ]
     * @param tokenId ID of the token for which we are retrieving the equipped object
     * @param targetCatalogAddress Address of the `Catalog` associated with the `Slot` part of the token
     * @param slotPartId ID of the `Slot` part that we are checking for equipped objects
     * @return The `Equipment` struct containing data about the equipped object
     */
    function getEquipment(
        uint256 tokenId,
        address targetCatalogAddress,
        uint64 slotPartId
    ) external view returns (Equipment memory);

    /**
     * @notice Used to get the asset and equippable data associated with given `assetId`.
     * @param tokenId ID of the token for which to retrieve the asset
     * @param assetId ID of the asset of which we are retrieving
     * @return metadataURI The metadata URI of the asset
     * @return equippableGroupId ID of the equippable group this asset belongs to
     * @return catalogAddress The address of the catalog the part belongs to
     * @return partIds An array of IDs of parts included in the asset
     */
    function getAssetAndEquippableData(
        uint256 tokenId,
        uint64 assetId
    )
        external
        view
        returns (
            string memory metadataURI,
            uint64 equippableGroupId,
            address catalogAddress,
            uint64[] memory partIds
        );
}

// SPDX-License-Identifier: Apache-2.0

//Generally all interactions should propagate downstream

pragma solidity ^0.8.18;

import "../catalog/IRMRKCatalog.sol";
import "../library/RMRKLib.sol";
import "../multiasset/AbstractMultiAsset.sol";
import "../nestable/RMRKNestable.sol";
import "../security/ReentrancyGuard.sol";
import "./IERC6220.sol";

/**
 * @title RMRKEquippable
 * @author RMRK team
 * @notice Smart contract of the RMRK Equippable module.
 */
contract RMRKEquippable is
    ReentrancyGuard,
    RMRKNestable,
    AbstractMultiAsset,
    IERC6220
{
    using RMRKLib for uint64[];

    // ------------------- ASSETS --------------

    // ------------------- ASSET APPROVALS --------------

    /**
     * @notice Mapping from token ID to approver address to approved address for assets.
     * @dev The approver is necessary so approvals are invalidated for nested children on transfer.
     * @dev WARNING: If a child NFT returns the original root owner, old permissions would be active again.
     */
    mapping(uint256 => mapping(address => address))
        private _tokenApprovalsForAssets;

    // ------------------- EQUIPPABLE --------------
    /// Mapping of uint64 asset ID to corresponding catalog address.
    mapping(uint64 => address) private _catalogAddresses;
    /// Mapping of uint64 ID to asset object.
    mapping(uint64 => uint64) private _equippableGroupIds;
    /// Mapping of assetId to catalog parts applicable to this asset, both fixed and slot
    mapping(uint64 => uint64[]) private _partIds;

    /// Mapping of token ID to catalog address to slot part ID to equipment information. Used to compose an NFT.
    mapping(uint256 => mapping(address => mapping(uint64 => Equipment)))
        private _equipments;

    /// Mapping of token ID to child (nestable) address to child ID to count of equipped items. Used to check if equipped.
    mapping(uint256 => mapping(address => mapping(uint256 => uint256)))
        private _equipCountPerChild;

    /// Mapping of `equippableGroupId` to parent contract address and valid `slotId`.
    mapping(uint64 => mapping(address => uint64)) private _validParentSlots;

    /**
     * @notice Used to verify that the caller is either the owner of the given token or approved to manage the token's assets
     *  of the owner.
     * @param tokenId ID of the token that we are checking
     */
    function _onlyApprovedForAssetsOrOwner(uint256 tokenId) private view {
        if (!_isApprovedForAssetsOrOwner(_msgSender(), tokenId))
            revert RMRKNotApprovedForAssetsOrOwner();
    }

    /**
     * @notice Used to ensure that the caller is either the owner of the given token or approved to manage the token's assets
     *  of the owner.
     * @dev If that is not the case, the execution of the function will be reverted.
     * @param tokenId ID of the token that we are checking
     */
    modifier onlyApprovedForAssetsOrOwner(uint256 tokenId) {
        _onlyApprovedForAssetsOrOwner(tokenId);
        _;
    }

    // ----------------------------- CONSTRUCTOR ------------------------------

    /**
     * @notice Initializes the contract by setting a `name` and a `symbol` of the token collection.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     */
    constructor(
        string memory name_,
        string memory symbol_
    ) RMRKNestable(name_, symbol_) {}

    /**
     * @inheritdoc IERC165
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(IERC165, RMRKNestable) returns (bool) {
        return
            RMRKNestable.supportsInterface(interfaceId) ||
            interfaceId == type(IERC5773).interfaceId ||
            interfaceId == type(IERC6220).interfaceId;
    }

    // ------------------------------- ASSETS ------------------------------

    // --------------------------- ASSET HANDLERS -------------------------

    /**
     * @notice Accepts a asset at from the pending array of given token.
     * @dev Migrates the asset from the token's pending asset array to the token's active asset array.
     * @dev Active assets cannot be removed by anyone, but can be replaced by a new asset.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     *  - `index` must be in range of the length of the pending asset array.
     * @dev Emits an {AssetAccepted} event.
     * @param tokenId ID of the token for which to accept the pending asset
     * @param index Index of the asset in the pending array to accept
     * @param assetId ID of the asset that is being accepted
     */
    function acceptAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _acceptAsset(tokenId, index, assetId);
    }

    /**
     * @notice Rejects a asset from the pending array of given token.
     * @dev Removes the asset from the token's pending asset array.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     *  - `index` must be in range of the length of the pending asset array.
     * @dev Emits a {AssetRejected} event.
     * @param tokenId ID of the token that the asset is being rejected from
     * @param index Index of the asset in the pending array to be rejected
     * @param assetId ID of the asset that is being rejected
     */
    function rejectAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _rejectAsset(tokenId, index, assetId);
    }

    /**
     * @notice Rejects all assets from the pending array of a given token.
     * @dev Effecitvely deletes the pending array.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     * @dev Emits a {AssetRejected} event with assetId = 0.
     * @param tokenId ID of the token of which to clear the pending array.
     * @param maxRejections Maximum number of expected assets to reject, used to prevent from rejecting assets which
     *  arrive just before this operation.
     */
    function rejectAllAssets(
        uint256 tokenId,
        uint256 maxRejections
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _rejectAllAssets(tokenId, maxRejections);
    }

    /**
     * @notice Sets a new priority array for a given token.
     * @dev The priority array is a non-sequential list of `uint64`s, where the lowest value is considered highest
     *  priority.
     * @dev Value `0` of a priority is a special case equivalent to unitialized.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     *  - The length of `priorities` must be equal the length of the active assets array.
     * @dev Emits a {AssetPrioritySet} event.
     * @param tokenId ID of the token to set the priorities for
     * @param priorities An array of priority values
     */
    function setPriority(
        uint256 tokenId,
        uint64[] calldata priorities
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _setPriority(tokenId, priorities);
    }

    // --------------------------- ASSET INTERNALS -------------------------

    /**
     * @notice Used to add a asset entry.
     * @dev This internal function warrants custom access control to be implemented when used.
     * @param id ID of the asset being added
     * @param equippableGroupId ID of the equippable group being marked as equippable into the slot associated with
     *  `Parts` of the `Slot` type
     * @param catalogAddress Address of the `Catalog` associated with the asset
     * @param metadataURI The metadata URI of the asset
     * @param partIds An array of IDs of fixed and slot parts to be included in the asset
     */
    function _addAssetEntry(
        uint64 id,
        uint64 equippableGroupId,
        address catalogAddress,
        string memory metadataURI,
        uint64[] memory partIds
    ) internal virtual {
        _addAssetEntry(id, metadataURI);

        if (catalogAddress == address(0) && partIds.length != 0)
            revert RMRKCatalogRequiredForParts();

        _catalogAddresses[id] = catalogAddress;
        _equippableGroupIds[id] = equippableGroupId;
        _partIds[id] = partIds;
    }

    // ----------------------- ASSET APPROVALS ------------------------

    /**
     * @notice Used to grant approvals for specific tokens to a specified address.
     * @dev This can only be called by the owner of the token or by an account that has been granted permission to
     *  manage all of the owner's assets.
     * @param to Address of the account to receive the approval to the specified token
     * @param tokenId ID of the token for which we are granting the permission
     */
    function approveForAssets(address to, uint256 tokenId) public virtual {
        address owner = ownerOf(tokenId);
        if (to == owner) revert RMRKApprovalForAssetsToCurrentOwner();

        if (
            _msgSender() != owner &&
            !isApprovedForAllForAssets(owner, _msgSender())
        ) revert RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll();
        _approveForAssets(to, tokenId);
    }

    /**
     * @notice Used to get the address of the user that is approved to manage the specified token from the current
     *  owner.
     * @param tokenId ID of the token we are checking
     * @return Address of the account that is approved to manage the token
     */
    function getApprovedForAssets(
        uint256 tokenId
    ) public view virtual returns (address) {
        _requireMinted(tokenId);
        return _tokenApprovalsForAssets[tokenId][ownerOf(tokenId)];
    }

    /**
     * @notice Internal function to check whether the queried user is either:
     *   1. The root owner of the token associated with `tokenId`.
     *   2. Is approved for all assets of the current owner via the `setApprovalForAllForAssets` function.
     *   3. Is granted approval for the specific tokenId for asset management via the `approveForAssets` function.
     * @param user Address of the user we are checking for permission
     * @param tokenId ID of the token to query for permission for a given `user`
     * @return A boolean value indicating whether the user is approved to manage the token or not
     */
    function _isApprovedForAssetsOrOwner(
        address user,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        address owner = ownerOf(tokenId);
        return (user == owner ||
            isApprovedForAllForAssets(owner, user) ||
            getApprovedForAssets(tokenId) == user);
    }

    /**
     * @notice Internal function for granting approvals for a specific token.
     * @param to Address of the account we are granting an approval to
     * @param tokenId ID of the token we are granting the approval for
     */
    function _approveForAssets(address to, uint256 tokenId) internal virtual {
        address owner = ownerOf(tokenId);
        _tokenApprovalsForAssets[tokenId][owner] = to;
        emit ApprovalForAssets(owner, to, tokenId);
    }

    /**
     * @notice Used to clear the approvals on a given token.
     * @param tokenId ID of the token we are clearing the approvals of
     */
    function _cleanApprovals(uint256 tokenId) internal virtual override {
        _approveForAssets(address(0), tokenId);
    }

    // ------------------------------- EQUIPPING ------------------------------

    /**
     * @inheritdoc RMRKNestable
     */
    function _transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual override {
        if (!isPending) {
            if (isChildEquipped(tokenId, childAddress, childId))
                revert RMRKMustUnequipFirst();
        }
        super._transferChild(
            tokenId,
            to,
            destinationId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );
    }

    /**
     * @inheritdoc IERC6220
     */
    function equip(
        IntakeEquip memory data
    ) public virtual onlyApprovedOrOwner(data.tokenId) nonReentrant {
        _equip(data);
    }

    /**
     * @notice Private function used to equip a child into a token.
     * @dev If the `Slot` already has an item equipped, the execution will be reverted.
     * @dev If the child can't be used in the given `Slot`, the execution will be reverted.
     * @dev If the catalog doesn't allow this equip to happen, the execution will be reverted.
     * @dev The `IntakeEquip` stuct contains the following data:
     *  [
     *      tokenId,
     *      childIndex,
     *      assetId,
     *      slotPartId,
     *      childAssetId
     *  ]
     * @dev Emits ***ChildAssetEquipped*** event.
     * @param data An `IntakeEquip` struct specifying the equip data
     */
    function _equip(IntakeEquip memory data) internal virtual {
        address catalogAddress = _catalogAddresses[data.assetId];
        uint64 slotPartId = data.slotPartId;
        if (
            _equipments[data.tokenId][catalogAddress][slotPartId]
                .childEquippableAddress != address(0)
        ) revert RMRKSlotAlreadyUsed();

        // Check from parent's asset perspective:
        _checkAssetAcceptsSlot(data.assetId, slotPartId);

        IERC6059.Child memory child = childOf(data.tokenId, data.childIndex);

        // Check from child perspective intention to be used in part
        // We add reentrancy guard because of this call, it happens before updating state
        if (
            !IERC6220(child.contractAddress)
                .canTokenBeEquippedWithAssetIntoSlot(
                    address(this),
                    child.tokenId,
                    data.childAssetId,
                    slotPartId
                )
        ) revert RMRKTokenCannotBeEquippedWithAssetIntoSlot();

        // Check from catalog perspective
        if (
            !IRMRKCatalog(catalogAddress).checkIsEquippable(
                slotPartId,
                child.contractAddress
            )
        ) revert RMRKEquippableEquipNotAllowedByCatalog();

        _beforeEquip(data);
        Equipment memory newEquip = Equipment({
            assetId: data.assetId,
            childAssetId: data.childAssetId,
            childId: child.tokenId,
            childEquippableAddress: child.contractAddress
        });

        _equipments[data.tokenId][catalogAddress][slotPartId] = newEquip;
        _equipCountPerChild[data.tokenId][child.contractAddress][
            child.tokenId
        ] += 1;

        emit ChildAssetEquipped(
            data.tokenId,
            data.assetId,
            slotPartId,
            child.tokenId,
            child.contractAddress,
            data.childAssetId
        );
        _afterEquip(data);
    }

    /**
     * @notice Private function to check if a given asset accepts a given slot or not.
     * @dev Execution will be reverted if the `Slot` does not apply for the asset.
     * @param assetId ID of the asset
     * @param slotPartId ID of the `Slot`
     */
    function _checkAssetAcceptsSlot(
        uint64 assetId,
        uint64 slotPartId
    ) private view {
        (, bool found) = _partIds[assetId].indexOf(slotPartId);
        if (!found) revert RMRKTargetAssetCannotReceiveSlot();
    }

    /**
     * @inheritdoc IERC6220
     */
    function unequip(
        uint256 tokenId,
        uint64 assetId,
        uint64 slotPartId
    ) public virtual onlyApprovedOrOwner(tokenId) {
        _unequip(tokenId, assetId, slotPartId);
    }

    /**
     * @notice Private function used to unequip child from parent token.
     * @dev Emits ***ChildAssetUnequipped*** event.
     * @param tokenId ID of the parent from which the child is being unequipped
     * @param assetId ID of the parent's asset that contains the `Slot` into which the child is equipped
     * @param slotPartId ID of the `Slot` from which to unequip the child
     */
    function _unequip(
        uint256 tokenId,
        uint64 assetId,
        uint64 slotPartId
    ) internal virtual {
        address targetCatalogAddress = _catalogAddresses[assetId];
        Equipment memory equipment = _equipments[tokenId][targetCatalogAddress][
            slotPartId
        ];
        if (equipment.childEquippableAddress == address(0))
            revert RMRKNotEquipped();
        _beforeUnequip(tokenId, assetId, slotPartId);

        delete _equipments[tokenId][targetCatalogAddress][slotPartId];
        _equipCountPerChild[tokenId][equipment.childEquippableAddress][
            equipment.childId
        ] -= 1;

        emit ChildAssetUnequipped(
            tokenId,
            assetId,
            slotPartId,
            equipment.childId,
            equipment.childEquippableAddress,
            equipment.childAssetId
        );
        _afterUnequip(tokenId, assetId, slotPartId);
    }

    /**
     * @inheritdoc IERC6220
     */
    function isChildEquipped(
        uint256 tokenId,
        address childAddress,
        uint256 childId
    ) public view virtual returns (bool) {
        return _equipCountPerChild[tokenId][childAddress][childId] != 0;
    }

    // --------------------- ADMIN VALIDATION ---------------------

    /**
     * @notice Internal function used to declare that the assets belonging to a given `equippableGroupId` are
     *  equippable into the `Slot` associated with the `partId` of the collection at the specified `parentAddress`.
     * @dev Emits ***ValidParentEquippableGroupIdSet*** event.
     * @param equippableGroupId ID of the equippable group
     * @param parentAddress Address of the parent into which the equippable group can be equipped into
     * @param slotPartId ID of the `Slot` that the items belonging to the equippable group can be equipped into
     */
    function _setValidParentForEquippableGroup(
        uint64 equippableGroupId,
        address parentAddress,
        uint64 slotPartId
    ) internal virtual {
        if (equippableGroupId == uint64(0) || slotPartId == uint64(0))
            revert RMRKIdZeroForbidden();
        _validParentSlots[equippableGroupId][parentAddress] = slotPartId;
        emit ValidParentEquippableGroupIdSet(
            equippableGroupId,
            slotPartId,
            parentAddress
        );
    }

    /**
     * @inheritdoc IERC6220
     */
    function canTokenBeEquippedWithAssetIntoSlot(
        address parent,
        uint256 tokenId,
        uint64 assetId,
        uint64 slotId
    ) public view virtual returns (bool) {
        uint64 equippableGroupId = _equippableGroupIds[assetId];
        uint64 equippableSlot = _validParentSlots[equippableGroupId][parent];
        if (equippableSlot == slotId) {
            (, bool found) = getActiveAssets(tokenId).indexOf(assetId);
            return found;
        }
        return false;
    }

    // --------------------- Getting Extended Assets ---------------------

    /**
     * @inheritdoc IERC6220
     */
    function getAssetAndEquippableData(
        uint256 tokenId,
        uint64 assetId
    )
        public
        view
        virtual
        returns (string memory, uint64, address, uint64[] memory)
    {
        return (
            getAssetMetadata(tokenId, assetId),
            _equippableGroupIds[assetId],
            _catalogAddresses[assetId],
            _partIds[assetId]
        );
    }

    ////////////////////////////////////////
    //              UTILS
    ////////////////////////////////////////

    /**
     * @inheritdoc IERC6220
     */
    function getEquipment(
        uint256 tokenId,
        address targetCatalogAddress,
        uint64 slotPartId
    ) public view virtual returns (Equipment memory) {
        return _equipments[tokenId][targetCatalogAddress][slotPartId];
    }

    // HOOKS

    /**
     * @notice A hook to be called before a equipping a asset to the token.
     * @dev The `IntakeEquip` struct consist of the following data:
     *  [
     *      tokenId,
     *      childIndex,
     *      assetId,
     *      slotPartId,
     *      childAssetId
     *  ]
     * @param data The `IntakeEquip` struct containing data of the asset that is being equipped
     */
    function _beforeEquip(IntakeEquip memory data) internal virtual {}

    /**
     * @notice A hook to be called after equipping a asset to the token.
     * @dev The `IntakeEquip` struct consist of the following data:
     *  [
     *      tokenId,
     *      childIndex,
     *      assetId,
     *      slotPartId,
     *      childAssetId
     *  ]
     * @param data The `IntakeEquip` struct containing data of the asset that was equipped
     */
    function _afterEquip(IntakeEquip memory data) internal virtual {}

    /**
     * @notice A hook to be called before unequipping a asset from the token.
     * @param tokenId ID of the token from which the asset is being unequipped
     * @param assetId ID of the asset being unequipped
     * @param slotPartId ID of the slot from which the asset is being unequipped
     */
    function _beforeUnequip(
        uint256 tokenId,
        uint64 assetId,
        uint64 slotPartId
    ) internal virtual {}

    /**
     * @notice A hook to be called after unequipping a asset from the token.
     * @param tokenId ID of the token from which the asset was unequipped
     * @param assetId ID of the asset that was unequipped
     * @param slotPartId ID of the slot from which the asset was unequipped
     */
    function _afterUnequip(
        uint256 tokenId,
        uint64 assetId,
        uint64 slotPartId
    ) internal virtual {}
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "../library/RMRKErrors.sol";

/**
 * @title RMRKRoyalties
 * @author RMRK team
 * @notice Smart contract of the RMRK Royalties module.
 */
abstract contract RMRKRoyalties is IERC2981 {
    address private _royaltyRecipient;
    uint256 private _royaltyPercentageBps;

    /**
     * @notice Used to initiate the smart contract.
     * @dev `royaltyPercentageBps` is expressed in basis points, so 1 basis point equals 0.01% and 500 basis points
     *  equal 5%.
     * @param royaltyRecipient Address to which royalties should be sent
     * @param royaltyPercentageBps The royalty percentage expressed in basis points
     */
    constructor(
        address royaltyRecipient,
        uint256 royaltyPercentageBps //in basis points
    ) {
        _setRoyaltyRecipient(royaltyRecipient);
        if (royaltyPercentageBps >= 10000) revert RMRKRoyaltiesTooHigh();
        _royaltyPercentageBps = royaltyPercentageBps;
    }

    /**
     * @notice Used to update recipient of royalties.
     * @dev Custom access control has to be implemented to ensure that only the intended actors can update the
     *  beneficiary.
     * @param newRoyaltyRecipient Address of the new recipient of royalties
     */
    function updateRoyaltyRecipient(
        address newRoyaltyRecipient
    ) external virtual;

    /**
     * @notice Used to update the royalty recipient.
     * @param newRoyaltyRecipient Address of the new recipient of royalties
     */
    function _setRoyaltyRecipient(address newRoyaltyRecipient) internal {
        _royaltyRecipient = newRoyaltyRecipient;
    }

    /**
     * @notice Used to retrieve the recipient of royalties.
     * @return Address of the recipient of royalties
     */
    function getRoyaltyRecipient() public view virtual returns (address) {
        return _royaltyRecipient;
    }

    /**
     * @notice Used to retrieve the specified royalty percentage.
     * @return The royalty percentage expressed in the basis points
     */
    function getRoyaltyPercentage() public view virtual returns (uint256) {
        return _royaltyPercentageBps;
    }

    /**
     * @notice Used to retrieve the information about who shall receive royalties of a sale of the specified token and
     *  how much they will be.
     * @param tokenId ID of the token for which the royalty info is being retrieved
     * @param salePrice Price of the token sale
     * @return receiver The beneficiary receiving royalties of the sale
     * @return royaltyAmount The value of the royalties recieved by the `receiver` from the sale
     */
    function royaltyInfo(
        uint256 tokenId,
        uint256 salePrice
    )
        external
        view
        virtual
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        receiver = _royaltyRecipient;
        royaltyAmount = (salePrice * _royaltyPercentageBps) / 10000;
    }
}

File 18 of 28 : RMRKErrors.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/// @title RMRKErrors
/// @author RMRK team
/// @notice A collection of errors used in the RMRK suite
/// @dev Errors are kept in a centralised file in order to provide a central point of reference and to avoid error
///  naming collisions due to inheritance

/// Attempting to grant the token to 0x0 address
error ERC721AddressZeroIsNotaValidOwner();
/// Attempting to grant approval to the current owner of the token
error ERC721ApprovalToCurrentOwner();
/// Attempting to grant approval when not being owner or approved for all should not be permitted
error ERC721ApproveCallerIsNotOwnerNorApprovedForAll();
/// Attempting to get approvals for a token owned by 0x0 (considered non-existent)
error ERC721ApprovedQueryForNonexistentToken();
/// Attempting to grant approval to self
error ERC721ApproveToCaller();
/// Attempting to use an invalid token ID
error ERC721InvalidTokenId();
/// Attempting to mint to 0x0 address
error ERC721MintToTheZeroAddress();
/// Attempting to manage a token without being its owner or approved by the owner
error ERC721NotApprovedOrOwner();
/// Attempting to mint an already minted token
error ERC721TokenAlreadyMinted();
/// Attempting to transfer the token from an address that is not the owner
error ERC721TransferFromIncorrectOwner();
/// Attempting to safe transfer to an address that is unable to receive the token
error ERC721TransferToNonReceiverImplementer();
/// Attempting to transfer the token to a 0x0 address
error ERC721TransferToTheZeroAddress();
/// Attempting to grant approval of assets to their current owner
error RMRKApprovalForAssetsToCurrentOwner();
/// Attempting to grant approval of assets without being the caller or approved for all
error RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll();
/// Attempting to incorrectly configue a Catalog item
error RMRKBadConfig();
/// Attempting to set the priorities with an array of length that doesn't match the length of active assets array
error RMRKBadPriorityListLength();
/// Attempting to add an asset entry with `Part`s, without setting the `Catalog` address
error RMRKCatalogRequiredForParts();
/// Attempting to transfer a soulbound (non-transferrable) token
error RMRKCannotTransferSoulbound();
/// Attempting to accept a child that has already been accepted
error RMRKChildAlreadyExists();
/// Attempting to interact with a child, using index that is higher than the number of children
error RMRKChildIndexOutOfRange();
/// Attempting to find the index of a child token on a parent which does not own it.
error RMRKChildNotFoundInParent();
/// Attempting to pass collaborator address array and collaborator permission array of different lengths
error RMRKCollaboratorArraysNotEqualLength();
/// Attempting to register a collection that is already registered
error RMRKCollectionAlreadyRegistered();
/// Attempting to manage or interact with colleciton that is not registered
error RMRKCollectionNotRegistered();
/// Attempting to equip a `Part` with a child not approved by the Catalog
error RMRKEquippableEquipNotAllowedByCatalog();
/// Attempting to use ID 0, which is not supported
/// @dev The ID 0 in RMRK suite is reserved for empty values. Guarding against its use ensures the expected operation
error RMRKIdZeroForbidden();
/// Attempting to interact with an asset, using index greater than number of assets
error RMRKIndexOutOfRange();
/// Attempting to reclaim a child that can't be reclaimed
error RMRKInvalidChildReclaim();
/// Attempting to interact with an end-user account when the contract account is expected
error RMRKIsNotContract();
/// Attempting to interact with a contract that had its operation locked
error RMRKLocked();
/// Attempting to add a pending child after the number of pending children has reached the limit (default limit is 128)
error RMRKMaxPendingChildrenReached();
/// Attempting to add a pending asset after the number of pending assets has reached the limit (default limit is
///  128)
error RMRKMaxPendingAssetsReached();
/// Attempting to burn a total number of recursive children higher than maximum set
/// @param childContract Address of the collection smart contract in which the maximum number of recursive burns was reached
/// @param childId ID of the child token at which the maximum number of recursive burns was reached
error RMRKMaxRecursiveBurnsReached(address childContract, uint256 childId);
/// Attempting to mint a number of tokens that would cause the total supply to be greater than maximum supply
error RMRKMintOverMax();
/// Attempting to mint a nested token to a smart contract that doesn't support nesting
error RMRKMintToNonRMRKNestableImplementer();
/// Attempting to pass complementary arrays of different lengths
error RMRKMismachedArrayLength();
/// Attempting to transfer a child before it is unequipped
error RMRKMustUnequipFirst();
/// Attempting to nest a child over the nestable limit (current limit is 100 levels of nesting)
error RMRKNestableTooDeep();
/// Attempting to nest the token to own descendant, which would create a loop and leave the looped tokens in limbo
error RMRKNestableTransferToDescendant();
/// Attempting to nest the token to a smart contract that doesn't support nesting
error RMRKNestableTransferToNonRMRKNestableImplementer();
/// Attempting to nest the token into itself
error RMRKNestableTransferToSelf();
/// Attempting to interact with an asset that can not be found
error RMRKNoAssetMatchingId();
/// Attempting to manage an asset without owning it or having been granted permission by the owner to do so
error RMRKNotApprovedForAssetsOrOwner();
/// Attempting to interact with a token without being its owner or having been granted permission by the
///  owner to do so
/// @dev When a token is nested, only the direct owner (NFT parent) can mange it. In that case, approved addresses are
///  not allowed to manage it, in order to ensure the expected behaviour
error RMRKNotApprovedOrDirectOwner();
/// Attempting to manage a collection without being the collection's collaborator
error RMRKNotCollectionCollaborator();
/// Attemting to manage a collection without being the collection's issuer
error RMRKNotCollectionIssuer();
/// Attempting to manage a collection without being the collection's issuer or collaborator
error RMRKNotCollectionIssuerOrCollaborator();
/// Attempting to compose an asset wihtout having an associated Catalog
error RMRKNotComposableAsset();
/// Attempting to unequip an item that isn't equipped
error RMRKNotEquipped();
/// Attempting to interact with a management function without being the smart contract's owner
error RMRKNotOwner();
/// Attempting to interact with a function without being the owner or contributor of the collection
error RMRKNotOwnerOrContributor();
/// Attempting to manage a collection without being the specific address
error RMRKNotSpecificAddress();
/// Attempting to manage a token without being its owner
error RMRKNotTokenOwner();
/// Attempting to transfer the ownership to the 0x0 address
error RMRKNewOwnerIsZeroAddress();
/// Attempting to assign a 0x0 address as a contributor
error RMRKNewContributorIsZeroAddress();
/// Attemtping to use `Ownable` interface without implementing it
error RMRKOwnableNotImplemented();
/// Attempting an operation requiring the token being nested, while it is not
error RMRKParentIsNotNFT();
/// Attempting to add a `Part` with an ID that is already used
error RMRKPartAlreadyExists();
/// Attempting to use a `Part` that doesn't exist
error RMRKPartDoesNotExist();
/// Attempting to use a `Part` that is `Fixed` when `Slot` kind of `Part` should be used
error RMRKPartIsNotSlot();
/// Attempting to interact with a pending child using an index greater than the size of pending array
error RMRKPendingChildIndexOutOfRange();
/// Attempting to add an asset using an ID that has already been used
error RMRKAssetAlreadyExists();
/// Attempting to equip an item into a slot that already has an item equipped
error RMRKSlotAlreadyUsed();
/// Attempting to equip an item into a `Slot` that the target asset does not implement
error RMRKTargetAssetCannotReceiveSlot();
/// Attempting to equip a child into a `Slot` and parent that the child's collection doesn't support
error RMRKTokenCannotBeEquippedWithAssetIntoSlot();
/// Attempting to compose a NFT of a token without active assets
error RMRKTokenDoesNotHaveAsset();
/// Attempting to determine the asset with the top priority on a token without assets
error RMRKTokenHasNoAssets();
/// Attempting to accept or transfer a child which does not match the one at the specified index
error RMRKUnexpectedChildId();
/// Attempting to reject all pending assets but more assets than expected are pending
error RMRKUnexpectedNumberOfAssets();
/// Attempting to reject all pending children but children assets than expected are pending
error RMRKUnexpectedNumberOfChildren();
/// Attempting to accept or reject an asset which does not match the one at the specified index
error RMRKUnexpectedAssetId();
/// Attempting an operation expecting a parent to the token which is not the actual one
error RMRKUnexpectedParent();
/// Attempting not to pass an empty array of equippable addresses when adding or setting the equippable addresses
error RMRKZeroLengthIdsPassed();
/// Attempting to set the royalties to a value higher than 100% (10000 in base points)
error RMRKRoyaltiesTooHigh();
/// Attempting to do a bulk operation on a token that is not owned by the caller
error RMRKCanOnlyDoBulkOperationsOnOwnedTokens();
/// Attempting to do a bulk operation with multiple tokens at a time
error RMRKCanOnlyDoBulkOperationsWithOneTokenAtATime();

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title RMRKLib
 * @author RMRK team
 * @notice RMRK library smart contract.
 */
library RMRKLib {
    /**
     * @notice Used to remove an item from the array using the specified index.
     * @dev The item is removed by replacing it with the last item and removing the last element.
     * @param array An array of items containing the item to be removed
     * @param index Index of the item to remove
     */
    function removeItemByIndex(uint64[] storage array, uint256 index) internal {
        //Check to see if this is already gated by require in all calls
        require(index < array.length);
        array[index] = array[array.length - 1];
        array.pop();
    }

    /**
     * @notice Used to determine the index of the item in the array by spedifying its value.
     * @dev This was adapted from Cryptofin-Solidity `arrayUtils`.
     * @dev If the item is not found the index returned will equal `0`.
     * @param A The array containing the item to be found
     * @param a The value of the item to find the index of
     * @return The index of the item in the array
     * @return A boolean value specifying whether the item was found
     */
    function indexOf(
        uint64[] memory A,
        uint64 a
    ) internal pure returns (uint256, bool) {
        uint256 length = A.length;
        for (uint256 i; i < length; ) {
            if (A[i] == a) {
                return (i, true);
            }
            unchecked {
                ++i;
            }
        }
        return (0, false);
    }
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "./IERC5773.sol";
import "../library/RMRKLib.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "../library/RMRKErrors.sol";

/**
 * @title AbstractMultiAsset
 * @author RMRK team
 * @notice Abstract Smart contract implementing most of the common logic for contracts implementing IERC5773
 */
abstract contract AbstractMultiAsset is Context, IERC5773 {
    using RMRKLib for uint64[];

    /// Mapping of uint64 Ids to asset metadata
    mapping(uint64 => string) private _assets;

    /// Mapping of tokenId to new asset, to asset to be replaced
    mapping(uint256 => mapping(uint64 => uint64)) private _assetReplacements;

    /// Mapping of tokenId to an array of active assets
    /// @dev Active recurses is unbounded, getting all would reach gas limit at around 30k items
    /// so we leave this as internal in case a custom implementation needs to implement pagination
    mapping(uint256 => uint64[]) internal _activeAssets;

    /// Mapping of tokenId to an array of pending assets
    mapping(uint256 => uint64[]) internal _pendingAssets;

    /// Mapping of tokenId to an array of priorities for active assets
    mapping(uint256 => uint64[]) internal _activeAssetPriorities;

    /// Mapping of tokenId to assetId to whether the token has this asset assigned
    mapping(uint256 => mapping(uint64 => bool)) private _tokenAssets;

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

    /**
     * @inheritdoc IERC5773
     */
    function getAssetMetadata(
        uint256 tokenId,
        uint64 assetId
    ) public view virtual returns (string memory) {
        if (!_tokenAssets[tokenId][assetId]) revert RMRKTokenDoesNotHaveAsset();
        return _assets[assetId];
    }

    /**
     * @inheritdoc IERC5773
     */
    function getActiveAssets(
        uint256 tokenId
    ) public view virtual returns (uint64[] memory) {
        return _activeAssets[tokenId];
    }

    /**
     * @inheritdoc IERC5773
     */
    function getPendingAssets(
        uint256 tokenId
    ) public view virtual returns (uint64[] memory) {
        return _pendingAssets[tokenId];
    }

    /**
     * @inheritdoc IERC5773
     */
    function getActiveAssetPriorities(
        uint256 tokenId
    ) public view virtual returns (uint64[] memory) {
        return _activeAssetPriorities[tokenId];
    }

    /**
     * @inheritdoc IERC5773
     */
    function getAssetReplacements(
        uint256 tokenId,
        uint64 newAssetId
    ) public view virtual returns (uint64) {
        return _assetReplacements[tokenId][newAssetId];
    }

    /**
     * @inheritdoc IERC5773
     */
    function isApprovedForAllForAssets(
        address owner,
        address operator
    ) public view virtual returns (bool) {
        return _operatorApprovalsForAssets[owner][operator];
    }

    /**
     * @inheritdoc IERC5773
     */
    function setApprovalForAllForAssets(
        address operator,
        bool approved
    ) public virtual {
        if (_msgSender() == operator)
            revert RMRKApprovalForAssetsToCurrentOwner();

        _operatorApprovalsForAssets[_msgSender()][operator] = approved;
        emit ApprovalForAllForAssets(_msgSender(), operator, approved);
    }

    /**
     * @notice Used to accept a pending asset.
     * @dev The call is reverted if there is no pending asset at a given index.
     * @dev Emits ***AssetAccepted*** event.
     * @param tokenId ID of the token for which to accept the pending asset
     * @param index Index of the asset in the pending array to accept
     * @param assetId ID of the asset to accept in token's pending array
     */
    function _acceptAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) internal virtual {
        _validatePendingAssetAtIndex(tokenId, index, assetId);
        _beforeAcceptAsset(tokenId, index, assetId);

        uint64 replacesId = _assetReplacements[tokenId][assetId];
        uint256 replaceIndex;
        bool replacefound;
        if (replacesId != uint64(0))
            (replaceIndex, replacefound) = _activeAssets[tokenId].indexOf(
                replacesId
            );

        if (replacefound) {
            // We don't want to remove and then push a new asset.
            // This way we also keep the priority of the original asset
            _activeAssets[tokenId][replaceIndex] = assetId;
            delete _tokenAssets[tokenId][replacesId];
        } else {
            // We use the current size as next priority, by default priorities would be [0,1,2...]
            _activeAssetPriorities[tokenId].push(
                uint64(_activeAssets[tokenId].length)
            );
            _activeAssets[tokenId].push(assetId);
            replacesId = uint64(0);
        }
        _removePendingAsset(tokenId, index, assetId);

        emit AssetAccepted(tokenId, assetId, replacesId);
        _afterAcceptAsset(tokenId, index, assetId);
    }

    /**
     * @notice Used to reject the specified asset from the pending array.
     * @dev The call is reverted if there is no pending asset at a given index.
     * @dev Emits ***AssetRejected*** event.
     * @param tokenId ID of the token that the asset is being rejected from
     * @param index Index of the asset in the pending array to be rejected
     * @param assetId ID of the asset expected to be in the index
     */
    function _rejectAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) internal virtual {
        _validatePendingAssetAtIndex(tokenId, index, assetId);
        _beforeRejectAsset(tokenId, index, assetId);

        _removePendingAsset(tokenId, index, assetId);
        delete _tokenAssets[tokenId][assetId];

        emit AssetRejected(tokenId, assetId);
        _afterRejectAsset(tokenId, index, assetId);
    }

    /**
     * @notice Used to validate the index on the pending assets array
     * @dev The call is reverted if the index is out of range or the asset Id is not present at the index.
     * @param tokenId ID of the token that the asset is validated from
     * @param index Index of the asset in the pending array
     * @param assetId Id of the asset expected to be in the index
     */
    function _validatePendingAssetAtIndex(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) private view {
        if (index >= _pendingAssets[tokenId].length)
            revert RMRKIndexOutOfRange();
        if (assetId != _pendingAssets[tokenId][index])
            revert RMRKUnexpectedAssetId();
    }

    /**
     * @notice Used to remove the asset at the index on the pending assets array
     * @param tokenId ID of the token that the asset is being removed from
     * @param index Index of the asset in the pending array
     * @param assetId Id of the asset expected to be in the index
     */
    function _removePendingAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) private {
        _pendingAssets[tokenId].removeItemByIndex(index);
        delete _assetReplacements[tokenId][assetId];
    }

    /**
     * @notice Used to reject all of the pending assets for the given token.
     * @dev When rejecting all assets, the pending array is indiscriminately cleared.
     * @dev If the number of pending assets is greater than the value of `maxRejections`, the exectuion will be
     *  reverted.
     * @dev Emits ***AssetRejected*** event.
     * @param tokenId ID of the token to reject all of the pending assets.
     * @param maxRejections Maximum number of expected assets to reject, used to prevent from
     *  rejecting assets which arrive just before this operation.
     */
    function _rejectAllAssets(
        uint256 tokenId,
        uint256 maxRejections
    ) internal virtual {
        uint256 len = _pendingAssets[tokenId].length;
        if (len > maxRejections) revert RMRKUnexpectedNumberOfAssets();

        _beforeRejectAllAssets(tokenId);

        for (uint256 i; i < len; ) {
            uint64 assetId = _pendingAssets[tokenId][i];
            delete _assetReplacements[tokenId][assetId];
            unchecked {
                ++i;
            }
        }
        delete (_pendingAssets[tokenId]);

        emit AssetRejected(tokenId, uint64(0));
        _afterRejectAllAssets(tokenId);
    }

    /**
     * @notice Used to specify the priorities for a given token's active assets.
     * @dev If the length of the priorities array doesn't match the length of the active assets array, the execution
     *  will be reverted.
     * @dev The position of the priority value in the array corresponds the position of the asset in the active
     *  assets array it will be applied to.
     * @dev Emits ***AssetPrioritySet*** event.
     * @param tokenId ID of the token for which the priorities are being set
     * @param priorities Array of priorities for the assets
     */
    function _setPriority(
        uint256 tokenId,
        uint64[] calldata priorities
    ) internal virtual {
        uint256 length = priorities.length;
        if (length != _activeAssets[tokenId].length)
            revert RMRKBadPriorityListLength();

        _beforeSetPriority(tokenId, priorities);
        _activeAssetPriorities[tokenId] = priorities;

        emit AssetPrioritySet(tokenId);
        _afterSetPriority(tokenId, priorities);
    }

    /**
     * @notice Used to add an asset entry.
     * @dev If the specified ID is already used by another asset, the execution will be reverted.
     * @dev This internal function warrants custom access control to be implemented when used.
     * @dev Emits ***AssetSet*** event.
     * @param id ID of the asset to assign to the new asset
     * @param metadataURI Metadata URI of the asset
     */
    function _addAssetEntry(
        uint64 id,
        string memory metadataURI
    ) internal virtual {
        if (id == uint64(0)) revert RMRKIdZeroForbidden();
        if (bytes(_assets[id]).length > 0) revert RMRKAssetAlreadyExists();

        _beforeAddAsset(id, metadataURI);
        _assets[id] = metadataURI;

        emit AssetSet(id);
        _afterAddAsset(id, metadataURI);
    }

    /**
     * @notice Used to add an asset to a token.
     * @dev If the given asset is already added to the token, the execution will be reverted.
     * @dev If the asset ID is invalid, the execution will be reverted.
     * @dev If the token already has the maximum amount of pending assets (128), the execution will be
     *  reverted.
     * @dev Emits ***AssetAddedToTokens*** event.
     * @param tokenId ID of the token to add the asset to
     * @param assetId ID of the asset to add to the token
     * @param replacesAssetWithId ID of the asset to replace from the token's list of active assets
     */
    function _addAssetToToken(
        uint256 tokenId,
        uint64 assetId,
        uint64 replacesAssetWithId
    ) internal virtual {
        if (_tokenAssets[tokenId][assetId]) revert RMRKAssetAlreadyExists();

        if (bytes(_assets[assetId]).length == uint256(0))
            revert RMRKNoAssetMatchingId();

        if (_pendingAssets[tokenId].length >= 128)
            revert RMRKMaxPendingAssetsReached();

        _beforeAddAssetToToken(tokenId, assetId, replacesAssetWithId);
        _tokenAssets[tokenId][assetId] = true;
        _pendingAssets[tokenId].push(assetId);

        if (replacesAssetWithId != uint64(0)) {
            _assetReplacements[tokenId][assetId] = replacesAssetWithId;
        }

        uint256[] memory tokenIds = new uint256[](1);
        tokenIds[0] = tokenId;
        emit AssetAddedToTokens(tokenIds, assetId, replacesAssetWithId);
        _afterAddAssetToToken(tokenId, assetId, replacesAssetWithId);
    }

    /**
     * @notice Hook that is called before an asset is added.
     * @param id ID of the asset
     * @param metadataURI Metadata URI of the asset
     */
    function _beforeAddAsset(
        uint64 id,
        string memory metadataURI
    ) internal virtual {}

    /**
     * @notice Hook that is called after an asset is added.
     * @param id ID of the asset
     * @param metadataURI Metadata URI of the asset
     */
    function _afterAddAsset(
        uint64 id,
        string memory metadataURI
    ) internal virtual {}

    /**
     * @notice Hook that is called before adding an asset to a token's pending assets array.
     * @dev If the asset doesn't intend to replace another asset, the `replacesAssetWithId` value should be `0`.
     * @param tokenId ID of the token to which the asset is being added
     * @param assetId ID of the asset that is being added
     * @param replacesAssetWithId ID of the asset that this asset is attempting to replace
     */
    function _beforeAddAssetToToken(
        uint256 tokenId,
        uint64 assetId,
        uint64 replacesAssetWithId
    ) internal virtual {}

    /**
     * @notice Hook that is called after an asset has been added to a token's pending assets array.
     * @dev If the asset doesn't intend to replace another asset, the `replacesAssetWithId` value should be `0`.
     * @param tokenId ID of the token to which the asset is has been added
     * @param assetId ID of the asset that is has been added
     * @param replacesAssetWithId ID of the asset that this asset is attempting to replace
     */
    function _afterAddAssetToToken(
        uint256 tokenId,
        uint64 assetId,
        uint64 replacesAssetWithId
    ) internal virtual {}

    /**
     * @notice Hook that is called before an asset is accepted to a token's active assets array.
     * @param tokenId ID of the token for which the asset is being accepted
     * @param index Index of the asset in the token's pending assets array
     * @param assetId ID of the asset expected to be located at the specified `index`
     */
    function _beforeAcceptAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) internal virtual {}

    /**
     * @notice Hook that is called after an asset is accepted to a token's active assets array.
     * @param tokenId ID of the token for which the asset has been accepted
     * @param index Index of the asset in the token's pending assets array
     * @param assetId ID of the asset expected to have been located at the specified `index`
     */
    function _afterAcceptAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) internal virtual {}

    /**
     * @notice Hook that is called before rejecting an asset.
     * @param tokenId ID of the token from which the asset is being rejected
     * @param index Index of the asset in the token's pending assets array
     * @param assetId ID of the asset expected to be located at the specified `index`
     */
    function _beforeRejectAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) internal virtual {}

    /**
     * @notice Hook that is called after rejecting an asset.
     * @param tokenId ID of the token from which the asset has been rejected
     * @param index Index of the asset in the token's pending assets array
     * @param assetId ID of the asset expected to have been located at the specified `index`
     */
    function _afterRejectAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) internal virtual {}

    /**
     * @notice Hook that is called before rejecting all assets of a token.
     * @param tokenId ID of the token from which all of the assets are being rejected
     */
    function _beforeRejectAllAssets(uint256 tokenId) internal virtual {}

    /**
     * @notice Hook that is called after rejecting all assets of a token.
     * @param tokenId ID of the token from which all of the assets have been rejected
     */
    function _afterRejectAllAssets(uint256 tokenId) internal virtual {}

    /**
     * @notice Hook that is called before the priorities for token's assets is set.
     * @param tokenId ID of the token for which the asset priorities are being set
     * @param priorities[] An array of priorities for token's active resources
     */
    function _beforeSetPriority(
        uint256 tokenId,
        uint64[] calldata priorities
    ) internal virtual {}

    /**
     * @notice Hook that is called after the priorities for token's assets is set.
     * @param tokenId ID of the token for which the asset priorities have been set
     * @param priorities[] An array of priorities for token's active resources
     */
    function _afterSetPriority(
        uint256 tokenId,
        uint64[] calldata priorities
    ) internal virtual {}
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @title IERC5773
 * @author RMRK team
 * @notice Interface smart contract of the RMRK multi asset module.
 */
interface IERC5773 is IERC165 {
    /**
     * @notice Used to notify listeners that an asset object is initialized at `assetId`.
     * @param assetId ID of the asset that was initialized
     */
    event AssetSet(uint64 indexed assetId);

    /**
     * @notice Used to notify listeners that an asset object at `assetId` is added to token's pending asset
     *  array.
     * @param tokenIds An array of token IDs that received a new pending asset
     * @param assetId ID of the asset that has been added to the token's pending assets array
     * @param replacesId ID of the asset that would be replaced
     */
    event AssetAddedToTokens(
        uint256[] tokenIds,
        uint64 indexed assetId,
        uint64 indexed replacesId
    );

    /**
     * @notice Used to notify listeners that an asset object at `assetId` is accepted by the token and migrated
     *  from token's pending assets array to active assets array of the token.
     * @param tokenId ID of the token that had a new asset accepted
     * @param assetId ID of the asset that was accepted
     * @param replacesId ID of the asset that was replaced
     */
    event AssetAccepted(
        uint256 indexed tokenId,
        uint64 indexed assetId,
        uint64 indexed replacesId
    );

    /**
     * @notice Used to notify listeners that an asset object at `assetId` is rejected from token and is dropped
     *  from the pending assets array of the token.
     * @param tokenId ID of the token that had an asset rejected
     * @param assetId ID of the asset that was rejected
     */
    event AssetRejected(uint256 indexed tokenId, uint64 indexed assetId);

    /**
     * @notice Used to notify listeners that token's prioritiy array is reordered.
     * @param tokenId ID of the token that had the asset priority array updated
     */
    event AssetPrioritySet(uint256 indexed tokenId);

    /**
     * @notice Used to notify listeners that owner has granted an approval to the user to manage the assets of a
     *  given token.
     * @dev Approvals must be cleared on transfer
     * @param owner Address of the account that has granted the approval for all token's assets
     * @param approved Address of the account that has been granted approval to manage the token's assets
     * @param tokenId ID of the token on which the approval was granted
     */
    event ApprovalForAssets(
        address indexed owner,
        address indexed approved,
        uint256 indexed tokenId
    );

    /**
     * @notice Used to notify listeners that owner has granted approval to the user to manage assets of all of their
     *  tokens.
     * @param owner Address of the account that has granted the approval for all assets on all of their tokens
     * @param operator Address of the account that has been granted the approval to manage the token's assets on all of
     *  the tokens
     * @param approved Boolean value signifying whether the permission has been granted (`true`) or revoked (`false`)
     */
    event ApprovalForAllForAssets(
        address indexed owner,
        address indexed operator,
        bool approved
    );

    /**
     * @notice Accepts an asset at from the pending array of given token.
     * @dev Migrates the asset from the token's pending asset array to the token's active asset array.
     * @dev Active assets cannot be removed by anyone, but can be replaced by a new asset.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     *  - `index` must be in range of the length of the pending asset array.
     * @dev Emits an {AssetAccepted} event.
     * @param tokenId ID of the token for which to accept the pending asset
     * @param index Index of the asset in the pending array to accept
     * @param assetId ID of the asset expected to be in the index
     */
    function acceptAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) external;

    /**
     * @notice Rejects an asset from the pending array of given token.
     * @dev Removes the asset from the token's pending asset array.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     *  - `index` must be in range of the length of the pending asset array.
     * @dev Emits a {AssetRejected} event.
     * @param tokenId ID of the token that the asset is being rejected from
     * @param index Index of the asset in the pending array to be rejected
     * @param assetId ID of the asset expected to be in the index
     */
    function rejectAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) external;

    /**
     * @notice Rejects all assets from the pending array of a given token.
     * @dev Effecitvely deletes the pending array.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     * @dev Emits a {AssetRejected} event with assetId = 0.
     * @param tokenId ID of the token of which to clear the pending array.
     * @param maxRejections Maximum number of expected assets to reject, used to prevent from rejecting assets which
     *  arrive just before this operation.
     */
    function rejectAllAssets(uint256 tokenId, uint256 maxRejections) external;

    /**
     * @notice Sets a new priority array for a given token.
     * @dev The priority array is a non-sequential list of `uint64`s, where the lowest value is considered highest
     *  priority.
     * @dev Value `0` of a priority is a special case equivalent to unitialized.
     * @dev Requirements:
     *
     *  - The caller must own the token or be approved to manage the token's assets
     *  - `tokenId` must exist.
     *  - The length of `priorities` must be equal the length of the active assets array.
     * @dev Emits a {AssetPrioritySet} event.
     * @param tokenId ID of the token to set the priorities for
     * @param priorities An array of priorities of active assets. The succesion of items in the priorities array
     *  matches that of the succesion of items in the active array
     */
    function setPriority(
        uint256 tokenId,
        uint64[] calldata priorities
    ) external;

    /**
     * @notice Used to retrieve IDs of the active assets of given token.
     * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call
     *  `getAssetMetadata(tokenId, assetId)`.
     * @dev You can safely get 10k
     * @param tokenId ID of the token to retrieve the IDs of the active assets
     * @return An array of active asset IDs of the given token
     */
    function getActiveAssets(
        uint256 tokenId
    ) external view returns (uint64[] memory);

    /**
     * @notice Used to retrieve IDs of the pending assets of given token.
     * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call
     *  `getAssetMetadata(tokenId, assetId)`.
     * @param tokenId ID of the token to retrieve the IDs of the pending assets
     * @return An array of pending asset IDs of the given token
     */
    function getPendingAssets(
        uint256 tokenId
    ) external view returns (uint64[] memory);

    /**
     * @notice Used to retrieve the priorities of the active resoources of a given token.
     * @dev Asset priorities are a non-sequential array of uint64 values with an array size equal to active asset
     *  priorites.
     * @param tokenId ID of the token for which to retrieve the priorities of the active assets
     * @return An array of priorities of the active assets of the given token
     */
    function getActiveAssetPriorities(
        uint256 tokenId
    ) external view returns (uint64[] memory);

    /**
     * @notice Used to retrieve the asset that will be replaced if a given asset from the token's pending array
     *  is accepted.
     * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call
     *  `getAssetMetadata(tokenId, assetId)`.
     * @param tokenId ID of the token to check
     * @param newAssetId ID of the pending asset which will be accepted
     * @return ID of the asset which will be replaced
     */
    function getAssetReplacements(
        uint256 tokenId,
        uint64 newAssetId
    ) external view returns (uint64);

    /**
     * @notice Used to fetch the asset metadata of the specified token's active asset with the given index.
     * @dev Assets are stored by reference mapping `_assets[assetId]`.
     * @dev Can be overriden to implement enumerate, fallback or other custom logic.
     * @param tokenId ID of the token from which to retrieve the asset metadata
     * @param assetId Asset Id, must be in the active assets array
     * @return The metadata of the asset belonging to the specified index in the token's active assets
     *  array
     */
    function getAssetMetadata(
        uint256 tokenId,
        uint64 assetId
    ) external view returns (string memory);

    // Approvals

    /**
     * @notice Used to grant permission to the user to manage token's assets.
     * @dev This differs from transfer approvals, as approvals are not cleared when the approved party accepts or
     *  rejects an asset, or sets asset priorities. This approval is cleared on token transfer.
     * @dev Only a single account can be approved at a time, so approving the `0x0` address clears previous approvals.
     * @dev Requirements:
     *
     *  - The caller must own the token or be an approved operator.
     *  - `tokenId` must exist.
     * @dev Emits an {ApprovalForAssets} event.
     * @param to Address of the account to grant the approval to
     * @param tokenId ID of the token for which the approval to manage the assets is granted
     */
    function approveForAssets(address to, uint256 tokenId) external;

    /**
     * @notice Used to retrieve the address of the account approved to manage assets of a given token.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @param tokenId ID of the token for which to retrieve the approved address
     * @return Address of the account that is approved to manage the specified token's assets
     */
    function getApprovedForAssets(
        uint256 tokenId
    ) external view returns (address);

    /**
     * @notice Used to add or remove an operator of assets for the caller.
     * @dev Operators can call {acceptAsset}, {rejectAsset}, {rejectAllAssets} or {setPriority} for any token
     *  owned by the caller.
     * @dev Requirements:
     *
     *  - The `operator` cannot be the caller.
     * @dev Emits an {ApprovalForAllForAssets} event.
     * @param operator Address of the account to which the operator role is granted or revoked from
     * @param approved The boolean value indicating whether the operator role is being granted (`true`) or revoked
     *  (`false`)
     */
    function setApprovalForAllForAssets(
        address operator,
        bool approved
    ) external;

    /**
     * @notice Used to check whether the address has been granted the operator role by a given address or not.
     * @dev See {setApprovalForAllForAssets}.
     * @param owner Address of the account that we are checking for whether it has granted the operator role
     * @param operator Address of the account that we are checking whether it has the operator role or not
     * @return A boolean value indicating wehter the account we are checking has been granted the operator role
     */
    function isApprovedForAllForAssets(
        address owner,
        address operator
    ) external view returns (bool);
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @title IERC6059
 * @author RMRK team
 * @notice Interface smart contract of the RMRK nestable module.
 */
interface IERC6059 is IERC165 {
    /**
     * @notice The core struct of RMRK ownership.
     * @dev The `DirectOwner` struct is used to store information of the next immediate owner, be it the parent token or
     *  the externally owned account.
     * @dev If the token is owned by the externally owned account, the `tokenId` should equal `0`.
     * @param tokenId ID of the parent token
     * @param ownerAddress Address of the owner of the token. If the owner is another token, then the address should be
     *  the one of the parent token's collection smart contract. If the owner is externally owned account, the address
     *  should be the address of this account
     * @param isNft A boolean value signifying whether the token is owned by another token (`true`) or by an externally
     *  owned account (`false`)
     */
    struct DirectOwner {
        uint256 tokenId;
        address ownerAddress;
    }

    /**
     * @notice Used to notify listeners that the token is being transferred.
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     * @param from Address of the previous immediate owner, which is a smart contract if the token was nested.
     * @param to Address of the new immediate owner, which is a smart contract if the token is being nested.
     * @param fromTokenId ID of the previous parent token. If the token was not nested before, the value should be `0`
     * @param toTokenId ID of the new parent token. If the token is not being nested, the value should be `0`
     * @param tokenId ID of the token being transferred
     */
    event NestTransfer(
        address indexed from,
        address indexed to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 indexed tokenId
    );

    /**
     * @notice Used to notify listeners that a new token has been added to a given token's pending children array.
     * @dev Emitted when a child NFT is added to a token's pending array.
     * @param tokenId ID of the token that received a new pending child token
     * @param childIndex Index of the proposed child token in the parent token's pending children array
     * @param childAddress Address of the proposed child token's collection smart contract
     * @param childId ID of the child token in the child token's collection smart contract
     */
    event ChildProposed(
        uint256 indexed tokenId,
        uint256 childIndex,
        address indexed childAddress,
        uint256 indexed childId
    );

    /**
     * @notice Used to notify listeners that a new child token was accepted by the parent token.
     * @dev Emitted when a parent token accepts a token from its pending array, migrating it to the active array.
     * @param tokenId ID of the token that accepted a new child token
     * @param childIndex Index of the newly accepted child token in the parent token's active children array
     * @param childAddress Address of the child token's collection smart contract
     * @param childId ID of the child token in the child token's collection smart contract
     */
    event ChildAccepted(
        uint256 indexed tokenId,
        uint256 childIndex,
        address indexed childAddress,
        uint256 indexed childId
    );

    /**
     * @notice Used to notify listeners that all pending child tokens of a given token have been rejected.
     * @dev Emitted when a token removes all a child tokens from its pending array.
     * @param tokenId ID of the token that rejected all of the pending children
     */
    event AllChildrenRejected(uint256 indexed tokenId);

    /**
     * @notice Used to notify listeners a child token has been transferred from parent token.
     * @dev Emitted when a token transfers a child from itself, transferring ownership to the root owner.
     * @param tokenId ID of the token that transferred a child token
     * @param childIndex Index of a child in the array from which it is being transferred
     * @param childAddress Address of the child token's collection smart contract
     * @param childId ID of the child token in the child token's collection smart contract
     * @param fromPending A boolean value signifying whether the token was in the pending child tokens array (`true`) or
     *  in the active child tokens array (`false`)
     * @param toZero A boolean value signifying whether the token is being transferred to the `0x0` address (`true`) or
     *  not (`false`)
     */
    event ChildTransferred(
        uint256 indexed tokenId,
        uint256 childIndex,
        address indexed childAddress,
        uint256 indexed childId,
        bool fromPending,
        bool toZero
    );

    /**
     * @notice The core child token struct, holding the information about the child tokens.
     * @return tokenId ID of the child token in the child token's collection smart contract
     * @return contractAddress Address of the child token's smart contract
     */
    struct Child {
        uint256 tokenId;
        address contractAddress;
    }

    /**
     * @notice Used to retrieve the *root* owner of a given token.
     * @dev The *root* owner of the token is an externally owned account (EOA). If the given token is child of another
     *  NFT, this will return an EOA address. Otherwise, if the token is owned by an EOA, this EOA wil be returned.
     * @param tokenId ID of the token for which the *root* owner has been retrieved
     * @return owner The *root* owner of the token
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @notice Used to retrieve the immediate owner of the given token.
     * @dev If the immediate owner is another token, the address returned, should be the one of the parent token's
     *  collection smart contract.
     * @param tokenId ID of the token for which the RMRK owner is being retrieved
     * @return Address of the given token's owner
     * @return The ID of the parent token. Should be `0` if the owner is an externally owned account
     * @return The boolean value signifying whether the owner is an NFT or not
     */
    function directOwnerOf(
        uint256 tokenId
    ) external view returns (address, uint256, bool);

    /**
     * @notice Used to burn a given token.
     * @dev When a token is burned, all of its child tokens are recursively burned as well.
     * @dev When specifying the maximum recursive burns, the execution will be reverted if there are more children to be
     *  burned.
     * @dev Setting the `maxRecursiveBurn` value to 0 will only attempt to burn the specified token and revert if there
     *  are any child tokens present.
     * @dev The approvals are cleared when the token is burned.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits a {Transfer} event.
     * @param tokenId ID of the token to burn
     * @param maxRecursiveBurns Maximum number of tokens to recursively burn
     * @return Number of recursively burned children
     */
    function burn(
        uint256 tokenId,
        uint256 maxRecursiveBurns
    ) external returns (uint256);

    /**
     * @notice Used to add a child token to a given parent token.
     * @dev This adds the child token into the given parent token's pending child tokens array.
     * @dev Requirements:
     *
     *  - `directOwnerOf` on the child contract must resolve to the called contract.
     *  - the pending array of the parent contract must not be full.
     * @param parentId ID of the parent token to receive the new child token
     * @param childId ID of the new proposed child token
     * @param data Additional data with no specified format
     */
    function addChild(
        uint256 parentId,
        uint256 childId,
        bytes memory data
    ) external;

    /**
     * @notice Used to accept a pending child token for a given parent token.
     * @dev This moves the child token from parent token's pending child tokens array into the active child tokens
     *  array.
     * @param parentId ID of the parent token for which the child token is being accepted
     * @param childIndex Index of a child tokem in the given parent's pending children array
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     */
    function acceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) external;

    /**
     * @notice Used to reject all pending children of a given parent token.
     * @dev Removes the children from the pending array mapping.
     * @dev This does not update the ownership storage data on children. If necessary, ownership can be reclaimed by the
     *  rootOwner of the previous parent.
     * @dev Requirements:
     *
     * Requirements:
     *
     * - `parentId` must exist
     * @param parentId ID of the parent token for which to reject all of the pending tokens.
     * @param maxRejections Maximum number of expected children to reject, used to prevent from rejecting children which
     *  arrive just before this operation.
     */
    function rejectAllChildren(
        uint256 parentId,
        uint256 maxRejections
    ) external;

    /**
     * @notice Used to transfer a child token from a given parent token.
     * @dev When transferring a child token, the owner of the token is set to `to`, or is not updated in the event of
     *  `to` being the `0x0` address.
     * @param tokenId ID of the parent token from which the child token is being transferred
     * @param to Address to which to transfer the token to
     * @param destinationId ID of the token to receive this child token (MUST be 0 if the destination is not a token)
     * @param childIndex Index of a token we are transferring, in the array it belongs to (can be either active array or
     *  pending array)
     * @param childAddress Address of the child token's collection smart contract.
     * @param childId ID of the child token in its own collection smart contract.
     * @param isPending A boolean value indicating whether the child token being transferred is in the pending array of
     *  the parent token (`true`) or in the active array (`false`)
     * @param data Additional data with no specified format, sent in call to `_to`
     */
    function transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) external;

    /**
     * @notice Used to retrieve the active child tokens of a given parent token.
     * @dev Returns array of Child structs existing for parent token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which to retrieve the active child tokens
     * @return An array of Child structs containing the parent token's active child tokens
     */
    function childrenOf(
        uint256 parentId
    ) external view returns (Child[] memory);

    /**
     * @notice Used to retrieve the pending child tokens of a given parent token.
     * @dev Returns array of pending Child structs existing for given parent.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which to retrieve the pending child tokens
     * @return An array of Child structs containing the parent token's pending child tokens
     */
    function pendingChildrenOf(
        uint256 parentId
    ) external view returns (Child[] memory);

    /**
     * @notice Used to retrieve a specific active child token for a given parent token.
     * @dev Returns a single Child struct locating at `index` of parent token's active child tokens array.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which the child is being retrieved
     * @param index Index of the child token in the parent token's active child tokens array
     * @return A Child struct containing data about the specified child
     */
    function childOf(
        uint256 parentId,
        uint256 index
    ) external view returns (Child memory);

    /**
     * @notice Used to retrieve a specific pending child token from a given parent token.
     * @dev Returns a single Child struct locating at `index` of parent token's active child tokens array.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which the pending child token is being retrieved
     * @param index Index of the child token in the parent token's pending child tokens array
     * @return A Child struct containting data about the specified child
     */
    function pendingChildOf(
        uint256 parentId,
        uint256 index
    ) external view returns (Child memory);

    /**
     * @notice Used to transfer the token into another token.
     * @param from Address of the direct owner of the token to be transferred
     * @param to Address of the receiving token's collection smart contract
     * @param tokenId ID of the token being transferred
     * @param destinationId ID of the token to receive the token being transferred
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function nestTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) external;
}

File 23 of 28 : RMRKNestable.sol
// SPDX-License-Identifier: Apache-2.0

//Generally all interactions should propagate downstream

pragma solidity ^0.8.18;

import "./IERC6059.sol";
import "../core/RMRKCore.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../library/RMRKErrors.sol";

/**
 * @title RMRKNestable
 * @author RMRK team
 * @notice Smart contract of the RMRK Nestable module.
 * @dev This contract is hierarchy agnostic and can support an arbitrary number of nested levels up and down, as long as
 *  gas limits allow it.
 */
contract RMRKNestable is Context, IERC165, IERC721, IERC6059, RMRKCore {
    using Address for address;

    uint256 private constant _MAX_LEVELS_TO_CHECK_FOR_INHERITANCE_LOOP = 100;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approver address to approved address
    // The approver is necessary so approvals are invalidated for nested children on transfer
    // WARNING: If a child NFT returns to a previous root owner, old permissions would be active again
    mapping(uint256 => mapping(address => address)) private _tokenApprovals;

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

    // ------------------- NESTABLE --------------

    // Mapping from token ID to DirectOwner struct
    mapping(uint256 => DirectOwner) private _RMRKOwners;

    // Mapping of tokenId to array of active children structs
    mapping(uint256 => Child[]) internal _activeChildren;

    // Mapping of tokenId to array of pending children structs
    mapping(uint256 => Child[]) internal _pendingChildren;

    // Mapping of child token address to child token ID to whether they are pending or active on any token
    // We might have a first extra mapping from token ID, but since the same child cannot be nested into multiple tokens
    //  we can strip it for size/gas savings.
    mapping(address => mapping(uint256 => uint256)) internal _childIsInActive;

    // -------------------------- MODIFIERS ----------------------------

    /**
     * @notice Used to verify that the caller is either the owner of the token or approved to manage it by its owner.
     * @dev If the caller is not the owner of the token or approved to manage it by its owner, the execution will be
     *  reverted.
     * @param tokenId ID of the token to check
     */
    function _onlyApprovedOrOwner(uint256 tokenId) private view {
        if (!_isApprovedOrOwner(_msgSender(), tokenId))
            revert ERC721NotApprovedOrOwner();
    }

    /**
     * @notice Used to verify that the caller is either the owner of the token or approved to manage it by its owner.
     * @param tokenId ID of the token to check
     */
    modifier onlyApprovedOrOwner(uint256 tokenId) {
        _onlyApprovedOrOwner(tokenId);
        _;
    }

    /**
     * @notice Used to verify that the caller is approved to manage the given token or it its direct owner.
     * @dev This does not delegate to ownerOf, which returns the root owner, but rater uses an owner from DirectOwner
     *  struct.
     * @dev The execution is reverted if the caller is not immediate owner or approved to manage the given token.
     * @dev Used for parent-scoped transfers.
     * @param tokenId ID of the token to check.
     */
    function _onlyApprovedOrDirectOwner(uint256 tokenId) private view {
        if (!_isApprovedOrDirectOwner(_msgSender(), tokenId))
            revert RMRKNotApprovedOrDirectOwner();
    }

    /**
     * @notice Used to verify that the caller is approved to manage the given token or is its direct owner.
     * @param tokenId ID of the token to check
     */
    modifier onlyApprovedOrDirectOwner(uint256 tokenId) {
        _onlyApprovedOrDirectOwner(tokenId);
        _;
    }

    // ----------------------------- CONSTRUCTOR ------------------------------

    /**
     * @notice Initializes the contract by setting a `name` and a `symbol` to the token collection.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     */
    constructor(
        string memory name_,
        string memory symbol_
    ) RMRKCore(name_, symbol_) {}

    // ------------------------------- ERC721 ---------------------------------
    /**
     * @inheritdoc IERC165
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual returns (bool) {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC6059).interfaceId;
    }

    /**
     * @notice Used to retrieve the number of tokens in `owner`'s account.
     * @param owner Address of the account being checked
     * @return The balance of the given account
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) revert ERC721AddressZeroIsNotaValidOwner();
        return _balances[owner];
    }

    ////////////////////////////////////////
    //              TRANSFERS
    ////////////////////////////////////////

    /**
     * @notice Transfers a given token from `from` to `to`.
     * @dev 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}.
     * @dev Emits a {Transfer} event.
     * @param from Address from which to transfer the token from
     * @param to Address to which to transfer the token to
     * @param tokenId ID of the token to transfer
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual onlyApprovedOrDirectOwner(tokenId) {
        _transfer(from, to, tokenId, "");
    }

    /**
     * @notice Used to safely transfer a given token token from `from` to `to`.
     * @dev 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.
     * @dev Emits a {Transfer} event.
     * @param from Address to transfer the tokens from
     * @param to Address to transfer the tokens to
     * @param tokenId ID of the token to transfer
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @notice Used to safely transfer a given token token from `from` to `to`.
     * @dev 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.
     * @dev Emits a {Transfer} event.
     * @param from Address to transfer the tokens from
     * @param to Address to transfer the tokens to
     * @param tokenId ID of the token to transfer
     * @param data Additional data without a specified format to be sent along with the token transaction
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual onlyApprovedOrDirectOwner(tokenId) {
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @inheritdoc IERC6059
     */
    function nestTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) public virtual onlyApprovedOrDirectOwner(tokenId) {
        _nestTransfer(from, to, tokenId, destinationId, data);
    }

    /**
     * @notice Used to safely transfer the token form `from` to `to`.
     * @dev The function checks that contract recipients are aware of the ERC721 protocol to prevent tokens from being
     *  forever locked.
     * @dev This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. implement alternative
     *  mechanisms to perform token transfer, such as signature-based.
     * @dev Requirements:
     *
     *  - `from` cannot be the zero address.
     *  - `to` cannot be the zero address.
     *  - `tokenId` token must exist and be owned by `from`.
     *  - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     * @dev Emits a {Transfer} event.
     * @param from Address of the account currently owning the given token
     * @param to Address to transfer the token to
     * @param tokenId ID of the token to transfer
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId, data);
        if (!_checkOnERC721Received(from, to, tokenId, data))
            revert ERC721TransferToNonReceiverImplementer();
    }

    /**
     * @notice Used to transfer the token from `from` to `to`.
     * @dev As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     * @dev Requirements:
     *
     *  - `to` cannot be the zero address.
     *  - `tokenId` token must be owned by `from`.
     * @dev Emits a {Transfer} event.
     * @param from Address of the account currently owning the given token
     * @param to Address to transfer the token to
     * @param tokenId ID of the token to transfer
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
        if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner();
        if (to == address(0)) revert ERC721TransferToTheZeroAddress();

        _beforeTokenTransfer(from, to, tokenId);
        _beforeNestedTokenTransfer(
            immediateOwner,
            to,
            parentId,
            0,
            tokenId,
            data
        );

        _balances[from] -= 1;
        _updateOwnerAndClearApprovals(tokenId, 0, to);
        _balances[to] += 1;

        emit Transfer(from, to, tokenId);
        emit NestTransfer(immediateOwner, to, parentId, 0, tokenId);

        _afterTokenTransfer(from, to, tokenId);
        _afterNestedTokenTransfer(
            immediateOwner,
            to,
            parentId,
            0,
            tokenId,
            data
        );
    }

    /**
     * @notice Used to transfer a token into another token.
     * @dev Attempting to nest a token into `0x0` address will result in reverted transaction.
     * @dev Attempting to nest a token into itself will result in reverted transaction.
     * @param from Address of the account currently owning the given token
     * @param to Address of the receiving token's collection smart contract
     * @param tokenId ID of the token to transfer
     * @param destinationId ID of the token receiving the given token
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _nestTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) internal virtual {
        (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
        if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner();
        if (to == address(0)) revert ERC721TransferToTheZeroAddress();
        if (to == address(this) && tokenId == destinationId)
            revert RMRKNestableTransferToSelf();

        // Destination contract checks:
        // It seems redundant, but otherwise it would revert with no error
        if (!to.isContract()) revert RMRKIsNotContract();
        if (!IERC165(to).supportsInterface(type(IERC6059).interfaceId))
            revert RMRKNestableTransferToNonRMRKNestableImplementer();
        _checkForInheritanceLoop(tokenId, to, destinationId);

        _beforeTokenTransfer(from, to, tokenId);
        _beforeNestedTokenTransfer(
            immediateOwner,
            to,
            parentId,
            destinationId,
            tokenId,
            data
        );
        _balances[from] -= 1;
        _updateOwnerAndClearApprovals(tokenId, destinationId, to);
        _balances[to] += 1;

        // Sending to NFT:
        _sendToNFT(immediateOwner, to, parentId, destinationId, tokenId, data);
    }

    /**
     * @notice Used to send a token to another token.
     * @dev If the token being sent is currently owned by an externally owned account, the `parentId` should equal `0`.
     * @dev Emits {Transfer} event.
     * @dev Emits {NestTransfer} event.
     * @param from Address from which the token is being sent
     * @param to Address of the collection smart contract of the token to receive the given token
     * @param parentId ID of the current parent token of the token being sent
     * @param destinationId ID of the tokento receive the token being sent
     * @param tokenId ID of the token being sent
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _sendToNFT(
        address from,
        address to,
        uint256 parentId,
        uint256 destinationId,
        uint256 tokenId,
        bytes memory data
    ) private {
        IERC6059 destContract = IERC6059(to);
        destContract.addChild(destinationId, tokenId, data);

        emit Transfer(from, to, tokenId);
        emit NestTransfer(from, to, parentId, destinationId, tokenId);

        _afterTokenTransfer(from, to, tokenId);
        _afterNestedTokenTransfer(
            from,
            to,
            parentId,
            destinationId,
            tokenId,
            data
        );
    }

    /**
     * @notice Used to check if nesting a given token into a specified token would create an inheritance loop.
     * @dev If a loop would occur, the tokens would be unmanageable, so the execution is reverted if one is detected.
     * @dev The check for inheritance loop is bounded to guard against too much gas being consumed.
     * @param currentId ID of the token that would be nested
     * @param targetContract Address of the collection smart contract of the token into which the given token would be
     *  nested
     * @param targetId ID of the token into which the given token would be nested
     */
    function _checkForInheritanceLoop(
        uint256 currentId,
        address targetContract,
        uint256 targetId
    ) private view {
        for (uint256 i; i < _MAX_LEVELS_TO_CHECK_FOR_INHERITANCE_LOOP; ) {
            (
                address nextOwner,
                uint256 nextOwnerTokenId,
                bool isNft
            ) = IERC6059(targetContract).directOwnerOf(targetId);
            // If there's a final address, we're good. There's no loop.
            if (!isNft) {
                return;
            }
            // Ff the current nft is an ancestor at some point, there is an inheritance loop
            if (nextOwner == address(this) && nextOwnerTokenId == currentId) {
                revert RMRKNestableTransferToDescendant();
            }
            // We reuse the parameters to save some contract size
            targetContract = nextOwner;
            targetId = nextOwnerTokenId;
            unchecked {
                ++i;
            }
        }
        revert RMRKNestableTooDeep();
    }

    ////////////////////////////////////////
    //              MINTING
    ////////////////////////////////////////

    /**
     * @notice Used to safely mint the token to the specified address while passing the additional data to contract
     *  recipients.
     * @param to Address to which to mint the token
     * @param tokenId ID of the token to mint
     * @param data Additional data to send with the tokens
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId, data);
        if (!_checkOnERC721Received(address(0), to, tokenId, data))
            revert ERC721TransferToNonReceiverImplementer();
    }

    /**
     * @notice Used to mint a specified token to a given address.
     * @dev WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible.
     * @dev Requirements:
     *
     *  - `tokenId` must not exist.
     *  - `to` cannot be the zero address.
     * @dev Emits a {Transfer} event.
     * @dev Emits a {NestTransfer} event.
     * @param to Address to mint the token to
     * @param tokenId ID of the token to mint
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _mint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _innerMint(to, tokenId, 0, data);

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

        _afterTokenTransfer(address(0), to, tokenId);
        _afterNestedTokenTransfer(address(0), to, 0, 0, tokenId, data);
    }

    /**
     * @notice Used to mint a child token to a given parent token.
     * @param to Address of the collection smart contract of the token into which to mint the child token
     * @param tokenId ID of the token to mint
     * @param destinationId ID of the token into which to mint the new child token
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _nestMint(
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) internal virtual {
        // It seems redundant, but otherwise it would revert with no error
        if (!to.isContract()) revert RMRKIsNotContract();
        if (!IERC165(to).supportsInterface(type(IERC6059).interfaceId))
            revert RMRKMintToNonRMRKNestableImplementer();

        _innerMint(to, tokenId, destinationId, data);
        _sendToNFT(address(0), to, 0, destinationId, tokenId, data);
    }

    /**
     * @notice Used to mint a child token into a given parent token.
     * @dev Requirements:
     *
     *  - `to` cannot be the zero address.
     *  - `tokenId` must not exist.
     *  - `tokenId` must not be `0`.
     * @param to Address of the collection smart contract of the token into which to mint the child token
     * @param tokenId ID of the token to mint
     * @param destinationId ID of the token into which to mint the new token
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _innerMint(
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) private {
        if (to == address(0)) revert ERC721MintToTheZeroAddress();
        if (_exists(tokenId)) revert ERC721TokenAlreadyMinted();
        if (tokenId == uint256(0)) revert RMRKIdZeroForbidden();

        _beforeTokenTransfer(address(0), to, tokenId);
        _beforeNestedTokenTransfer(
            address(0),
            to,
            0,
            destinationId,
            tokenId,
            data
        );

        _balances[to] += 1;
        _RMRKOwners[tokenId] = DirectOwner({
            ownerAddress: to,
            tokenId: destinationId
        });
    }

    ////////////////////////////////////////
    //              Ownership
    ////////////////////////////////////////

    /**
     * @inheritdoc IERC6059
     */
    function ownerOf(
        uint256 tokenId
    ) public view virtual override(IERC6059, IERC721) returns (address) {
        (address owner, uint256 ownerTokenId, bool isNft) = directOwnerOf(
            tokenId
        );
        if (isNft) {
            owner = IERC6059(owner).ownerOf(ownerTokenId);
        }
        return owner;
    }

    /**
     * @inheritdoc IERC6059
     */
    function directOwnerOf(
        uint256 tokenId
    ) public view virtual returns (address, uint256, bool) {
        DirectOwner memory owner = _RMRKOwners[tokenId];
        if (owner.ownerAddress == address(0)) revert ERC721InvalidTokenId();

        return (owner.ownerAddress, owner.tokenId, owner.tokenId != 0);
    }

    ////////////////////////////////////////
    //              BURNING
    ////////////////////////////////////////

    /**
     * @notice Used to burn a given token.
     * @dev In case the token has any child tokens, the execution will be reverted.
     * @param tokenId ID of the token to burn
     */
    function burn(uint256 tokenId) public virtual {
        burn(tokenId, 0);
    }

    /**
     * @inheritdoc IERC6059
     */
    function burn(
        uint256 tokenId,
        uint256 maxChildrenBurns
    ) public virtual onlyApprovedOrDirectOwner(tokenId) returns (uint256) {
        return _burn(tokenId, maxChildrenBurns);
    }

    /**
     * @notice Used to burn a token.
     * @dev When a token is burned, its children are recursively burned as well.
     * @dev The approvals are cleared when the token is burned.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits a {Transfer} event.
     * @dev Emits a {NestTransfer} event.
     * @param tokenId ID of the token to burn
     * @param maxChildrenBurns Maximum children to recursively burn
     * @return The number of recursive burns it took to burn all of the children
     */
    function _burn(
        uint256 tokenId,
        uint256 maxChildrenBurns
    ) internal virtual returns (uint256) {
        (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
        address owner = ownerOf(tokenId);
        _balances[immediateOwner] -= 1;

        _beforeTokenTransfer(owner, address(0), tokenId);
        _beforeNestedTokenTransfer(
            immediateOwner,
            address(0),
            parentId,
            0,
            tokenId,
            ""
        );

        _approve(address(0), tokenId);
        _cleanApprovals(tokenId);

        Child[] memory children = childrenOf(tokenId);

        delete _activeChildren[tokenId];
        delete _pendingChildren[tokenId];
        delete _tokenApprovals[tokenId][owner];

        uint256 pendingRecursiveBurns;
        uint256 totalChildBurns;

        uint256 length = children.length; //gas savings
        for (uint256 i; i < length; ) {
            if (totalChildBurns >= maxChildrenBurns)
                revert RMRKMaxRecursiveBurnsReached(
                    children[i].contractAddress,
                    children[i].tokenId
                );
            delete _childIsInActive[children[i].contractAddress][
                children[i].tokenId
            ];
            unchecked {
                // At this point we know pendingRecursiveBurns must be at least 1
                pendingRecursiveBurns = maxChildrenBurns - totalChildBurns;
            }
            // We substract one to the next level to count for the token being burned, then add it again on returns
            // This is to allow the behavior of 0 recursive burns meaning only the current token is deleted.
            totalChildBurns +=
                IERC6059(children[i].contractAddress).burn(
                    children[i].tokenId,
                    pendingRecursiveBurns - 1
                ) +
                1;
            unchecked {
                ++i;
            }
        }
        // Can't remove before burning child since child will call back to get root owner
        delete _RMRKOwners[tokenId];

        emit Transfer(owner, address(0), tokenId);
        emit NestTransfer(immediateOwner, address(0), parentId, 0, tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
        _afterNestedTokenTransfer(
            immediateOwner,
            address(0),
            parentId,
            0,
            tokenId,
            ""
        );

        return totalChildBurns;
    }

    ////////////////////////////////////////
    //              APPROVALS
    ////////////////////////////////////////

    /**
     * @notice Used to grant a one-time approval to manage one's token.
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * @dev The approval is cleared when the token is transferred.
     * @dev Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     * @dev Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     * @dev Emits an {Approval} event.
     * @param to Address receiving the approval
     * @param tokenId ID of the token for which the approval is being granted
     */
    function approve(address to, uint256 tokenId) public virtual {
        address owner = ownerOf(tokenId);
        if (to == owner) revert ERC721ApprovalToCurrentOwner();

        if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender()))
            revert ERC721ApproveCallerIsNotOwnerNorApprovedForAll();

        _approve(to, tokenId);
    }

    /**
     * @notice Used to retrieve the account approved to manage given token.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @param tokenId ID of the token to check for approval
     * @return Address of the account approved to manage the token
     */
    function getApproved(
        uint256 tokenId
    ) public view virtual returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId][ownerOf(tokenId)];
    }

    /**
     * @notice Used to approve or remove `operator` as an operator for the caller.
     * @dev Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     * @dev Requirements:
     *
     * - The `operator` cannot be the caller.
     * @dev Emits an {ApprovalForAll} event.
     * @param operator Address of the operator being managed
     * @param approved A boolean value signifying whether the approval is being granted (`true`) or (`revoked`)
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        if (_msgSender() == operator) revert ERC721ApproveToCaller();
        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @notice Used to check if the given address is allowed to manage the tokens of the specified address.
     * @param owner Address of the owner of the tokens
     * @param operator Address being checked for approval
     * @return A boolean value signifying whether the *operator* is allowed to manage the tokens of the *owner* (`true`)
     *  or not (`false`)
     */
    function isApprovedForAll(
        address owner,
        address operator
    ) public view virtual returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @notice Used to grant an approval to manage a given token.
     * @dev Emits an {Approval} event.
     * @param to Address to which the approval is being granted
     * @param tokenId ID of the token for which the approval is being granted
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        address owner = ownerOf(tokenId);
        _tokenApprovals[tokenId][owner] = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @notice Used to update the owner of the token and clear the approvals associated with the previous owner.
     * @dev The `destinationId` should equal `0` if the new owner is an externally owned account.
     * @param tokenId ID of the token being updated
     * @param destinationId ID of the token to receive the given token
     * @param to Address of account to receive the token
     */
    function _updateOwnerAndClearApprovals(
        uint256 tokenId,
        uint256 destinationId,
        address to
    ) internal {
        _RMRKOwners[tokenId] = DirectOwner({
            ownerAddress: to,
            tokenId: destinationId
        });

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

    /**
     * @notice Used to remove approvals for the current owner of the given token.
     * @param tokenId ID of the token to clear the approvals for
     */
    function _cleanApprovals(uint256 tokenId) internal virtual {}

    ////////////////////////////////////////
    //              UTILS
    ////////////////////////////////////////

    /**
     * @notice Used to check whether the given account is allowed to manage the given token.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @param spender Address that is being checked for approval
     * @param tokenId ID of the token being checked
     * @return A boolean value indicating whether the `spender` is approved to manage the given token
     */
    function _isApprovedOrOwner(
        address spender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        address owner = ownerOf(tokenId);
        return (spender == owner ||
            isApprovedForAll(owner, spender) ||
            getApproved(tokenId) == spender);
    }

    /**
     * @notice Used to check whether the account is approved to manage the token or its direct owner.
     * @param spender Address that is being checked for approval or direct ownership
     * @param tokenId ID of the token being checked
     * @return A boolean value indicating whether the `spender` is approved to manage the given token or its
     *  direct owner
     */
    function _isApprovedOrDirectOwner(
        address spender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        (address owner, uint256 parentId, ) = directOwnerOf(tokenId);
        // When the parent is an NFT, only it can do operations
        if (parentId != 0) {
            return (spender == owner);
        }
        // Otherwise, the owner or approved address can
        return (spender == owner ||
            isApprovedForAll(owner, spender) ||
            getApproved(tokenId) == spender);
    }

    /**
     * @notice Used to enforce that the given token has been minted.
     * @dev Reverts if the `tokenId` has not been minted yet.
     * @dev The validation checks whether the owner of a given token is a `0x0` address and considers it not minted if
     *  it is. This means that both tokens that haven't been minted yet as well as the ones that have already been
     *  burned will cause the transaction to be reverted.
     * @param tokenId ID of the token to check
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        if (!_exists(tokenId)) revert ERC721InvalidTokenId();
    }

    /**
     * @notice Used to check whether the given token exists.
     * @dev Tokens start existing when they are minted (`_mint`) and stop existing when they are burned (`_burn`).
     * @param tokenId ID of the token being checked
     * @return A boolean value signifying whether the token exists
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _RMRKOwners[tokenId].ownerAddress != address(0);
    }

    /**
     * @notice Used to invoke {IERC721Receiver-onERC721Received} on a target address.
     * @dev The call is not executed if the target address is not a contract.
     * @param from Address representing the previous owner of the given token
     * @param to Yarget address that will receive the tokens
     * @param tokenId ID of the token to be transferred
     * @param data Optional data to send along with the call
     * @return Boolean value signifying whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try
                IERC721Receiver(to).onERC721Received(
                    _msgSender(),
                    from,
                    tokenId,
                    data
                )
            returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == uint256(0)) {
                    revert ERC721TransferToNonReceiverImplementer();
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    ////////////////////////////////////////
    //      CHILD MANAGEMENT PUBLIC
    ////////////////////////////////////////

    /**
     * @inheritdoc IERC6059
     */
    function addChild(
        uint256 parentId,
        uint256 childId,
        bytes memory data
    ) public virtual {
        _requireMinted(parentId);

        address childAddress = _msgSender();
        if (!childAddress.isContract()) revert RMRKIsNotContract();

        Child memory child = Child({
            contractAddress: childAddress,
            tokenId: childId
        });

        _beforeAddChild(parentId, childAddress, childId, data);

        uint256 length = pendingChildrenOf(parentId).length;

        if (length < 128) {
            _pendingChildren[parentId].push(child);
        } else {
            revert RMRKMaxPendingChildrenReached();
        }

        // Previous length matches the index for the new child
        emit ChildProposed(parentId, length, childAddress, childId);

        _afterAddChild(parentId, childAddress, childId, data);
    }

    /**
     * @inheritdoc IERC6059
     */
    function acceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) public virtual onlyApprovedOrOwner(parentId) {
        _acceptChild(parentId, childIndex, childAddress, childId);
    }

    /**
     * @notice Used to accept a pending child token for a given parent token.
     * @dev This moves the child token from parent token's pending child tokens array into the active child tokens
     *  array.
     * @dev Requirements:
     *
     *  - `tokenId` must exist
     *  - `index` must be in range of the pending children array
     * @dev Emits ***ChildAccepted*** event.
     * @param parentId ID of the parent token for which the child token is being accepted
     * @param childIndex Index of a child tokem in the given parent's pending children array
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     */
    function _acceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) internal virtual {
        Child memory child = pendingChildOf(parentId, childIndex);
        _checkExpectedChild(child, childAddress, childId);
        if (_childIsInActive[childAddress][childId] != 0)
            revert RMRKChildAlreadyExists();

        _beforeAcceptChild(parentId, childIndex, childAddress, childId);

        // Remove from pending:
        _removeChildByIndex(_pendingChildren[parentId], childIndex);

        // Add to active:
        _activeChildren[parentId].push(child);
        _childIsInActive[childAddress][childId] = 1; // We use 1 as true

        emit ChildAccepted(parentId, childIndex, childAddress, childId);

        _afterAcceptChild(parentId, childIndex, childAddress, childId);
    }

    /**
     * @inheritdoc IERC6059
     */
    function rejectAllChildren(
        uint256 tokenId,
        uint256 maxRejections
    ) public virtual onlyApprovedOrOwner(tokenId) {
        _rejectAllChildren(tokenId, maxRejections);
    }

    /**
     * @notice Used to reject all pending children of a given parent token.
     * @dev Removes the children from the pending array mapping.
     * @dev This does not update the ownership storage data on children. If necessary, ownership can be reclaimed by the
     *  rootOwner of the previous parent.
     * @dev Requirements:
     *
     *  - `tokenId` must exist
     * @dev Emits ***AllChildrenRejected*** event.
     * @param tokenId ID of the parent token for which to reject all of the pending tokens.
     * @param maxRejections Maximum number of expected children to reject, used to prevent from rejecting children which
     *  arrive just before this operation.
     */
    function _rejectAllChildren(
        uint256 tokenId,
        uint256 maxRejections
    ) internal virtual {
        if (_pendingChildren[tokenId].length > maxRejections)
            revert RMRKUnexpectedNumberOfChildren();

        _beforeRejectAllChildren(tokenId);
        delete _pendingChildren[tokenId];
        emit AllChildrenRejected(tokenId);
        _afterRejectAllChildren(tokenId);
    }

    /**
     * @inheritdoc IERC6059
     */
    function transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) public virtual onlyApprovedOrOwner(tokenId) {
        _transferChild(
            tokenId,
            to,
            destinationId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );
    }

    /**
     * @notice Used to transfer a child token from a given parent token.
     * @dev When transferring a child token, the owner of the token is set to `to`, or is not updated in the event of
     *  `to` being the `0x0` address.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits {ChildTransferred} event.
     * @param tokenId ID of the parent token from which the child token is being transferred
     * @param to Address to which to transfer the token to
     * @param destinationId ID of the token to receive this child token (MUST be 0 if the destination is not a token)
     * @param childIndex Index of a token we are transferring, in the array it belongs to (can be either active array or
     *  pending array)
     * @param childAddress Address of the child token's collection smart contract.
     * @param childId ID of the child token in its own collection smart contract.
     * @param isPending A boolean value indicating whether the child token being transferred is in the pending array of
     *  the parent token (`true`) or in the active array (`false`)
     * @param data Additional data with no specified format, sent in call to `_to`
     */
    function _transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId, // newParentId
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual {
        Child memory child;
        if (isPending) {
            child = pendingChildOf(tokenId, childIndex);
        } else {
            child = childOf(tokenId, childIndex);
        }
        _checkExpectedChild(child, childAddress, childId);

        _beforeTransferChild(
            tokenId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );

        if (isPending) {
            _removeChildByIndex(_pendingChildren[tokenId], childIndex);
        } else {
            delete _childIsInActive[childAddress][childId];
            _removeChildByIndex(_activeChildren[tokenId], childIndex);
        }

        if (to != address(0)) {
            if (destinationId == uint256(0)) {
                IERC721(childAddress).safeTransferFrom(
                    address(this),
                    to,
                    childId,
                    data
                );
            } else {
                // Destination is an NFT
                IERC6059(child.contractAddress).nestTransferFrom(
                    address(this),
                    to,
                    child.tokenId,
                    destinationId,
                    data
                );
            }
        }

        emit ChildTransferred(
            tokenId,
            childIndex,
            childAddress,
            childId,
            isPending,
            to == address(0)
        );
        _afterTransferChild(
            tokenId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );
    }

    /**
     * @notice Used to verify that the child being accessed is the intended child.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param child A Child struct of a child being accessed
     * @param expectedAddress The address expected to be the one of the child
     * @param expectedId The token ID expected to be the one of the child
     */
    function _checkExpectedChild(
        Child memory child,
        address expectedAddress,
        uint256 expectedId
    ) private pure {
        if (
            expectedAddress != child.contractAddress ||
            expectedId != child.tokenId
        ) revert RMRKUnexpectedChildId();
    }

    ////////////////////////////////////////
    //      CHILD MANAGEMENT GETTERS
    ////////////////////////////////////////

    /**
     * @inheritdoc IERC6059
     */

    function childrenOf(
        uint256 parentId
    ) public view virtual returns (Child[] memory) {
        Child[] memory children = _activeChildren[parentId];
        return children;
    }

    /**
     * @inheritdoc IERC6059
     */

    function pendingChildrenOf(
        uint256 parentId
    ) public view virtual returns (Child[] memory) {
        Child[] memory pendingChildren = _pendingChildren[parentId];
        return pendingChildren;
    }

    /**
     * @inheritdoc IERC6059
     */
    function childOf(
        uint256 parentId,
        uint256 index
    ) public view virtual returns (Child memory) {
        if (childrenOf(parentId).length <= index)
            revert RMRKChildIndexOutOfRange();
        Child memory child = _activeChildren[parentId][index];
        return child;
    }

    /**
     * @inheritdoc IERC6059
     */
    function pendingChildOf(
        uint256 parentId,
        uint256 index
    ) public view virtual returns (Child memory) {
        if (pendingChildrenOf(parentId).length <= index)
            revert RMRKPendingChildIndexOutOfRange();
        Child memory child = _pendingChildren[parentId][index];
        return child;
    }

    // HOOKS

    /**
     * @notice Hook that is called before nested token transfer.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token is being transferred
     * @param to Address to which the token is being transferred
     * @param fromTokenId ID of the token from which the given token is being transferred
     * @param toTokenId ID of the token to which the given token is being transferred
     * @param tokenId ID of the token being transferred
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _beforeNestedTokenTransfer(
        address from,
        address to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called after nested token transfer.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token was transferred
     * @param to Address to which the token was transferred
     * @param fromTokenId ID of the token from which the given token was transferred
     * @param toTokenId ID of the token to which the given token was transferred
     * @param tokenId ID of the token that was transferred
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _afterNestedTokenTransfer(
        address from,
        address to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called before a child is added to the pending tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that will receive a new pending child token
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     * @param data Additional data with no specified format
     */
    function _beforeAddChild(
        uint256 tokenId,
        address childAddress,
        uint256 childId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called after a child is added to the pending tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that has received a new pending child token
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     * @param data Additional data with no specified format
     */
    function _afterAddChild(
        uint256 tokenId,
        address childAddress,
        uint256 childId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called before a child is accepted to the active tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param parentId ID of the token that will accept a pending child token
     * @param childIndex Index of the child token to accept in the given parent token's pending children array
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     */
    function _beforeAcceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) internal virtual {}

    /**
     * @notice Hook that is called after a child is accepted to the active tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param parentId ID of the token that has accepted a pending child token
     * @param childIndex Index of the child token that was accpeted in the given parent token's pending children array
     * @param childAddress Address of the collection smart contract of the child token that was expected to be located
     *  at the specified index of the given parent token's pending children array
     * @param childId ID of the child token that was expected to be located at the specified index of the given parent
     *  token's pending children array
     */
    function _afterAcceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) internal virtual {}

    /**
     * @notice Hook that is called before a child is transferred from a given child token array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that will transfer a child token
     * @param childIndex Index of the child token that will be transferred from the given parent token's children array
     * @param childAddress Address of the collection smart contract of the child token that is expected to be located
     *  at the specified index of the given parent token's children array
     * @param childId ID of the child token that is expected to be located at the specified index of the given parent
     *  token's children array
     * @param isPending A boolean value signifying whether the child token is being transferred from the pending child
     *  tokens array (`true`) or from the active child tokens array (`false`)
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _beforeTransferChild(
        uint256 tokenId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called after a child is transferred from a given child token array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that has transferred a child token
     * @param childIndex Index of the child token that was transferred from the given parent token's children array
     * @param childAddress Address of the collection smart contract of the child token that was expected to be located
     *  at the specified index of the given parent token's children array
     * @param childId ID of the child token that was expected to be located at the specified index of the given parent
     *  token's children array
     * @param isPending A boolean value signifying whether the child token was transferred from the pending child tokens
     *  array (`true`) or from the active child tokens array (`false`)
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _afterTransferChild(
        uint256 tokenId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called before a pending child tokens array of a given token is cleared.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that will reject all of the pending child tokens
     */
    function _beforeRejectAllChildren(uint256 tokenId) internal virtual {}

    /**
     * @notice Hook that is called after a pending child tokens array of a given token is cleared.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that has rejected all of the pending child tokens
     */
    function _afterRejectAllChildren(uint256 tokenId) internal virtual {}

    // HELPERS

    /**
     * @notice Used to remove a specified child token form an array using its index within said array.
     * @dev The caller must ensure that the length of the array is valid compared to the index passed.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param array An array od Child struct containing info about the child tokens in a given child tokens array
     * @param index An index of the child token to remove in the accompanying array
     */
    function _removeChildByIndex(Child[] storage array, uint256 index) private {
        array[index] = array[array.length - 1];
        array.pop();
    }
}

File 24 of 28 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.18;

error RentrantCall();

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

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

    uint256 private _status;

    /**
     * @notice Initializes the ReentrancyGuard with the `_status` of `_NOT_ENTERED`.
     */
    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @notice Used to ensure that the function it is applied to cannot be reentered.
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantIn();
        _;
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @notice Used to ensure that the current call is not a reentrant call.
     * @dev If reentrant call is detected, the execution will be reverted.
     */
    function _nonReentrantIn() private {
        // On the first call to nonReentrant, _notEntered will be true
        if (_status == _ENTERED) revert RentrantCall();

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title RMRKCollectionMetadata
 * @author RMRK team
 * @notice Smart contract of the RMRK Collection metadata module.
 */
contract RMRKCollectionMetadata {
    string private _collectionMetadata;

    /**
     * @notice Used to initialize the contract with the given metadata.
     * @param collectionMetadata_ The collection metadata with which to initialize the smart contract
     */
    constructor(string memory collectionMetadata_) {
        _setCollectionMetadata(collectionMetadata_);
    }

    /**
     * @notice Used to set the metadata of the collection.
     * @param newMetadata The new metadata of the collection
     */
    function _setCollectionMetadata(string memory newMetadata) internal {
        _collectionMetadata = newMetadata;
    }

    /**
     * @notice Used to retrieve the metadata of the collection.
     * @return string The metadata URI of the collection
     */
    function collectionMetadata() public view returns (string memory) {
        return _collectionMetadata;
    }
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.19;
error NegativeAttribute();
error TooManyAttributes();
error AttributeTooLarge();

/**
 * @title AssetAttributes
 * @author Store Attributes for nestable assets within mappings
 */

contract AssetAttributes {
    /** Storage for numerical attributes */
    mapping(uint256 => uint256) private _attributes;
    mapping(uint256 => uint256) private _totalAttributes;
    uint256 internal constant MAX_ATTRIBUTES = 25;
    uint256 internal constant MAX_ATTRIBUTE_SIZE = 10; // 2^10 - 1 = 1023

    constructor() {}

    /** 
    @notice Set on-chain attributes by assetId
    @dev each value within supplied attributes array must be less than or equal to 2^MAX_ATTRIBUTE_SIZE-1
    @dev the size of attributes array must be less than or equal to MAX_ATTRIBUTES
    */
    function _setAttributes(
        uint256 assetId,
        uint16[] memory attributes
    ) internal {
        // use bit shift to store all attributes in a single uint256
        uint256 newAttributes;
        unchecked {
            if (attributes.length > MAX_ATTRIBUTES) revert TooManyAttributes();
            uint256 maxSize = 2 ** MAX_ATTRIBUTE_SIZE - 1;
            for (uint256 i; i < attributes.length; ) {
                if (attributes[i] > maxSize) revert AttributeTooLarge();
                newAttributes |=
                    uint256(attributes[i]) <<
                    (i * MAX_ATTRIBUTE_SIZE);
                ++i;
            }
        }

        _totalAttributes[assetId] = newAttributes;
        _attributes[assetId] = newAttributes;
    }

    /**
     * @notice Updates the calculated TotalAttributes to reflect a replacement asset
     * @dev Needed because total attributes are tracked with equip/unequip events
     */
    function _replaceTotalAttributes(
        uint256 assetId,
        uint256 replacesId
    ) internal {
        _totalAttributes[assetId] = _totalAttributes[replacesId]; // 111
        uint16[] memory assetIdAttributes = getAttributes(assetId, false); // 222
        uint16[] memory replacementIdAttributes = getAttributes( // 111
            replacesId,
            false
        );
        _subTotalAttributes(assetId, replacementIdAttributes);
        _addTotalAttributes(assetId, assetIdAttributes);
    }

    /**
     * @notice Subtract supplied attributes from existing attributes
     * @dev Be sure that the supplied attributes array (i.e. length, positioning) matches the stored attributes.
     */
    function _subTotalAttributes(
        uint256 assetId,
        uint16[] memory suppliedAttributes
    ) internal {
        // Load all attributes, perform subtraction, and store back
        uint256 newAttributes;
        unchecked {
            uint256 currentAttributes = _totalAttributes[assetId];
            uint16 mask = uint16(1 << MAX_ATTRIBUTE_SIZE) - 1;
            for (uint256 i; i < suppliedAttributes.length; ) {
                uint16 nextAttribute = uint16(
                    currentAttributes >> (i * MAX_ATTRIBUTE_SIZE)
                ) & mask;
                if (nextAttribute < suppliedAttributes[i])
                    revert NegativeAttribute();
                newAttributes |=
                    uint256(nextAttribute - suppliedAttributes[i]) <<
                    (i * MAX_ATTRIBUTE_SIZE);
                ++i;
            }
            _totalAttributes[assetId] = newAttributes;
        }
    }

    /**
     * @notice Add supplied attributes to existing attributes
     * @dev Be sure that the supplied attributes array (i.e. length, positioning) matches the stored attributes
     */
    function _addTotalAttributes(
        uint256 assetId,
        uint16[] memory attributes
    ) internal {
        // use bit shift to store all attributes in a single uint256, perform addition, and store back
        uint256 newAttributes;
        unchecked {
            uint256 currentAttributes = _totalAttributes[assetId];
            uint16 mask = uint16(1 << MAX_ATTRIBUTE_SIZE) - 1;
            for (uint256 i; i < attributes.length; ++i) {
                newAttributes |=
                    uint256(
                        (uint16(currentAttributes >> (i * MAX_ATTRIBUTE_SIZE)) &
                            mask) + attributes[i]
                    ) <<
                    (i * MAX_ATTRIBUTE_SIZE);
            }
            _totalAttributes[assetId] = newAttributes;
        }
    }

    /**
     * @notice Retrieve values for each supplied attribute
     * @dev Fixed length array
     */
    function getAttributes(
        uint256 assetId,
        bool getTotal
    ) public view returns (uint16[] memory attributes) {
        // return attributes as a uint16 array
        attributes = new uint16[](MAX_ATTRIBUTES);
        unchecked {
            uint256 currentAttributes = getTotal
                ? _totalAttributes[assetId]
                : _attributes[assetId];
            uint16 mask = uint16(1 << MAX_ATTRIBUTE_SIZE) - 1;
            for (uint256 i; i < MAX_ATTRIBUTES; ++i) {
                attributes[i] =
                    uint16(currentAttributes >> (i * MAX_ATTRIBUTE_SIZE)) &
                    mask;
            }
        }
    }
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.16;

import "@openzeppelin/contracts/utils/Strings.sol";

/**
 * @title IndexedItemPools
 * @author Evrloot team
 * @notice Implementation enables groupings (i.e. pools) of items with common poolId, but unique memberId within that pool.
 * @notice Token URI is indexed by a poolId, enabling pools to share the same token metadata.
 * @notice ItemId is a combination of poolId and memberId, where memberId is unique within a pool.
 * @notice memberId is assigned via the Identify contract, enabling item identification within evrloot.

 */
contract IndexedItemPools {
    uint256 private constant MAXSUPPLY = 2 ** 256 - 1;
    using Strings for uint256;

    mapping(uint16 => string) private _tokenURI; // mapping of pool to tokenURI
    mapping(uint256 => uint16) private _itemId; // tokenID mapped to item id (pool & member)
    uint256 internal _totalSupply;

    /**
     * @notice Used to initiate the smart contract.
     * @param poolIds Item Pool to apply tokenUri to
     * @param tokenURI_ Metadata URI to apply to all tokens, either as base or as full URI for every token
     */
    constructor(uint16[] memory poolIds, string[] memory tokenURI_) {
        for (uint256 i = 0; i < tokenURI_.length; ) {
            _setPoolTokenURI(poolIds[i], tokenURI_[i]);

            unchecked {
                ++i;
            }
        }
    }

    function _setPoolTokenURI(
        uint16 poolId_,
        string memory tokenURI_
    ) internal {
        _tokenURI[poolId_] = tokenURI_;
    }

    function _setItemId(
        uint256 tokenId,
        uint16 poolId_,
        uint16 memberId_
    ) internal {
        _itemId[tokenId] = (poolId_ << 8) | memberId_;
    }

    function _setItemId(uint256 tokenId, uint16 itemId_) internal {
        _itemId[tokenId] = itemId_;
    }

    function tokenURI(uint256 tokenId) public view returns (string memory) {
        return _tokenURI[_itemId[tokenId] >> 8];
    }

    function itemId(uint256 tokenId) public view returns (uint16) {
        return _itemId[tokenId];
    }

    function poolId(uint256 tokenId) public view returns (uint16) {
        return _itemId[tokenId] >> 8;
    }

    function memberId(uint256 tokenId) public view returns (uint16) {
        return uint16(uint8(_itemId[tokenId]));
    }

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    function maxSupply() public pure returns (uint256) {
        return MAXSUPPLY;
    }

    function _decrementSupply() internal {
        unchecked {
            --_totalSupply;
        }
    }
}

File 28 of 28 : IIndexedInitData.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.19;

/**
 * @title IIndexedInitData
 * @dev This interface provides a struct used to pack data to avoid stack too deep error for too many arguments.
 */
interface IIndexedInitData {
    /**
     * @notice Used to provide initialization data without running into stack too deep errors.
     * @dev not all properties are necessarily used
     */
    struct IndexedInitData {
        uint256[] maxSupply;
        uint256[] pricePerMint;
        string[] tokenURI;
        uint256[] isEnumerable;
        string[] name;
        uint16[] poolIds;
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"collectionMetadata_","type":"string"},{"internalType":"address","name":"royaltyRecipient","type":"address"},{"internalType":"uint256","name":"royaltyPercentageBps","type":"uint256"},{"components":[{"internalType":"uint256[]","name":"maxSupply","type":"uint256[]"},{"internalType":"uint256[]","name":"pricePerMint","type":"uint256[]"},{"internalType":"string[]","name":"tokenURI","type":"string[]"},{"internalType":"uint256[]","name":"isEnumerable","type":"uint256[]"},{"internalType":"string[]","name":"name","type":"string[]"},{"internalType":"uint16[]","name":"poolIds","type":"uint16[]"}],"internalType":"struct IIndexedInitData.IndexedInitData","name":"initData","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AttributeTooLarge","type":"error"},{"inputs":[],"name":"BadInputLengths","type":"error"},{"inputs":[],"name":"ERC721AddressZeroIsNotaValidOwner","type":"error"},{"inputs":[],"name":"ERC721ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ERC721ApproveCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"ERC721ApproveToCaller","type":"error"},{"inputs":[],"name":"ERC721InvalidTokenId","type":"error"},{"inputs":[],"name":"ERC721MintToTheZeroAddress","type":"error"},{"inputs":[],"name":"ERC721NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"ERC721TokenAlreadyMinted","type":"error"},{"inputs":[],"name":"ERC721TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"ERC721TransferToNonReceiverImplementer","type":"error"},{"inputs":[],"name":"ERC721TransferToTheZeroAddress","type":"error"},{"inputs":[],"name":"RMRKApprovalForAssetsToCurrentOwner","type":"error"},{"inputs":[],"name":"RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"RMRKAssetAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKBadPriorityListLength","type":"error"},{"inputs":[],"name":"RMRKCatalogRequiredForParts","type":"error"},{"inputs":[],"name":"RMRKChildAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKEquippableEquipNotAllowedByCatalog","type":"error"},{"inputs":[],"name":"RMRKIdZeroForbidden","type":"error"},{"inputs":[],"name":"RMRKIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKIsNotContract","type":"error"},{"inputs":[],"name":"RMRKMaxPendingAssetsReached","type":"error"},{"inputs":[],"name":"RMRKMaxPendingChildrenReached","type":"error"},{"inputs":[{"internalType":"address","name":"childContract","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"RMRKMaxRecursiveBurnsReached","type":"error"},{"inputs":[],"name":"RMRKMintToNonRMRKNestableImplementer","type":"error"},{"inputs":[],"name":"RMRKMintZero","type":"error"},{"inputs":[],"name":"RMRKMustUnequipFirst","type":"error"},{"inputs":[],"name":"RMRKNestableTooDeep","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToDescendant","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToNonRMRKNestableImplementer","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToSelf","type":"error"},{"inputs":[],"name":"RMRKNewContributorIsZeroAddress","type":"error"},{"inputs":[],"name":"RMRKNewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"RMRKNoAssetMatchingId","type":"error"},{"inputs":[],"name":"RMRKNotApprovedForAssetsOrOwner","type":"error"},{"inputs":[],"name":"RMRKNotApprovedOrDirectOwner","type":"error"},{"inputs":[],"name":"RMRKNotEquipped","type":"error"},{"inputs":[],"name":"RMRKNotOwner","type":"error"},{"inputs":[],"name":"RMRKNotOwnerOrContributor","type":"error"},{"inputs":[],"name":"RMRKPendingChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKRoyaltiesTooHigh","type":"error"},{"inputs":[],"name":"RMRKSlotAlreadyUsed","type":"error"},{"inputs":[],"name":"RMRKTargetAssetCannotReceiveSlot","type":"error"},{"inputs":[],"name":"RMRKTokenCannotBeEquippedWithAssetIntoSlot","type":"error"},{"inputs":[],"name":"RMRKTokenDoesNotHaveAsset","type":"error"},{"inputs":[],"name":"RMRKUnexpectedAssetId","type":"error"},{"inputs":[],"name":"RMRKUnexpectedChildId","type":"error"},{"inputs":[],"name":"RMRKUnexpectedNumberOfAssets","type":"error"},{"inputs":[],"name":"RMRKUnexpectedNumberOfChildren","type":"error"},{"inputs":[],"name":"RentrantCall","type":"error"},{"inputs":[],"name":"TooManyAttributes","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AllChildrenRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAllForAssets","type":"event"},{"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":"ApprovalForAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"replacesId","type":"uint64"}],"name":"AssetAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"replacesId","type":"uint64"}],"name":"AssetAddedToTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AssetPrioritySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"AssetRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"AssetSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"}],"name":"ChildAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"address","name":"childAddress","type":"address"},{"indexed":false,"internalType":"uint64","name":"childAssetId","type":"uint64"}],"name":"ChildAssetEquipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"address","name":"childAddress","type":"address"},{"indexed":false,"internalType":"uint64","name":"childAssetId","type":"uint64"}],"name":"ChildAssetUnequipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"}],"name":"ChildProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"fromPending","type":"bool"},{"indexed":false,"internalType":"bool","name":"toZero","type":"bool"}],"name":"ChildTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"bool","name":"isContributor","type":"bool"}],"name":"ContributorUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NestTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"address","name":"parentAddress","type":"address"}],"name":"ValidParentEquippableGroupIdSet","type":"event"},{"inputs":[],"name":"RMRK_INTERFACE","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"acceptAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"acceptChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"metadataURI","type":"string"}],"name":"addAssetEntry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"replacesAssetWithId","type":"uint64"},{"internalType":"uint16","name":"setsItemId","type":"uint16"}],"name":"addAssetToToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"addChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"internalType":"address","name":"catalogAddress","type":"address"},{"internalType":"string","name":"metadataURI","type":"string"},{"internalType":"uint64[]","name":"partIds","type":"uint64[]"},{"internalType":"uint16[]","name":"attributes","type":"uint16[]"}],"name":"addEquippableAssetEntry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approveForAssets","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":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint64[]","name":"assetIds","type":"uint64[]"},{"internalType":"uint64[]","name":"replacesAssetWithIds","type":"uint64[]"},{"internalType":"uint16[]","name":"setsItemId","type":"uint16[]"}],"name":"batchAddAssetsToTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxChildrenBurns","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"parent","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"slotId","type":"uint64"}],"name":"canTokenBeEquippedWithAssetIntoSlot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"childOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"}],"name":"childrenOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectionMetadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"directOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"slotPartId","type":"uint64"},{"internalType":"uint64","name":"childAssetId","type":"uint64"}],"internalType":"struct IERC6220.IntakeEquip","name":"data","type":"tuple"}],"name":"equip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getActiveAssetPriorities","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getActiveAssets","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"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":"getApprovedForAssets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"getAssetAndEquippableData","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"getAssetMetadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"newAssetId","type":"uint64"}],"name":"getAssetReplacements","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"bool","name":"getTotal","type":"bool"}],"name":"getAttributes","outputs":[{"internalType":"uint16[]","name":"attributes","type":"uint16[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"targetCatalogAddress","type":"address"},{"internalType":"uint64","name":"slotPartId","type":"uint64"}],"name":"getEquipment","outputs":[{"components":[{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"childAssetId","type":"uint64"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"address","name":"childEquippableAddress","type":"address"}],"internalType":"struct IERC6220.Equipment","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getPendingAssets","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAllForAssets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"isChildEquipped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"isContributor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"itemId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"},{"internalType":"bool","name":"grantRole","type":"bool"}],"name":"manageContributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"memberId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numToMint","type":"uint256"},{"internalType":"uint16","name":"itemId_","type":"uint16"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numToMint","type":"uint256"},{"internalType":"uint16[]","name":"itemId_","type":"uint16[]"},{"internalType":"uint64[][]","name":"assets","type":"uint64[][]"}],"name":"mintWithAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numToMint","type":"uint256"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"uint16","name":"itemId_","type":"uint16"}],"name":"nestMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numToMint","type":"uint256"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"uint64[]","name":"assets","type":"uint64[]"},{"internalType":"uint16","name":"itemId_","type":"uint16"}],"name":"nestMintWithAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"nestTransferFrom","outputs":[],"stateMutability":"nonpayable","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":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"pendingChildOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"}],"name":"pendingChildrenOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"poolId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxRejections","type":"uint256"}],"name":"rejectAllAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxRejections","type":"uint256"}],"name":"rejectAllChildren","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"rejectAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","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":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAllForAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"identifyContract","type":"address"}],"name":"setIdentifyContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"poolId_","type":"uint16"},{"internalType":"string","name":"tokenURI_","type":"string"}],"name":"setPoolTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64[]","name":"priorities","type":"uint64[]"}],"name":"setPriority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"internalType":"address","name":"parentAddress","type":"address"},{"internalType":"uint64","name":"partId","type":"uint64"}],"name":"setValidParentForEquippableGroup","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":"totalAssets","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":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"bool","name":"isPending","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"slotPartId","type":"uint64"}],"name":"unequip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRoyaltyRecipient","type":"address"}],"name":"updateRoyaltyRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506040516200677a3803806200677a83398101604081905262000034916200059e565b60a081015160408201518787818181818a8a8d620000528162000146565b5060018055600280546001600160a01b0319166001600160a01b03841617905561271081106200009557604051634006185d60e11b815260040160405180910390fd5b600355506004620000a7838262000700565b506005620000b6828262000700565b5050505050505060005b81518110156200011d5762000114838281518110620000e357620000e3620007cc565b6020026020010151838381518110620001005762000100620007cc565b60200260200101516200015860201b60201c565b600101620000c0565b5050506200013a620001346200017c60201b60201c565b62000180565b505050505050620007e2565b600062000154828262000700565b5050565b61ffff82166000908152601b6020526040902062000177828262000700565b505050565b3390565b601e80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b03811182821017156200020d576200020d620001d2565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200023e576200023e620001d2565b604052919050565b600082601f8301126200025857600080fd5b81516001600160401b03811115620002745762000274620001d2565b60206200028a601f8301601f1916820162000213565b82815285828487010111156200029f57600080fd5b60005b83811015620002bf578581018301518282018401528201620002a2565b506000928101909101919091529392505050565b80516001600160a01b0381168114620002eb57600080fd5b919050565b60006001600160401b038211156200030c576200030c620001d2565b5060051b60200190565b600082601f8301126200032857600080fd5b81516020620003416200033b83620002f0565b62000213565b82815260059290921b840181019181810190868411156200036157600080fd5b8286015b848110156200037e578051835291830191830162000365565b509695505050505050565b600082601f8301126200039b57600080fd5b81516020620003ae6200033b83620002f0565b82815260059290921b84018101918181019086841115620003ce57600080fd5b8286015b848110156200037e5780516001600160401b03811115620003f35760008081fd5b620004038986838b010162000246565b845250918301918301620003d2565b600082601f8301126200042457600080fd5b81516020620004376200033b83620002f0565b82815260059290921b840181019181810190868411156200045757600080fd5b8286015b848110156200037e57805161ffff81168114620004785760008081fd5b83529183019183016200045b565b600060c082840312156200049957600080fd5b620004a3620001e8565b82519091506001600160401b0380821115620004be57600080fd5b620004cc8583860162000316565b83526020840151915080821115620004e357600080fd5b620004f18583860162000316565b602084015260408401519150808211156200050b57600080fd5b620005198583860162000389565b604084015260608401519150808211156200053357600080fd5b620005418583860162000316565b606084015260808401519150808211156200055b57600080fd5b620005698583860162000389565b608084015260a08401519150808211156200058357600080fd5b50620005928482850162000412565b60a08301525092915050565b60008060008060008060c08789031215620005b857600080fd5b86516001600160401b0380821115620005d057600080fd5b620005de8a838b0162000246565b97506020890151915080821115620005f557600080fd5b620006038a838b0162000246565b965060408901519150808211156200061a57600080fd5b620006288a838b0162000246565b95506200063860608a01620002d3565b94506080890151935060a08901519150808211156200065657600080fd5b506200066589828a0162000486565b9150509295509295509295565b600181811c908216806200068757607f821691505b602082108103620006a857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200017757600081815260208120601f850160051c81016020861015620006d75750805b601f850160051c820191505b81811015620006f857828155600101620006e3565b505050505050565b81516001600160401b038111156200071c576200071c620001d2565b62000734816200072d845462000672565b84620006ae565b602080601f8311600181146200076c5760008415620007535750858301515b600019600386901b1c1916600185901b178555620006f8565b600085815260208120601f198616915b828110156200079d578886015182559484019460019091019084016200077c565b5085821015620007bc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b615f8880620007f26000396000f3fe6080604052600436106104315760003560e01c80637280281e11610229578063bce5172e1161012e578063e467a48f116100b6578063ee1dffcf1161007a578063ee1dffcf14610e9a578063f2fde38b14610eba578063fb25fb7a14610eda578063fc3517c814610f21578063ffa1ad7414610f4157600080fd5b8063e467a48f14610dfa578063e6af0c6e14610e1a578063e7de4de414610e3a578063e97ceaa814610e5a578063e985e9c514610e7a57600080fd5b8063d0e02f60116100fd578063d0e02f6014610d29578063d5abeb0114610d49578063de8e602c14610d5e578063defa80c314610d7e578063df6f556b14610d9e57600080fd5b8063bce5172e14610ca9578063c259a98814610cc9578063c87b56dd14610ce9578063c95b970d14610d0957600080fd5b80638da5cb5b116101b1578063a22cb46511610180578063a22cb46514610c09578063a898e36414610c29578063b30a6cee14610c49578063b390c0ab14610c69578063b88d4fde14610c8957600080fd5b80638da5cb5b14610b9857806395d89b4114610bb657806395edc18c14610bcb5780639796133614610be957600080fd5b80637ab390bc116101f85780637ab390bc14610ade5780638507dc2814610afe57806389ed2edf14610b1e5780638a779afa14610b335780638d4f3bf514610b6457600080fd5b80637280281e1461098a5780637507e2ae146109aa57806377b79ae114610a9e57806379e8ca9e14610abe57600080fd5b80633600261d1161033a5780635e2e3292116102c257806365b67eb91161028657806365b67eb9146108f55780636f19951c1461091557806370a0823114610935578063715018a614610955578063727788391461096a57600080fd5b80635e2e3292146108555780635e94354a146108755780635ea72f36146108955780636352211e146108b5578063635490cc146108d557600080fd5b806344ec93441161030957806344ec9344146107835780634e60edba146107b057806351532e5a146107e057806359c8b7dd1461080d5780635a3617fc1461082257600080fd5b80633600261d146106f657806338dcf74c1461072357806342842e0e1461074357806342966c681461076357600080fd5b80631d0d35f5116103bd5780632a55205a1161038c5780632a55205a146106145780632f32f9371461065357806330ffb1d6146106805780633167611f146106a057806331fef5a6146106b357600080fd5b80631d0d35f51461057b57806322e6d160146105b457806322f6da9c146105d457806323b872dd146105f457600080fd5b8063074334fb11610404578063074334fb146104ce578063081812fc146104ee578063095ea7b3146105265780630fc499f51461054657806318160ddd1461056657600080fd5b806301e1d1141461043657806301ffc9a71461045a578063064c0a3a1461048a57806306fdde03146104ac575b600080fd5b34801561044257600080fd5b506022545b6040519081526020015b60405180910390f35b34801561046657600080fd5b5061047a610475366004614dbd565b610f72565b6040519015158152602001610451565b34801561049657600080fd5b506104aa6104a5366004614eab565b610fb9565b005b3480156104b857600080fd5b506104c1610fd8565b6040516104519190614f66565b3480156104da57600080fd5b5061047a6104e9366004614f90565b61106a565b3480156104fa57600080fd5b5061050e610509366004614fdf565b6110e4565b6040516001600160a01b039091168152602001610451565b34801561053257600080fd5b506104aa610541366004614ff8565b61112a565b34801561055257600080fd5b506104aa610561366004615024565b6111b6565b34801561057257600080fd5b50601d54610447565b34801561058757600080fd5b5061047a610596366004615024565b6001600160a01b03166000908152601f602052604090205460011490565b3480156105c057600080fd5b5061047a6105cf366004615041565b6111df565b3480156105e057600080fd5b506104aa6105ef36600461507a565b61120d565b34801561060057600080fd5b506104aa61060f3660046150af565b611228565b34801561062057600080fd5b5061063461062f3660046150f0565b61124d565b604080516001600160a01b039093168352602083019190915201610451565b34801561065f57600080fd5b5061067361066e3660046150f0565b611283565b6040516104519190615112565b34801561068c57600080fd5b5061044761069b366004615132565b61131c565b6104aa6106ae366004615178565b611345565b3480156106bf57600080fd5b506106e36106ce366004614fdf565b6000908152601c602052604090205460ff1690565b60405161ffff9091168152602001610451565b34801561070257600080fd5b506107166107113660046151bb565b611394565b60405161045191906151e0565b34801561072f57600080fd5b506104aa61073e36600461521c565b611433565b34801561074f57600080fd5b506104aa61075e3660046150af565b611457565b34801561076f57600080fd5b506104aa61077e366004614fdf565b611472565b34801561078f57600080fd5b506107a361079e366004614fdf565b611481565b604051610451919061529d565b3480156107bc57600080fd5b506107d06107cb3660046152fd565b611500565b6040516104519493929190615364565b3480156107ec57600080fd5b506108006107fb366004614fdf565b6115dd565b60405161045191906153b2565b34801561081957600080fd5b50600354610447565b34801561082e57600080fd5b506106e361083d366004614fdf565b6000908152601c602052604090205460081c60ff1690565b34801561086157600080fd5b506104c16108703660046152fd565b611671565b34801561088157600080fd5b50610800610890366004614fdf565b611760565b3480156108a157600080fd5b506104aa6108b0366004614ff8565b6117c9565b3480156108c157600080fd5b5061050e6108d0366004614fdf565b611850565b3480156108e157600080fd5b506104aa6108f03660046153c5565b6118e0565b34801561090157600080fd5b506104aa610910366004615414565b6119f2565b34801561092157600080fd5b506107a3610930366004614fdf565b611a05565b34801561094157600080fd5b50610447610950366004615024565b611a75565b34801561096157600080fd5b506104aa611aba565b34801561097657600080fd5b506104aa610985366004615547565b611ace565b34801561099657600080fd5b506108006109a5366004614fdf565b611c20565b3480156109b657600080fd5b50610a546109c5366004615648565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601887528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352600160401b9004909416958101959095526001830154918501919091526002909101549091169082015290565b604051610451919081516001600160401b03908116825260208084015190911690820152604080830151908201526060918201516001600160a01b03169181019190915260800190565b348015610aaa57600080fd5b50610447610ab936600461566f565b611c89565b348015610aca57600080fd5b506104aa610ad93660046156e2565b611d44565b348015610aea57600080fd5b506104aa610af9366004615024565b611e01565b348015610b0a57600080fd5b506104aa610b193660046156e2565b611e2b565b348015610b2a57600080fd5b506104c1611ec1565b348015610b3f57600080fd5b506106e3610b4e366004614fdf565b6000908152601c602052604090205461ffff1690565b348015610b7057600080fd5b50610b7f63524d524b60e01b81565b6040516001600160e01b03199091168152602001610451565b348015610ba457600080fd5b50601e546001600160a01b031661050e565b348015610bc257600080fd5b506104c1611ed0565b348015610bd757600080fd5b506002546001600160a01b031661050e565b348015610bf557600080fd5b506104aa610c043660046150f0565b611edf565b348015610c1557600080fd5b506104aa610c243660046156e2565b611ef3565b348015610c3557600080fd5b50610673610c443660046150f0565b611f81565b348015610c5557600080fd5b506104aa610c64366004615710565b611fde565b348015610c7557600080fd5b50610447610c843660046150f0565b61202c565b348015610c9557600080fd5b506104aa610ca4366004615752565b612051565b348015610cb557600080fd5b50610447610cc43660046157f5565b612067565b348015610cd557600080fd5b506104aa610ce43660046158ac565b61210a565b348015610cf557600080fd5b506104c1610d04366004614fdf565b61211f565b348015610d1557600080fd5b506104aa610d243660046158d1565b6121ca565b348015610d3557600080fd5b50610447610d4436600461591e565b6121dc565b348015610d5557600080fd5b50600019610447565b348015610d6a57600080fd5b506104aa610d7936600461595b565b61223a565b348015610d8a57600080fd5b506104aa610d993660046159a6565b61224f565b348015610daa57600080fd5b50610de2610db93660046152fd565b6000918252600e602090815260408084206001600160401b039384168552909152909120541690565b6040516001600160401b039091168152602001610451565b348015610e0657600080fd5b5061050e610e15366004614fdf565b612274565b348015610e2657600080fd5b506104aa610e35366004615a41565b612297565b348015610e4657600080fd5b506104aa610e553660046150f0565b61241e565b348015610e6657600080fd5b506104aa610e75366004615b32565b612432565b348015610e8657600080fd5b5061047a610e95366004615041565b612448565b348015610ea657600080fd5b5061047a610eb5366004615b71565b612476565b348015610ec657600080fd5b506104aa610ed5366004615024565b6124a7565b348015610ee657600080fd5b50610efa610ef5366004614fdf565b6124df565b604080516001600160a01b0390941684526020840192909252151590820152606001610451565b348015610f2d57600080fd5b506104aa610f3c36600461507a565b612547565b348015610f4d57600080fd5b506104c160405180604001604052806005815260200164312e322e3160d81b81525081565b6000610f7d8261255c565b80610f9857506001600160e01b0319821663035a194d60e11b145b80610fb357506001600160e01b03198216630a2f26b960e21b145b92915050565b82610fc3816125c8565b610fd086868686866125ef565b505050505050565b606060048054610fe790615b98565b80601f016020809104026020016040519081016040528092919081815260200182805461101390615b98565b80156110605780601f1061103557610100808354040283529160200191611060565b820191906000526020600020905b81548152906001019060200180831161104357829003601f168201915b5050505050905090565b6001600160401b038083166000908152601660209081526040808320548416808452601a83528184206001600160a01b038a16855290925282205491929091811690841681036110d55760006110c9866110c389611c20565b906127c4565b94506110dc9350505050565b6000925050505b949350505050565b60006110ef8261282d565b60008281526007602052604081209061110784611850565b6001600160a01b0390811682526020820192909252604001600020541692915050565b600061113582611850565b9050806001600160a01b0316836001600160a01b03160361116957604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b0382161480159061118957506111878133612448565b155b156111a757604051634c12315960e11b815260040160405180910390fd5b6111b18383612865565b505050565b6111be6128de565b600280546001600160a01b0319166001600160a01b03831617905550565b50565b6001600160a01b03918216600090815260136020908152604080832093909416825291909152205460ff1690565b8261121781612909565b611222848484612930565b50505050565b80611232816125c8565b6112228484846040518060200160405280600081525061299f565b6002546003546001600160a01b0390911690600090612710906112709085615be8565b61127a9190615bff565b90509250929050565b6040805180820190915260008082526020820152816112a184611a05565b51116112c05760405163653e642560e11b815260040160405180910390fd5b6000838152600a602052604081208054849081106112e0576112e0615c21565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b6000611326612ad9565b602280546001019081905561133b9083612b18565b506022545b919050565b61134d612ad9565b60008061135984612bde565b9092509050815b81811015610fd057611382868260405180602001604052806000815250612c37565b61138c8185612c6c565b600101611360565b604080516019808252610340820190925260609160208201610320803683370190505090506000826113d35760008481526020805260409020546113e3565b6000848152602160205260409020545b90506103ff60005b601981101561142a5781600a820284901c1684828151811061140f5761140f615c21565b61ffff909216602092830291909101909101526001016113eb565b50505092915050565b805161143e81612c90565b611446612cb7565b61144f82612ce1565b505060018055565b6111b183838360405180602001604052806000815250612051565b61147d81600061202c565b5050565b6000818152600b60209081526040808320805482518185028101850190935280835260609493849084015b828210156114f45760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ac565b50929695505050505050565b606060008060606115118686611671565b6001600160401b0380871660009081526016602090815260408083205460158352818420546017845293829020805483518186028101860190945280845291909516946001600160a01b0390941693928391908301828280156115c557602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116115825790505b50505050509050935093509350935092959194509250565b60008181526010602090815260409182902080548351818402810184019094528084526060939283018282801561166557602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116116225790505b50505050509050919050565b60008281526012602090815260408083206001600160401b038516845290915290205460609060ff166116b757604051631b9928fd60e31b815260040160405180910390fd5b6001600160401b0382166000908152600d6020526040902080546116da90615b98565b80601f016020809104026020016040519081016040528092919081815260200182805461170690615b98565b80156117535780601f1061172857610100808354040283529160200191611753565b820191906000526020600020905b81548152906001019060200180831161173657829003601f168201915b5050505050905092915050565b60008181526011602090815260409182902080548351818402810184019094528084526060939283018282801561166557600091825260209182902080546001600160401b03168452908202830192909160089101808411611622575094979650505050505050565b60006117d482611850565b9050806001600160a01b0316836001600160a01b031603611808576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b03821614801590611828575061182681336111df565b155b15611846576040516357a2e94960e11b815260040160405180910390fd5b6111b1838361304d565b60008060008061185f856124df565b92509250925080156118d7576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa1580156118b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d49190615c37565b92505b50909392505050565b6118e98361282d565b33803b6119095760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b0382166020820152600061193086611481565b519050608081101561198f576000868152600b60209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b039092169190911790556119a8565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd846040516119e591815260200190565b60405180910390a4610fd0565b6119fa612ad9565b6111b18383836130c6565b6000818152600a602090815260408083208054825181850281018501909352808352606094938490840182156114f45760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ac565b60006001600160a01b038216611a9e57604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526006602052604090205490565b611ac26128de565b611acc600061317a565b565b611ad6612ad9565b60005b8451811015611c1957611b38858281518110611af757611af7615c21565b6020026020010151858381518110611b1157611b11615c21565b6020026020010151858481518110611b2b57611b2b615c21565b60200260200101516131cc565b611bb0858281518110611b4d57611b4d615c21565b6020026020010151600160106000898681518110611b6d57611b6d615c21565b6020026020010151815260200190815260200160002080549050611b919190615c54565b868481518110611ba357611ba3615c21565b60200260200101516133c2565b818181518110611bc257611bc2615c21565b602002602001015161ffff16600014611c1157611c11858281518110611bea57611bea615c21565b6020026020010151838381518110611c0457611c04615c21565b6020026020010151612c6c565b600101611ad9565b5050505050565b6000818152600f602090815260409182902080548351818402810184019094528084526060939283018282801561166557600091825260209182902080546001600160401b03168452908202830192909160089101808411611622575094979650505050505050565b6000611c93612ad9565b600080611c9f87612bde565b9092509050815b81811015611d3757611cc989828960405180602001604052806000815250613602565b611cd38186612c6c565b60005b8651811015611d2e57611d0482888381518110611cf557611cf5615c21565b602002602001015160006131cc565b611d1c826000898481518110611ba357611ba3615c21565b80611d2681615c67565b915050611cd6565b50600101611ca6565b5090979650505050505050565b611d4c6128de565b6001600160a01b038216611d735760405163016b812760e71b815260040160405180910390fd5b80611d98576001600160a01b0382166000908152601f60205260408120819055611db7565b6001600160a01b0382166000908152601f602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac282604051611df5911515815260200190565b60405180910390a25050565b611e096128de565b602380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0382163303611e54576040516375f45abd60e01b815260040160405180910390fd5b3360008181526013602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060008054610fe790615b98565b606060058054610fe790615b98565b81611ee981612c90565b6111b183836136d2565b6001600160a01b0382163303611f1c57604051630b7b99b960e21b815260040160405180910390fd5b3360008181526008602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611eb5565b604080518082019091526000808252602082015281611f9f84611481565b5111611fbe5760405163da22687f60e01b815260040160405180910390fd5b6000838152600b602052604081208054849081106112e0576112e0615c21565b611fe6612ad9565b611ff18484846131cc565b60008481526010602052604090205461201890859061201290600190615c54565b856133c2565b61ffff811615611222576112228482612c6c565b60008261203881612c90565b612047601d8054600019019055565b6110dc8484613747565b8161205b816125c8565b611c1985858585613a9d565b6000612071612ad9565b602280546001019081905560408051602080880282810182019093528782526120bc93928c928c928c928c918c918291850190849080828437600092019190915250613ad292505050565b6120fb602254848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250613b7992505050565b50602254979650505050505050565b8261211481612c90565b611222848484613c41565b6000818152601c602090815260408083205460081c60ff168352601b909152902080546060919061214f90615b98565b80601f016020809104026020016040519081016040528092919081815260200182805461217b90615b98565b80156116655780601f1061219d57610100808354040283529160200191611665565b820191906000526020600020905b8154815290600101906020018083116121ab5750939695505050505050565b6121d2612ad9565b61147d8282613df3565b60006121e6612ad9565b6000806121f286612bde565b9092509050815b8181101561222e5761221c88828860405180602001604052806000815250613602565b6122268186612c6c565b6001016121f9565b50909695505050505050565b8261224481612909565b611222848484613e10565b8761225981612c90565b6122698989898989898989613e8a565b505050505050505050565b600061227f8261282d565b60008281526014602052604081209061110784611850565b61229f612ad9565b828151146122c0576040516337151d3b60e01b815260040160405180910390fd5b828251146122e1576040516337151d3b60e01b815260040160405180910390fd5b6000806122ed85612bde565b9092509050815b8181101561241557612316878260405180602001604052806000815250612c37565b6000856123238584615c54565b8151811061233357612333615c21565b602002602001015161ffff1611156123645761236481866123548683615c54565b81518110611c0457611c04615c21565b60005b846123728584615c54565b8151811061238257612382615c21565b60200260200101515181101561240c576123ca82866123a18783615c54565b815181106123b1576123b1615c21565b60200260200101518381518110611cf557611cf5615c21565b612404826000876123db8884615c54565b815181106123eb576123eb615c21565b60200260200101518481518110611ba357611ba3615c21565b600101612367565b506001016122f4565b50505050505050565b8161242881612909565b6111b18383613ed2565b8361243c81612c90565b611c1985858585613fca565b6001600160a01b03918216600090815260086020908152604080832093909416825291909152205460ff1690565b60009283526019602090815260408085206001600160a01b0394909416855292815282842091845252902054151590565b6124af6128de565b6001600160a01b0381166124d657604051634ece6ecf60e01b815260040160405180910390fd5b6111dc8161317a565b6000818152600960209081526040808320815180830190925280548252600101546001600160a01b031691810182905282918291906125315760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b8261255181612909565b6112228484846133c2565b60006001600160e01b031982166301ffc9a760e01b148061258d57506001600160e01b031982166380ac58cd60e01b145b806125a857506001600160e01b03198216635b5e139f60e01b145b80610fb357506001600160e01b031982166342b0e56f60e01b1492915050565b6125d233826140d0565b6111dc576040516345f3c98360e11b815260040160405180910390fd5b6000806125fb856124df565b5091509150866001600160a01b0316826001600160a01b0316146126325760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616612659576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561267057508385145b1561268e57604051633d76b10760e01b815260040160405180910390fd5b6001600160a01b0386163b6126b65760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa158015612701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127259190615c80565b61274257604051631784ec7360e21b815260040160405180910390fd5b61274d858786614153565b6001600160a01b0387166000908152600660205260408120805460019290612776908490615c54565b909155506127879050858588614244565b6001600160a01b03861660009081526006602052604081208054600192906127b0908490615c9d565b9091555061241590508287838789886142a3565b81516000908190815b8181101561281c57846001600160401b03168682815181106127f1576127f1615c21565b60200260200101516001600160401b031603612814579250600191506128269050565b6001016127cd565b5060008092509250505b9250929050565b6000818152600960205260409020600101546001600160a01b03166111dc5760405163089ba7e160e41b815260040160405180910390fd5b600061287082611850565b60008381526007602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b601e546001600160a01b03163314611acc57604051631c62d58f60e11b815260040160405180910390fd5b6129133382614380565b6111dc57604051635d64832960e01b815260040160405180910390fd5b61293b8383836143de565b612946838383614486565b60008381526012602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b6000806129ab846124df565b5091509150856001600160a01b0316826001600160a01b0316146129e25760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038516612a09576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386166000908152600660205260408120805460019290612a32908490615c54565b90915550612a44905084600087614244565b6001600160a01b0385166000908152600660205260408120805460019290612a6d908490615c9d565b909155505060405184906001600160a01b038088169190891690600080516020615f3383398151915290600090a483856001600160a01b0316836001600160a01b0316600080516020615f138339815191528460006040516119e5929190918252602082015260400190565b601e546001600160a01b03163314801590612afa5750612af833610596565b155b15611acc576040516301eca16760e41b815260040160405180910390fd5b6001600160401b038216612b3f576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b0382166000908152600d602052604081208054612b6290615b98565b90501115612b83576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600d60205260409020612ba58282615cf6565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b60008082612bff5760405163376bec4d60e01b815260040160405180910390fd5b6000601d546001612c109190615c9d565b601d8054860190819055909150600090612c2b906001615c9d565b91959194509092505050565b612c428383836144d4565b612c4f6000848484614542565b6111b15760405163bcb5663760e01b815260040160405180910390fd5b6000918252601c6020526040909120805461ffff191661ffff909216919091179055565b612c9a338261463e565b6111dc576040516302728a9d60e41b815260040160405180910390fd5b600260015403612cda576040516362bfeae960e11b815260040160405180910390fd5b6002600155565b6040808201516001600160401b0390811660009081526015602090815283822054606086015186518452601883528584206001600160a01b039283168086529084528685209582168552949092529390912060020154919290911615612d5a5760405163bd0650ab60e01b815260040160405180910390fd5b612d6883604001518261468a565b6000612d7c84600001518560200151611283565b60208101518151608087015160405163074334fb60e01b815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015612ded573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e119190615c80565b612e2e57604051632c36cd3360e01b815260040160405180910390fd5b6020810151604051636e5bceab60e11b81526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa158015612e89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ead9190615c80565b612eca57604051637228eff560e11b815260040160405180910390fd5b6040805160808082018352868301516001600160401b0390811683529087015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528c516000908152601886528981208d8416825286528981208c8916825286528981208951815498518a16600160401b026001600160801b031990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558a5184526019835286842090519094168352928152848220865183529052928320805492939192909190612fba908490615c9d565b92505081905550826001600160401b031685604001516001600160401b031686600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518a60800151604051613040939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a4611c19565b600061305882611850565b60008381526014602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160401b03831615806130e357506001600160401b038116155b15613101576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152601a602090815260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b601e80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008381526012602090815260408083206001600160401b038616845290915290205460ff1615613210576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600d60205260408120805461323390615b98565b90500361325357604051632aa5eff960e11b815260040160405180910390fd5b6000838152601060205260409020546080116132825760405163bade3a7b60e01b815260040160405180910390fd5b60008381526012602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260108552928520805493840181558552929093206004820401805460039092166008026101000a808402199092169190930217909155811615613330576000838152600e602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061336657613366615c21565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516133b59190615db5565b60405180910390a3611222565b6133cd8383836143de565b6000838152600e602090815260408083206001600160401b038086168552925282205416908082156134a55761349f83600f600089815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561348c57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116134495790505b50505050506127c490919063ffffffff16565b90925090505b8015613526576000868152600f602052604090208054859190849081106134ce576134ce615c21565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526012835260408083209187168352925220805460ff191690556135b1565b6000868152601160209081526040808320600f83529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6135bc868686614486565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4610fd0565b6001600160a01b0384163b61362a5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038516906301ffc9a790602401602060405180830381865afa158015613675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136999190615c80565b6136b65760405163fbd5d8b960e01b815260040160405180910390fd5b6136c28484848461472a565b61122260008560008587866142a3565b6000828152600b602052604090205481101561370157604051631e73178b60e11b815260040160405180910390fd5b6000828152600b6020526040812061371891614bf1565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b6000806000613755856124df565b5091509150600061376586611850565b6001600160a01b03841660009081526006602052604081208054929350600192909190613793908490615c54565b9091555061379e9050565b6040805160208101909152600090526137b8600087612865565b6137c186614821565b60006137cc87611a05565b6000888152600a602052604081209192506137e79190614bf1565b6000878152600b602052604081206137fe91614bf1565b60008781526007602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015613a03578983106138ad5784818151811061385557613855615c21565b60200260200101516020015185828151811061387357613873615c21565b6020908102919091010151516040516306177b2560e41b81526001600160a01b039092166004830152602482015260440160405180910390fd5b600c60008683815181106138c3576138c3615c21565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061390357613903615c21565b602002602001015160000151815260200190815260200160002060009055828a03935084818151811061393857613938615c21565b6020026020010151602001516001600160a01b031663b390c0ab86838151811061396457613964615c21565b60200260200101516000015160018761397d9190615c54565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156139c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139e49190615ded565b6139ef906001615c9d565b6139f99084615c9d565b9250600101613834565b5060008a81526009602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b03881690600080516020615f33833981519152908390a4604080518781526000602082018190528c9290916001600160a01b038b1691600080516020615f13833981519152910160405180910390a46040805160208101909152600090525098975050505050505050565b613aa98484848461299f565b613ab584848484614542565b6112225760405163bcb5663760e01b815260040160405180910390fd5b613adc8583612b18565b6001600160a01b038316158015613af35750805115155b15613b1157604051631035ad0760e11b815260040160405180910390fd5b6001600160401b03858116600090815260156020908152604080832080546001600160a01b0319166001600160a01b03891617905560168252808320805467ffffffffffffffff191694891694909417909355601781529190208251610fd092840190614c12565b6000601982511115613b9e57604051631af1729760e31b815260040160405180910390fd5b6103ff60005b8351811015613c1e5781848281518110613bc057613bc0615c21565b602002602001015161ffff161115613beb57604051634d80548d60e01b815260040160405180910390fd5b600a8102848281518110613c0157613c01615c21565b602002602001015161ffff16901b83179250806001019050613ba4565b505060009283526021602090815260408085208390559080529092209190915550565b6001600160401b03808316600090815260156020908152604080832054878452601883528184206001600160a01b039182168086529084528285208787168652845293829020825160808101845281548088168252600160401b9004909616938601939093526001830154918501919091526002909101541660608301819052909190613ce1576040516317de7dd760e21b815260040160405180910390fd5b60008581526018602090815260408083206001600160a01b0380871685529083528184206001600160401b0388168552835281842080546001600160801b03191681556001808201869055600290910180546001600160a01b031916905589855260198452828520606087015190921685529083528184208583015185529092528220805491929091613d75908490615c54565b92505081905550826001600160401b0316846001600160401b0316867f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da13846040015185606001518660200151604051613040939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b61ffff82166000908152601b602052604090206111b18282615cf6565b6000838152600f602052604090205481908114613e4057604051633581be1d60e11b815260040160405180910390fd5b6000848152601160205260409020613e59908484614cca565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2611222565b81613eb857613e9a888585612476565b15613eb857604051630619dc9d60e21b815260040160405180910390fd5b613ec8888888888888888861482c565b5050505050505050565b60008281526010602052604090205481811115613f0257604051635134ce8960e01b815260040160405180910390fd5b60005b81811015613f80576000848152601060205260408120805483908110613f2d57613f2d615c21565b60009182526020808320600483040154888452600e8252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101613f05565b506000838152601060205260408120613f9891614d46565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6000613fd68585611f81565b9050613fe3818484614a0d565b6001600160a01b0383166000908152600c60209081526040808320858452909152902054156140255760405163188a497360e01b815260040160405180910390fd5b6000858152600b6020526040902061403d9085614a51565b6000858152600a6020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b039283161790558716808552600c8452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a99101613040565b60008060006140de846124df565b50915091508060001461410157506001600160a01b038481169116149050610fb3565b816001600160a01b0316856001600160a01b0316148061412657506141268286612448565b8061414a5750846001600160a01b031661413f856110e4565b6001600160a01b0316145b95945050505050565b60005b606481101561422a576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b815260040161419291815260200190565b606060405180830381865afa1580156141af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141d39190615e06565b925092509250806141e75750505050505050565b6001600160a01b038316301480156141fe57508682145b1561421c576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101614156565b50604051630349a6bd60e51b815260040160405180910390fd5b6040805180820182528381526001600160a01b038381166020808401918252600088815260099091529384209251835551600190920180546001600160a01b0319169290911691909117905561429a9084612865565b6111b183614821565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc906142d590879087908790600401615e49565b600060405180830381600087803b1580156142ef57600080fd5b505af1158015614303573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b0316600080516020615f3383398151915260405160405180910390a482866001600160a01b0316886001600160a01b0316600080516020615f138339815191528888604051614373929190918252602082015260400190565b60405180910390a4612415565b60008061438c83611850565b9050806001600160a01b0316846001600160a01b031614806143b357506143b381856111df565b806110dc5750836001600160a01b03166143cc84612274565b6001600160a01b031614949350505050565b600083815260106020526040902054821061440c57604051630757d52160e01b815260040160405180910390fd5b600083815260106020526040902080548390811061442c5761442c615c21565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b0316146111b1576040516378eeeecf60e01b815260040160405180910390fd5b600083815260106020526040902061449e9083614b0b565b6000928352600e602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b6144e1838360008461472a565b60405182906001600160a01b03851690600090600080516020615f33833981519152908290a46040805160008082526020820181905284926001600160a01b03871692600080516020615f13833981519152910160405180910390a4505050565b60006001600160a01b0384163b1561463657604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614586903390899088908890600401615e68565b6020604051808303816000875af19250505080156145c1575060408051601f3d908101601f191682019092526145be91810190615ea5565b60015b61461c573d8080156145ef576040519150601f19603f3d011682016040523d82523d6000602084013e6145f4565b606091505b5080516146145760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506110dc565b5060016110dc565b60008061464a83611850565b9050806001600160a01b0316846001600160a01b0316148061467157506146718185612448565b806110dc5750836001600160a01b03166143cc846110e4565b6001600160401b0382166000908152601760209081526040808320805482518185028101850190935280835261470993869392919083018282801561348c57600091825260209182902080546001600160401b0316845290820283019290916008910180841161344957905050505050506127c490919063ffffffff16565b915050806111b157604051634ef44ed560e01b815260040160405180910390fd5b6001600160a01b038416614751576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600960205260409020600101546001600160a01b03161561478a5760405163c5a8d37160e01b815260040160405180910390fd5b826147a8576040516312c33ce360e01b815260040160405180910390fd5b6001600160a01b03841660009081526006602052604081208054600192906147d1908490615c9d565b90915550506040805180820182529283526001600160a01b039485166020808501918252600095865260099052932091518255509051600190910180546001600160a01b03191691909216179055565b6111dc60008261304d565b60408051808201909152600080825260208201528215614857576148508987611f81565b9050614864565b6148618987611283565b90505b61486f818686614a0d565b8215614892576000898152600b6020526040902061488d9087614a51565b6148cc565b6001600160a01b0385166000908152600c6020908152604080832087845282528083208390558b8352600a90915290206148cc9087614a51565b6001600160a01b038816156149b6578661494957604051635c46a7ef60e11b81526001600160a01b0386169063b88d4fde906149129030908c9089908890600401615e68565b600060405180830381600087803b15801561492c57600080fd5b505af1158015614940573d6000803e3d6000fd5b505050506149b6565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a916149839130918d918d908990600401615ec2565b600060405180830381600087803b15801561499d57600080fd5b505af11580156149b1573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a4612269565b82602001516001600160a01b0316826001600160a01b0316141580614a33575082518114155b156111b157604051637383f2c160e11b815260040160405180910390fd5b81548290614a6190600190615c54565b81548110614a7157614a71615c21565b9060005260206000209060020201828281548110614a9157614a91615c21565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b039092169190911790558154829080614adc57614adc615efc565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b81548110614b1857600080fd5b81548290614b2890600190615c54565b81548110614b3857614b38615c21565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110614b7357614b73615c21565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480614bbc57614bbc615efc565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b50805460008255600202906000526020600020908101906111dc9190614d6b565b82805482825590600052602060002090600301600490048101928215614cba5791602002820160005b83821115614c8557835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614c3b565b8015614cb85782816101000a8154906001600160401b030219169055600801602081600701049283019260010302614c85565b505b50614cc6929150614d92565b5090565b82805482825590600052602060002090600301600490048101928215614cba5791602002820160005b83821115614c855783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614cf3565b5080546000825560030160049004906000526020600020908101906111dc9190614d92565b5b80821115614cc657600081556001810180546001600160a01b0319169055600201614d6c565b5b80821115614cc65760008155600101614d93565b6001600160e01b0319811681146111dc57600080fd5b600060208284031215614dcf57600080fd5b8135614dda81614da7565b9392505050565b6001600160a01b03811681146111dc57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e3457614e34614df6565b604052919050565b600082601f830112614e4d57600080fd5b81356001600160401b03811115614e6657614e66614df6565b614e79601f8201601f1916602001614e0c565b818152846020838601011115614e8e57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215614ec357600080fd5b8535614ece81614de1565b94506020860135614ede81614de1565b9350604086013592506060860135915060808601356001600160401b03811115614f0757600080fd5b614f1388828901614e3c565b9150509295509295909350565b6000815180845260005b81811015614f4657602081850181015186830182015201614f2a565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000614dda6020830184614f20565b80356001600160401b038116811461134057600080fd5b60008060008060808587031215614fa657600080fd5b8435614fb181614de1565b935060208501359250614fc660408601614f79565b9150614fd460608601614f79565b905092959194509250565b600060208284031215614ff157600080fd5b5035919050565b6000806040838503121561500b57600080fd5b823561501681614de1565b946020939093013593505050565b60006020828403121561503657600080fd5b8135614dda81614de1565b6000806040838503121561505457600080fd5b823561505f81614de1565b9150602083013561506f81614de1565b809150509250929050565b60008060006060848603121561508f57600080fd5b83359250602084013591506150a660408501614f79565b90509250925092565b6000806000606084860312156150c457600080fd5b83356150cf81614de1565b925060208401356150df81614de1565b929592945050506040919091013590565b6000806040838503121561510357600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610fb3565b60006020828403121561514457600080fd5b81356001600160401b0381111561515a57600080fd5b6110dc84828501614e3c565b803561ffff8116811461134057600080fd5b60008060006060848603121561518d57600080fd5b833561519881614de1565b9250602084013591506150a660408501615166565b80151581146111dc57600080fd5b600080604083850312156151ce57600080fd5b82359150602083013561506f816151ad565b6020808252825182820181905260009190848201906040850190845b8181101561222e57835161ffff16835292840192918401916001016151fc565b600060a0828403121561522e57600080fd5b60405160a081018181106001600160401b038211171561525057615250614df6565b8060405250823581526020830135602082015261526f60408401614f79565b604082015261528060608401614f79565b606082015261529160808401614f79565b60808201529392505050565b602080825282518282018190526000919060409081850190868401855b828110156152f0576152e0848351805182526020908101516001600160a01b0316910152565b92840192908501906001016152ba565b5091979650505050505050565b6000806040838503121561531057600080fd5b8235915061127a60208401614f79565b600081518084526020808501945080840160005b838110156153595781516001600160401b031687529582019590820190600101615334565b509495945050505050565b6080815260006153776080830187614f20565b6001600160401b03861660208401526001600160a01b038516604084015282810360608401526153a78185615320565b979650505050505050565b602081526000614dda6020830184615320565b6000806000606084860312156153da57600080fd5b833592506020840135915060408401356001600160401b038111156153fe57600080fd5b61540a86828701614e3c565b9150509250925092565b60008060006060848603121561542957600080fd5b61543284614f79565b9250602084013561544281614de1565b91506150a660408501614f79565b60006001600160401b0382111561546957615469614df6565b5060051b60200190565b600082601f83011261548457600080fd5b8135602061549961549483615450565b614e0c565b82815260059290921b840181019181810190868411156154b857600080fd5b8286015b848110156154da576154cd81614f79565b83529183019183016154bc565b509695505050505050565b600082601f8301126154f657600080fd5b8135602061550661549483615450565b82815260059290921b8401810191818101908684111561552557600080fd5b8286015b848110156154da5761553a81615166565b8352918301918301615529565b6000806000806080858703121561555d57600080fd5b84356001600160401b038082111561557457600080fd5b818701915087601f83011261558857600080fd5b8135602061559861549483615450565b82815260059290921b8401810191818101908b8411156155b757600080fd5b948201945b838610156155d5578535825294820194908201906155bc565b985050880135925050808211156155eb57600080fd5b6155f788838901615473565b9450604087013591508082111561560d57600080fd5b61561988838901615473565b9350606087013591508082111561562f57600080fd5b5061563c878288016154e5565b91505092959194509250565b60008060006060848603121561565d57600080fd5b83359250602084013561544281614de1565b600080600080600060a0868803121561568757600080fd5b853561569281614de1565b9450602086013593506040860135925060608601356001600160401b038111156156bb57600080fd5b6156c788828901615473565b9250506156d660808701615166565b90509295509295909350565b600080604083850312156156f557600080fd5b823561570081614de1565b9150602083013561506f816151ad565b6000806000806080858703121561572657600080fd5b8435935061573660208601614f79565b925061574460408601614f79565b9150614fd460608601615166565b6000806000806080858703121561576857600080fd5b843561577381614de1565b9350602085013561578381614de1565b92506040850135915060608501356001600160401b038111156157a557600080fd5b61563c87828801614e3c565b60008083601f8401126157c357600080fd5b5081356001600160401b038111156157da57600080fd5b6020830191508360208260051b850101111561282657600080fd5b600080600080600080600060a0888a03121561581057600080fd5b61581988614f79565b9650602088013561582981614de1565b955060408801356001600160401b038082111561584557600080fd5b6158518b838c01614e3c565b965060608a013591508082111561586757600080fd5b6158738b838c016157b1565b909650945060808a013591508082111561588c57600080fd5b506158998a828b016157b1565b989b979a50959850939692959293505050565b6000806000606084860312156158c157600080fd5b8335925061544260208501614f79565b600080604083850312156158e457600080fd5b6158ed83615166565b915060208301356001600160401b0381111561590857600080fd5b61591485828601614e3c565b9150509250929050565b6000806000806080858703121561593457600080fd5b843561593f81614de1565b93506020850135925060408501359150614fd460608601615166565b60008060006040848603121561597057600080fd5b8335925060208401356001600160401b0381111561598d57600080fd5b615999868287016157b1565b9497909650939450505050565b600080600080600080600080610100898b0312156159c357600080fd5b8835975060208901356159d581614de1565b9650604089013595506060890135945060808901356159f381614de1565b935060a0890135925060c0890135615a0a816151ad565b915060e08901356001600160401b03811115615a2557600080fd5b615a318b828c01614e3c565b9150509295985092959890939650565b60008060008060808587031215615a5757600080fd5b8435615a6281614de1565b9350602085810135935060408601356001600160401b0380821115615a8657600080fd5b615a9289838a016154e5565b94506060880135915080821115615aa857600080fd5b818801915088601f830112615abc57600080fd5b8135615aca61549482615450565b81815260059190911b8301840190848101908b831115615ae957600080fd5b8585015b83811015615b2157803585811115615b055760008081fd5b615b138e89838a0101615473565b845250918601918601615aed565b50989b979a50959850505050505050565b60008060008060808587031215615b4857600080fd5b84359350602085013592506040850135615b6181614de1565b9396929550929360600135925050565b600080600060608486031215615b8657600080fd5b8335925060208401356150df81614de1565b600181811c90821680615bac57607f821691505b602082108103615bcc57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610fb357610fb3615bd2565b600082615c1c57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615c4957600080fd5b8151614dda81614de1565b81810381811115610fb357610fb3615bd2565b600060018201615c7957615c79615bd2565b5060010190565b600060208284031215615c9257600080fd5b8151614dda816151ad565b80820180821115610fb357610fb3615bd2565b601f8211156111b157600081815260208120601f850160051c81016020861015615cd75750805b601f850160051c820191505b81811015610fd057828155600101615ce3565b81516001600160401b03811115615d0f57615d0f614df6565b615d2381615d1d8454615b98565b84615cb0565b602080601f831160018114615d585760008415615d405750858301515b600019600386901b1c1916600185901b178555610fd0565b600085815260208120601f198616915b82811015615d8757888601518255948401946001909101908401615d68565b5085821015615da55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6020808252825182820181905260009190848201906040850190845b8181101561222e57835183529284019291840191600101615dd1565b600060208284031215615dff57600080fd5b5051919050565b600080600060608486031215615e1b57600080fd5b8351615e2681614de1565b602085015160408601519194509250615e3e816151ad565b809150509250925092565b83815282602082015260606040820152600061414a6060830184614f20565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615e9b90830184614f20565b9695505050505050565b600060208284031215615eb757600080fd5b8151614dda81614da7565b6001600160a01b03868116825285166020820152604081018490526060810183905260a0608082018190526000906153a790830184614f20565b634e487b7160e01b600052603160045260246000fdfe04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552dddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122081bbfa0dc893f408c114ee96d07dec01006d0bf044158110fff081fd3485dfc464736f6c6343000813003300000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000cb5eb537bad9c54a4dae2e3526473d79a496456a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000f4576726c6f6f742046697368696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4556524c4f4f545f46495348494e4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d57433832717271575747596d324a454376503948584d4d46794a6b6d5756754e5a42327338314c3644754757000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000006e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d556452353977726663713445635a63597934714744654433777a54584b583131794651335a4231775254536f00000000000000000000000000000000000000000000000000000000000000000000000000000000000046697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f636f6d6d6f6e466973682e6a736f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f72617265466973682e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f65706963466973682e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f6c6567656e64617279466973682e6a736f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000f4576726c6f6f742046697368696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b436f6d6d6f6e20466973680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009526172652046697368000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094570696320466973680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4c6567656e646172792046697368000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004

Deployed Bytecode

0x6080604052600436106104315760003560e01c80637280281e11610229578063bce5172e1161012e578063e467a48f116100b6578063ee1dffcf1161007a578063ee1dffcf14610e9a578063f2fde38b14610eba578063fb25fb7a14610eda578063fc3517c814610f21578063ffa1ad7414610f4157600080fd5b8063e467a48f14610dfa578063e6af0c6e14610e1a578063e7de4de414610e3a578063e97ceaa814610e5a578063e985e9c514610e7a57600080fd5b8063d0e02f60116100fd578063d0e02f6014610d29578063d5abeb0114610d49578063de8e602c14610d5e578063defa80c314610d7e578063df6f556b14610d9e57600080fd5b8063bce5172e14610ca9578063c259a98814610cc9578063c87b56dd14610ce9578063c95b970d14610d0957600080fd5b80638da5cb5b116101b1578063a22cb46511610180578063a22cb46514610c09578063a898e36414610c29578063b30a6cee14610c49578063b390c0ab14610c69578063b88d4fde14610c8957600080fd5b80638da5cb5b14610b9857806395d89b4114610bb657806395edc18c14610bcb5780639796133614610be957600080fd5b80637ab390bc116101f85780637ab390bc14610ade5780638507dc2814610afe57806389ed2edf14610b1e5780638a779afa14610b335780638d4f3bf514610b6457600080fd5b80637280281e1461098a5780637507e2ae146109aa57806377b79ae114610a9e57806379e8ca9e14610abe57600080fd5b80633600261d1161033a5780635e2e3292116102c257806365b67eb91161028657806365b67eb9146108f55780636f19951c1461091557806370a0823114610935578063715018a614610955578063727788391461096a57600080fd5b80635e2e3292146108555780635e94354a146108755780635ea72f36146108955780636352211e146108b5578063635490cc146108d557600080fd5b806344ec93441161030957806344ec9344146107835780634e60edba146107b057806351532e5a146107e057806359c8b7dd1461080d5780635a3617fc1461082257600080fd5b80633600261d146106f657806338dcf74c1461072357806342842e0e1461074357806342966c681461076357600080fd5b80631d0d35f5116103bd5780632a55205a1161038c5780632a55205a146106145780632f32f9371461065357806330ffb1d6146106805780633167611f146106a057806331fef5a6146106b357600080fd5b80631d0d35f51461057b57806322e6d160146105b457806322f6da9c146105d457806323b872dd146105f457600080fd5b8063074334fb11610404578063074334fb146104ce578063081812fc146104ee578063095ea7b3146105265780630fc499f51461054657806318160ddd1461056657600080fd5b806301e1d1141461043657806301ffc9a71461045a578063064c0a3a1461048a57806306fdde03146104ac575b600080fd5b34801561044257600080fd5b506022545b6040519081526020015b60405180910390f35b34801561046657600080fd5b5061047a610475366004614dbd565b610f72565b6040519015158152602001610451565b34801561049657600080fd5b506104aa6104a5366004614eab565b610fb9565b005b3480156104b857600080fd5b506104c1610fd8565b6040516104519190614f66565b3480156104da57600080fd5b5061047a6104e9366004614f90565b61106a565b3480156104fa57600080fd5b5061050e610509366004614fdf565b6110e4565b6040516001600160a01b039091168152602001610451565b34801561053257600080fd5b506104aa610541366004614ff8565b61112a565b34801561055257600080fd5b506104aa610561366004615024565b6111b6565b34801561057257600080fd5b50601d54610447565b34801561058757600080fd5b5061047a610596366004615024565b6001600160a01b03166000908152601f602052604090205460011490565b3480156105c057600080fd5b5061047a6105cf366004615041565b6111df565b3480156105e057600080fd5b506104aa6105ef36600461507a565b61120d565b34801561060057600080fd5b506104aa61060f3660046150af565b611228565b34801561062057600080fd5b5061063461062f3660046150f0565b61124d565b604080516001600160a01b039093168352602083019190915201610451565b34801561065f57600080fd5b5061067361066e3660046150f0565b611283565b6040516104519190615112565b34801561068c57600080fd5b5061044761069b366004615132565b61131c565b6104aa6106ae366004615178565b611345565b3480156106bf57600080fd5b506106e36106ce366004614fdf565b6000908152601c602052604090205460ff1690565b60405161ffff9091168152602001610451565b34801561070257600080fd5b506107166107113660046151bb565b611394565b60405161045191906151e0565b34801561072f57600080fd5b506104aa61073e36600461521c565b611433565b34801561074f57600080fd5b506104aa61075e3660046150af565b611457565b34801561076f57600080fd5b506104aa61077e366004614fdf565b611472565b34801561078f57600080fd5b506107a361079e366004614fdf565b611481565b604051610451919061529d565b3480156107bc57600080fd5b506107d06107cb3660046152fd565b611500565b6040516104519493929190615364565b3480156107ec57600080fd5b506108006107fb366004614fdf565b6115dd565b60405161045191906153b2565b34801561081957600080fd5b50600354610447565b34801561082e57600080fd5b506106e361083d366004614fdf565b6000908152601c602052604090205460081c60ff1690565b34801561086157600080fd5b506104c16108703660046152fd565b611671565b34801561088157600080fd5b50610800610890366004614fdf565b611760565b3480156108a157600080fd5b506104aa6108b0366004614ff8565b6117c9565b3480156108c157600080fd5b5061050e6108d0366004614fdf565b611850565b3480156108e157600080fd5b506104aa6108f03660046153c5565b6118e0565b34801561090157600080fd5b506104aa610910366004615414565b6119f2565b34801561092157600080fd5b506107a3610930366004614fdf565b611a05565b34801561094157600080fd5b50610447610950366004615024565b611a75565b34801561096157600080fd5b506104aa611aba565b34801561097657600080fd5b506104aa610985366004615547565b611ace565b34801561099657600080fd5b506108006109a5366004614fdf565b611c20565b3480156109b657600080fd5b50610a546109c5366004615648565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601887528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352600160401b9004909416958101959095526001830154918501919091526002909101549091169082015290565b604051610451919081516001600160401b03908116825260208084015190911690820152604080830151908201526060918201516001600160a01b03169181019190915260800190565b348015610aaa57600080fd5b50610447610ab936600461566f565b611c89565b348015610aca57600080fd5b506104aa610ad93660046156e2565b611d44565b348015610aea57600080fd5b506104aa610af9366004615024565b611e01565b348015610b0a57600080fd5b506104aa610b193660046156e2565b611e2b565b348015610b2a57600080fd5b506104c1611ec1565b348015610b3f57600080fd5b506106e3610b4e366004614fdf565b6000908152601c602052604090205461ffff1690565b348015610b7057600080fd5b50610b7f63524d524b60e01b81565b6040516001600160e01b03199091168152602001610451565b348015610ba457600080fd5b50601e546001600160a01b031661050e565b348015610bc257600080fd5b506104c1611ed0565b348015610bd757600080fd5b506002546001600160a01b031661050e565b348015610bf557600080fd5b506104aa610c043660046150f0565b611edf565b348015610c1557600080fd5b506104aa610c243660046156e2565b611ef3565b348015610c3557600080fd5b50610673610c443660046150f0565b611f81565b348015610c5557600080fd5b506104aa610c64366004615710565b611fde565b348015610c7557600080fd5b50610447610c843660046150f0565b61202c565b348015610c9557600080fd5b506104aa610ca4366004615752565b612051565b348015610cb557600080fd5b50610447610cc43660046157f5565b612067565b348015610cd557600080fd5b506104aa610ce43660046158ac565b61210a565b348015610cf557600080fd5b506104c1610d04366004614fdf565b61211f565b348015610d1557600080fd5b506104aa610d243660046158d1565b6121ca565b348015610d3557600080fd5b50610447610d4436600461591e565b6121dc565b348015610d5557600080fd5b50600019610447565b348015610d6a57600080fd5b506104aa610d7936600461595b565b61223a565b348015610d8a57600080fd5b506104aa610d993660046159a6565b61224f565b348015610daa57600080fd5b50610de2610db93660046152fd565b6000918252600e602090815260408084206001600160401b039384168552909152909120541690565b6040516001600160401b039091168152602001610451565b348015610e0657600080fd5b5061050e610e15366004614fdf565b612274565b348015610e2657600080fd5b506104aa610e35366004615a41565b612297565b348015610e4657600080fd5b506104aa610e553660046150f0565b61241e565b348015610e6657600080fd5b506104aa610e75366004615b32565b612432565b348015610e8657600080fd5b5061047a610e95366004615041565b612448565b348015610ea657600080fd5b5061047a610eb5366004615b71565b612476565b348015610ec657600080fd5b506104aa610ed5366004615024565b6124a7565b348015610ee657600080fd5b50610efa610ef5366004614fdf565b6124df565b604080516001600160a01b0390941684526020840192909252151590820152606001610451565b348015610f2d57600080fd5b506104aa610f3c36600461507a565b612547565b348015610f4d57600080fd5b506104c160405180604001604052806005815260200164312e322e3160d81b81525081565b6000610f7d8261255c565b80610f9857506001600160e01b0319821663035a194d60e11b145b80610fb357506001600160e01b03198216630a2f26b960e21b145b92915050565b82610fc3816125c8565b610fd086868686866125ef565b505050505050565b606060048054610fe790615b98565b80601f016020809104026020016040519081016040528092919081815260200182805461101390615b98565b80156110605780601f1061103557610100808354040283529160200191611060565b820191906000526020600020905b81548152906001019060200180831161104357829003601f168201915b5050505050905090565b6001600160401b038083166000908152601660209081526040808320548416808452601a83528184206001600160a01b038a16855290925282205491929091811690841681036110d55760006110c9866110c389611c20565b906127c4565b94506110dc9350505050565b6000925050505b949350505050565b60006110ef8261282d565b60008281526007602052604081209061110784611850565b6001600160a01b0390811682526020820192909252604001600020541692915050565b600061113582611850565b9050806001600160a01b0316836001600160a01b03160361116957604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b0382161480159061118957506111878133612448565b155b156111a757604051634c12315960e11b815260040160405180910390fd5b6111b18383612865565b505050565b6111be6128de565b600280546001600160a01b0319166001600160a01b03831617905550565b50565b6001600160a01b03918216600090815260136020908152604080832093909416825291909152205460ff1690565b8261121781612909565b611222848484612930565b50505050565b80611232816125c8565b6112228484846040518060200160405280600081525061299f565b6002546003546001600160a01b0390911690600090612710906112709085615be8565b61127a9190615bff565b90509250929050565b6040805180820190915260008082526020820152816112a184611a05565b51116112c05760405163653e642560e11b815260040160405180910390fd5b6000838152600a602052604081208054849081106112e0576112e0615c21565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b6000611326612ad9565b602280546001019081905561133b9083612b18565b506022545b919050565b61134d612ad9565b60008061135984612bde565b9092509050815b81811015610fd057611382868260405180602001604052806000815250612c37565b61138c8185612c6c565b600101611360565b604080516019808252610340820190925260609160208201610320803683370190505090506000826113d35760008481526020805260409020546113e3565b6000848152602160205260409020545b90506103ff60005b601981101561142a5781600a820284901c1684828151811061140f5761140f615c21565b61ffff909216602092830291909101909101526001016113eb565b50505092915050565b805161143e81612c90565b611446612cb7565b61144f82612ce1565b505060018055565b6111b183838360405180602001604052806000815250612051565b61147d81600061202c565b5050565b6000818152600b60209081526040808320805482518185028101850190935280835260609493849084015b828210156114f45760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ac565b50929695505050505050565b606060008060606115118686611671565b6001600160401b0380871660009081526016602090815260408083205460158352818420546017845293829020805483518186028101860190945280845291909516946001600160a01b0390941693928391908301828280156115c557602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116115825790505b50505050509050935093509350935092959194509250565b60008181526010602090815260409182902080548351818402810184019094528084526060939283018282801561166557602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116116225790505b50505050509050919050565b60008281526012602090815260408083206001600160401b038516845290915290205460609060ff166116b757604051631b9928fd60e31b815260040160405180910390fd5b6001600160401b0382166000908152600d6020526040902080546116da90615b98565b80601f016020809104026020016040519081016040528092919081815260200182805461170690615b98565b80156117535780601f1061172857610100808354040283529160200191611753565b820191906000526020600020905b81548152906001019060200180831161173657829003601f168201915b5050505050905092915050565b60008181526011602090815260409182902080548351818402810184019094528084526060939283018282801561166557600091825260209182902080546001600160401b03168452908202830192909160089101808411611622575094979650505050505050565b60006117d482611850565b9050806001600160a01b0316836001600160a01b031603611808576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b03821614801590611828575061182681336111df565b155b15611846576040516357a2e94960e11b815260040160405180910390fd5b6111b1838361304d565b60008060008061185f856124df565b92509250925080156118d7576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa1580156118b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d49190615c37565b92505b50909392505050565b6118e98361282d565b33803b6119095760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b0382166020820152600061193086611481565b519050608081101561198f576000868152600b60209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b039092169190911790556119a8565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd846040516119e591815260200190565b60405180910390a4610fd0565b6119fa612ad9565b6111b18383836130c6565b6000818152600a602090815260408083208054825181850281018501909352808352606094938490840182156114f45760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ac565b60006001600160a01b038216611a9e57604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526006602052604090205490565b611ac26128de565b611acc600061317a565b565b611ad6612ad9565b60005b8451811015611c1957611b38858281518110611af757611af7615c21565b6020026020010151858381518110611b1157611b11615c21565b6020026020010151858481518110611b2b57611b2b615c21565b60200260200101516131cc565b611bb0858281518110611b4d57611b4d615c21565b6020026020010151600160106000898681518110611b6d57611b6d615c21565b6020026020010151815260200190815260200160002080549050611b919190615c54565b868481518110611ba357611ba3615c21565b60200260200101516133c2565b818181518110611bc257611bc2615c21565b602002602001015161ffff16600014611c1157611c11858281518110611bea57611bea615c21565b6020026020010151838381518110611c0457611c04615c21565b6020026020010151612c6c565b600101611ad9565b5050505050565b6000818152600f602090815260409182902080548351818402810184019094528084526060939283018282801561166557600091825260209182902080546001600160401b03168452908202830192909160089101808411611622575094979650505050505050565b6000611c93612ad9565b600080611c9f87612bde565b9092509050815b81811015611d3757611cc989828960405180602001604052806000815250613602565b611cd38186612c6c565b60005b8651811015611d2e57611d0482888381518110611cf557611cf5615c21565b602002602001015160006131cc565b611d1c826000898481518110611ba357611ba3615c21565b80611d2681615c67565b915050611cd6565b50600101611ca6565b5090979650505050505050565b611d4c6128de565b6001600160a01b038216611d735760405163016b812760e71b815260040160405180910390fd5b80611d98576001600160a01b0382166000908152601f60205260408120819055611db7565b6001600160a01b0382166000908152601f602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac282604051611df5911515815260200190565b60405180910390a25050565b611e096128de565b602380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0382163303611e54576040516375f45abd60e01b815260040160405180910390fd5b3360008181526013602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060008054610fe790615b98565b606060058054610fe790615b98565b81611ee981612c90565b6111b183836136d2565b6001600160a01b0382163303611f1c57604051630b7b99b960e21b815260040160405180910390fd5b3360008181526008602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611eb5565b604080518082019091526000808252602082015281611f9f84611481565b5111611fbe5760405163da22687f60e01b815260040160405180910390fd5b6000838152600b602052604081208054849081106112e0576112e0615c21565b611fe6612ad9565b611ff18484846131cc565b60008481526010602052604090205461201890859061201290600190615c54565b856133c2565b61ffff811615611222576112228482612c6c565b60008261203881612c90565b612047601d8054600019019055565b6110dc8484613747565b8161205b816125c8565b611c1985858585613a9d565b6000612071612ad9565b602280546001019081905560408051602080880282810182019093528782526120bc93928c928c928c928c918c918291850190849080828437600092019190915250613ad292505050565b6120fb602254848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250613b7992505050565b50602254979650505050505050565b8261211481612c90565b611222848484613c41565b6000818152601c602090815260408083205460081c60ff168352601b909152902080546060919061214f90615b98565b80601f016020809104026020016040519081016040528092919081815260200182805461217b90615b98565b80156116655780601f1061219d57610100808354040283529160200191611665565b820191906000526020600020905b8154815290600101906020018083116121ab5750939695505050505050565b6121d2612ad9565b61147d8282613df3565b60006121e6612ad9565b6000806121f286612bde565b9092509050815b8181101561222e5761221c88828860405180602001604052806000815250613602565b6122268186612c6c565b6001016121f9565b50909695505050505050565b8261224481612909565b611222848484613e10565b8761225981612c90565b6122698989898989898989613e8a565b505050505050505050565b600061227f8261282d565b60008281526014602052604081209061110784611850565b61229f612ad9565b828151146122c0576040516337151d3b60e01b815260040160405180910390fd5b828251146122e1576040516337151d3b60e01b815260040160405180910390fd5b6000806122ed85612bde565b9092509050815b8181101561241557612316878260405180602001604052806000815250612c37565b6000856123238584615c54565b8151811061233357612333615c21565b602002602001015161ffff1611156123645761236481866123548683615c54565b81518110611c0457611c04615c21565b60005b846123728584615c54565b8151811061238257612382615c21565b60200260200101515181101561240c576123ca82866123a18783615c54565b815181106123b1576123b1615c21565b60200260200101518381518110611cf557611cf5615c21565b612404826000876123db8884615c54565b815181106123eb576123eb615c21565b60200260200101518481518110611ba357611ba3615c21565b600101612367565b506001016122f4565b50505050505050565b8161242881612909565b6111b18383613ed2565b8361243c81612c90565b611c1985858585613fca565b6001600160a01b03918216600090815260086020908152604080832093909416825291909152205460ff1690565b60009283526019602090815260408085206001600160a01b0394909416855292815282842091845252902054151590565b6124af6128de565b6001600160a01b0381166124d657604051634ece6ecf60e01b815260040160405180910390fd5b6111dc8161317a565b6000818152600960209081526040808320815180830190925280548252600101546001600160a01b031691810182905282918291906125315760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b8261255181612909565b6112228484846133c2565b60006001600160e01b031982166301ffc9a760e01b148061258d57506001600160e01b031982166380ac58cd60e01b145b806125a857506001600160e01b03198216635b5e139f60e01b145b80610fb357506001600160e01b031982166342b0e56f60e01b1492915050565b6125d233826140d0565b6111dc576040516345f3c98360e11b815260040160405180910390fd5b6000806125fb856124df565b5091509150866001600160a01b0316826001600160a01b0316146126325760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616612659576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561267057508385145b1561268e57604051633d76b10760e01b815260040160405180910390fd5b6001600160a01b0386163b6126b65760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa158015612701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127259190615c80565b61274257604051631784ec7360e21b815260040160405180910390fd5b61274d858786614153565b6001600160a01b0387166000908152600660205260408120805460019290612776908490615c54565b909155506127879050858588614244565b6001600160a01b03861660009081526006602052604081208054600192906127b0908490615c9d565b9091555061241590508287838789886142a3565b81516000908190815b8181101561281c57846001600160401b03168682815181106127f1576127f1615c21565b60200260200101516001600160401b031603612814579250600191506128269050565b6001016127cd565b5060008092509250505b9250929050565b6000818152600960205260409020600101546001600160a01b03166111dc5760405163089ba7e160e41b815260040160405180910390fd5b600061287082611850565b60008381526007602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b601e546001600160a01b03163314611acc57604051631c62d58f60e11b815260040160405180910390fd5b6129133382614380565b6111dc57604051635d64832960e01b815260040160405180910390fd5b61293b8383836143de565b612946838383614486565b60008381526012602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b6000806129ab846124df565b5091509150856001600160a01b0316826001600160a01b0316146129e25760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038516612a09576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386166000908152600660205260408120805460019290612a32908490615c54565b90915550612a44905084600087614244565b6001600160a01b0385166000908152600660205260408120805460019290612a6d908490615c9d565b909155505060405184906001600160a01b038088169190891690600080516020615f3383398151915290600090a483856001600160a01b0316836001600160a01b0316600080516020615f138339815191528460006040516119e5929190918252602082015260400190565b601e546001600160a01b03163314801590612afa5750612af833610596565b155b15611acc576040516301eca16760e41b815260040160405180910390fd5b6001600160401b038216612b3f576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b0382166000908152600d602052604081208054612b6290615b98565b90501115612b83576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600d60205260409020612ba58282615cf6565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b60008082612bff5760405163376bec4d60e01b815260040160405180910390fd5b6000601d546001612c109190615c9d565b601d8054860190819055909150600090612c2b906001615c9d565b91959194509092505050565b612c428383836144d4565b612c4f6000848484614542565b6111b15760405163bcb5663760e01b815260040160405180910390fd5b6000918252601c6020526040909120805461ffff191661ffff909216919091179055565b612c9a338261463e565b6111dc576040516302728a9d60e41b815260040160405180910390fd5b600260015403612cda576040516362bfeae960e11b815260040160405180910390fd5b6002600155565b6040808201516001600160401b0390811660009081526015602090815283822054606086015186518452601883528584206001600160a01b039283168086529084528685209582168552949092529390912060020154919290911615612d5a5760405163bd0650ab60e01b815260040160405180910390fd5b612d6883604001518261468a565b6000612d7c84600001518560200151611283565b60208101518151608087015160405163074334fb60e01b815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015612ded573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e119190615c80565b612e2e57604051632c36cd3360e01b815260040160405180910390fd5b6020810151604051636e5bceab60e11b81526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa158015612e89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ead9190615c80565b612eca57604051637228eff560e11b815260040160405180910390fd5b6040805160808082018352868301516001600160401b0390811683529087015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528c516000908152601886528981208d8416825286528981208c8916825286528981208951815498518a16600160401b026001600160801b031990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558a5184526019835286842090519094168352928152848220865183529052928320805492939192909190612fba908490615c9d565b92505081905550826001600160401b031685604001516001600160401b031686600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518a60800151604051613040939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a4611c19565b600061305882611850565b60008381526014602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160401b03831615806130e357506001600160401b038116155b15613101576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152601a602090815260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b601e80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008381526012602090815260408083206001600160401b038616845290915290205460ff1615613210576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600d60205260408120805461323390615b98565b90500361325357604051632aa5eff960e11b815260040160405180910390fd5b6000838152601060205260409020546080116132825760405163bade3a7b60e01b815260040160405180910390fd5b60008381526012602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260108552928520805493840181558552929093206004820401805460039092166008026101000a808402199092169190930217909155811615613330576000838152600e602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061336657613366615c21565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516133b59190615db5565b60405180910390a3611222565b6133cd8383836143de565b6000838152600e602090815260408083206001600160401b038086168552925282205416908082156134a55761349f83600f600089815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561348c57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116134495790505b50505050506127c490919063ffffffff16565b90925090505b8015613526576000868152600f602052604090208054859190849081106134ce576134ce615c21565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526012835260408083209187168352925220805460ff191690556135b1565b6000868152601160209081526040808320600f83529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6135bc868686614486565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4610fd0565b6001600160a01b0384163b61362a5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038516906301ffc9a790602401602060405180830381865afa158015613675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136999190615c80565b6136b65760405163fbd5d8b960e01b815260040160405180910390fd5b6136c28484848461472a565b61122260008560008587866142a3565b6000828152600b602052604090205481101561370157604051631e73178b60e11b815260040160405180910390fd5b6000828152600b6020526040812061371891614bf1565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b6000806000613755856124df565b5091509150600061376586611850565b6001600160a01b03841660009081526006602052604081208054929350600192909190613793908490615c54565b9091555061379e9050565b6040805160208101909152600090526137b8600087612865565b6137c186614821565b60006137cc87611a05565b6000888152600a602052604081209192506137e79190614bf1565b6000878152600b602052604081206137fe91614bf1565b60008781526007602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015613a03578983106138ad5784818151811061385557613855615c21565b60200260200101516020015185828151811061387357613873615c21565b6020908102919091010151516040516306177b2560e41b81526001600160a01b039092166004830152602482015260440160405180910390fd5b600c60008683815181106138c3576138c3615c21565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061390357613903615c21565b602002602001015160000151815260200190815260200160002060009055828a03935084818151811061393857613938615c21565b6020026020010151602001516001600160a01b031663b390c0ab86838151811061396457613964615c21565b60200260200101516000015160018761397d9190615c54565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156139c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139e49190615ded565b6139ef906001615c9d565b6139f99084615c9d565b9250600101613834565b5060008a81526009602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b03881690600080516020615f33833981519152908390a4604080518781526000602082018190528c9290916001600160a01b038b1691600080516020615f13833981519152910160405180910390a46040805160208101909152600090525098975050505050505050565b613aa98484848461299f565b613ab584848484614542565b6112225760405163bcb5663760e01b815260040160405180910390fd5b613adc8583612b18565b6001600160a01b038316158015613af35750805115155b15613b1157604051631035ad0760e11b815260040160405180910390fd5b6001600160401b03858116600090815260156020908152604080832080546001600160a01b0319166001600160a01b03891617905560168252808320805467ffffffffffffffff191694891694909417909355601781529190208251610fd092840190614c12565b6000601982511115613b9e57604051631af1729760e31b815260040160405180910390fd5b6103ff60005b8351811015613c1e5781848281518110613bc057613bc0615c21565b602002602001015161ffff161115613beb57604051634d80548d60e01b815260040160405180910390fd5b600a8102848281518110613c0157613c01615c21565b602002602001015161ffff16901b83179250806001019050613ba4565b505060009283526021602090815260408085208390559080529092209190915550565b6001600160401b03808316600090815260156020908152604080832054878452601883528184206001600160a01b039182168086529084528285208787168652845293829020825160808101845281548088168252600160401b9004909616938601939093526001830154918501919091526002909101541660608301819052909190613ce1576040516317de7dd760e21b815260040160405180910390fd5b60008581526018602090815260408083206001600160a01b0380871685529083528184206001600160401b0388168552835281842080546001600160801b03191681556001808201869055600290910180546001600160a01b031916905589855260198452828520606087015190921685529083528184208583015185529092528220805491929091613d75908490615c54565b92505081905550826001600160401b0316846001600160401b0316867f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da13846040015185606001518660200151604051613040939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b61ffff82166000908152601b602052604090206111b18282615cf6565b6000838152600f602052604090205481908114613e4057604051633581be1d60e11b815260040160405180910390fd5b6000848152601160205260409020613e59908484614cca565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2611222565b81613eb857613e9a888585612476565b15613eb857604051630619dc9d60e21b815260040160405180910390fd5b613ec8888888888888888861482c565b5050505050505050565b60008281526010602052604090205481811115613f0257604051635134ce8960e01b815260040160405180910390fd5b60005b81811015613f80576000848152601060205260408120805483908110613f2d57613f2d615c21565b60009182526020808320600483040154888452600e8252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101613f05565b506000838152601060205260408120613f9891614d46565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6000613fd68585611f81565b9050613fe3818484614a0d565b6001600160a01b0383166000908152600c60209081526040808320858452909152902054156140255760405163188a497360e01b815260040160405180910390fd5b6000858152600b6020526040902061403d9085614a51565b6000858152600a6020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b039283161790558716808552600c8452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a99101613040565b60008060006140de846124df565b50915091508060001461410157506001600160a01b038481169116149050610fb3565b816001600160a01b0316856001600160a01b0316148061412657506141268286612448565b8061414a5750846001600160a01b031661413f856110e4565b6001600160a01b0316145b95945050505050565b60005b606481101561422a576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b815260040161419291815260200190565b606060405180830381865afa1580156141af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141d39190615e06565b925092509250806141e75750505050505050565b6001600160a01b038316301480156141fe57508682145b1561421c576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101614156565b50604051630349a6bd60e51b815260040160405180910390fd5b6040805180820182528381526001600160a01b038381166020808401918252600088815260099091529384209251835551600190920180546001600160a01b0319169290911691909117905561429a9084612865565b6111b183614821565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc906142d590879087908790600401615e49565b600060405180830381600087803b1580156142ef57600080fd5b505af1158015614303573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b0316600080516020615f3383398151915260405160405180910390a482866001600160a01b0316886001600160a01b0316600080516020615f138339815191528888604051614373929190918252602082015260400190565b60405180910390a4612415565b60008061438c83611850565b9050806001600160a01b0316846001600160a01b031614806143b357506143b381856111df565b806110dc5750836001600160a01b03166143cc84612274565b6001600160a01b031614949350505050565b600083815260106020526040902054821061440c57604051630757d52160e01b815260040160405180910390fd5b600083815260106020526040902080548390811061442c5761442c615c21565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b0316146111b1576040516378eeeecf60e01b815260040160405180910390fd5b600083815260106020526040902061449e9083614b0b565b6000928352600e602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b6144e1838360008461472a565b60405182906001600160a01b03851690600090600080516020615f33833981519152908290a46040805160008082526020820181905284926001600160a01b03871692600080516020615f13833981519152910160405180910390a4505050565b60006001600160a01b0384163b1561463657604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614586903390899088908890600401615e68565b6020604051808303816000875af19250505080156145c1575060408051601f3d908101601f191682019092526145be91810190615ea5565b60015b61461c573d8080156145ef576040519150601f19603f3d011682016040523d82523d6000602084013e6145f4565b606091505b5080516146145760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506110dc565b5060016110dc565b60008061464a83611850565b9050806001600160a01b0316846001600160a01b0316148061467157506146718185612448565b806110dc5750836001600160a01b03166143cc846110e4565b6001600160401b0382166000908152601760209081526040808320805482518185028101850190935280835261470993869392919083018282801561348c57600091825260209182902080546001600160401b0316845290820283019290916008910180841161344957905050505050506127c490919063ffffffff16565b915050806111b157604051634ef44ed560e01b815260040160405180910390fd5b6001600160a01b038416614751576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600960205260409020600101546001600160a01b03161561478a5760405163c5a8d37160e01b815260040160405180910390fd5b826147a8576040516312c33ce360e01b815260040160405180910390fd5b6001600160a01b03841660009081526006602052604081208054600192906147d1908490615c9d565b90915550506040805180820182529283526001600160a01b039485166020808501918252600095865260099052932091518255509051600190910180546001600160a01b03191691909216179055565b6111dc60008261304d565b60408051808201909152600080825260208201528215614857576148508987611f81565b9050614864565b6148618987611283565b90505b61486f818686614a0d565b8215614892576000898152600b6020526040902061488d9087614a51565b6148cc565b6001600160a01b0385166000908152600c6020908152604080832087845282528083208390558b8352600a90915290206148cc9087614a51565b6001600160a01b038816156149b6578661494957604051635c46a7ef60e11b81526001600160a01b0386169063b88d4fde906149129030908c9089908890600401615e68565b600060405180830381600087803b15801561492c57600080fd5b505af1158015614940573d6000803e3d6000fd5b505050506149b6565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a916149839130918d918d908990600401615ec2565b600060405180830381600087803b15801561499d57600080fd5b505af11580156149b1573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a4612269565b82602001516001600160a01b0316826001600160a01b0316141580614a33575082518114155b156111b157604051637383f2c160e11b815260040160405180910390fd5b81548290614a6190600190615c54565b81548110614a7157614a71615c21565b9060005260206000209060020201828281548110614a9157614a91615c21565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b039092169190911790558154829080614adc57614adc615efc565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b81548110614b1857600080fd5b81548290614b2890600190615c54565b81548110614b3857614b38615c21565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110614b7357614b73615c21565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480614bbc57614bbc615efc565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b50805460008255600202906000526020600020908101906111dc9190614d6b565b82805482825590600052602060002090600301600490048101928215614cba5791602002820160005b83821115614c8557835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614c3b565b8015614cb85782816101000a8154906001600160401b030219169055600801602081600701049283019260010302614c85565b505b50614cc6929150614d92565b5090565b82805482825590600052602060002090600301600490048101928215614cba5791602002820160005b83821115614c855783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614cf3565b5080546000825560030160049004906000526020600020908101906111dc9190614d92565b5b80821115614cc657600081556001810180546001600160a01b0319169055600201614d6c565b5b80821115614cc65760008155600101614d93565b6001600160e01b0319811681146111dc57600080fd5b600060208284031215614dcf57600080fd5b8135614dda81614da7565b9392505050565b6001600160a01b03811681146111dc57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e3457614e34614df6565b604052919050565b600082601f830112614e4d57600080fd5b81356001600160401b03811115614e6657614e66614df6565b614e79601f8201601f1916602001614e0c565b818152846020838601011115614e8e57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215614ec357600080fd5b8535614ece81614de1565b94506020860135614ede81614de1565b9350604086013592506060860135915060808601356001600160401b03811115614f0757600080fd5b614f1388828901614e3c565b9150509295509295909350565b6000815180845260005b81811015614f4657602081850181015186830182015201614f2a565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000614dda6020830184614f20565b80356001600160401b038116811461134057600080fd5b60008060008060808587031215614fa657600080fd5b8435614fb181614de1565b935060208501359250614fc660408601614f79565b9150614fd460608601614f79565b905092959194509250565b600060208284031215614ff157600080fd5b5035919050565b6000806040838503121561500b57600080fd5b823561501681614de1565b946020939093013593505050565b60006020828403121561503657600080fd5b8135614dda81614de1565b6000806040838503121561505457600080fd5b823561505f81614de1565b9150602083013561506f81614de1565b809150509250929050565b60008060006060848603121561508f57600080fd5b83359250602084013591506150a660408501614f79565b90509250925092565b6000806000606084860312156150c457600080fd5b83356150cf81614de1565b925060208401356150df81614de1565b929592945050506040919091013590565b6000806040838503121561510357600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610fb3565b60006020828403121561514457600080fd5b81356001600160401b0381111561515a57600080fd5b6110dc84828501614e3c565b803561ffff8116811461134057600080fd5b60008060006060848603121561518d57600080fd5b833561519881614de1565b9250602084013591506150a660408501615166565b80151581146111dc57600080fd5b600080604083850312156151ce57600080fd5b82359150602083013561506f816151ad565b6020808252825182820181905260009190848201906040850190845b8181101561222e57835161ffff16835292840192918401916001016151fc565b600060a0828403121561522e57600080fd5b60405160a081018181106001600160401b038211171561525057615250614df6565b8060405250823581526020830135602082015261526f60408401614f79565b604082015261528060608401614f79565b606082015261529160808401614f79565b60808201529392505050565b602080825282518282018190526000919060409081850190868401855b828110156152f0576152e0848351805182526020908101516001600160a01b0316910152565b92840192908501906001016152ba565b5091979650505050505050565b6000806040838503121561531057600080fd5b8235915061127a60208401614f79565b600081518084526020808501945080840160005b838110156153595781516001600160401b031687529582019590820190600101615334565b509495945050505050565b6080815260006153776080830187614f20565b6001600160401b03861660208401526001600160a01b038516604084015282810360608401526153a78185615320565b979650505050505050565b602081526000614dda6020830184615320565b6000806000606084860312156153da57600080fd5b833592506020840135915060408401356001600160401b038111156153fe57600080fd5b61540a86828701614e3c565b9150509250925092565b60008060006060848603121561542957600080fd5b61543284614f79565b9250602084013561544281614de1565b91506150a660408501614f79565b60006001600160401b0382111561546957615469614df6565b5060051b60200190565b600082601f83011261548457600080fd5b8135602061549961549483615450565b614e0c565b82815260059290921b840181019181810190868411156154b857600080fd5b8286015b848110156154da576154cd81614f79565b83529183019183016154bc565b509695505050505050565b600082601f8301126154f657600080fd5b8135602061550661549483615450565b82815260059290921b8401810191818101908684111561552557600080fd5b8286015b848110156154da5761553a81615166565b8352918301918301615529565b6000806000806080858703121561555d57600080fd5b84356001600160401b038082111561557457600080fd5b818701915087601f83011261558857600080fd5b8135602061559861549483615450565b82815260059290921b8401810191818101908b8411156155b757600080fd5b948201945b838610156155d5578535825294820194908201906155bc565b985050880135925050808211156155eb57600080fd5b6155f788838901615473565b9450604087013591508082111561560d57600080fd5b61561988838901615473565b9350606087013591508082111561562f57600080fd5b5061563c878288016154e5565b91505092959194509250565b60008060006060848603121561565d57600080fd5b83359250602084013561544281614de1565b600080600080600060a0868803121561568757600080fd5b853561569281614de1565b9450602086013593506040860135925060608601356001600160401b038111156156bb57600080fd5b6156c788828901615473565b9250506156d660808701615166565b90509295509295909350565b600080604083850312156156f557600080fd5b823561570081614de1565b9150602083013561506f816151ad565b6000806000806080858703121561572657600080fd5b8435935061573660208601614f79565b925061574460408601614f79565b9150614fd460608601615166565b6000806000806080858703121561576857600080fd5b843561577381614de1565b9350602085013561578381614de1565b92506040850135915060608501356001600160401b038111156157a557600080fd5b61563c87828801614e3c565b60008083601f8401126157c357600080fd5b5081356001600160401b038111156157da57600080fd5b6020830191508360208260051b850101111561282657600080fd5b600080600080600080600060a0888a03121561581057600080fd5b61581988614f79565b9650602088013561582981614de1565b955060408801356001600160401b038082111561584557600080fd5b6158518b838c01614e3c565b965060608a013591508082111561586757600080fd5b6158738b838c016157b1565b909650945060808a013591508082111561588c57600080fd5b506158998a828b016157b1565b989b979a50959850939692959293505050565b6000806000606084860312156158c157600080fd5b8335925061544260208501614f79565b600080604083850312156158e457600080fd5b6158ed83615166565b915060208301356001600160401b0381111561590857600080fd5b61591485828601614e3c565b9150509250929050565b6000806000806080858703121561593457600080fd5b843561593f81614de1565b93506020850135925060408501359150614fd460608601615166565b60008060006040848603121561597057600080fd5b8335925060208401356001600160401b0381111561598d57600080fd5b615999868287016157b1565b9497909650939450505050565b600080600080600080600080610100898b0312156159c357600080fd5b8835975060208901356159d581614de1565b9650604089013595506060890135945060808901356159f381614de1565b935060a0890135925060c0890135615a0a816151ad565b915060e08901356001600160401b03811115615a2557600080fd5b615a318b828c01614e3c565b9150509295985092959890939650565b60008060008060808587031215615a5757600080fd5b8435615a6281614de1565b9350602085810135935060408601356001600160401b0380821115615a8657600080fd5b615a9289838a016154e5565b94506060880135915080821115615aa857600080fd5b818801915088601f830112615abc57600080fd5b8135615aca61549482615450565b81815260059190911b8301840190848101908b831115615ae957600080fd5b8585015b83811015615b2157803585811115615b055760008081fd5b615b138e89838a0101615473565b845250918601918601615aed565b50989b979a50959850505050505050565b60008060008060808587031215615b4857600080fd5b84359350602085013592506040850135615b6181614de1565b9396929550929360600135925050565b600080600060608486031215615b8657600080fd5b8335925060208401356150df81614de1565b600181811c90821680615bac57607f821691505b602082108103615bcc57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610fb357610fb3615bd2565b600082615c1c57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615c4957600080fd5b8151614dda81614de1565b81810381811115610fb357610fb3615bd2565b600060018201615c7957615c79615bd2565b5060010190565b600060208284031215615c9257600080fd5b8151614dda816151ad565b80820180821115610fb357610fb3615bd2565b601f8211156111b157600081815260208120601f850160051c81016020861015615cd75750805b601f850160051c820191505b81811015610fd057828155600101615ce3565b81516001600160401b03811115615d0f57615d0f614df6565b615d2381615d1d8454615b98565b84615cb0565b602080601f831160018114615d585760008415615d405750858301515b600019600386901b1c1916600185901b178555610fd0565b600085815260208120601f198616915b82811015615d8757888601518255948401946001909101908401615d68565b5085821015615da55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6020808252825182820181905260009190848201906040850190845b8181101561222e57835183529284019291840191600101615dd1565b600060208284031215615dff57600080fd5b5051919050565b600080600060608486031215615e1b57600080fd5b8351615e2681614de1565b602085015160408601519194509250615e3e816151ad565b809150509250925092565b83815282602082015260606040820152600061414a6060830184614f20565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615e9b90830184614f20565b9695505050505050565b600060208284031215615eb757600080fd5b8151614dda81614da7565b6001600160a01b03868116825285166020820152604081018490526060810183905260a0608082018190526000906153a790830184614f20565b634e487b7160e01b600052603160045260246000fdfe04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552dddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122081bbfa0dc893f408c114ee96d07dec01006d0bf044158110fff081fd3485dfc464736f6c63430008130033

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

00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000cb5eb537bad9c54a4dae2e3526473d79a496456a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000f4576726c6f6f742046697368696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4556524c4f4f545f46495348494e4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d57433832717271575747596d324a454376503948584d4d46794a6b6d5756754e5a42327338314c3644754757000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000006e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d556452353977726663713445635a63597934714744654433777a54584b583131794651335a4231775254536f00000000000000000000000000000000000000000000000000000000000000000000000000000000000046697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f636f6d6d6f6e466973682e6a736f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f72617265466973682e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f65706963466973682e6a736f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049697066733a2f2f516d57545867386e4569434562486756726d69686d64695063487279516a42454e5a5a65614e72454c69616268552f2f6c6567656e64617279466973682e6a736f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000f4576726c6f6f742046697368696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b436f6d6d6f6e20466973680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009526172652046697368000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094570696320466973680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4c6567656e646172792046697368000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004

-----Decoded View---------------
Arg [0] : name_ (string): Evrloot Fishing
Arg [1] : symbol_ (string): EVRLOOT_FISHING
Arg [2] : collectionMetadata_ (string): ipfs://QmWC82qrqWWGYm2JECvP9HXMMFyJkmWVuNZB2s81L6DuGW
Arg [3] : royaltyRecipient (address): 0xCb5eB537bAd9C54A4Dae2E3526473D79A496456A
Arg [4] : royaltyPercentageBps (uint256): 500
Arg [5] : initData (tuple):


-----Encoded View---------------
74 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [3] : 000000000000000000000000cb5eb537bad9c54a4dae2e3526473d79a496456a
Arg [4] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [5] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [6] : 000000000000000000000000000000000000000000000000000000000000000f
Arg [7] : 4576726c6f6f742046697368696e670000000000000000000000000000000000
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000f
Arg [9] : 4556524c4f4f545f46495348494e470000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [11] : 697066733a2f2f516d57433832717271575747596d324a454376503948584d4d
Arg [12] : 46794a6b6d5756754e5a42327338314c36447547570000000000000000000000
Arg [13] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [14] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [15] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [16] : 00000000000000000000000000000000000000000000000000000000000004c0
Arg [17] : 00000000000000000000000000000000000000000000000000000000000004e0
Arg [18] : 00000000000000000000000000000000000000000000000000000000000006e0
Arg [19] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [21] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [25] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [26] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [27] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [28] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [29] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [30] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [31] : 0000000000000000000000000000000000000000000000000000000000000280
Arg [32] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [33] : 697066733a2f2f516d556452353977726663713445635a635979347147446544
Arg [34] : 33777a54584b583131794651335a4231775254536f0000000000000000000000
Arg [35] : 0000000000000000000000000000000000000000000000000000000000000046
Arg [36] : 697066733a2f2f516d57545867386e4569434562486756726d69686d64695063
Arg [37] : 487279516a42454e5a5a65614e72454c69616268552f2f636f6d6d6f6e466973
Arg [38] : 682e6a736f6e0000000000000000000000000000000000000000000000000000
Arg [39] : 0000000000000000000000000000000000000000000000000000000000000044
Arg [40] : 697066733a2f2f516d57545867386e4569434562486756726d69686d64695063
Arg [41] : 487279516a42454e5a5a65614e72454c69616268552f2f72617265466973682e
Arg [42] : 6a736f6e00000000000000000000000000000000000000000000000000000000
Arg [43] : 0000000000000000000000000000000000000000000000000000000000000044
Arg [44] : 697066733a2f2f516d57545867386e4569434562486756726d69686d64695063
Arg [45] : 487279516a42454e5a5a65614e72454c69616268552f2f65706963466973682e
Arg [46] : 6a736f6e00000000000000000000000000000000000000000000000000000000
Arg [47] : 0000000000000000000000000000000000000000000000000000000000000049
Arg [48] : 697066733a2f2f516d57545867386e4569434562486756726d69686d64695063
Arg [49] : 487279516a42454e5a5a65614e72454c69616268552f2f6c6567656e64617279
Arg [50] : 466973682e6a736f6e0000000000000000000000000000000000000000000000
Arg [51] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [52] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [53] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [54] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [55] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [56] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [57] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [58] : 000000000000000000000000000000000000000000000000000000000000000f
Arg [59] : 4576726c6f6f742046697368696e670000000000000000000000000000000000
Arg [60] : 000000000000000000000000000000000000000000000000000000000000000b
Arg [61] : 436f6d6d6f6e2046697368000000000000000000000000000000000000000000
Arg [62] : 0000000000000000000000000000000000000000000000000000000000000009
Arg [63] : 5261726520466973680000000000000000000000000000000000000000000000
Arg [64] : 0000000000000000000000000000000000000000000000000000000000000009
Arg [65] : 4570696320466973680000000000000000000000000000000000000000000000
Arg [66] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [67] : 4c6567656e646172792046697368000000000000000000000000000000000000
Arg [68] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [69] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [70] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [71] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [72] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [73] : 0000000000000000000000000000000000000000000000000000000000000004


Block Transaction Gas Used Reward
view all blocks collator

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

OVERVIEW

Evrloot is a low-touch RPG inspired by dark fantasy and built with the cutting-edge RMRK NFT standard.

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.