Moonbase Alpha Testnet

Token

Spacers (SPACERS)
ERC-721

Overview

Max Total Supply

220 SPACERS

Holders

32

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-
Balance
1 SPACERS
0x4138574878c133d3a12009d6f54b8f26de700834
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
Spacers

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 29 : Spacers.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

/**
 * @title Spacers
 * @author RMRK team
 */
contract Spacers is SpacerBase {
    using Strings for uint256;
    mapping(address => bool) private _autoAcceptCollection;

    constructor(
        string memory name_,
        string memory symbol_,
        string memory collectionMetadata_,
        InitData memory data
    ) SpacerBase(name_, symbol_, collectionMetadata_, data) {}

    function mintTokensWithNewAsset(
        address[] memory to,
        uint256[] memory tokenIds,
        address catalogAddress,
        string memory metadataURI,
        uint64[][] memory partIds
    ) public virtual notLocked onlyOwnerOrContributor {
        uint256 numToMint = tokenIds.length;
        if (numToMint == 0) revert RMRKMintZero();
        if (numToMint + _nextId > _maxSupply) revert RMRKMintOverMax();
        uint64 assetId;

        unchecked {
            _nextId += numToMint;
            _totalSupply += numToMint;
        }

        for (uint256 i; i < numToMint; ) {
            assetId = addEquippableAssetEntry(
                uint64(0),
                catalogAddress,
                string(abi.encodePacked(metadataURI, tokenIds[i].toString(), ".json")),
                partIds[i]
            );
            _safeMint(to[i], tokenIds[i], "");
            _addAssetToToken(tokenIds[i], assetId, 0);
            _acceptAsset(tokenIds[i], 0, assetId);
            unchecked {
                ++i;
            }
        }
    }

    function setAutoAcceptCollection(
        address collection
    ) public virtual onlyOwner {
        _autoAcceptCollection[collection] = true;
    }

    function _afterAddChild(
        uint256 tokenId,
        address childAddress,
        uint256 childId,
        bytes memory
    ) internal override {
        // Auto accept children if they are from known collections
        if (_autoAcceptCollection[childAddress]) {
            _acceptChild(
                tokenId,
                _pendingChildren[tokenId].length - 1,
                childAddress,
                childId
            );
        }
    }
}

File 2 of 29 : IERC2981.sol
// 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);
}

File 3 of 29 : IERC721Metadata.sol
// 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);
}

File 4 of 29 : IERC721.sol
// 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 29 : 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);
}

File 6 of 29 : Address.sol
// 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);
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

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

File 9 of 29 : Math.sol
// 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);
        }
    }
}

File 10 of 29 : Strings.sol
// 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);
    }
}

File 11 of 29 : IRMRKInitData.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title IRMRKInitData
 * @author RMRK team
 * @notice Interface representation of RMRK initialization data.
 * @dev This interface provides a struct used to pack data to avoid stack too deep error for too many arguments.
 */
interface IRMRKInitData {
    /**
     * @notice Used to provide initialization data without running into stack too deep errors.
     * @return erc20TokenAddress Address of the ERC20 token to be used when initializing a smart contract that supports
     *  ERC20 pay module
     * @return tokenUriIsEnumerable Weather the token URI is enumerable or not
     * @return royaltyRecipient Recipient of resale royalties
     * @return royaltyPercentageBps The percentage to be paid from the sale of the token expressed in basis points
     * @return maxSupply The maximum supply of tokens
     * @return pricePerMint The price per single-token mint expressed in the lowest denomination of native currency
     */
    struct InitData {
        address erc20TokenAddress; // 20 bytes
        bool tokenUriIsEnumerable; // 1 byte
        // --- new slot ---
        address royaltyRecipient; // 20 bytes
        uint16 royaltyPercentageBps; // 2 bytes
        // --- new slot ---
        uint256 maxSupply;
        // --- new slot ---
        uint256 pricePerMint;
        // another 32 bytes
    }
}

File 12 of 29 : Ownable.sol
// 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();
    }
}

File 13 of 29 : OwnableLock.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "./Ownable.sol";
import "../library/RMRKErrors.sol";

/**
 * @title OwnableLock
 * @author RMRK team
 * @notice A minimal ownable lock smart contract.
 */
contract OwnableLock is Ownable {
    uint256 private _lock;

    /**
     * @notice Reverts if the lock flag is set to true.
     */
    modifier notLocked() {
        _onlyNotLocked();
        _;
    }

    /**
     * @notice Locks the operation.
     * @dev Once locked, functions using `notLocked` modifier cannot be executed.
     */
    function setLock() public virtual onlyOwner {
        _lock = 1;
    }

    /**
     * @notice Used to retrieve the status of a lockable smart contract.
     * @return A boolean value signifying whether the smart contract has been locked
     */
    function getLock() public view returns (bool) {
        return _lock == 1;
    }

    /**
     * @notice Used to verify that the operation of the smart contract is not locked.
     * @dev If the operation of the smart contract is locked, the execution will be reverted.
     */
    function _onlyNotLocked() private view {
        if (_lock == 1) revert RMRKLocked();
    }
}

File 14 of 29 : IRMRKCatalog.sol
// 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);
}

File 15 of 29 : IRMRKCore.sol
// 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);
}

File 16 of 29 : RMRKCore.sol
// 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.0.1";

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

File 17 of 29 : IERC6220.sol
// 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
        );
}

File 18 of 29 : RMRKEquippable.sol
// 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 {}
}

File 19 of 29 : RMRKRoyalties.sol
// 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 20 of 29 : 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 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();

File 21 of 29 : RMRKLib.sol
// 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);
    }
}

File 22 of 29 : AbstractMultiAsset.sol
// 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 {}
}

File 23 of 29 : IERC5773.sol
// 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);
}

File 24 of 29 : IERC6059.sol
// 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 25 of 29 : 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 26 of 29 : 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;
    }
}

File 27 of 29 : RMRKCollectionMetadata.sol
// 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;
    }
}

File 28 of 29 : RMRKMintingUtils.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;

import "../access/OwnableLock.sol";
import "../library/RMRKErrors.sol";

/**
 * @title RMRKMintingUtils
 * @author RMRK team
 * @notice Smart contract of the RMRK minting utils module.
 * @dev This smart contract includes the top-level utilities for managing minting and implements OwnableLock by default.
 * @dev Max supply-related and pricing variables are immutable after deployment.
 */
contract RMRKMintingUtils is OwnableLock {
    uint256 internal _nextId;
    uint256 internal _totalSupply;
    uint256 internal _maxSupply;
    uint256 internal immutable _pricePerMint;

    /**
     * @notice Initializes the smart contract with a given maximum supply and minting price.
     * @param maxSupply_ The maximum supply of tokens to initialize the smart contract with
     * @param pricePerMint_ The minting price to initialize the smart contract with, expressed in the smallest
     *  denomination of the native currency of the chain to which the smart contract is deployed to
     */
    constructor(uint256 maxSupply_, uint256 pricePerMint_) {
        _maxSupply = maxSupply_;
        _pricePerMint = pricePerMint_;
    }

    /**
     * @notice Used to verify that the sale of the given token is still available.
     * @dev If the maximum supply is reached, the execution will be reverted.
     */
    modifier saleIsOpen() {
        _checkSaleIsOpen();
        _;
    }

    /**
     * @inheritdoc OwnableLock
     */
    function setLock() public virtual override onlyOwner {
        super.setLock();
        _maxSupply = _totalSupply;
    }

    /**
     * @notice Used to retrieve the total supply of the tokens in a collection.
     * @return The number of tokens in a collection
     */
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

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

    /**
     * @notice Used to retrieve the price per mint.
     * @return The price per mint of a single token expressed in the lowest denomination of a native currency
     */
    function pricePerMint() public view returns (uint256) {
        return _pricePerMint;
    }

    /**
     * @notice Used to withdraw the minting proceedings to a specified address.
     * @dev This function can only be called by the owner.
     * @param to Address to receive the given amount of minting proceedings
     * @param amount The amount to withdraw
     */
    function withdrawRaised(address to, uint256 amount) external onlyOwner {
        _withdraw(to, amount);
    }

    /**
     * @notice Used to withdraw the minting proceedings to a specified address.
     * @param _address Address to receive the given amount of minting proceedings
     * @param _amount The amount to withdraw
     */
    function _withdraw(address _address, uint256 _amount) private {
        (bool success, ) = _address.call{value: _amount}("");
        require(success, "Transfer failed.");
    }

    /**
     * @notice Used to verify that the sale is still open.
     * @dev In case the maximum supply of the collection is reached, the execution is reverted.
     */
    function _checkSaleIsOpen() private view {
        if (_nextId >= _maxSupply) revert RMRKMintOverMax();
    }
}

File 29 of 29 : SpacerBase.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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/utils/RMRKMintingUtils.sol";
import "@rmrk-team/evm-contracts/contracts/implementations/IRMRKInitData.sol";

error RMRKMintZero();

/**
 * @title SpacerItems
 * @author RMRK team
 */
abstract contract SpacerBase is
    IRMRKInitData,
    RMRKMintingUtils,
    RMRKCollectionMetadata,
    RMRKRoyalties,
    RMRKEquippable
{
    uint256 private _totalAssets;
    uint64 private constant _LOWEST_POSSIBLE_PRIORITY = 2 ^ (64 - 1);

    constructor(
        string memory name_,
        string memory symbol_,
        string memory collectionMetadata_,
        InitData memory data
    )
        RMRKMintingUtils(data.maxSupply, data.pricePerMint)
        RMRKCollectionMetadata(collectionMetadata_)
        RMRKRoyalties(data.royaltyRecipient, data.royaltyPercentageBps)
        RMRKEquippable(name_, symbol_)
    {}

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId);
        if (to == address(0)) {
            unchecked {
                _totalSupply -= 1;
            }
        }
    }

    function addAssetsToTokens(
        uint256[] memory tokenIds,
        uint64[] memory assetIds,
        uint64 replacesAssetWithId
    ) public virtual onlyOwnerOrContributor {
        uint256 len = tokenIds.length;
        for (uint256 i; i < len; ) {
            _addAssetToToken(tokenIds[0], assetIds[0], replacesAssetWithId);
            unchecked {
                ++i;
            }
        }
    }

    function addAssetToToken(
        uint256 tokenId,
        uint64 assetId,
        uint64 replacesAssetWithId
    ) public virtual onlyOwnerOrContributor {
        _addAssetToToken(tokenId, assetId, replacesAssetWithId);
    }

    function addAssetEntry(
        string memory metadataURI
    ) public virtual onlyOwnerOrContributor returns (uint256) {
        unchecked {
            _totalAssets += 1;
        }
        _addAssetEntry(uint64(_totalAssets), metadataURI);
        return _totalAssets;
    }

    function addEquippableAssetEntries(
        uint64 equippableGroupId,
        address catalogAddress,
        string[] memory metadataURI,
        uint64[] memory partIds
    ) public virtual onlyOwnerOrContributor {
        uint256 len = metadataURI.length;
        for (uint256 i; i < len; ) {
            addEquippableAssetEntry(
                equippableGroupId,
                catalogAddress,
                metadataURI[i],
                partIds
            );
            unchecked {
                ++i;
            }
        }
    }

    function addEquippableAssetEntry(
        uint64 equippableGroupId,
        address catalogAddress,
        string memory metadataURI,
        uint64[] memory partIds
    ) public virtual onlyOwnerOrContributor returns (uint64) {
        unchecked {
            _totalAssets += 1;
        }
        _addAssetEntry(
            uint64(_totalAssets),
            equippableGroupId,
            catalogAddress,
            metadataURI,
            partIds
        );
        return uint64(_totalAssets);
    }

    function setValidParentForEquippableGroup(
        uint64 equippableGroupId,
        address parentAddress,
        uint64 partId
    ) public virtual onlyOwnerOrContributor {
        _setValidParentForEquippableGroup(
            equippableGroupId,
            parentAddress,
            partId
        );
    }

    function totalAssets() public view virtual returns (uint256) {
        return _totalAssets;
    }

    function updateRoyaltyRecipient(
        address newRoyaltyRecipient
    ) public virtual override onlyOwner {
        _setRoyaltyRecipient(newRoyaltyRecipient);
    }

    function tokenURI(
        uint256 tokenId
    ) public view virtual returns (string memory) {
        _requireMinted(tokenId);
        uint64[] memory priorities = getActiveAssetPriorities(tokenId);
        // This will always be at least 1, since we can only mint with an asset directly
        uint256 len = priorities.length;
        uint64 maxPriority = _LOWEST_POSSIBLE_PRIORITY;
        uint64 maxPriorityIndex;
        for (uint64 i; i < len; ) {
            uint64 currentPrio = priorities[i];
            if (currentPrio < maxPriority) {
                maxPriority = currentPrio;
                maxPriorityIndex = i;
            }
            unchecked {
                ++i;
            }
        }
        uint64 maxPriorityAssetId = getActiveAssets(tokenId)[maxPriorityIndex];
        return getAssetMetadata(tokenId, maxPriorityAssetId);
    }
}

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

Contract ABI

[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"collectionMetadata_","type":"string"},{"components":[{"internalType":"address","name":"erc20TokenAddress","type":"address"},{"internalType":"bool","name":"tokenUriIsEnumerable","type":"bool"},{"internalType":"address","name":"royaltyRecipient","type":"address"},{"internalType":"uint16","name":"royaltyPercentageBps","type":"uint16"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"pricePerMint","type":"uint256"}],"internalType":"struct IRMRKInitData.InitData","name":"data","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"RMRKLocked","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":"RMRKMintOverMax","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"},{"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":"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"}],"name":"addAssetToToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint64[]","name":"assetIds","type":"uint64[]"},{"internalType":"uint64","name":"replacesAssetWithId","type":"uint64"}],"name":"addAssetsToTokens","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[]"}],"name":"addEquippableAssetEntries","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[]"}],"name":"addEquippableAssetEntry","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"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":"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":"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":[],"name":"getLock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"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":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"to","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"catalogAddress","type":"address"},{"internalType":"string","name":"metadataURI","type":"string"},{"internalType":"uint64[][]","name":"partIds","type":"uint64[][]"}],"name":"mintTokensWithNewAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"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":[],"name":"pricePerMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"collection","type":"address"}],"name":"setAutoAcceptCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setLock","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"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawRaised","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b506040516200638638038062006386833981016040819052620000349162000288565b838383838383818181818660400151876060015161ffff168989608001518a60a00151600160008190555062000079620000736200010260201b60201c565b62000106565b6006919091556080526200008d8162000158565b50600880546001600160a01b0319166001600160a01b0384161790556127108110620000cc57604051634006185d60e11b815260040160405180910390fd5b60095550600a620000de83826200043a565b50600b620000ed82826200043a565b50505050505050505050505050505062000506565b3390565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60076200016682826200043a565b5050565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715620001a557620001a56200016a565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620001d657620001d66200016a565b604052919050565b600082601f830112620001f057600080fd5b81516001600160401b038111156200020c576200020c6200016a565b602062000222601f8301601f19168201620001ab565b82815285828487010111156200023757600080fd5b60005b83811015620002575785810183015182820184015282016200023a565b506000928101909101919091529392505050565b80516001600160a01b03811681146200028357600080fd5b919050565b600080600080848603610120811215620002a157600080fd5b85516001600160401b0380821115620002b957600080fd5b620002c789838a01620001de565b96506020880151915080821115620002de57600080fd5b620002ec89838a01620001de565b955060408801519150808211156200030357600080fd5b506200031288828901620001de565b93505060c0605f19820112156200032857600080fd5b506200033362000180565b62000341606087016200026b565b8152608086015180151581146200035757600080fd5b60208201526200036a60a087016200026b565b604082015260c086015161ffff811681146200038557600080fd5b606082015260e086015160808201526101009095015160a0860152509194909350909190565b600181811c90821680620003c057607f821691505b602082108103620003e157634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200043557600081815260208120601f850160051c81016020861015620004105750805b601f850160051c820191505b8181101562000431578281556001016200041c565b5050505b505050565b81516001600160401b038111156200045657620004566200016a565b6200046e81620004678454620003ab565b84620003e7565b602080601f831160018114620004a657600084156200048d5750858301515b600019600386901b1c1916600185901b17855562000431565b600085815260208120601f198616915b82811015620004d757888601518255948401946001909101908401620004b6565b5085821015620004f65787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b608051615e646200052260003960006109fd0152615e646000f3fe608060405234801561001057600080fd5b50600436106104725760003560e01c80636f19951c11610250578063b390c0ab11610150578063e467a48f116100c8578063ee1dffcf11610097578063fb25fb7a1161007c578063fb25fb7a14610b60578063fc3517c814610b9a578063ffa1ad7414610bad57600080fd5b8063ee1dffcf14610b0e578063f2fde38b14610b4d57600080fd5b8063e467a48f14610a99578063e7de4de414610aac578063e97ceaa814610abf578063e985e9c514610ad257600080fd5b8063c87b56dd1161011f578063de8e602c11610104578063de8e602c14610a3c578063defa80c314610a4f578063df6f556b14610a6257600080fd5b8063c87b56dd14610a21578063d5abeb0114610a3457600080fd5b8063b390c0ab146109c2578063b88d4fde146109d5578063c259a988146109e8578063c5610a29146109fb57600080fd5b80638507dc28116101e357806395edc18c116101b2578063a22cb46511610197578063a22cb46514610989578063a898e3641461099c578063ace61e00146109af57600080fd5b806395edc18c14610965578063979613361461097657600080fd5b80638507dc281461093157806389ed2edf146109445780638da5cb5b1461094c57806395d89b411461095d57600080fd5b80637507e2ae1161021f5780637507e2ae1461081b57806379e8ca9e1461090b5780637aba6f371461091e5780637e5852d91461092657600080fd5b80636f19951c146107da57806370a08231146107ed578063715018a6146108005780637280281e1461080857600080fd5b80632f32f937116103765780634e60edba116102ee5780635e94354a116102bd5780636352211e116102a25780636352211e146107a1578063635490cc146107b457806365b67eb9146107c757600080fd5b80635e94354a1461077b5780635ea72f361461078e57600080fd5b80634e60edba1461071d57806351532e5a1461074057806359c8b7dd146107605780635e2e32921461076857600080fd5b8063362b67f41161034557806342842e0e1161032a57806342842e0e146106d757806342966c68146106ea57806344ec9344146106fd57600080fd5b8063362b67f4146106b157806338dcf74c146106c457600080fd5b80632f32f9371461065857806330ffb1d614610678578063326d526d1461068b57806333ed28451461069e57600080fd5b80630fc499f51161040957806322e6d160116103d857806323b872dd116103bd57806323b872dd146105e85780632452cd91146105fb5780632a55205a1461062657600080fd5b806322e6d1601461059957806322f6da9c146105d557600080fd5b80630fc499f51461053f57806318160ddd146105525780631c7bb4611461055a5780631d0d35f51461056d57600080fd5b806306fdde031161044557806306fdde03146104d9578063074334fb146104ee578063081812fc14610501578063095ea7b31461052c57600080fd5b806301e1d1141461047757806301ffc9a71461048e57806302549760146104b1578063064c0a3a146104c6575b600080fd5b6021545b6040519081526020015b60405180910390f35b6104a161049c366004614dc7565b610be9565b6040519015158152602001610485565b6104c46104bf366004614f6c565b610c62565b005b6104c46104d4366004615064565b610cad565b6104e1610cc4565b6040516104859190615129565b6104a16104fc36600461513c565b610d56565b61051461050f36600461518b565b610dcf565b6040516001600160a01b039091168152602001610485565b6104c461053a3660046151a4565b610e15565b6104c461054d3660046151d0565b610ed3565b60055461047b565b6104c46105683660046151ed565b610efc565b6104a161057b3660046151d0565b6001600160a01b031660009081526002602052604090205460011490565b6104a16105a7366004615229565b6001600160a01b03918216600090815260196020908152604080832093909416825291909152205460ff1690565b6104c46105e3366004615262565b610f0f565b6104c46105f636600461528e565b610f2a565b61060e6106093660046152cf565b610f4f565b6040516001600160401b039091168152602001610485565b610639610634366004615349565b610f7d565b604080516001600160a01b039093168352602083019190915201610485565b61066b610666366004615349565b610fb3565b604051610485919061536b565b61047b61068636600461538b565b611065565b6104c46106993660046151d0565b61108e565b6104c46106ac3660046151a4565b6110ba565b6104c46106bf36600461541a565b6110d0565b6104c46106d2366004615484565b611133565b6104c46106e536600461528e565b611158565b6104c46106f836600461518b565b611173565b61071061070b36600461518b565b61117e565b6040516104859190615505565b61073061072b366004615565565b6111fd565b60405161048594939291906155cc565b61075361074e36600461518b565b6112da565b604051610485919061560f565b60095461047b565b6104e1610776366004615565565b61136e565b61075361078936600461518b565b611476565b6104c461079c3660046151a4565b6114df565b6105146107af36600461518b565b61159c565b6104c46107c2366004615622565b611645565b6104c46107d5366004615671565b611777565b6107106107e836600461518b565b61178a565b61047b6107fb3660046151d0565b6117fa565b6104c4611858565b61075361081636600461518b565b61186c565b6108bd61082936600461569f565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601e87528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352680100000000000000009004909416958101959095526001830154918501919091526002909101549091169082015290565b604051610485919060006080820190506001600160401b0380845116835280602085015116602084015250604083015160408301526001600160a01b03606084015116606083015292915050565b6104c46109193660046156d4565b6118d5565b6104c46119ab565b6003546001146104a1565b6104c461093f3660046156d4565b6119c3565b6104e1611a59565b6001546001600160a01b0316610514565b6104e1611a68565b6008546001600160a01b0316610514565b6104c4610984366004615349565b611a77565b6104c46109973660046156d4565b611a8b565b61066b6109aa366004615349565b611b32565b6104c46109bd366004615781565b611ba8565b61047b6109d0366004615349565b611d6b565b6104c46109e336600461588f565b611d81565b6104c46109f63660046151ed565b611d97565b7f000000000000000000000000000000000000000000000000000000000000000061047b565b6104e1610a2f36600461518b565b611dac565b60065461047b565b6104c4610a4a3660046158ee565b611e74565b6104c4610a5d36600461596c565b611e89565b61060e610a70366004615565565b60009182526014602090815260408084206001600160401b039384168552909152909120541690565b610514610aa736600461518b565b611eae565b6104c4610aba366004615349565b611ed1565b6104c4610acd366004615a07565b611ee5565b6104a1610ae0366004615229565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205460ff1690565b6104a1610b1c366004615a46565b6000928352601f602090815260408085206001600160a01b0394909416855292815282842091845252902054151590565b6104c4610b5b3660046151d0565b611efb565b610b73610b6e36600461518b565b611f4c565b604080516001600160a01b0390941684526020840192909252151590820152606001610485565b6104c4610ba8366004615262565b611fb4565b6104e16040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b6000610bf482611fc9565b80610c2857506001600160e01b031982167f06b4329a00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982167f28bc9ae400000000000000000000000000000000000000000000000000000000145b92915050565b610c6a612067565b815160005b81811015610ca557610c9c8686868481518110610c8e57610c8e615a6d565b602002602001015186610f4f565b50600101610c6f565b505050505050565b82610cb7816120bf565b610ca586868686866120ff565b6060600a8054610cd390615a83565b80601f0160208091040260200160405190810160405280929190818152602001828054610cff90615a83565b8015610d4c5780601f10610d2157610100808354040283529160200191610d4c565b820191906000526020600020905b815481529060010190602001808311610d2f57829003601f168201915b5050505050905090565b6001600160401b038083166000908152601c602090815260408083205484168084528280528184206001600160a01b038a1685529092528220549192909181169084168103610dc0576000610db486610dae8961186c565b9061231a565b9450610dc79350505050565b6000925050505b949350505050565b6000610dda82612383565b6000828152600d6020526040812090610df28461159c565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6000610e208261159c565b9050806001600160a01b0316836001600160a01b031603610e6d576040517f0591db6d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03821614801590610e8d5750610e8b8133610ae0565b155b15610ec4576040517f982462b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece83836123bb565b505050565b610edb612434565b600880546001600160a01b0319166001600160a01b03831617905550565b50565b610f04612067565b610ece838383612478565b82610f19816126a0565b610f248484846126e0565b50505050565b80610f34816120bf565b610f248484846040518060200160405280600081525061274f565b6000610f59612067565b6021805460010190819055610f7190868686866128c5565b50602154949350505050565b6008546009546001600160a01b039091169060009061271090610fa09085615ad3565b610faa9190615aea565b90509250929050565b604080518082019091526000808252602082015281610fd18461178a565b5111611009576040517fca7cc84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040812080548490811061102957611029615a6d565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b600061106f612067565b60218054600101908190556110849083612985565b506021545b919050565b611096612434565b6001600160a01b03166000908152602260205260409020805460ff19166001179055565b6110c2612434565b6110cc8282612a4b565b5050565b6110d8612067565b825160005b8181101561112c57611124856000815181106110fb576110fb615a6d565b60200260200101518560008151811061111657611116615a6d565b602002602001015185612478565b6001016110dd565b5050505050565b805161113e81612b0d565b611146612b4d565b61114f82612b90565b50506001600055565b610ece83838360405180602001604052806000815250611d81565b6110cc816000611d6b565b6000818152601160209081526040808320805482518185028101850190935280835260609493849084015b828210156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b50929695505050505050565b6060600080606061120e868661136e565b6001600160401b038087166000908152601c6020908152604080832054601b835281842054601d845293829020805483518186028101860190945280845291909516946001600160a01b0390941693928391908301828280156112c257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161127f5790505b50505050509050935093509350935092959194509250565b60008181526016602090815260409182902080548351818402810184019094528084526060939283018282801561136257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161131f5790505b50505050509050919050565b60008281526018602090815260408083206001600160401b038516845290915290205460609060ff166113cd576040517fdcc947e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038216600090815260136020526040902080546113f090615a83565b80601f016020809104026020016040519081016040528092919081815260200182805461141c90615a83565b80156114695780601f1061143e57610100808354040283529160200191611469565b820191906000526020600020905b81548152906001019060200180831161144c57829003601f168201915b5050505050905092915050565b60008181526017602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b60006114ea8261159c565b9050806001600160a01b0316836001600160a01b03160361151e576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b0382161480159061155b57506001600160a01b038116600090815260196020908152604080832033845290915290205460ff16155b15611592576040517faf45d29200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece8383612f87565b6000806000806115ab85611f4c565b925092509250801561163c576040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015611615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116399190615b0c565b92505b50909392505050565b61164e83612383565b33803b61166e5760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b038216602082015260006116958661117e565b51905060808110156116f4576000868152601160209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055611726565b6040517fa53c8c0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd8460405161176391815260200190565b60405180910390a4610ca586848787613000565b61177f612067565b610ece838383613049565b60008181526010602090815260408083208054825181850281018501909352808352606094938490840182156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b60006001600160a01b03821661183c576040517f7772286600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b611860612434565b61186a60006130fb565b565b60008181526015602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b6118dd612434565b6001600160a01b03821661191d576040517fb5c0938000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611942576001600160a01b0382166000908152600260205260408120819055611961565b6001600160a01b03821660009081526002602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac28260405161199f911515815260200190565b60405180910390a25050565b6119b3612434565b6119bb61314d565b600554600655565b6001600160a01b03821633036119ec576040516375f45abd60e01b815260040160405180910390fd5b3360008181526019602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060078054610cd390615a83565b6060600b8054610cd390615a83565b81611a8181612b0d565b610ece838361315c565b6001600160a01b0382163303611acd576040517f2dee66e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611a4d565b604080518082019091526000808252602082015281611b508461117e565b5111611b88576040517fda22687f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260116020526040812080548490811061102957611029615a6d565b611bb06131ea565b611bb8612067565b83516000819003611bf5576040517f376bec4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600654600454611c059083615b29565b1115611c3d576040517fbd239bf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480548201905560058054820190556000805b82811015611d6157611cc160008787611c828b8681518110611c7557611c75615a6d565b6020026020010151613226565b604051602001611c93929190615b3c565b604051602081830303815290604052878581518110611cb457611cb4615a6d565b6020026020010151610f4f565b9150611d0f888281518110611cd857611cd8615a6d565b6020026020010151888381518110611cf257611cf2615a6d565b6020026020010151604051806020016040528060008152506132c5565b611d34878281518110611d2457611d24615a6d565b6020026020010151836000612478565b611d59878281518110611d4957611d49615a6d565b60200260200101516000846132fa565b600101611c51565b5050505050505050565b600082611d77816120bf565b610dc7848461353a565b81611d8b816120bf565b61112c858585856138d0565b82611da181612b0d565b610f24848484613905565b6060611db782612383565b6000611dc283611476565b8051909150603d6000805b83816001600160401b03161015611e2f57600085826001600160401b031681518110611dfb57611dfb615a6d565b60200260200101519050836001600160401b0316816001600160401b03161015611e26578093508192505b50600101611dcd565b506000611e3b8761186c565b826001600160401b031681518110611e5557611e55615a6d565b60200260200101519050611e69878261136e565b979650505050505050565b82611e7e816126a0565b610f24848484613ade565b87611e9381612b0d565b611ea38989898989898989613b71565b505050505050505050565b6000611eb982612383565b6000828152601a6020526040812090610df28461159c565b81611edb816126a0565b610ece8383613be8565b83611eef81612b0d565b61112c85858585613cf9565b611f03612434565b6001600160a01b038116611f43576040517f4ece6ecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ef9816130fb565b6000818152600f60209081526040808320815180830190925280548252600101546001600160a01b03169181018290528291829190611f9e5760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b82611fbe816126a0565b610f248484846132fa565b60006001600160e01b031982166301ffc9a760e01b148061201357506001600160e01b031982167f80ac58cd00000000000000000000000000000000000000000000000000000000145b8061204757506001600160e01b031982167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982166342b0e56f60e01b1492915050565b6001546001600160a01b0316331480159061208857506120863361057b565b155b1561186a576040517f1eca167000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120c93382613e18565b610ef9576040517f8be7930600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061210b85611f4c565b5091509150866001600160a01b0316826001600160a01b0316146121425760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616612169576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561218057508385145b156121b7576040517f3d76b10700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0386163b6121df5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa15801561222a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224e9190615b93565b612284576040517f5e13b1cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61228f858786613ebb565b61229a878787613fde565b6001600160a01b0387166000908152600c602052604081208054600192906122c3908490615bb0565b909155506122d49050858588613ffb565b6001600160a01b0386166000908152600c602052604081208054600192906122fd908490615b29565b90915550612311905082878387898861405a565b50505050505050565b81516000908190815b8181101561237257846001600160401b031686828151811061234757612347615a6d565b60200260200101516001600160401b03160361236a5792506001915061237c9050565b600101612323565b5060008092509250505b9250929050565b6000818152600f60205260409020600101546001600160a01b0316610ef95760405163089ba7e160e41b815260040160405180910390fd5b60006123c68261159c565b6000838152600d602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6001546001600160a01b0316331461186a576040517f38c5ab1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038616845290915290205460ff16156124bc576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546124df90615a83565b905003612518576040517f554bdff200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902054608011612560576040517fbade3a7b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260168552928520805493840181558552929093206004820401805460039092166008026101000a80840219909216919093021790915581161561260e5760008381526014602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061264457612644615a6d565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516126939190615bc3565b60405180910390a3610f24565b6126aa3382614174565b610ef9576040517f5d64832900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126eb8383836141f2565b6126f68383836142cc565b60008381526018602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b60008061275b84611f4c565b5091509150856001600160a01b0316826001600160a01b0316146127925760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166127b9576040516338f646ff60e21b815260040160405180910390fd5b6127c4868686613fde565b6001600160a01b0386166000908152600c602052604081208054600192906127ed908490615bb0565b909155506127ff905084600087613ffb565b6001600160a01b0385166000908152600c60205260408120805460019290612828908490615b29565b909155505060405184906001600160a01b0380881691908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a483856001600160a01b0316836001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8460006040516128b8929190918252602082015260400190565b60405180910390a4610ca5565b6128cf8583612985565b6001600160a01b0383161580156128e65750805115155b1561291d576040517f206b5a0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038581166000908152601b6020908152604080832080546001600160a01b0319166001600160a01b038916179055601c8252808320805467ffffffffffffffff191694891694909417909355601d81529190208251610ca592840190614bff565b6001600160401b0382166129ac576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546129cf90615a83565b905011156129f0576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152601360205260409020612a128282615c4d565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612a98576040519150601f19603f3d011682016040523d82523d6000602084013e612a9d565b606091505b5050905080610ece576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5472616e73666572206661696c65642e0000000000000000000000000000000060448201526064015b60405180910390fd5b612b17338261431a565b610ef9576040517f2728a9d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600260005403612b89576040517fc57fd5d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600055565b6040808201516001600160401b039081166000908152601b602090815283822054606086015186518452601e83528584206001600160a01b039283168086529084528685209582168552949092529390912060020154919290911615612c22576040517fbd0650ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c30836040015182614386565b6000612c4484600001518560200151610fb3565b6020810151815160808701516040517f074334fb00000000000000000000000000000000000000000000000000000000815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015612cce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf29190615b93565b612d28576040517f2c36cd3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516040517fdcb79d560000000000000000000000000000000000000000000000000000000081526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa158015612d9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc09190615b93565b612df6576040517fe451dfea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808082018352868301516001600160401b0390811683529087015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528c516000908152601e86528981208d8416825286528981208c8916825286528981208951815498518a1668010000000000000000026fffffffffffffffffffffffffffffffff1990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558a518452601f835286842090519094168352928152848220865183529052928320805492939192909190612ef4908490615b29565b92505081905550826001600160401b031685604001516001600160401b031686600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518a60800151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a461112c565b6000612f928261159c565b6000838152601a602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160a01b03831660009081526022602052604090205460ff1615610f2457600084815260116020526040902054610f2490859061304290600190615bb0565b8585613cf9565b6001600160401b038316158061306657506001600160401b038116155b15613084576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152602080805260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b613155612434565b6001600355565b6000828152601160205260409020548110156131a4576040517f3ce62f1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526011602052604081206131bb91614cb7565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b60035460010361186a576040517fed1fa96f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606060006132338361443f565b60010190506000816001600160401b0381111561325257613252614e22565b6040519080825280601f01601f19166020018201604052801561327c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461328657509392505050565b6132d0838383614521565b6132dd60008484846145b3565b610ece5760405163bcb5663760e01b815260040160405180910390fd5b6133058383836141f2565b60008381526014602090815260408083206001600160401b038086168552925282205416908082156133dd576133d783601560008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156133c457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116133815790505b505050505061231a90919063ffffffff16565b90925090505b801561345e57600086815260156020526040902080548591908490811061340657613406615a6d565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526018835260408083209187168352925220805460ff191690556134e9565b6000868152601760209081526040808320601583529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6134f48686866142cc565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4610ca5565b600080600061354885611f4c565b509150915060006135588661159c565b6001600160a01b0384166000908152600c602052604081208054929350600192909190613586908490615bb0565b90915550613598905081600088613fde565b6040805160208101909152600090526135b26000876123bb565b6135bb866146af565b60006135c68761178a565b60008881526010602052604081209192506135e19190614cb7565b60008781526011602052604081206135f891614cb7565b6000878152600d602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015613812578983106136bc5784818151811061364f5761364f615a6d565b60200260200101516020015185828151811061366d5761366d615a6d565b6020908102919091010151516040517f6177b2500000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526024820152604401612b04565b601260008683815181106136d2576136d2615a6d565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061371257613712615a6d565b602002602001015160000151815260200190815260200160002060009055828a03935084818151811061374757613747615a6d565b6020026020010151602001516001600160a01b031663b390c0ab86838151811061377357613773615a6d565b60200260200101516000015160018761378c9190615bb0565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156137cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f39190615d0c565b6137fe906001615b29565b6138089084615b29565b925060010161362e565b5060008a8152600f602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b038816907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a4604080518781526000602082018190528c9290916001600160a01b038b16917f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a46040805160208101909152600090525098975050505050505050565b6138dc8484848461274f565b6138e8848484846145b3565b610f245760405163bcb5663760e01b815260040160405180910390fd5b6001600160401b038083166000908152601b6020908152604080832054878452601e83528184206001600160a01b0391821680865290845282852087871686528452938290208251608081018452815480881682526801000000000000000090049096169386019390935260018301549185019190915260029091015416606083018190529091906139c3576040517f5f79f75c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601e602090815260408083206001600160a01b0380871685529083528184206001600160401b0388168552835281842080546fffffffffffffffffffffffffffffffff191681556001808201869055600290910180546001600160a01b0319169055898552601f8452828520606087015190921685529083528184208583015185529092528220805491929091613a60908490615bb0565b92505081905550826001600160401b0316846001600160401b0316867f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da13846040015185606001518660200151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60008381526015602052604090205481908114613b27576040517f6b037c3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152601760205260409020613b40908484614cd8565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610f24565b81613bd8576000888152601f602090815260408083206001600160a01b0388168452825280832086845290915290205415613bd8576040517f1867727400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d6188888888888888886146ba565b60008281526016602052604090205481811115613c31576040517f5134ce8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015613caf576000848152601660205260408120805483908110613c5c57613c5c615a6d565b6000918252602080832060048304015488845260148252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101613c34565b506000838152601660205260408120613cc791614d54565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6000613d058585611b32565b9050613d128184846148cd565b6001600160a01b038316600090815260126020908152604080832085845290915290205415613d6d576040517f188a497300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601160205260409020613d85908561492a565b600085815260106020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260128452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a99101612f7a565b6000806000613e2684611f4c565b509150915080600014613e4957506001600160a01b038481169116149050610c5c565b816001600160a01b0316856001600160a01b03161480613e8e57506001600160a01b038083166000908152600e602090815260408083209389168352929052205460ff165b80613eb25750846001600160a01b0316613ea785610dcf565b6001600160a01b0316145b95945050505050565b60005b6064811015613fab576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b8152600401613efa91815260200190565b606060405180830381865afa158015613f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f3b9190615d25565b92509250925080613f4f5750505050505050565b6001600160a01b03831630148015613f6657508682145b15613f9d576040517f48a87cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509093509150600101613ebe565b506040517f6934d7a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216610ece5760058054600019019055505050565b6040805180820182528381526001600160a01b0383811660208084019182526000888152600f9091529384209251835551600190920180546001600160a01b0319169290911691909117905561405190846123bb565b610ece836146af565b6040517f635490cc00000000000000000000000000000000000000000000000000000000815285906001600160a01b0382169063635490cc906140a590879087908790600401615d68565b600060405180830381600087803b1580156140bf57600080fd5b505af11580156140d3573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a482866001600160a01b0316886001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8888604051614167929190918252602082015260400190565b60405180910390a4612311565b6000806141808361159c565b9050806001600160a01b0316846001600160a01b031614806141c757506001600160a01b0380821660009081526019602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084611eae565b6001600160a01b031614949350505050565b6000838152601660205260409020548210614239576040517f0757d52100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902080548390811061425957614259615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610ece576040517f78eeeecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526016602052604090206142e490836149e4565b60009283526014602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b6000806143268361159c565b9050806001600160a01b0316846001600160a01b0316148061436d57506001600160a01b038082166000908152600e602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084610dcf565b6001600160401b0382166000908152601d6020908152604080832080548251818502810185019093528083526144059386939291908301828280156133c457600091825260209182902080546001600160401b03168452908202830192909160089101808411613381579050505050505061231a90919063ffffffff16565b91505080610ece576040517f4ef44ed500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310614488577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106144b4576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144d257662386f26fc10000830492506010015b6305f5e10083106144ea576305f5e100830492506008015b61271083106144fe57612710830492506004015b60648310614510576064830492506002015b600a8310610c5c5760010192915050565b61452e8383600084614aca565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46040805160008082526020820181905284926001600160a01b038716927f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a4505050565b60006001600160a01b0384163b156146a757604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906145f7903390899088908890600401615d87565b6020604051808303816000875af1925050508015614632575060408051601f3d908101601f1916820190925261462f91810190615dc3565b60015b61468d573d808015614660576040519150601f19603f3d011682016040523d82523d6000602084013e614665565b606091505b5080516146855760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610dc7565b506001610dc7565b610ef9600082612f87565b604080518082019091526000808252602082015282156146e5576146de8987611b32565b90506146f2565b6146ef8987610fb3565b90505b6146fd8186866148cd565b821561472057600089815260116020526040902061471b908761492a565b61475a565b6001600160a01b038516600090815260126020908152604080832087845282528083208390558b83526010909152902061475a908761492a565b6001600160a01b0388161561487657866147f0576040517fb88d4fde0000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063b88d4fde906147b99030908c9089908890600401615d87565b600060405180830381600087803b1580156147d357600080fd5b505af11580156147e7573d6000803e3d6000fd5b50505050614876565b602081015181516040517f064c0a3a0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163064c0a3a916148439130918d918d908990600401615de0565b600060405180830381600087803b15801561485d57600080fd5b505af1158015614871573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a4611ea3565b82602001516001600160a01b0316826001600160a01b03161415806148f3575082518114155b15610ece576040517fe707e58200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8154829061493a90600190615bb0565b8154811061494a5761494a615a6d565b906000526020600020906002020182828154811061496a5761496a615a6d565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b0390921691909117905581548290806149b5576149b5615e18565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b815481106149f157600080fd5b81548290614a0190600190615bb0565b81548110614a1157614a11615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110614a4c57614a4c615a6d565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480614a9557614a95615e18565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b6001600160a01b038416614b0a576040517f25bd6bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600f60205260409020600101546001600160a01b031615614b5c576040517fc5a8d37100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82614b7a576040516312c33ce360e01b815260040160405180910390fd5b614b8660008585613fde565b6001600160a01b0384166000908152600c60205260408120805460019290614baf908490615b29565b90915550506040805180820182529283526001600160a01b0394851660208085019182526000958652600f9052932091518255509051600190910180546001600160a01b03191691909216179055565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c7257835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614c28565b8015614ca55782816101000a8154906001600160401b030219169055600801602081600701049283019260010302614c72565b505b50614cb3929150614d75565b5090565b5080546000825560020290600052602060002090810190610ef99190614d8a565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c725783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614d01565b508054600082556003016004900490600052602060002090810190610ef991905b5b80821115614cb35760008155600101614d76565b5b80821115614cb357600081556001810180546001600160a01b0319169055600201614d8b565b6001600160e01b031981168114610ef957600080fd5b600060208284031215614dd957600080fd5b8135614de481614db1565b9392505050565b80356001600160401b038116811461108957600080fd5b6001600160a01b0381168114610ef957600080fd5b803561108981614e02565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e6057614e60614e22565b604052919050565b60006001600160401b03821115614e8157614e81614e22565b5060051b60200190565b600082601f830112614e9c57600080fd5b81356001600160401b03811115614eb557614eb5614e22565b614ec8601f8201601f1916602001614e38565b818152846020838601011115614edd57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614f0b57600080fd5b81356020614f20614f1b83614e68565b614e38565b82815260059290921b84018101918181019086841115614f3f57600080fd5b8286015b84811015614f6157614f5481614deb565b8352918301918301614f43565b509695505050505050565b60008060008060808587031215614f8257600080fd5b614f8b85614deb565b9350602080860135614f9c81614e02565b935060408601356001600160401b0380821115614fb857600080fd5b818801915088601f830112614fcc57600080fd5b8135614fda614f1b82614e68565b81815260059190911b8301840190848101908b831115614ff957600080fd5b8585015b83811015615031578035858111156150155760008081fd5b6150238e89838a0101614e8b565b845250918601918601614ffd565b5096505050606088013592508083111561504a57600080fd5b505061505887828801614efa565b91505092959194509250565b600080600080600060a0868803121561507c57600080fd5b853561508781614e02565b9450602086013561509781614e02565b9350604086013592506060860135915060808601356001600160401b038111156150c057600080fd5b6150cc88828901614e8b565b9150509295509295909350565b60005b838110156150f45781810151838201526020016150dc565b50506000910152565b600081518084526151158160208601602086016150d9565b601f01601f19169290920160200192915050565b602081526000614de460208301846150fd565b6000806000806080858703121561515257600080fd5b843561515d81614e02565b93506020850135925061517260408601614deb565b915061518060608601614deb565b905092959194509250565b60006020828403121561519d57600080fd5b5035919050565b600080604083850312156151b757600080fd5b82356151c281614e02565b946020939093013593505050565b6000602082840312156151e257600080fd5b8135614de481614e02565b60008060006060848603121561520257600080fd5b8335925061521260208501614deb565b915061522060408501614deb565b90509250925092565b6000806040838503121561523c57600080fd5b823561524781614e02565b9150602083013561525781614e02565b809150509250929050565b60008060006060848603121561527757600080fd5b833592506020840135915061522060408501614deb565b6000806000606084860312156152a357600080fd5b83356152ae81614e02565b925060208401356152be81614e02565b929592945050506040919091013590565b600080600080608085870312156152e557600080fd5b6152ee85614deb565b935060208501356152fe81614e02565b925060408501356001600160401b038082111561531a57600080fd5b61532688838901614e8b565b9350606087013591508082111561533c57600080fd5b5061505887828801614efa565b6000806040838503121561535c57600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610c5c565b60006020828403121561539d57600080fd5b81356001600160401b038111156153b357600080fd5b610dc784828501614e8b565b600082601f8301126153d057600080fd5b813560206153e0614f1b83614e68565b82815260059290921b840181019181810190868411156153ff57600080fd5b8286015b84811015614f615780358352918301918301615403565b60008060006060848603121561542f57600080fd5b83356001600160401b038082111561544657600080fd5b615452878388016153bf565b9450602086013591508082111561546857600080fd5b5061547586828701614efa565b92505061522060408501614deb565b600060a0828403121561549657600080fd5b60405160a081018181106001600160401b03821117156154b8576154b8614e22565b806040525082358152602083013560208201526154d760408401614deb565b60408201526154e860608401614deb565b60608201526154f960808401614deb565b60808201529392505050565b602080825282518282018190526000919060409081850190868401855b8281101561555857615548848351805182526020908101516001600160a01b0316910152565b9284019290850190600101615522565b5091979650505050505050565b6000806040838503121561557857600080fd5b82359150610faa60208401614deb565b600081518084526020808501945080840160005b838110156155c15781516001600160401b03168752958201959082019060010161559c565b509495945050505050565b6080815260006155df60808301876150fd565b6001600160401b03861660208401526001600160a01b03851660408401528281036060840152611e698185615588565b602081526000614de46020830184615588565b60008060006060848603121561563757600080fd5b833592506020840135915060408401356001600160401b0381111561565b57600080fd5b61566786828701614e8b565b9150509250925092565b60008060006060848603121561568657600080fd5b61568f84614deb565b9250602084013561521281614e02565b6000806000606084860312156156b457600080fd5b83359250602084013561521281614e02565b8015158114610ef957600080fd5b600080604083850312156156e757600080fd5b82356156f281614e02565b91506020830135615257816156c6565b600082601f83011261571357600080fd5b81356020615723614f1b83614e68565b82815260059290921b8401810191818101908684111561574257600080fd5b8286015b84811015614f615780356001600160401b038111156157655760008081fd5b6157738986838b0101614efa565b845250918301918301615746565b600080600080600060a0868803121561579957600080fd5b85356001600160401b03808211156157b057600080fd5b818801915088601f8301126157c457600080fd5b813560206157d4614f1b83614e68565b82815260059290921b8401810191818101908c8411156157f357600080fd5b948201945b8386101561581a57853561580b81614e02565b825294820194908201906157f8565b9950508901359250508082111561583057600080fd5b61583c89838a016153bf565b955061584a60408901614e17565b9450606088013591508082111561586057600080fd5b61586c89838a01614e8b565b9350608088013591508082111561588257600080fd5b506150cc88828901615702565b600080600080608085870312156158a557600080fd5b84356158b081614e02565b935060208501356158c081614e02565b92506040850135915060608501356001600160401b038111156158e257600080fd5b61505887828801614e8b565b60008060006040848603121561590357600080fd5b8335925060208401356001600160401b038082111561592157600080fd5b818601915086601f83011261593557600080fd5b81358181111561594457600080fd5b8760208260051b850101111561595957600080fd5b6020830194508093505050509250925092565b600080600080600080600080610100898b03121561598957600080fd5b88359750602089013561599b81614e02565b9650604089013595506060890135945060808901356159b981614e02565b935060a0890135925060c08901356159d0816156c6565b915060e08901356001600160401b038111156159eb57600080fd5b6159f78b828c01614e8b565b9150509295985092959890939650565b60008060008060808587031215615a1d57600080fd5b84359350602085013592506040850135615a3681614e02565b9396929550929360600135925050565b600080600060608486031215615a5b57600080fd5b8335925060208401356152be81614e02565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680615a9757607f821691505b602082108103615ab757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610c5c57610c5c615abd565b600082615b0757634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615b1e57600080fd5b8151614de481614e02565b80820180821115610c5c57610c5c615abd565b60008351615b4e8184602088016150d9565b835190830190615b628183602088016150d9565b7f2e6a736f6e0000000000000000000000000000000000000000000000000000009101908152600501949350505050565b600060208284031215615ba557600080fd5b8151614de4816156c6565b81810381811115610c5c57610c5c615abd565b6020808252825182820181905260009190848201906040850190845b81811015615bfb57835183529284019291840191600101615bdf565b50909695505050505050565b601f821115610ece57600081815260208120601f850160051c81016020861015615c2e5750805b601f850160051c820191505b81811015610ca557828155600101615c3a565b81516001600160401b03811115615c6657615c66614e22565b615c7a81615c748454615a83565b84615c07565b602080601f831160018114615caf5760008415615c975750858301515b600019600386901b1c1916600185901b178555610ca5565b600085815260208120601f198616915b82811015615cde57888601518255948401946001909101908401615cbf565b5085821015615cfc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615d1e57600080fd5b5051919050565b600080600060608486031215615d3a57600080fd5b8351615d4581614e02565b602085015160408601519194509250615d5d816156c6565b809150509250925092565b838152826020820152606060408201526000613eb260608301846150fd565b60006001600160a01b03808716835280861660208401525083604083015260806060830152615db960808301846150fd565b9695505050505050565b600060208284031215615dd557600080fd5b8151614de481614db1565b60006001600160a01b03808816835280871660208401525084604083015283606083015260a06080830152611e6960a08301846150fd565b634e487b7160e01b600052603160045260246000fdfea264697066735822122047b1c52c6a661e31a9451ea2f083f2a14788bea6dac65656fb85b2f841f3641f64736f6c634300081200330000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b32153aa5b3dd930d265bc800d2f7d657c994828000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000075370616365727300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000753504143455253000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f516d506f7268456a4479337348753548564d465735426277743835335378384132436667614845737a556d5643342f537061636572732e6a736f6e000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104725760003560e01c80636f19951c11610250578063b390c0ab11610150578063e467a48f116100c8578063ee1dffcf11610097578063fb25fb7a1161007c578063fb25fb7a14610b60578063fc3517c814610b9a578063ffa1ad7414610bad57600080fd5b8063ee1dffcf14610b0e578063f2fde38b14610b4d57600080fd5b8063e467a48f14610a99578063e7de4de414610aac578063e97ceaa814610abf578063e985e9c514610ad257600080fd5b8063c87b56dd1161011f578063de8e602c11610104578063de8e602c14610a3c578063defa80c314610a4f578063df6f556b14610a6257600080fd5b8063c87b56dd14610a21578063d5abeb0114610a3457600080fd5b8063b390c0ab146109c2578063b88d4fde146109d5578063c259a988146109e8578063c5610a29146109fb57600080fd5b80638507dc28116101e357806395edc18c116101b2578063a22cb46511610197578063a22cb46514610989578063a898e3641461099c578063ace61e00146109af57600080fd5b806395edc18c14610965578063979613361461097657600080fd5b80638507dc281461093157806389ed2edf146109445780638da5cb5b1461094c57806395d89b411461095d57600080fd5b80637507e2ae1161021f5780637507e2ae1461081b57806379e8ca9e1461090b5780637aba6f371461091e5780637e5852d91461092657600080fd5b80636f19951c146107da57806370a08231146107ed578063715018a6146108005780637280281e1461080857600080fd5b80632f32f937116103765780634e60edba116102ee5780635e94354a116102bd5780636352211e116102a25780636352211e146107a1578063635490cc146107b457806365b67eb9146107c757600080fd5b80635e94354a1461077b5780635ea72f361461078e57600080fd5b80634e60edba1461071d57806351532e5a1461074057806359c8b7dd146107605780635e2e32921461076857600080fd5b8063362b67f41161034557806342842e0e1161032a57806342842e0e146106d757806342966c68146106ea57806344ec9344146106fd57600080fd5b8063362b67f4146106b157806338dcf74c146106c457600080fd5b80632f32f9371461065857806330ffb1d614610678578063326d526d1461068b57806333ed28451461069e57600080fd5b80630fc499f51161040957806322e6d160116103d857806323b872dd116103bd57806323b872dd146105e85780632452cd91146105fb5780632a55205a1461062657600080fd5b806322e6d1601461059957806322f6da9c146105d557600080fd5b80630fc499f51461053f57806318160ddd146105525780631c7bb4611461055a5780631d0d35f51461056d57600080fd5b806306fdde031161044557806306fdde03146104d9578063074334fb146104ee578063081812fc14610501578063095ea7b31461052c57600080fd5b806301e1d1141461047757806301ffc9a71461048e57806302549760146104b1578063064c0a3a146104c6575b600080fd5b6021545b6040519081526020015b60405180910390f35b6104a161049c366004614dc7565b610be9565b6040519015158152602001610485565b6104c46104bf366004614f6c565b610c62565b005b6104c46104d4366004615064565b610cad565b6104e1610cc4565b6040516104859190615129565b6104a16104fc36600461513c565b610d56565b61051461050f36600461518b565b610dcf565b6040516001600160a01b039091168152602001610485565b6104c461053a3660046151a4565b610e15565b6104c461054d3660046151d0565b610ed3565b60055461047b565b6104c46105683660046151ed565b610efc565b6104a161057b3660046151d0565b6001600160a01b031660009081526002602052604090205460011490565b6104a16105a7366004615229565b6001600160a01b03918216600090815260196020908152604080832093909416825291909152205460ff1690565b6104c46105e3366004615262565b610f0f565b6104c46105f636600461528e565b610f2a565b61060e6106093660046152cf565b610f4f565b6040516001600160401b039091168152602001610485565b610639610634366004615349565b610f7d565b604080516001600160a01b039093168352602083019190915201610485565b61066b610666366004615349565b610fb3565b604051610485919061536b565b61047b61068636600461538b565b611065565b6104c46106993660046151d0565b61108e565b6104c46106ac3660046151a4565b6110ba565b6104c46106bf36600461541a565b6110d0565b6104c46106d2366004615484565b611133565b6104c46106e536600461528e565b611158565b6104c46106f836600461518b565b611173565b61071061070b36600461518b565b61117e565b6040516104859190615505565b61073061072b366004615565565b6111fd565b60405161048594939291906155cc565b61075361074e36600461518b565b6112da565b604051610485919061560f565b60095461047b565b6104e1610776366004615565565b61136e565b61075361078936600461518b565b611476565b6104c461079c3660046151a4565b6114df565b6105146107af36600461518b565b61159c565b6104c46107c2366004615622565b611645565b6104c46107d5366004615671565b611777565b6107106107e836600461518b565b61178a565b61047b6107fb3660046151d0565b6117fa565b6104c4611858565b61075361081636600461518b565b61186c565b6108bd61082936600461569f565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601e87528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352680100000000000000009004909416958101959095526001830154918501919091526002909101549091169082015290565b604051610485919060006080820190506001600160401b0380845116835280602085015116602084015250604083015160408301526001600160a01b03606084015116606083015292915050565b6104c46109193660046156d4565b6118d5565b6104c46119ab565b6003546001146104a1565b6104c461093f3660046156d4565b6119c3565b6104e1611a59565b6001546001600160a01b0316610514565b6104e1611a68565b6008546001600160a01b0316610514565b6104c4610984366004615349565b611a77565b6104c46109973660046156d4565b611a8b565b61066b6109aa366004615349565b611b32565b6104c46109bd366004615781565b611ba8565b61047b6109d0366004615349565b611d6b565b6104c46109e336600461588f565b611d81565b6104c46109f63660046151ed565b611d97565b7f0000000000000000000000000000000000000000000000000de0b6b3a764000061047b565b6104e1610a2f36600461518b565b611dac565b60065461047b565b6104c4610a4a3660046158ee565b611e74565b6104c4610a5d36600461596c565b611e89565b61060e610a70366004615565565b60009182526014602090815260408084206001600160401b039384168552909152909120541690565b610514610aa736600461518b565b611eae565b6104c4610aba366004615349565b611ed1565b6104c4610acd366004615a07565b611ee5565b6104a1610ae0366004615229565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205460ff1690565b6104a1610b1c366004615a46565b6000928352601f602090815260408085206001600160a01b0394909416855292815282842091845252902054151590565b6104c4610b5b3660046151d0565b611efb565b610b73610b6e36600461518b565b611f4c565b604080516001600160a01b0390941684526020840192909252151590820152606001610485565b6104c4610ba8366004615262565b611fb4565b6104e16040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b6000610bf482611fc9565b80610c2857506001600160e01b031982167f06b4329a00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982167f28bc9ae400000000000000000000000000000000000000000000000000000000145b92915050565b610c6a612067565b815160005b81811015610ca557610c9c8686868481518110610c8e57610c8e615a6d565b602002602001015186610f4f565b50600101610c6f565b505050505050565b82610cb7816120bf565b610ca586868686866120ff565b6060600a8054610cd390615a83565b80601f0160208091040260200160405190810160405280929190818152602001828054610cff90615a83565b8015610d4c5780601f10610d2157610100808354040283529160200191610d4c565b820191906000526020600020905b815481529060010190602001808311610d2f57829003601f168201915b5050505050905090565b6001600160401b038083166000908152601c602090815260408083205484168084528280528184206001600160a01b038a1685529092528220549192909181169084168103610dc0576000610db486610dae8961186c565b9061231a565b9450610dc79350505050565b6000925050505b949350505050565b6000610dda82612383565b6000828152600d6020526040812090610df28461159c565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6000610e208261159c565b9050806001600160a01b0316836001600160a01b031603610e6d576040517f0591db6d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03821614801590610e8d5750610e8b8133610ae0565b155b15610ec4576040517f982462b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece83836123bb565b505050565b610edb612434565b600880546001600160a01b0319166001600160a01b03831617905550565b50565b610f04612067565b610ece838383612478565b82610f19816126a0565b610f248484846126e0565b50505050565b80610f34816120bf565b610f248484846040518060200160405280600081525061274f565b6000610f59612067565b6021805460010190819055610f7190868686866128c5565b50602154949350505050565b6008546009546001600160a01b039091169060009061271090610fa09085615ad3565b610faa9190615aea565b90509250929050565b604080518082019091526000808252602082015281610fd18461178a565b5111611009576040517fca7cc84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040812080548490811061102957611029615a6d565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b600061106f612067565b60218054600101908190556110849083612985565b506021545b919050565b611096612434565b6001600160a01b03166000908152602260205260409020805460ff19166001179055565b6110c2612434565b6110cc8282612a4b565b5050565b6110d8612067565b825160005b8181101561112c57611124856000815181106110fb576110fb615a6d565b60200260200101518560008151811061111657611116615a6d565b602002602001015185612478565b6001016110dd565b5050505050565b805161113e81612b0d565b611146612b4d565b61114f82612b90565b50506001600055565b610ece83838360405180602001604052806000815250611d81565b6110cc816000611d6b565b6000818152601160209081526040808320805482518185028101850190935280835260609493849084015b828210156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b50929695505050505050565b6060600080606061120e868661136e565b6001600160401b038087166000908152601c6020908152604080832054601b835281842054601d845293829020805483518186028101860190945280845291909516946001600160a01b0390941693928391908301828280156112c257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161127f5790505b50505050509050935093509350935092959194509250565b60008181526016602090815260409182902080548351818402810184019094528084526060939283018282801561136257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161131f5790505b50505050509050919050565b60008281526018602090815260408083206001600160401b038516845290915290205460609060ff166113cd576040517fdcc947e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038216600090815260136020526040902080546113f090615a83565b80601f016020809104026020016040519081016040528092919081815260200182805461141c90615a83565b80156114695780601f1061143e57610100808354040283529160200191611469565b820191906000526020600020905b81548152906001019060200180831161144c57829003601f168201915b5050505050905092915050565b60008181526017602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b60006114ea8261159c565b9050806001600160a01b0316836001600160a01b03160361151e576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b0382161480159061155b57506001600160a01b038116600090815260196020908152604080832033845290915290205460ff16155b15611592576040517faf45d29200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece8383612f87565b6000806000806115ab85611f4c565b925092509250801561163c576040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015611615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116399190615b0c565b92505b50909392505050565b61164e83612383565b33803b61166e5760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b038216602082015260006116958661117e565b51905060808110156116f4576000868152601160209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055611726565b6040517fa53c8c0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd8460405161176391815260200190565b60405180910390a4610ca586848787613000565b61177f612067565b610ece838383613049565b60008181526010602090815260408083208054825181850281018501909352808352606094938490840182156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b60006001600160a01b03821661183c576040517f7772286600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b611860612434565b61186a60006130fb565b565b60008181526015602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b6118dd612434565b6001600160a01b03821661191d576040517fb5c0938000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611942576001600160a01b0382166000908152600260205260408120819055611961565b6001600160a01b03821660009081526002602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac28260405161199f911515815260200190565b60405180910390a25050565b6119b3612434565b6119bb61314d565b600554600655565b6001600160a01b03821633036119ec576040516375f45abd60e01b815260040160405180910390fd5b3360008181526019602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060078054610cd390615a83565b6060600b8054610cd390615a83565b81611a8181612b0d565b610ece838361315c565b6001600160a01b0382163303611acd576040517f2dee66e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611a4d565b604080518082019091526000808252602082015281611b508461117e565b5111611b88576040517fda22687f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260116020526040812080548490811061102957611029615a6d565b611bb06131ea565b611bb8612067565b83516000819003611bf5576040517f376bec4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600654600454611c059083615b29565b1115611c3d576040517fbd239bf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480548201905560058054820190556000805b82811015611d6157611cc160008787611c828b8681518110611c7557611c75615a6d565b6020026020010151613226565b604051602001611c93929190615b3c565b604051602081830303815290604052878581518110611cb457611cb4615a6d565b6020026020010151610f4f565b9150611d0f888281518110611cd857611cd8615a6d565b6020026020010151888381518110611cf257611cf2615a6d565b6020026020010151604051806020016040528060008152506132c5565b611d34878281518110611d2457611d24615a6d565b6020026020010151836000612478565b611d59878281518110611d4957611d49615a6d565b60200260200101516000846132fa565b600101611c51565b5050505050505050565b600082611d77816120bf565b610dc7848461353a565b81611d8b816120bf565b61112c858585856138d0565b82611da181612b0d565b610f24848484613905565b6060611db782612383565b6000611dc283611476565b8051909150603d6000805b83816001600160401b03161015611e2f57600085826001600160401b031681518110611dfb57611dfb615a6d565b60200260200101519050836001600160401b0316816001600160401b03161015611e26578093508192505b50600101611dcd565b506000611e3b8761186c565b826001600160401b031681518110611e5557611e55615a6d565b60200260200101519050611e69878261136e565b979650505050505050565b82611e7e816126a0565b610f24848484613ade565b87611e9381612b0d565b611ea38989898989898989613b71565b505050505050505050565b6000611eb982612383565b6000828152601a6020526040812090610df28461159c565b81611edb816126a0565b610ece8383613be8565b83611eef81612b0d565b61112c85858585613cf9565b611f03612434565b6001600160a01b038116611f43576040517f4ece6ecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ef9816130fb565b6000818152600f60209081526040808320815180830190925280548252600101546001600160a01b03169181018290528291829190611f9e5760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b82611fbe816126a0565b610f248484846132fa565b60006001600160e01b031982166301ffc9a760e01b148061201357506001600160e01b031982167f80ac58cd00000000000000000000000000000000000000000000000000000000145b8061204757506001600160e01b031982167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982166342b0e56f60e01b1492915050565b6001546001600160a01b0316331480159061208857506120863361057b565b155b1561186a576040517f1eca167000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120c93382613e18565b610ef9576040517f8be7930600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061210b85611f4c565b5091509150866001600160a01b0316826001600160a01b0316146121425760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616612169576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561218057508385145b156121b7576040517f3d76b10700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0386163b6121df5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa15801561222a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224e9190615b93565b612284576040517f5e13b1cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61228f858786613ebb565b61229a878787613fde565b6001600160a01b0387166000908152600c602052604081208054600192906122c3908490615bb0565b909155506122d49050858588613ffb565b6001600160a01b0386166000908152600c602052604081208054600192906122fd908490615b29565b90915550612311905082878387898861405a565b50505050505050565b81516000908190815b8181101561237257846001600160401b031686828151811061234757612347615a6d565b60200260200101516001600160401b03160361236a5792506001915061237c9050565b600101612323565b5060008092509250505b9250929050565b6000818152600f60205260409020600101546001600160a01b0316610ef95760405163089ba7e160e41b815260040160405180910390fd5b60006123c68261159c565b6000838152600d602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6001546001600160a01b0316331461186a576040517f38c5ab1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038616845290915290205460ff16156124bc576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546124df90615a83565b905003612518576040517f554bdff200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902054608011612560576040517fbade3a7b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260168552928520805493840181558552929093206004820401805460039092166008026101000a80840219909216919093021790915581161561260e5760008381526014602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061264457612644615a6d565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516126939190615bc3565b60405180910390a3610f24565b6126aa3382614174565b610ef9576040517f5d64832900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126eb8383836141f2565b6126f68383836142cc565b60008381526018602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b60008061275b84611f4c565b5091509150856001600160a01b0316826001600160a01b0316146127925760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166127b9576040516338f646ff60e21b815260040160405180910390fd5b6127c4868686613fde565b6001600160a01b0386166000908152600c602052604081208054600192906127ed908490615bb0565b909155506127ff905084600087613ffb565b6001600160a01b0385166000908152600c60205260408120805460019290612828908490615b29565b909155505060405184906001600160a01b0380881691908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a483856001600160a01b0316836001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8460006040516128b8929190918252602082015260400190565b60405180910390a4610ca5565b6128cf8583612985565b6001600160a01b0383161580156128e65750805115155b1561291d576040517f206b5a0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038581166000908152601b6020908152604080832080546001600160a01b0319166001600160a01b038916179055601c8252808320805467ffffffffffffffff191694891694909417909355601d81529190208251610ca592840190614bff565b6001600160401b0382166129ac576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546129cf90615a83565b905011156129f0576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152601360205260409020612a128282615c4d565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612a98576040519150601f19603f3d011682016040523d82523d6000602084013e612a9d565b606091505b5050905080610ece576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5472616e73666572206661696c65642e0000000000000000000000000000000060448201526064015b60405180910390fd5b612b17338261431a565b610ef9576040517f2728a9d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600260005403612b89576040517fc57fd5d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600055565b6040808201516001600160401b039081166000908152601b602090815283822054606086015186518452601e83528584206001600160a01b039283168086529084528685209582168552949092529390912060020154919290911615612c22576040517fbd0650ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c30836040015182614386565b6000612c4484600001518560200151610fb3565b6020810151815160808701516040517f074334fb00000000000000000000000000000000000000000000000000000000815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015612cce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf29190615b93565b612d28576040517f2c36cd3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516040517fdcb79d560000000000000000000000000000000000000000000000000000000081526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa158015612d9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc09190615b93565b612df6576040517fe451dfea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808082018352868301516001600160401b0390811683529087015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528c516000908152601e86528981208d8416825286528981208c8916825286528981208951815498518a1668010000000000000000026fffffffffffffffffffffffffffffffff1990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558a518452601f835286842090519094168352928152848220865183529052928320805492939192909190612ef4908490615b29565b92505081905550826001600160401b031685604001516001600160401b031686600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518a60800151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a461112c565b6000612f928261159c565b6000838152601a602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160a01b03831660009081526022602052604090205460ff1615610f2457600084815260116020526040902054610f2490859061304290600190615bb0565b8585613cf9565b6001600160401b038316158061306657506001600160401b038116155b15613084576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152602080805260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b613155612434565b6001600355565b6000828152601160205260409020548110156131a4576040517f3ce62f1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526011602052604081206131bb91614cb7565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b60035460010361186a576040517fed1fa96f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606060006132338361443f565b60010190506000816001600160401b0381111561325257613252614e22565b6040519080825280601f01601f19166020018201604052801561327c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461328657509392505050565b6132d0838383614521565b6132dd60008484846145b3565b610ece5760405163bcb5663760e01b815260040160405180910390fd5b6133058383836141f2565b60008381526014602090815260408083206001600160401b038086168552925282205416908082156133dd576133d783601560008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156133c457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116133815790505b505050505061231a90919063ffffffff16565b90925090505b801561345e57600086815260156020526040902080548591908490811061340657613406615a6d565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526018835260408083209187168352925220805460ff191690556134e9565b6000868152601760209081526040808320601583529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6134f48686866142cc565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4610ca5565b600080600061354885611f4c565b509150915060006135588661159c565b6001600160a01b0384166000908152600c602052604081208054929350600192909190613586908490615bb0565b90915550613598905081600088613fde565b6040805160208101909152600090526135b26000876123bb565b6135bb866146af565b60006135c68761178a565b60008881526010602052604081209192506135e19190614cb7565b60008781526011602052604081206135f891614cb7565b6000878152600d602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015613812578983106136bc5784818151811061364f5761364f615a6d565b60200260200101516020015185828151811061366d5761366d615a6d565b6020908102919091010151516040517f6177b2500000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526024820152604401612b04565b601260008683815181106136d2576136d2615a6d565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061371257613712615a6d565b602002602001015160000151815260200190815260200160002060009055828a03935084818151811061374757613747615a6d565b6020026020010151602001516001600160a01b031663b390c0ab86838151811061377357613773615a6d565b60200260200101516000015160018761378c9190615bb0565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156137cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f39190615d0c565b6137fe906001615b29565b6138089084615b29565b925060010161362e565b5060008a8152600f602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b038816907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a4604080518781526000602082018190528c9290916001600160a01b038b16917f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a46040805160208101909152600090525098975050505050505050565b6138dc8484848461274f565b6138e8848484846145b3565b610f245760405163bcb5663760e01b815260040160405180910390fd5b6001600160401b038083166000908152601b6020908152604080832054878452601e83528184206001600160a01b0391821680865290845282852087871686528452938290208251608081018452815480881682526801000000000000000090049096169386019390935260018301549185019190915260029091015416606083018190529091906139c3576040517f5f79f75c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601e602090815260408083206001600160a01b0380871685529083528184206001600160401b0388168552835281842080546fffffffffffffffffffffffffffffffff191681556001808201869055600290910180546001600160a01b0319169055898552601f8452828520606087015190921685529083528184208583015185529092528220805491929091613a60908490615bb0565b92505081905550826001600160401b0316846001600160401b0316867f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da13846040015185606001518660200151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60008381526015602052604090205481908114613b27576040517f6b037c3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152601760205260409020613b40908484614cd8565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610f24565b81613bd8576000888152601f602090815260408083206001600160a01b0388168452825280832086845290915290205415613bd8576040517f1867727400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d6188888888888888886146ba565b60008281526016602052604090205481811115613c31576040517f5134ce8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015613caf576000848152601660205260408120805483908110613c5c57613c5c615a6d565b6000918252602080832060048304015488845260148252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101613c34565b506000838152601660205260408120613cc791614d54565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6000613d058585611b32565b9050613d128184846148cd565b6001600160a01b038316600090815260126020908152604080832085845290915290205415613d6d576040517f188a497300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601160205260409020613d85908561492a565b600085815260106020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260128452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a99101612f7a565b6000806000613e2684611f4c565b509150915080600014613e4957506001600160a01b038481169116149050610c5c565b816001600160a01b0316856001600160a01b03161480613e8e57506001600160a01b038083166000908152600e602090815260408083209389168352929052205460ff165b80613eb25750846001600160a01b0316613ea785610dcf565b6001600160a01b0316145b95945050505050565b60005b6064811015613fab576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b8152600401613efa91815260200190565b606060405180830381865afa158015613f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f3b9190615d25565b92509250925080613f4f5750505050505050565b6001600160a01b03831630148015613f6657508682145b15613f9d576040517f48a87cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509093509150600101613ebe565b506040517f6934d7a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216610ece5760058054600019019055505050565b6040805180820182528381526001600160a01b0383811660208084019182526000888152600f9091529384209251835551600190920180546001600160a01b0319169290911691909117905561405190846123bb565b610ece836146af565b6040517f635490cc00000000000000000000000000000000000000000000000000000000815285906001600160a01b0382169063635490cc906140a590879087908790600401615d68565b600060405180830381600087803b1580156140bf57600080fd5b505af11580156140d3573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a482866001600160a01b0316886001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8888604051614167929190918252602082015260400190565b60405180910390a4612311565b6000806141808361159c565b9050806001600160a01b0316846001600160a01b031614806141c757506001600160a01b0380821660009081526019602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084611eae565b6001600160a01b031614949350505050565b6000838152601660205260409020548210614239576040517f0757d52100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902080548390811061425957614259615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610ece576040517f78eeeecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526016602052604090206142e490836149e4565b60009283526014602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b6000806143268361159c565b9050806001600160a01b0316846001600160a01b0316148061436d57506001600160a01b038082166000908152600e602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084610dcf565b6001600160401b0382166000908152601d6020908152604080832080548251818502810185019093528083526144059386939291908301828280156133c457600091825260209182902080546001600160401b03168452908202830192909160089101808411613381579050505050505061231a90919063ffffffff16565b91505080610ece576040517f4ef44ed500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310614488577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106144b4576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144d257662386f26fc10000830492506010015b6305f5e10083106144ea576305f5e100830492506008015b61271083106144fe57612710830492506004015b60648310614510576064830492506002015b600a8310610c5c5760010192915050565b61452e8383600084614aca565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46040805160008082526020820181905284926001600160a01b038716927f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a4505050565b60006001600160a01b0384163b156146a757604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906145f7903390899088908890600401615d87565b6020604051808303816000875af1925050508015614632575060408051601f3d908101601f1916820190925261462f91810190615dc3565b60015b61468d573d808015614660576040519150601f19603f3d011682016040523d82523d6000602084013e614665565b606091505b5080516146855760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610dc7565b506001610dc7565b610ef9600082612f87565b604080518082019091526000808252602082015282156146e5576146de8987611b32565b90506146f2565b6146ef8987610fb3565b90505b6146fd8186866148cd565b821561472057600089815260116020526040902061471b908761492a565b61475a565b6001600160a01b038516600090815260126020908152604080832087845282528083208390558b83526010909152902061475a908761492a565b6001600160a01b0388161561487657866147f0576040517fb88d4fde0000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063b88d4fde906147b99030908c9089908890600401615d87565b600060405180830381600087803b1580156147d357600080fd5b505af11580156147e7573d6000803e3d6000fd5b50505050614876565b602081015181516040517f064c0a3a0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163064c0a3a916148439130918d918d908990600401615de0565b600060405180830381600087803b15801561485d57600080fd5b505af1158015614871573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a4611ea3565b82602001516001600160a01b0316826001600160a01b03161415806148f3575082518114155b15610ece576040517fe707e58200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8154829061493a90600190615bb0565b8154811061494a5761494a615a6d565b906000526020600020906002020182828154811061496a5761496a615a6d565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b0390921691909117905581548290806149b5576149b5615e18565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b815481106149f157600080fd5b81548290614a0190600190615bb0565b81548110614a1157614a11615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110614a4c57614a4c615a6d565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480614a9557614a95615e18565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b6001600160a01b038416614b0a576040517f25bd6bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600f60205260409020600101546001600160a01b031615614b5c576040517fc5a8d37100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82614b7a576040516312c33ce360e01b815260040160405180910390fd5b614b8660008585613fde565b6001600160a01b0384166000908152600c60205260408120805460019290614baf908490615b29565b90915550506040805180820182529283526001600160a01b0394851660208085019182526000958652600f9052932091518255509051600190910180546001600160a01b03191691909216179055565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c7257835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614c28565b8015614ca55782816101000a8154906001600160401b030219169055600801602081600701049283019260010302614c72565b505b50614cb3929150614d75565b5090565b5080546000825560020290600052602060002090810190610ef99190614d8a565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c725783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614d01565b508054600082556003016004900490600052602060002090810190610ef991905b5b80821115614cb35760008155600101614d76565b5b80821115614cb357600081556001810180546001600160a01b0319169055600201614d8b565b6001600160e01b031981168114610ef957600080fd5b600060208284031215614dd957600080fd5b8135614de481614db1565b9392505050565b80356001600160401b038116811461108957600080fd5b6001600160a01b0381168114610ef957600080fd5b803561108981614e02565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e6057614e60614e22565b604052919050565b60006001600160401b03821115614e8157614e81614e22565b5060051b60200190565b600082601f830112614e9c57600080fd5b81356001600160401b03811115614eb557614eb5614e22565b614ec8601f8201601f1916602001614e38565b818152846020838601011115614edd57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614f0b57600080fd5b81356020614f20614f1b83614e68565b614e38565b82815260059290921b84018101918181019086841115614f3f57600080fd5b8286015b84811015614f6157614f5481614deb565b8352918301918301614f43565b509695505050505050565b60008060008060808587031215614f8257600080fd5b614f8b85614deb565b9350602080860135614f9c81614e02565b935060408601356001600160401b0380821115614fb857600080fd5b818801915088601f830112614fcc57600080fd5b8135614fda614f1b82614e68565b81815260059190911b8301840190848101908b831115614ff957600080fd5b8585015b83811015615031578035858111156150155760008081fd5b6150238e89838a0101614e8b565b845250918601918601614ffd565b5096505050606088013592508083111561504a57600080fd5b505061505887828801614efa565b91505092959194509250565b600080600080600060a0868803121561507c57600080fd5b853561508781614e02565b9450602086013561509781614e02565b9350604086013592506060860135915060808601356001600160401b038111156150c057600080fd5b6150cc88828901614e8b565b9150509295509295909350565b60005b838110156150f45781810151838201526020016150dc565b50506000910152565b600081518084526151158160208601602086016150d9565b601f01601f19169290920160200192915050565b602081526000614de460208301846150fd565b6000806000806080858703121561515257600080fd5b843561515d81614e02565b93506020850135925061517260408601614deb565b915061518060608601614deb565b905092959194509250565b60006020828403121561519d57600080fd5b5035919050565b600080604083850312156151b757600080fd5b82356151c281614e02565b946020939093013593505050565b6000602082840312156151e257600080fd5b8135614de481614e02565b60008060006060848603121561520257600080fd5b8335925061521260208501614deb565b915061522060408501614deb565b90509250925092565b6000806040838503121561523c57600080fd5b823561524781614e02565b9150602083013561525781614e02565b809150509250929050565b60008060006060848603121561527757600080fd5b833592506020840135915061522060408501614deb565b6000806000606084860312156152a357600080fd5b83356152ae81614e02565b925060208401356152be81614e02565b929592945050506040919091013590565b600080600080608085870312156152e557600080fd5b6152ee85614deb565b935060208501356152fe81614e02565b925060408501356001600160401b038082111561531a57600080fd5b61532688838901614e8b565b9350606087013591508082111561533c57600080fd5b5061505887828801614efa565b6000806040838503121561535c57600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610c5c565b60006020828403121561539d57600080fd5b81356001600160401b038111156153b357600080fd5b610dc784828501614e8b565b600082601f8301126153d057600080fd5b813560206153e0614f1b83614e68565b82815260059290921b840181019181810190868411156153ff57600080fd5b8286015b84811015614f615780358352918301918301615403565b60008060006060848603121561542f57600080fd5b83356001600160401b038082111561544657600080fd5b615452878388016153bf565b9450602086013591508082111561546857600080fd5b5061547586828701614efa565b92505061522060408501614deb565b600060a0828403121561549657600080fd5b60405160a081018181106001600160401b03821117156154b8576154b8614e22565b806040525082358152602083013560208201526154d760408401614deb565b60408201526154e860608401614deb565b60608201526154f960808401614deb565b60808201529392505050565b602080825282518282018190526000919060409081850190868401855b8281101561555857615548848351805182526020908101516001600160a01b0316910152565b9284019290850190600101615522565b5091979650505050505050565b6000806040838503121561557857600080fd5b82359150610faa60208401614deb565b600081518084526020808501945080840160005b838110156155c15781516001600160401b03168752958201959082019060010161559c565b509495945050505050565b6080815260006155df60808301876150fd565b6001600160401b03861660208401526001600160a01b03851660408401528281036060840152611e698185615588565b602081526000614de46020830184615588565b60008060006060848603121561563757600080fd5b833592506020840135915060408401356001600160401b0381111561565b57600080fd5b61566786828701614e8b565b9150509250925092565b60008060006060848603121561568657600080fd5b61568f84614deb565b9250602084013561521281614e02565b6000806000606084860312156156b457600080fd5b83359250602084013561521281614e02565b8015158114610ef957600080fd5b600080604083850312156156e757600080fd5b82356156f281614e02565b91506020830135615257816156c6565b600082601f83011261571357600080fd5b81356020615723614f1b83614e68565b82815260059290921b8401810191818101908684111561574257600080fd5b8286015b84811015614f615780356001600160401b038111156157655760008081fd5b6157738986838b0101614efa565b845250918301918301615746565b600080600080600060a0868803121561579957600080fd5b85356001600160401b03808211156157b057600080fd5b818801915088601f8301126157c457600080fd5b813560206157d4614f1b83614e68565b82815260059290921b8401810191818101908c8411156157f357600080fd5b948201945b8386101561581a57853561580b81614e02565b825294820194908201906157f8565b9950508901359250508082111561583057600080fd5b61583c89838a016153bf565b955061584a60408901614e17565b9450606088013591508082111561586057600080fd5b61586c89838a01614e8b565b9350608088013591508082111561588257600080fd5b506150cc88828901615702565b600080600080608085870312156158a557600080fd5b84356158b081614e02565b935060208501356158c081614e02565b92506040850135915060608501356001600160401b038111156158e257600080fd5b61505887828801614e8b565b60008060006040848603121561590357600080fd5b8335925060208401356001600160401b038082111561592157600080fd5b818601915086601f83011261593557600080fd5b81358181111561594457600080fd5b8760208260051b850101111561595957600080fd5b6020830194508093505050509250925092565b600080600080600080600080610100898b03121561598957600080fd5b88359750602089013561599b81614e02565b9650604089013595506060890135945060808901356159b981614e02565b935060a0890135925060c08901356159d0816156c6565b915060e08901356001600160401b038111156159eb57600080fd5b6159f78b828c01614e8b565b9150509295985092959890939650565b60008060008060808587031215615a1d57600080fd5b84359350602085013592506040850135615a3681614e02565b9396929550929360600135925050565b600080600060608486031215615a5b57600080fd5b8335925060208401356152be81614e02565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680615a9757607f821691505b602082108103615ab757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610c5c57610c5c615abd565b600082615b0757634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615b1e57600080fd5b8151614de481614e02565b80820180821115610c5c57610c5c615abd565b60008351615b4e8184602088016150d9565b835190830190615b628183602088016150d9565b7f2e6a736f6e0000000000000000000000000000000000000000000000000000009101908152600501949350505050565b600060208284031215615ba557600080fd5b8151614de4816156c6565b81810381811115610c5c57610c5c615abd565b6020808252825182820181905260009190848201906040850190845b81811015615bfb57835183529284019291840191600101615bdf565b50909695505050505050565b601f821115610ece57600081815260208120601f850160051c81016020861015615c2e5750805b601f850160051c820191505b81811015610ca557828155600101615c3a565b81516001600160401b03811115615c6657615c66614e22565b615c7a81615c748454615a83565b84615c07565b602080601f831160018114615caf5760008415615c975750858301515b600019600386901b1c1916600185901b178555610ca5565b600085815260208120601f198616915b82811015615cde57888601518255948401946001909101908401615cbf565b5085821015615cfc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615d1e57600080fd5b5051919050565b600080600060608486031215615d3a57600080fd5b8351615d4581614e02565b602085015160408601519194509250615d5d816156c6565b809150509250925092565b838152826020820152606060408201526000613eb260608301846150fd565b60006001600160a01b03808716835280861660208401525083604083015260806060830152615db960808301846150fd565b9695505050505050565b600060208284031215615dd557600080fd5b8151614de481614db1565b60006001600160a01b03808816835280871660208401525084604083015283606083015260a06080830152611e6960a08301846150fd565b634e487b7160e01b600052603160045260246000fdfea264697066735822122047b1c52c6a661e31a9451ea2f083f2a14788bea6dac65656fb85b2f841f3641f64736f6c63430008120033

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

0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b32153aa5b3dd930d265bc800d2f7d657c994828000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000075370616365727300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000753504143455253000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f516d506f7268456a4479337348753548564d465735426277743835335378384132436667614845737a556d5643342f537061636572732e6a736f6e000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : name_ (string): Spacers
Arg [1] : symbol_ (string): SPACERS
Arg [2] : collectionMetadata_ (string): ipfs://QmPorhEjDy3sHu5HVMFW5Bbwt853Sx8A2CfgaHEszUmVC4/Spacers.json
Arg [3] : data (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [2] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 000000000000000000000000b32153aa5b3dd930d265bc800d2f7d657c994828
Arg [6] : 000000000000000000000000000000000000000000000000000000000000012c
Arg [7] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [8] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [10] : 5370616365727300000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [12] : 5350414345525300000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000042
Arg [14] : 697066733a2f2f516d506f7268456a4479337348753548564d46573542627774
Arg [15] : 3835335378384132436667614845737a556d5643342f537061636572732e6a73
Arg [16] : 6f6e000000000000000000000000000000000000000000000000000000000000


[ Download: CSV Export  ]
[ Download: CSV Export  ]

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