GLMR Price: $0.020791 (-1.39%)

Contract

0x686fFBAD7dB758c1D302524e87CD336d9148A3E6

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Set Approval For...136833422025-12-13 1:57:2444 days ago1765591044IN
0x686fFBAD...d9148A3E6
0 GLMR0.0036651432.25
Set Approval For...132045792025-11-03 10:27:2483 days ago1762165644IN
0x686fFBAD...d9148A3E6
0 GLMR0.0043531138.39
Set Approval For...128535972025-10-05 13:32:54112 days ago1759671174IN
0x686fFBAD...d9148A3E6
0 GLMR0.0037416733
Set Approval For...122245862025-08-18 22:26:42160 days ago1755556002IN
0x686fFBAD...d9148A3E6
0 GLMR0.0037416733
Set Approval For...111959232025-06-07 3:09:06233 days ago1749265746IN
0x686fFBAD...d9148A3E6
0 GLMR0.007444833
Set Approval For...99749912025-03-11 18:21:00320 days ago1741717260IN
0x686fFBAD...d9148A3E6
0 GLMR0.0070528431.25
Set Approval For...94936292025-02-05 20:15:18354 days ago1738786518IN
0x686fFBAD...d9148A3E6
0 GLMR0.0074321233
Set Approval For...85908362024-12-03 9:51:30418 days ago1733219490IN
0x686fFBAD...d9148A3E6
0 GLMR0.02821137125
Set Approval For...84554142024-11-23 20:57:18428 days ago1732395438IN
0x686fFBAD...d9148A3E6
0 GLMR0.02821137125
Set Approval For...84518352024-11-23 14:51:36428 days ago1732373496IN
0x686fFBAD...d9148A3E6
0 GLMR0.02821137125
Set Approval For...80800562024-10-28 7:25:18455 days ago1730100318IN
0x686fFBAD...d9148A3E6
0 GLMR0.028216125
Set Approval For...79321452024-10-17 19:33:00465 days ago1729193580IN
0x686fFBAD...d9148A3E6
0 GLMR0.02837721126
Set Approval For...74876282024-09-16 13:04:06496 days ago1726491846IN
0x686fFBAD...d9148A3E6
0 GLMR0.014076125
Set Approval For...74728212024-09-15 12:07:48497 days ago1726402068IN
0x686fFBAD...d9148A3E6
0 GLMR0.014108125
Set Approval For...74728212024-09-15 12:07:48497 days ago1726402068IN
0x686fFBAD...d9148A3E6
0 GLMR0.0155188137.5
Set Approval For...66082352024-07-16 14:56:30558 days ago1721141790IN
0x686fFBAD...d9148A3E6
0 GLMR0.0070943126
Set Approval For...64739902024-06-28 17:35:36576 days ago1719596136IN
0x686fFBAD...d9148A3E6
0 GLMR0.007054125
Set Approval For...63704912024-06-14 1:08:30591 days ago1718327310IN
0x686fFBAD...d9148A3E6
0 GLMR0.007054125
Set Approval For...63367252024-06-09 7:17:18596 days ago1717917438IN
0x686fFBAD...d9148A3E6
0 GLMR0.007054125
Set Approval For...61499522024-05-13 22:35:12622 days ago1715639712IN
0x686fFBAD...d9148A3E6
0 GLMR0.00720435128
Set Approval For...61499342024-05-13 22:31:24622 days ago1715639484IN
0x686fFBAD...d9148A3E6
0 GLMR0.00731692130
Set Approval For...61498782024-05-13 22:20:12622 days ago1715638812IN
0x686fFBAD...d9148A3E6
0 GLMR0.00731692130
Set Approval For...61498472024-05-13 22:14:00622 days ago1715638440IN
0x686fFBAD...d9148A3E6
0 GLMR0.00709178126
Withdraw Raised61303002024-05-11 4:21:24625 days ago1715401284IN
0x686fFBAD...d9148A3E6
0 GLMR0.0115432200
Set Approval For...60843402024-05-04 16:40:48631 days ago1714840848IN
0x686fFBAD...d9148A3E6
0 GLMR0.00718521127.35225
View all transactions

Latest 7 internal transactions

Parent Transaction Hash Block From To
61303002024-05-11 4:21:24625 days ago1715401284
0x686fFBAD...d9148A3E6
2 GLMR
55818702024-02-23 14:24:48702 days ago1708698288
0x686fFBAD...d9148A3E6
325 GLMR
55672812024-02-21 12:51:24704 days ago1708519884
0x686fFBAD...d9148A3E6
400 GLMR
55569902024-02-20 1:36:06706 days ago1708392966
0x686fFBAD...d9148A3E6
300 GLMR
55497892024-02-19 0:54:48707 days ago1708304088
0x686fFBAD...d9148A3E6
150 GLMR
55470092024-02-18 15:19:00707 days ago1708269540
0x686fFBAD...d9148A3E6
100 GLMR
55469972024-02-18 15:16:30707 days ago1708269390
0x686fFBAD...d9148A3E6
10 GLMR
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RMRKEvrlootPinkPromo

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 175 runs

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

pragma solidity ^0.8.18;

import "@rmrk-team/evm-contracts/contracts/RMRK/extension/RMRKRoyalties.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/multiasset/RMRKMultiAsset.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/utils/RMRKCollectionMetadata.sol";
import {IAttributes} from "./IAttributes.sol";
import "./MintingUtils.sol";
import "./InitData.sol";

error RMRKMintZero();
error RMRKWrongValueSent();
error MintOverMaxVIP();
error MintOverMaxPer();

/**
 * @title RMRKAbstractMultiAssetImpl
 * @author RMRK team
 * @notice Abstract implementation of RMRK multi asset module.
 */
contract RMRKEvrlootPinkPromo is
    IRMRKInitData,
    RMRKMintingUtils,
    RMRKCollectionMetadata,
    RMRKRoyalties,
    RMRKMultiAsset,
    IAttributes
{
    uint256 private _totalAssets;
    string private _tokenUri;
    string private _tokenUriVIP;
    mapping(uint256 => uint8) internal _vipStatus;

    /**
     * @notice Used to initialize the smart contract.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     * @param collectionMetadata_ The collection metadata URI
     * @param tokenURI_ The base URI of the token metadata
     * @param data The `InitData` struct containing additional initialization data
     */
    constructor(
        string memory name_,
        string memory symbol_,
        string memory collectionMetadata_,
        string memory tokenURI_,
        InitData memory data
    )
        RMRKMintingUtils(
            data.maxSupply,
            data.maxVIPSupply,
            data.pricePerMintVIP,
            data.pricePerMintNonVIP
        )
        RMRKCollectionMetadata(collectionMetadata_)
        RMRKRoyalties(data.royaltyRecipient, data.royaltyPercentageBps)
        RMRKMultiAsset(name_, symbol_)
    {
        _setTokenURIs(tokenURI_, data.tokenURIVIP);
    }

    /**
     * @notice Used to mint the desired number of tokens to the specified address.
     * @dev The `data` value of the `_safeMint` method is set to an empty value.
     * @dev Can only be called while the open sale is open.
     * @param to Address to which to mint the token
     * @param numToMint Number of tokens to mint
     * @return The ID of the first token to be minted in the current minting cycle
     */
    function mintVIP(
        address to,
        uint256 numToMint
    ) public payable virtual notLocked saleIsOpen returns (uint256) {
        if (numToMint == uint256(0)) revert RMRKMintZero();
        if (numToMint + _nextId > _maxVIPSupply) revert RMRKMintOverMax();

        uint256 mintPriceRequired = numToMint * _pricePerMintVIP;
        if (mintPriceRequired != msg.value) revert RMRKWrongValueSent();
        if (_vipMinted[to] + numToMint > MAX_VIP_PER_ACCOUNT)
            revert MintOverMaxVIP();
        uint256 nextToken = _nextId + 1;
        unchecked {
            _nextId += numToMint;
            _totalSupply += numToMint;
            _totalVIPSupply += numToMint;
            _vipMinted[to] += numToMint;
        }
        uint256 totalSupplyOffset = _nextId + 1;

        for (uint256 i = nextToken; i < totalSupplyOffset; ) {
            _safeMint(to, i, "");

            // Set VIP Status
            _vipStatus[i] = 1;
            unchecked {
                ++i;
            }
        }

        return nextToken;
    }

    /**
     * @notice Used to mint the desired number of tokens to the specified address.
     * @dev The `data` value of the `_safeMint` method is set to an empty value.
     * @dev Can only be called while the open sale is open.
     * @param to Address to which to mint the token
     * @param numToMint Number of tokens to mint
     * @return The ID of the first token to be minted in the current minting cycle
     */
    function mint(
        address to,
        uint256 numToMint
    ) public payable virtual notLocked saleIsOpen returns (uint256) {
        if (numToMint == uint256(0)) revert RMRKMintZero();
        if (numToMint + _nextId > _maxSupply) revert RMRKMintOverMax();

        uint256 mintPriceRequired = numToMint * _pricePerMintNonVIP;
        if (mintPriceRequired != msg.value) revert RMRKWrongValueSent();
        if (_nonVipMinted[to] + numToMint > MAX_NON_VIP_PER_ACCOUNT)
            revert MintOverMaxPer();
        uint256 nextToken = _nextId + 1;
        unchecked {
            _nextId += numToMint;
            _totalSupply += numToMint;
            _nonVipMinted[to] += numToMint;
        }
        uint256 totalSupplyOffset = _nextId + 1;

        for (uint256 i = nextToken; i < totalSupplyOffset; ) {
            _safeMint(to, i, "");

            unchecked {
                ++i;
            }
        }

        return nextToken;
    }

    /**
     * @notice Used to retrieve token VIP status.
     * @param tokenId ID of the token to check
     */
    function isVIP(uint256 tokenId) public view returns (bool) {
        return _vipStatus[tokenId] == 1;
    }

    /**
     * @notice Retrieves attributes for use in missions -- for pink promo we will use luck attributes (position 0) and fishing (position 10)
     * @dev If VIP status is 1, the luck/fishing attributes are 7/13, otherwise 3/9
     * @param tokenId ID of the token to check
     */
    function getAttributes(
        uint256 tokenId,
        bool
    ) public view returns (uint16[] memory attributes) {
        uint8 vipStatus = _vipStatus[tokenId];
        attributes = new uint16[](25);
        if (vipStatus != 0) {
            attributes[0] = 7; // Luck
            attributes[10] = 13; // Fishing skill
        } else {
            attributes[0] = 3; // Luck
            attributes[10] = 9; // Fishing skill
        }
        return attributes;
    }

    /**
     * @notice Used to destroy the specified token.
     * @dev The approval is cleared when the token is burned.
     * @dev Requirements:
     *  - `tokenId` must exist.
     * @param tokenId ID of the token to burn
     */
    function burn(uint256 tokenId) public virtual onlyApprovedOrOwner(tokenId) {
        _burn(tokenId);
    }

    /**
     * @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.
     * @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
    ) public virtual onlyOwnerOrContributor {
        _addAssetToToken(tokenId, assetId, replacesAssetWithId);
    }

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

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

    /**
     * @inheritdoc RMRKRoyalties
     */
    function updateRoyaltyRecipient(
        address newRoyaltyRecipient
    ) public virtual override onlyOwner {
        _setRoyaltyRecipient(newRoyaltyRecipient);
    }

    /**
     * @inheritdoc IERC165
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(IERC165, RMRKMultiAsset) returns (bool) {
        return
            super.supportsInterface(interfaceId) ||
            interfaceId == type(IERC2981).interfaceId ||
            interfaceId == RMRK_INTERFACE ||
            interfaceId == type(IAttributes).interfaceId;
    }

    /**
     * @notice Used to retrieve the metadata URI of a token.
     * @param tokenId ID of the token to retrieve the metadata URI for
     * @return Metadata URI of the specified token
     */
    function tokenURI(
        uint256 tokenId
    ) public view virtual returns (string memory) {
        if (_vipStatus[tokenId] == 1) {
            return _tokenUriVIP;
        } else {
            return _tokenUri;
        }
    }

    /**
     * @notice Used to set the token URI configuration.
     * @param tokenURI_ Metadata URI to apply to all tokens, either as base or as full URI for every token
     * @param tokenURIVIP_ Metadata URI for VIP tokens
     */
    function _setTokenURIs(
        string memory tokenURI_,
        string memory tokenURIVIP_
    ) internal virtual {
        _tokenUri = tokenURI_;
        _tokenUriVIP = tokenURIVIP_;
    }

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

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

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 22 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

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

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "./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 Emitted when the smart contract is locked.
     */
    event LockSet();

    /**
     * @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.
     * @dev Emits ***LockSet*** event.
     */
    function setLock() public virtual onlyOwner {
        _lock = 1;
        emit LockSet();
    }

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "./IRMRKCore.sol";

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

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

    /// Token name
    string private _name;

    /// Token symbol
    string private _symbol;

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

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

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

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@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 14 of 22 : RMRKErrors.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        _beforeRejectAllAssets(tokenId);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // Approvals

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

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

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

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

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

pragma solidity ^0.8.18;

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

/**
 * @title RMRKMultiAsset
 * @author RMRK team
 * @notice Smart contract of the RMRK Multi asset module.
 */
contract RMRKMultiAsset is IERC165, IERC721, AbstractMultiAsset, RMRKCore {
    using Address for address;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

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

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

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

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

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

    // -------------------------- ERC721 MODIFIERS ----------------------------

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

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

    // ----------------------- MODIFIERS FOR ASSETS ------------------------

    /**
     * @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 Used to verify that the caller is either the owner of the given token or approved by its owner to manage
     *  the assets on the given token.
     * @dev If the caller is not the owner of the given token or approved by its owner to manage the assets on the
     *  given token, the execution will be reverted.
     * @param tokenId ID of the token being checked
     */
    function _onlyApprovedForAssetsOrOwner(uint256 tokenId) private view {
        if (!_isApprovedForAssetsOrOwner(_msgSender(), tokenId))
            revert RMRKNotApprovedForAssetsOrOwner();
    }

    /**
     * @notice Used to verify that the caller is either the owner of the given token or approved by its owner to manage
     *  the assets on the given token.
     * @param tokenId ID of the token being checked
     */
    modifier onlyApprovedForAssetsOrOwner(uint256 tokenId) {
        _onlyApprovedForAssetsOrOwner(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(IERC5773).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];
    }

    /**
     * @notice Used to retrieve the owner of the given token.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @param tokenId ID of the token for which to retrieve the token for
     * @return Address of the account owning the token
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        address owner = _owners[tokenId];
        if (owner == address(0)) revert ERC721InvalidTokenId();
        return owner;
    }

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

    /**
     * @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 {
        _setApprovalForAll(_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 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 onlyApprovedOrOwner(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 onlyApprovedOrOwner(tokenId) {
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @notice 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.
     * @dev `data` is additional data, it has no specified format and it is sent in call to `to`.
     * @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 from which to send the token from
     * @param to Address to which to send the token to
     * @param tokenId ID of the token to be sent
     * @param data Additional data to send with the tokens
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        if (!_checkOnERC721Received(from, to, tokenId, data))
            revert ERC721TransferToNonReceiverImplementer();
    }

    /**
     * @notice Used to check whether the given token exists.
     * @dev Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     * @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 _owners[tokenId] != address(0);
    }

    /**
     * @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 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);
        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.
     * @param to Address to mint the token to
     * @param tokenId ID of the token to mint
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        if (to == address(0)) revert ERC721MintToTheZeroAddress();
        if (_exists(tokenId)) revert ERC721TokenAlreadyMinted();
        if (tokenId == uint256(0)) revert RMRKIdZeroForbidden();

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

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

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

    /**
     * @notice Used to destroy the specified token.
     * @dev The approval is cleared when the token is burned.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits a {Transfer} event.
     * @param tokenId ID of the token to burn
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);
        _approveForAssets(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @notice Used to transfer the specified token from one user to another.
     * @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 from which to transfer the token
     * @param to Address to which to transfer the token
     * @param tokenId ID of the token to transfer
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        if (ownerOf(tokenId) != from) revert ERC721TransferFromIncorrectOwner();
        if (to == address(0)) revert ERC721TransferToTheZeroAddress();

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];
        delete _tokenApprovalsForAssets[tokenId];

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

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

    /**
     * @notice Used to manage an approval to an address to manage all of the tokens of the user.
     * @dev If the user attempts to grant the approval to themselves, the execution is reverted.
     * @dev Emits an {ApprovalForAll} event.
     * @param owner Address of the account for which the approval is being granted
     * @param operator Address receiving approval to manage all of the tokens of the `owner`
     * @param approved Boolean value signifying whether
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        if (owner == operator) revert ERC721ApproveToCaller();
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @notice Used to verify thet the token has been minted.
     * @dev The token is considered minted if its owner is not the `0x0` address.
     * @dev This function doesn't output any feedback about the token existing, but it reverts if the token doesn't
     *  exist.
     * @param tokenId ID of the token being checked
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        if (!_exists(tokenId)) revert ERC721InvalidTokenId();
    }

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

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

    /**
     * @inheritdoc IERC5773
     */
    function acceptAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _acceptAsset(tokenId, index, assetId);
    }

    /**
     * @inheritdoc IERC5773
     */
    function rejectAsset(
        uint256 tokenId,
        uint256 index,
        uint64 assetId
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _rejectAsset(tokenId, index, assetId);
    }

    /**
     * @inheritdoc IERC5773
     */
    function rejectAllAssets(
        uint256 tokenId,
        uint256 maxRejections
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _rejectAllAssets(tokenId, maxRejections);
    }

    /**
     * @inheritdoc IERC5773
     */
    function setPriority(
        uint256 tokenId,
        uint64[] calldata priorities
    ) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
        _setPriority(tokenId, priorities);
    }

    // ----------------------- APPROVALS FOR ASSETS ------------------------

    /**
     * @inheritdoc IERC5773
     */
    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 grant an approval to an address to manage assets of a given token.
     * @dev Emits ***ApprovalForAssets*** event.
     * @param to Address of the account to grant the approval to
     * @param tokenId ID of the token for which the approval is being given
     */
    function _approveForAssets(address to, uint256 tokenId) internal virtual {
        _tokenApprovalsForAssets[tokenId] = to;
        emit ApprovalForAssets(ownerOf(tokenId), to, tokenId);
    }

    /**
     * @inheritdoc IERC5773
     */
    function getApprovedForAssets(
        uint256 tokenId
    ) public view virtual returns (address) {
        _requireMinted(tokenId);
        return _tokenApprovalsForAssets[tokenId];
    }
}

// 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 20 of 22 : IAttributes.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.16;

/**
 * @title IAttributes
 * @author Evrloot team
 * @notice Interface for Evrloot attributes calls
 * @dev getTotal switch is used to get the total attributes for the asset considering equipped children

 */
interface IAttributes {
    function getAttributes(
        uint256 assetId,
        bool getTotal
    ) external view returns (uint16[] memory);
}

File 21 of 22 : InitData.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;
        uint256 maxVIPSupply;
        // --- new slot ---
        uint256 pricePerMintVIP;
        uint256 pricePerMintNonVIP;
        string tokenURIVIP;
        // another 32 bytes
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;

import "@rmrk-team/evm-contracts/contracts/RMRK/access/OwnableLock.sol";
import "@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol";

error RMRKMintSaleClosed();

/**
 * @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 _totalVIPSupply;
    uint256 internal _maxSupply;
    uint256 internal _maxVIPSupply;
    uint256 internal immutable _pricePerMintVIP;
    uint256 internal immutable _pricePerMintNonVIP;
    uint256 constant MAX_VIP_PER_ACCOUNT = 5;
    uint256 constant MAX_NON_VIP_PER_ACCOUNT = 1;
    mapping(address => uint256) internal _vipMinted;
    mapping(address => uint256) internal _nonVipMinted;
    bool internal _saleEnabled = false;

    /**
     * @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 pricePerMintVIP_ The minting price to initialize the smart contract with, expressed in the smallest
     * @param pricePerMintNonVIP_ Minting price for non-vip
     *  denomination of the native currency of the chain to which the smart contract is deployed to
     */
    constructor(
        uint256 maxSupply_,
        uint256 maxVIPSupply_,
        uint256 pricePerMintVIP_,
        uint256 pricePerMintNonVIP_
    ) {
        _maxSupply = maxSupply_;
        _maxVIPSupply = maxVIPSupply_;
        _pricePerMintVIP = pricePerMintVIP_;
        _pricePerMintNonVIP = pricePerMintNonVIP_;
    }

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

    function enableSale(bool enabled) public onlyOwner {
        _saleEnabled = enabled;
    }

    // function saleOpen() public view returns (bool) {
    //     return _saleEnabled;
    // }

    /**
     * @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 total supply of the tokens in a collection.
     * @return The number of tokens in a collection
     */
    function totalVIPSupply() public view returns (uint256) {
        return _totalVIPSupply;
    }

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

    /**
     * @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 pricePerMintVIP() public view returns (uint256) {
        return _pricePerMintVIP;
    }

    /**
     * @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 pricePerMintNonVIP() public view returns (uint256) {
        return _pricePerMintNonVIP;
    }

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

    function balanceOfVIP(address account) public view returns (uint256) {
        return _vipMinted[account];
    }

    /**
     * @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 (!_saleEnabled) revert RMRKMintSaleClosed();
        if (_nextId >= _maxSupply) revert RMRKMintOverMax();
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"collectionMetadata_","type":"string"},{"internalType":"string","name":"tokenURI_","type":"string"},{"components":[{"internalType":"address","name":"royaltyRecipient","type":"address"},{"internalType":"uint16","name":"royaltyPercentageBps","type":"uint16"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"maxVIPSupply","type":"uint256"},{"internalType":"uint256","name":"pricePerMintVIP","type":"uint256"},{"internalType":"uint256","name":"pricePerMintNonVIP","type":"uint256"},{"internalType":"string","name":"tokenURIVIP","type":"string"}],"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":"MintOverMaxPer","type":"error"},{"inputs":[],"name":"MintOverMaxVIP","type":"error"},{"inputs":[],"name":"RMRKApprovalForAssetsToCurrentOwner","type":"error"},{"inputs":[],"name":"RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"RMRKAssetAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKBadPriorityListLength","type":"error"},{"inputs":[],"name":"RMRKIdZeroForbidden","type":"error"},{"inputs":[],"name":"RMRKIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKLocked","type":"error"},{"inputs":[],"name":"RMRKMaxPendingAssetsReached","type":"error"},{"inputs":[],"name":"RMRKMintOverMax","type":"error"},{"inputs":[],"name":"RMRKMintSaleClosed","type":"error"},{"inputs":[],"name":"RMRKMintZero","type":"error"},{"inputs":[],"name":"RMRKNewContributorIsZeroAddress","type":"error"},{"inputs":[],"name":"RMRKNewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"RMRKNoAssetMatchingId","type":"error"},{"inputs":[],"name":"RMRKNotApprovedForAssetsOrOwner","type":"error"},{"inputs":[],"name":"RMRKNotOwner","type":"error"},{"inputs":[],"name":"RMRKNotOwnerOrContributor","type":"error"},{"inputs":[],"name":"RMRKRoyaltiesTooHigh","type":"error"},{"inputs":[],"name":"RMRKTokenDoesNotHaveAsset","type":"error"},{"inputs":[],"name":"RMRKUnexpectedAssetId","type":"error"},{"inputs":[],"name":"RMRKUnexpectedNumberOfAssets","type":"error"},{"inputs":[],"name":"RMRKWrongValueSent","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"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":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"bool","name":"isContributor","type":"bool"}],"name":"ContributorUpdate","type":"event"},{"anonymous":false,"inputs":[],"name":"LockSet","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"},{"inputs":[],"name":"RMRK_INTERFACE","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"acceptAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"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":"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":"address","name":"account","type":"address"}],"name":"balanceOfVIP","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":[],"name":"collectionMetadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableSale","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":"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":"bool","name":"","type":"bool"}],"name":"getAttributes","outputs":[{"internalType":"uint16[]","name":"attributes","type":"uint16[]"}],"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":"address","name":"contributor","type":"address"}],"name":"isContributor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isVIP","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":[],"name":"maxVIPSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numToMint","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"numToMint","type":"uint256"}],"name":"mintVIP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerMintNonVIP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerMintVIP","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":"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":[],"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":"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":[],"name":"totalVIPSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"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"}]

60c0604052600a805460ff191690553480156200001b57600080fd5b50604051620039d8380380620039d88339810160408190526200003e9162000375565b848481818460000151856020015161ffff16888760400151886060015189608001518a60a001516200007f620000796200012260201b60201c565b62000126565b60069390935560079190915560805260a0526200009c8162000176565b50600c80546001600160a01b0319166001600160a01b0384161790556127108110620000db57604051634006185d60e11b815260040160405180910390fd5b600d55506015620000ed8382620004e4565b506016620000fc8282620004e4565b505050505062000117828260c001516200018860201b60201c565b5050505050620005b0565b3390565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600b620001848282620004e4565b5050565b601d620001968382620004e4565b50601e620001a58282620004e4565b505050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715620001e557620001e5620001aa565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620002165762000216620001aa565b604052919050565b600082601f8301126200023057600080fd5b81516001600160401b038111156200024c576200024c620001aa565b602062000262601f8301601f19168201620001eb565b82815285828487010111156200027757600080fd5b60005b83811015620002975785810183015182820184015282016200027a565b506000928101909101919091529392505050565b80516001600160a01b0381168114620002c357600080fd5b919050565b805161ffff81168114620002c357600080fd5b600060e08284031215620002ee57600080fd5b620002f8620001c0565b90506200030582620002ab565b81526200031560208301620002c8565b602082015260408201516040820152606082015160608201526080820151608082015260a082015160a082015260c082015160018060401b038111156200035b57600080fd5b62000369848285016200021e565b60c08301525092915050565b600080600080600060a086880312156200038e57600080fd5b85516001600160401b0380821115620003a657600080fd5b620003b489838a016200021e565b96506020880151915080821115620003cb57600080fd5b620003d989838a016200021e565b95506040880151915080821115620003f057600080fd5b620003fe89838a016200021e565b945060608801519150808211156200041557600080fd5b6200042389838a016200021e565b935060808801519150808211156200043a57600080fd5b506200044988828901620002db565b9150509295509295909350565b600181811c908216806200046b57607f821691505b6020821081036200048c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620001a557600081815260208120601f850160051c81016020861015620004bb5750805b601f850160051c820191505b81811015620004dc57828155600101620004c7565b505050505050565b81516001600160401b03811115620005005762000500620001aa565b620005188162000511845462000456565b8462000492565b602080601f831160018114620005505760008415620005375750858301515b600019600386901b1c1916600185901b178555620004dc565b600085815260208120601f198616915b82811015620005815788860151825594840194600190910190840162000560565b5085821015620005a05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a0516133f4620005e46000396000818161036e0152610f1701526000818161087401526115f701526133f46000f3fe6080604052600436106103065760003560e01c8063715018a6116101a1578063b88d4fde116100f2578063df6f556b116100a0578063e985e9c51161006f578063e985e9c514610a44578063f2fde38b14610a8d578063fc3517c814610aad578063ffa1ad7414610acd57600080fd5b8063df6f556b14610975578063e467a48f146109d1578063e4dc951f146109f1578063e7de4de414610a2457600080fd5b8063b88d4fde146108b8578063bb0f8b6d146108d8578063c70b5ae5146108f8578063c87b56dd1461090b578063d49304be1461092b578063d5abeb0114610940578063de8e602c1461095557600080fd5b806389ed2edf1161014f57806389ed2edf146107cb5780638d4f3bf5146107e05780638da5cb5b1461081457806395d89b411461083257806395edc18c146108475780639e74673f14610865578063a22cb4651461089857600080fd5b8063715018a6146106f35780637280281e1461070857806379e8ca9e146107285780637a573275146107485780637aba6f371461077e5780637e5852d9146107935780638507dc28146107ab57600080fd5b80632a55205a1161025b57806351532e5a1161020957806351532e5a1461061157806359c8b7dd1461063e5780635e2e3292146106535780635e94354a146106735780635ea72f36146106935780636352211e146106b357806370a08231146106d357600080fd5b80632a55205a1461051257806330ffb1d61461055157806333ed2845146105715780633600261d1461059157806340c10f19146105be57806342842e0e146105d157806342966c68146105f157600080fd5b8063149a4f01116102b8578063149a4f011461042e57806318160ddd146104435780631c7bb461146104585780631d0d35f51461047857806322e6d160146104b257806322f6da9c146104d257806323b872dd146104f257600080fd5b806301e1d1141461030b57806301ffc9a71461032f578063042242f81461035f57806306fdde0314610392578063081812fc146103b4578063095ea7b3146103ec5780630fc499f51461040e575b600080fd5b34801561031757600080fd5b50601c545b6040519081526020015b60405180910390f35b34801561033b57600080fd5b5061034f61034a366004612c63565b610afe565b6040519015158152602001610326565b34801561036b57600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061031c565b34801561039e57600080fd5b506103a7610b60565b6040516103269190612ccd565b3480156103c057600080fd5b506103d46103cf366004612ce0565b610bf2565b6040516001600160a01b039091168152602001610326565b3480156103f857600080fd5b5061040c610407366004612d10565b610c19565b005b34801561041a57600080fd5b5061040c610429366004612d3a565b610ca5565b34801561043a57600080fd5b5060075461031c565b34801561044f57600080fd5b5060045461031c565b34801561046457600080fd5b5061040c610473366004612d6c565b610cce565b34801561048457600080fd5b5061034f610493366004612d3a565b6001600160a01b03166000908152600160208190526040909120541490565b3480156104be57600080fd5b5061034f6104cd366004612da8565b610ce1565b3480156104de57600080fd5b5061040c6104ed366004612dd2565b610d0f565b3480156104fe57600080fd5b5061040c61050d366004612dfe565b610d2a565b34801561051e57600080fd5b5061053261052d366004612e3a565b610d3f565b604080516001600160a01b039093168352602083019190915201610326565b34801561055d57600080fd5b5061031c61056c366004612ee7565b610d75565b34801561057d57600080fd5b5061040c61058c366004612d10565b610d9e565b34801561059d57600080fd5b506105b16105ac366004612f3f565b610db4565b6040516103269190612f62565b61031c6105cc366004612d10565b610eb1565b3480156105dd57600080fd5b5061040c6105ec366004612dfe565b61102d565b3480156105fd57600080fd5b5061040c61060c366004612ce0565b611048565b34801561061d57600080fd5b5061063161062c366004612ce0565b61105b565b6040516103269190612faa565b34801561064a57600080fd5b50600d5461031c565b34801561065f57600080fd5b506103a761066e366004612feb565b6110ef565b34801561067f57600080fd5b5061063161068e366004612ce0565b6111de565b34801561069f57600080fd5b5061040c6106ae366004612d10565b611247565b3480156106bf57600080fd5b506103d46106ce366004612ce0565b6112ce565b3480156106df57600080fd5b5061031c6106ee366004612d3a565b611304565b3480156106ff57600080fd5b5061040c611349565b34801561071457600080fd5b50610631610723366004612ce0565b61135d565b34801561073457600080fd5b5061040c61074336600461300e565b6113c6565b34801561075457600080fd5b5061031c610763366004612d3a565b6001600160a01b031660009081526008602052604090205490565b34801561078a57600080fd5b5061040c611483565b34801561079f57600080fd5b5060025460011461034f565b3480156107b757600080fd5b5061040c6107c636600461300e565b61149b565b3480156107d757600080fd5b506103a7611530565b3480156107ec57600080fd5b506107fb63524d524b60e01b81565b6040516001600160e01b03199091168152602001610326565b34801561082057600080fd5b506000546001600160a01b03166103d4565b34801561083e57600080fd5b506103a761153f565b34801561085357600080fd5b50600c546001600160a01b03166103d4565b34801561087157600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061031c565b3480156108a457600080fd5b5061040c6108b336600461300e565b61154e565b3480156108c457600080fd5b5061040c6108d3366004613038565b611559565b3480156108e457600080fd5b5061040c6108f33660046130b3565b611576565b61031c610906366004612d10565b611591565b34801561091757600080fd5b506103a7610926366004612ce0565b611724565b34801561093757600080fd5b5060055461031c565b34801561094c57600080fd5b5060065461031c565b34801561096157600080fd5b5061040c6109703660046130ce565b6117d5565b34801561098157600080fd5b506109b9610990366004612feb565b6000918252600f602090815260408084206001600160401b039384168552909152909120541690565b6040516001600160401b039091168152602001610326565b3480156109dd57600080fd5b506103d46109ec366004612ce0565b6117ea565b3480156109fd57600080fd5b5061034f610a0c366004612ce0565b6000908152601f602052604090205460ff1660011490565b348015610a3057600080fd5b5061040c610a3f366004612e3a565b611811565b348015610a5057600080fd5b5061034f610a5f366004612da8565b6001600160a01b039182166000908152601a6020908152604080832093909416825291909152205460ff1690565b348015610a9957600080fd5b5061040c610aa8366004612d3a565b611825565b348015610ab957600080fd5b5061040c610ac8366004612dd2565b61185d565b348015610ad957600080fd5b506103a760405180604001604052806005815260200164312e322e3160d81b81525081565b6000610b0982611872565b80610b2457506001600160e01b0319821663152a902d60e11b145b80610b3f57506001600160e01b0319821663524d524b60e01b145b80610b5a57506001600160e01b03198216633600261d60e01b145b92915050565b606060158054610b6f9061314c565b80601f0160208091040260200160405190810160405280929190818152602001828054610b9b9061314c565b8015610be85780601f10610bbd57610100808354040283529160200191610be8565b820191906000526020600020905b815481529060010190602001808311610bcb57829003601f168201915b5050505050905090565b6000610bfd826118de565b506000908152601960205260409020546001600160a01b031690565b6000610c24826112ce565b9050806001600160a01b0316836001600160a01b031603610c5857604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b03821614801590610c785750610c768133610a5f565b155b15610c9657604051634c12315960e11b815260040160405180910390fd5b610ca08383611913565b505050565b610cad611981565b600c80546001600160a01b0319166001600160a01b03831617905550565b50565b610cd66119ac565b610ca08383836119eb565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205460ff1690565b82610d1981611be1565b610d24848484611c08565b50505050565b80610d3481611c77565b610d24848484611c9e565b600c54600d546001600160a01b039091169060009061271090610d62908561319c565b610d6c91906131b3565b90509250929050565b6000610d7f6119ac565b601c805460010190819055610d949083611ded565b50601c545b919050565b610da6611981565b610db08282611eb3565b5050565b6000828152601f6020526040908190205481516019808252610340820190935260609260ff9092169181602001602082028036833701905050915060ff811615610e5357600782600081518110610e0d57610e0d6131d5565b602002602001019061ffff16908161ffff1681525050600d82600a81518110610e3857610e386131d5565b602002602001019061ffff16908161ffff1681525050610eaa565b600382600081518110610e6857610e686131d5565b602002602001019061ffff16908161ffff1681525050600982600a81518110610e9357610e936131d5565b602002602001019061ffff16908161ffff16815250505b5092915050565b6000610ebb611f4d565b610ec3611f70565b81610ee15760405163376bec4d60e01b815260040160405180910390fd5b600654600354610ef190846131eb565b1115610f1057604051635e91cdfb60e11b815260040160405180910390fd5b6000610f3c7f00000000000000000000000000000000000000000000000000000000000000008461319c565b9050348114610f5e576040516336ce5e4360e01b815260040160405180910390fd5b6001600160a01b038416600090815260096020526040902054600190610f859085906131eb565b1115610fa457604051633be4b58160e21b815260040160405180910390fd5b60006003546001610fb591906131eb565b600380548601815560048054870190556001600160a01b0387166000908152600960205260408120805488019055905491925090610ff49060016131eb565b9050815b818110156110225761101a878260405180602001604052806000815250611fb7565b600101610ff8565b509095945050505050565b610ca083838360405180602001604052806000815250611559565b8061105281611c77565b610db082611feb565b6000818152601160209081526040918290208054835181840281018401909452808452606093928301828280156110e357602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116110a05790505b50505050509050919050565b60008281526013602090815260408083206001600160401b038516845290915290205460609060ff1661113557604051631b9928fd60e31b815260040160405180910390fd5b6001600160401b0382166000908152600e6020526040902080546111589061314c565b80601f01602080910402602001604051908101604052809291908181526020018280546111849061314c565b80156111d15780601f106111a6576101008083540402835291602001916111d1565b820191906000526020600020905b8154815290600101906020018083116111b457829003601f168201915b5050505050905092915050565b6000818152601260209081526040918290208054835181840281018401909452808452606093928301828280156110e357600091825260209182902080546001600160401b031684529082028301929091600891018084116110a0575094979650505050505050565b6000611252826112ce565b9050806001600160a01b0316836001600160a01b031603611286576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b038216148015906112a657506112a48133610ce1565b155b156112c4576040516357a2e94960e11b815260040160405180910390fd5b610ca0838361209d565b6000818152601760205260408120546001600160a01b031680610b5a5760405163089ba7e160e41b815260040160405180910390fd5b60006001600160a01b03821661132d57604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526018602052604090205490565b611351611981565b61135b600061210b565b565b6000818152601060209081526040918290208054835181840281018401909452808452606093928301828280156110e357600091825260209182902080546001600160401b031684529082028301929091600891018084116110a0575094979650505050505050565b6113ce611981565b6001600160a01b0382166113f55760405163016b812760e71b815260040160405180910390fd5b8061141a576001600160a01b0382166000908152600160205260408120819055611439565b6001600160a01b03821660009081526001602081905260409091208190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac282604051611477911515815260200190565b60405180910390a25050565b61148b611981565b61149361215b565b600454600655565b6001600160a01b03821633036114c4576040516375f45abd60e01b815260040160405180910390fd5b3360008181526014602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a26235910160405180910390a35050565b6060600b8054610b6f9061314c565b606060168054610b6f9061314c565b610db0338383612193565b8161156381611c77565b61156f85858585612232565b5050505050565b61157e611981565b600a805460ff1916911515919091179055565b600061159b611f4d565b6115a3611f70565b816115c15760405163376bec4d60e01b815260040160405180910390fd5b6007546003546115d190846131eb565b11156115f057604051635e91cdfb60e11b815260040160405180910390fd5b600061161c7f00000000000000000000000000000000000000000000000000000000000000008461319c565b905034811461163e576040516336ce5e4360e01b815260040160405180910390fd5b6001600160a01b0384166000908152600860205260409020546005906116659085906131eb565b111561168457604051630464dc2960e51b815260040160405180910390fd5b6000600354600161169591906131eb565b6003805486018155600480548701905560058054870190556001600160a01b03871660009081526008602052604081208054880190559054919250906116dc9060016131eb565b9050815b8181101561102257611702878260405180602001604052806000815250611fb7565b6000818152601f60205260409020805460ff19166001908117909155016116e0565b6000818152601f602052604090205460609060ff166001036117c857601e805461174d9061314c565b80601f01602080910402602001604051908101604052809291908181526020018280546117799061314c565b80156110e35780601f1061179b576101008083540402835291602001916110e3565b820191906000526020600020905b8154815290600101906020018083116117a95750939695505050505050565b601d805461174d9061314c565b826117df81611be1565b610d24848484612266565b60006117f5826118de565b506000908152601b60205260409020546001600160a01b031690565b8161181b81611be1565b610ca083836122e0565b61182d611981565b6001600160a01b03811661185457604051634ece6ecf60e01b815260040160405180910390fd5b610ccb8161210b565b8261186781611be1565b610d248484846123d8565b60006001600160e01b031982166301ffc9a760e01b14806118a357506001600160e01b031982166380ac58cd60e01b145b806118be57506001600160e01b03198216635b5e139f60e01b145b80610b5a57506001600160e01b0319821663035a194d60e11b1492915050565b6000818152601760205260409020546001600160a01b0316610ccb5760405163089ba7e160e41b815260040160405180910390fd5b600081815260196020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611948826112ce565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000546001600160a01b0316331461135b57604051631c62d58f60e11b815260040160405180910390fd5b6000546001600160a01b031633148015906119cd57506119cb33610493565b155b1561135b576040516301eca16760e41b815260040160405180910390fd5b60008381526013602090815260408083206001600160401b038616845290915290205460ff1615611a2f576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600e602052604081208054611a529061314c565b905003611a7257604051632aa5eff960e11b815260040160405180910390fd5b600083815260116020526040902054608011611aa15760405163bade3a7b60e01b815260040160405180910390fd5b60008381526013602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260118552928520805493840181558552929093206004820401805460039092166008026101000a808402199092169190930217909155811615611b4f576000838152600f602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b604080516001808252818301909252600091602080830190803683370190505090508381600081518110611b8557611b856131d5565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b83604051611bd491906131fe565b60405180910390a3610d24565b611beb338261261c565b610ccb57604051635d64832960e01b815260040160405180910390fd5b611c1383838361267b565b611c1e838383612723565b60008381526013602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b611c813382612771565b610ccb576040516302728a9d60e41b815260040160405180910390fd5b826001600160a01b0316611cb1826112ce565b6001600160a01b031614611cd85760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038216611cff576040516338f646ff60e21b815260040160405180910390fd5b611d0a8383836127dd565b600081815260196020908152604080832080546001600160a01b0319908116909155601b8352818420805490911690556001600160a01b038616835260189091528120805460019290611d5e908490613236565b90915550506001600160a01b0382166000908152601860205260408120805460019290611d8c9084906131eb565b909155505060008181526017602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6001600160401b038216611e14576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b0382166000908152600e602052604081208054611e379061314c565b90501115611e58576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600e60205260409020611e7a828261328f565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611f00576040519150601f19603f3d011682016040523d82523d6000602084013e611f05565b606091505b5050905080610ca05760405162461bcd60e51b815260206004820152601060248201526f2a3930b739b332b9103330b4b632b21760811b604482015260640160405180910390fd5b60025460010361135b5760405163ed1fa96f60e01b815260040160405180910390fd5b600a5460ff16611f93576040516306d3818760e11b815260040160405180910390fd5b6006546003541061135b57604051635e91cdfb60e11b815260040160405180910390fd5b611fc183836127fa565b611fce6000848484612908565b610ca05760405163bcb5663760e01b815260040160405180910390fd5b6000611ff6826112ce565b9050612004816000846127dd565b61200f600083611913565b61201a60008361209d565b6001600160a01b0381166000908152601860205260408120805460019290612043908490613236565b909155505060008281526017602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000818152601b6020526040902080546001600160a01b0319166001600160a01b03841690811790915581906120d2826112ce565b6001600160a01b03167fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e60405160405180910390a45050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b612163611981565b60016002556040517f3e423347941b5c6e8c727e4071ffeb6869244ce75121d6a56ba8356086851c6c90600090a1565b816001600160a01b0316836001600160a01b0316036121c557604051630b7b99b960e21b815260040160405180910390fd5b6001600160a01b038381166000818152601a6020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b61223d848484611c9e565b61224984848484612908565b610d245760405163bcb5663760e01b815260040160405180910390fd5b6000838152601060205260409020548190811461229657604051633581be1d60e11b815260040160405180910390fd5b60008481526012602052604090206122af908484612b56565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610d24565b6000828152601160205260409020548181111561231057604051635134ce8960e01b815260040160405180910390fd5b60005b8181101561238e57600084815260116020526040812080548390811061233b5761233b6131d5565b60009182526020808320600483040154888452600f8252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101612313565b5060008381526011602052604081206123a691612c17565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6123e383838361267b565b6000838152600f602090815260408083206001600160401b038086168552925282205416908082156124bb576124b583601060008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156124a257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161245f5790505b5050505050612a0790919063ffffffff16565b90925090505b801561253c5760008681526010602052604090208054859190849081106124e4576124e46131d5565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526013835260408083209187168352925220805460ff191690556125c7565b6000868152601260209081526040808320601083529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6125d2868686612723565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a45b505050505050565b600080612628836112ce565b9050806001600160a01b0316846001600160a01b0316148061264f575061264f8185610ce1565b806126735750836001600160a01b0316612668846117ea565b6001600160a01b0316145b949350505050565b60008381526011602052604090205482106126a957604051630757d52160e01b815260040160405180910390fd5b60008381526011602052604090208054839081106126c9576126c96131d5565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610ca0576040516378eeeecf60e01b815260040160405180910390fd5b600083815260116020526040902061273b9083612a70565b6000928352600f602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b60008061277d836112ce565b9050806001600160a01b0316846001600160a01b031614806127c457506001600160a01b038082166000908152601a602090815260408083209388168352929052205460ff165b806126735750836001600160a01b031661266884610bf2565b6001600160a01b038216610ca05760048054600019019055505050565b6001600160a01b038216612821576040516325bd6bd360e01b815260040160405180910390fd5b6000818152601760205260409020546001600160a01b0316156128575760405163c5a8d37160e01b815260040160405180910390fd5b80612875576040516312c33ce360e01b815260040160405180910390fd5b612881600083836127dd565b6001600160a01b03821660009081526018602052604081208054600192906128aa9084906131eb565b909155505060008181526017602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60006001600160a01b0384163b156129fc57604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061294c90339089908890889060040161334e565b6020604051808303816000875af1925050508015612987575060408051601f3d908101601f191682019092526129849181019061338b565b60015b6129e2573d8080156129b5576040519150601f19603f3d011682016040523d82523d6000602084013e6129ba565b606091505b5080516129da5760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612673565b506001949350505050565b81516000908190815b81811015612a5f57846001600160401b0316868281518110612a3457612a346131d5565b60200260200101516001600160401b031603612a5757925060019150612a699050565b600101612a10565b5060008092509250505b9250929050565b81548110612a7d57600080fd5b81548290612a8d90600190613236565b81548110612a9d57612a9d6131d5565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110612ad857612ad86131d5565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480612b2157612b216133a8565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b82805482825590600052602060002090600301600490048101928215612c075791602002820160005b83821115612bd25783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302612b7f565b8015612c055782816101000a8154906001600160401b030219169055600801602081600701049283019260010302612bd2565b505b50612c13929150612c38565b5090565b508054600082556003016004900490600052602060002090810190610ccb91905b5b80821115612c135760008155600101612c39565b6001600160e01b031981168114610ccb57600080fd5b600060208284031215612c7557600080fd5b8135612c8081612c4d565b9392505050565b6000815180845260005b81811015612cad57602081850181015186830182015201612c91565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612c806020830184612c87565b600060208284031215612cf257600080fd5b5035919050565b80356001600160a01b0381168114610d9957600080fd5b60008060408385031215612d2357600080fd5b612d2c83612cf9565b946020939093013593505050565b600060208284031215612d4c57600080fd5b612c8082612cf9565b80356001600160401b0381168114610d9957600080fd5b600080600060608486031215612d8157600080fd5b83359250612d9160208501612d55565b9150612d9f60408501612d55565b90509250925092565b60008060408385031215612dbb57600080fd5b612dc483612cf9565b9150610d6c60208401612cf9565b600080600060608486031215612de757600080fd5b8335925060208401359150612d9f60408501612d55565b600080600060608486031215612e1357600080fd5b612e1c84612cf9565b9250612e2a60208501612cf9565b9150604084013590509250925092565b60008060408385031215612e4d57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b60006001600160401b0380841115612e8c57612e8c612e5c565b604051601f8501601f19908116603f01168101908282118183101715612eb457612eb4612e5c565b81604052809350858152868686011115612ecd57600080fd5b858560208301376000602087830101525050509392505050565b600060208284031215612ef957600080fd5b81356001600160401b03811115612f0f57600080fd5b8201601f81018413612f2057600080fd5b61267384823560208401612e72565b80358015158114610d9957600080fd5b60008060408385031215612f5257600080fd5b82359150610d6c60208401612f2f565b6020808252825182820181905260009190848201906040850190845b81811015612f9e57835161ffff1683529284019291840191600101612f7e565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612f9e5783516001600160401b031683529284019291840191600101612fc6565b60008060408385031215612ffe57600080fd5b82359150610d6c60208401612d55565b6000806040838503121561302157600080fd5b61302a83612cf9565b9150610d6c60208401612f2f565b6000806000806080858703121561304e57600080fd5b61305785612cf9565b935061306560208601612cf9565b92506040850135915060608501356001600160401b0381111561308757600080fd5b8501601f8101871361309857600080fd5b6130a787823560208401612e72565b91505092959194509250565b6000602082840312156130c557600080fd5b612c8082612f2f565b6000806000604084860312156130e357600080fd5b8335925060208401356001600160401b038082111561310157600080fd5b818601915086601f83011261311557600080fd5b81358181111561312457600080fd5b8760208260051b850101111561313957600080fd5b6020830194508093505050509250925092565b600181811c9082168061316057607f821691505b60208210810361318057634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610b5a57610b5a613186565b6000826131d057634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b80820180821115610b5a57610b5a613186565b6020808252825182820181905260009190848201906040850190845b81811015612f9e5783518352928401929184019160010161321a565b81810381811115610b5a57610b5a613186565b601f821115610ca057600081815260208120601f850160051c810160208610156132705750805b601f850160051c820191505b818110156126145782815560010161327c565b81516001600160401b038111156132a8576132a8612e5c565b6132bc816132b6845461314c565b84613249565b602080601f8311600181146132f157600084156132d95750858301515b600019600386901b1c1916600185901b178555612614565b600085815260208120601f198616915b8281101561332057888601518255948401946001909101908401613301565b508582101561333e5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061338190830184612c87565b9695505050505050565b60006020828403121561339d57600080fd5b8151612c8081612c4d565b634e487b7160e01b600052603160045260246000fdfea264697066735822122030042155a931f73c020fd80f7595313bbb6bb78e8d8fc3c28a99a769a90d789864736f6c6343000815003300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000172450494e4b2078204576726c6f6f74205065726d697473000000000000000000000000000000000000000000000000000000000000000000000000000000000c50494e4b784576726c6f6f7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d576661764574444d7738664338744b6d7351325456596252704d4d70674e417a373471396d6d5a6e43624d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5a457858796f5259594c74554e6e77554e737937735366644b535969324c79414258455567784b64647450500000000000000000000000000000000000000000000000cb5eb537bad9c54a4dae2e3526473d79a496456a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000950000000000000000000000000000000000000000000000055de6a779bbac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d595041386368476a75517a6171334a476b524b576932636a6d50734a4b316346594d54765a52524c736e38710000000000000000000000

Deployed Bytecode

0x6080604052600436106103065760003560e01c8063715018a6116101a1578063b88d4fde116100f2578063df6f556b116100a0578063e985e9c51161006f578063e985e9c514610a44578063f2fde38b14610a8d578063fc3517c814610aad578063ffa1ad7414610acd57600080fd5b8063df6f556b14610975578063e467a48f146109d1578063e4dc951f146109f1578063e7de4de414610a2457600080fd5b8063b88d4fde146108b8578063bb0f8b6d146108d8578063c70b5ae5146108f8578063c87b56dd1461090b578063d49304be1461092b578063d5abeb0114610940578063de8e602c1461095557600080fd5b806389ed2edf1161014f57806389ed2edf146107cb5780638d4f3bf5146107e05780638da5cb5b1461081457806395d89b411461083257806395edc18c146108475780639e74673f14610865578063a22cb4651461089857600080fd5b8063715018a6146106f35780637280281e1461070857806379e8ca9e146107285780637a573275146107485780637aba6f371461077e5780637e5852d9146107935780638507dc28146107ab57600080fd5b80632a55205a1161025b57806351532e5a1161020957806351532e5a1461061157806359c8b7dd1461063e5780635e2e3292146106535780635e94354a146106735780635ea72f36146106935780636352211e146106b357806370a08231146106d357600080fd5b80632a55205a1461051257806330ffb1d61461055157806333ed2845146105715780633600261d1461059157806340c10f19146105be57806342842e0e146105d157806342966c68146105f157600080fd5b8063149a4f01116102b8578063149a4f011461042e57806318160ddd146104435780631c7bb461146104585780631d0d35f51461047857806322e6d160146104b257806322f6da9c146104d257806323b872dd146104f257600080fd5b806301e1d1141461030b57806301ffc9a71461032f578063042242f81461035f57806306fdde0314610392578063081812fc146103b4578063095ea7b3146103ec5780630fc499f51461040e575b600080fd5b34801561031757600080fd5b50601c545b6040519081526020015b60405180910390f35b34801561033b57600080fd5b5061034f61034a366004612c63565b610afe565b6040519015158152602001610326565b34801561036b57600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061031c565b34801561039e57600080fd5b506103a7610b60565b6040516103269190612ccd565b3480156103c057600080fd5b506103d46103cf366004612ce0565b610bf2565b6040516001600160a01b039091168152602001610326565b3480156103f857600080fd5b5061040c610407366004612d10565b610c19565b005b34801561041a57600080fd5b5061040c610429366004612d3a565b610ca5565b34801561043a57600080fd5b5060075461031c565b34801561044f57600080fd5b5060045461031c565b34801561046457600080fd5b5061040c610473366004612d6c565b610cce565b34801561048457600080fd5b5061034f610493366004612d3a565b6001600160a01b03166000908152600160208190526040909120541490565b3480156104be57600080fd5b5061034f6104cd366004612da8565b610ce1565b3480156104de57600080fd5b5061040c6104ed366004612dd2565b610d0f565b3480156104fe57600080fd5b5061040c61050d366004612dfe565b610d2a565b34801561051e57600080fd5b5061053261052d366004612e3a565b610d3f565b604080516001600160a01b039093168352602083019190915201610326565b34801561055d57600080fd5b5061031c61056c366004612ee7565b610d75565b34801561057d57600080fd5b5061040c61058c366004612d10565b610d9e565b34801561059d57600080fd5b506105b16105ac366004612f3f565b610db4565b6040516103269190612f62565b61031c6105cc366004612d10565b610eb1565b3480156105dd57600080fd5b5061040c6105ec366004612dfe565b61102d565b3480156105fd57600080fd5b5061040c61060c366004612ce0565b611048565b34801561061d57600080fd5b5061063161062c366004612ce0565b61105b565b6040516103269190612faa565b34801561064a57600080fd5b50600d5461031c565b34801561065f57600080fd5b506103a761066e366004612feb565b6110ef565b34801561067f57600080fd5b5061063161068e366004612ce0565b6111de565b34801561069f57600080fd5b5061040c6106ae366004612d10565b611247565b3480156106bf57600080fd5b506103d46106ce366004612ce0565b6112ce565b3480156106df57600080fd5b5061031c6106ee366004612d3a565b611304565b3480156106ff57600080fd5b5061040c611349565b34801561071457600080fd5b50610631610723366004612ce0565b61135d565b34801561073457600080fd5b5061040c61074336600461300e565b6113c6565b34801561075457600080fd5b5061031c610763366004612d3a565b6001600160a01b031660009081526008602052604090205490565b34801561078a57600080fd5b5061040c611483565b34801561079f57600080fd5b5060025460011461034f565b3480156107b757600080fd5b5061040c6107c636600461300e565b61149b565b3480156107d757600080fd5b506103a7611530565b3480156107ec57600080fd5b506107fb63524d524b60e01b81565b6040516001600160e01b03199091168152602001610326565b34801561082057600080fd5b506000546001600160a01b03166103d4565b34801561083e57600080fd5b506103a761153f565b34801561085357600080fd5b50600c546001600160a01b03166103d4565b34801561087157600080fd5b507f0000000000000000000000000000000000000000000000055de6a779bbac000061031c565b3480156108a457600080fd5b5061040c6108b336600461300e565b61154e565b3480156108c457600080fd5b5061040c6108d3366004613038565b611559565b3480156108e457600080fd5b5061040c6108f33660046130b3565b611576565b61031c610906366004612d10565b611591565b34801561091757600080fd5b506103a7610926366004612ce0565b611724565b34801561093757600080fd5b5060055461031c565b34801561094c57600080fd5b5060065461031c565b34801561096157600080fd5b5061040c6109703660046130ce565b6117d5565b34801561098157600080fd5b506109b9610990366004612feb565b6000918252600f602090815260408084206001600160401b039384168552909152909120541690565b6040516001600160401b039091168152602001610326565b3480156109dd57600080fd5b506103d46109ec366004612ce0565b6117ea565b3480156109fd57600080fd5b5061034f610a0c366004612ce0565b6000908152601f602052604090205460ff1660011490565b348015610a3057600080fd5b5061040c610a3f366004612e3a565b611811565b348015610a5057600080fd5b5061034f610a5f366004612da8565b6001600160a01b039182166000908152601a6020908152604080832093909416825291909152205460ff1690565b348015610a9957600080fd5b5061040c610aa8366004612d3a565b611825565b348015610ab957600080fd5b5061040c610ac8366004612dd2565b61185d565b348015610ad957600080fd5b506103a760405180604001604052806005815260200164312e322e3160d81b81525081565b6000610b0982611872565b80610b2457506001600160e01b0319821663152a902d60e11b145b80610b3f57506001600160e01b0319821663524d524b60e01b145b80610b5a57506001600160e01b03198216633600261d60e01b145b92915050565b606060158054610b6f9061314c565b80601f0160208091040260200160405190810160405280929190818152602001828054610b9b9061314c565b8015610be85780601f10610bbd57610100808354040283529160200191610be8565b820191906000526020600020905b815481529060010190602001808311610bcb57829003601f168201915b5050505050905090565b6000610bfd826118de565b506000908152601960205260409020546001600160a01b031690565b6000610c24826112ce565b9050806001600160a01b0316836001600160a01b031603610c5857604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b03821614801590610c785750610c768133610a5f565b155b15610c9657604051634c12315960e11b815260040160405180910390fd5b610ca08383611913565b505050565b610cad611981565b600c80546001600160a01b0319166001600160a01b03831617905550565b50565b610cd66119ac565b610ca08383836119eb565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205460ff1690565b82610d1981611be1565b610d24848484611c08565b50505050565b80610d3481611c77565b610d24848484611c9e565b600c54600d546001600160a01b039091169060009061271090610d62908561319c565b610d6c91906131b3565b90509250929050565b6000610d7f6119ac565b601c805460010190819055610d949083611ded565b50601c545b919050565b610da6611981565b610db08282611eb3565b5050565b6000828152601f6020526040908190205481516019808252610340820190935260609260ff9092169181602001602082028036833701905050915060ff811615610e5357600782600081518110610e0d57610e0d6131d5565b602002602001019061ffff16908161ffff1681525050600d82600a81518110610e3857610e386131d5565b602002602001019061ffff16908161ffff1681525050610eaa565b600382600081518110610e6857610e686131d5565b602002602001019061ffff16908161ffff1681525050600982600a81518110610e9357610e936131d5565b602002602001019061ffff16908161ffff16815250505b5092915050565b6000610ebb611f4d565b610ec3611f70565b81610ee15760405163376bec4d60e01b815260040160405180910390fd5b600654600354610ef190846131eb565b1115610f1057604051635e91cdfb60e11b815260040160405180910390fd5b6000610f3c7f00000000000000000000000000000000000000000000000000000000000000008461319c565b9050348114610f5e576040516336ce5e4360e01b815260040160405180910390fd5b6001600160a01b038416600090815260096020526040902054600190610f859085906131eb565b1115610fa457604051633be4b58160e21b815260040160405180910390fd5b60006003546001610fb591906131eb565b600380548601815560048054870190556001600160a01b0387166000908152600960205260408120805488019055905491925090610ff49060016131eb565b9050815b818110156110225761101a878260405180602001604052806000815250611fb7565b600101610ff8565b509095945050505050565b610ca083838360405180602001604052806000815250611559565b8061105281611c77565b610db082611feb565b6000818152601160209081526040918290208054835181840281018401909452808452606093928301828280156110e357602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116110a05790505b50505050509050919050565b60008281526013602090815260408083206001600160401b038516845290915290205460609060ff1661113557604051631b9928fd60e31b815260040160405180910390fd5b6001600160401b0382166000908152600e6020526040902080546111589061314c565b80601f01602080910402602001604051908101604052809291908181526020018280546111849061314c565b80156111d15780601f106111a6576101008083540402835291602001916111d1565b820191906000526020600020905b8154815290600101906020018083116111b457829003601f168201915b5050505050905092915050565b6000818152601260209081526040918290208054835181840281018401909452808452606093928301828280156110e357600091825260209182902080546001600160401b031684529082028301929091600891018084116110a0575094979650505050505050565b6000611252826112ce565b9050806001600160a01b0316836001600160a01b031603611286576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b038216148015906112a657506112a48133610ce1565b155b156112c4576040516357a2e94960e11b815260040160405180910390fd5b610ca0838361209d565b6000818152601760205260408120546001600160a01b031680610b5a5760405163089ba7e160e41b815260040160405180910390fd5b60006001600160a01b03821661132d57604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526018602052604090205490565b611351611981565b61135b600061210b565b565b6000818152601060209081526040918290208054835181840281018401909452808452606093928301828280156110e357600091825260209182902080546001600160401b031684529082028301929091600891018084116110a0575094979650505050505050565b6113ce611981565b6001600160a01b0382166113f55760405163016b812760e71b815260040160405180910390fd5b8061141a576001600160a01b0382166000908152600160205260408120819055611439565b6001600160a01b03821660009081526001602081905260409091208190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac282604051611477911515815260200190565b60405180910390a25050565b61148b611981565b61149361215b565b600454600655565b6001600160a01b03821633036114c4576040516375f45abd60e01b815260040160405180910390fd5b3360008181526014602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a26235910160405180910390a35050565b6060600b8054610b6f9061314c565b606060168054610b6f9061314c565b610db0338383612193565b8161156381611c77565b61156f85858585612232565b5050505050565b61157e611981565b600a805460ff1916911515919091179055565b600061159b611f4d565b6115a3611f70565b816115c15760405163376bec4d60e01b815260040160405180910390fd5b6007546003546115d190846131eb565b11156115f057604051635e91cdfb60e11b815260040160405180910390fd5b600061161c7f0000000000000000000000000000000000000000000000055de6a779bbac00008461319c565b905034811461163e576040516336ce5e4360e01b815260040160405180910390fd5b6001600160a01b0384166000908152600860205260409020546005906116659085906131eb565b111561168457604051630464dc2960e51b815260040160405180910390fd5b6000600354600161169591906131eb565b6003805486018155600480548701905560058054870190556001600160a01b03871660009081526008602052604081208054880190559054919250906116dc9060016131eb565b9050815b8181101561102257611702878260405180602001604052806000815250611fb7565b6000818152601f60205260409020805460ff19166001908117909155016116e0565b6000818152601f602052604090205460609060ff166001036117c857601e805461174d9061314c565b80601f01602080910402602001604051908101604052809291908181526020018280546117799061314c565b80156110e35780601f1061179b576101008083540402835291602001916110e3565b820191906000526020600020905b8154815290600101906020018083116117a95750939695505050505050565b601d805461174d9061314c565b826117df81611be1565b610d24848484612266565b60006117f5826118de565b506000908152601b60205260409020546001600160a01b031690565b8161181b81611be1565b610ca083836122e0565b61182d611981565b6001600160a01b03811661185457604051634ece6ecf60e01b815260040160405180910390fd5b610ccb8161210b565b8261186781611be1565b610d248484846123d8565b60006001600160e01b031982166301ffc9a760e01b14806118a357506001600160e01b031982166380ac58cd60e01b145b806118be57506001600160e01b03198216635b5e139f60e01b145b80610b5a57506001600160e01b0319821663035a194d60e11b1492915050565b6000818152601760205260409020546001600160a01b0316610ccb5760405163089ba7e160e41b815260040160405180910390fd5b600081815260196020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611948826112ce565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000546001600160a01b0316331461135b57604051631c62d58f60e11b815260040160405180910390fd5b6000546001600160a01b031633148015906119cd57506119cb33610493565b155b1561135b576040516301eca16760e41b815260040160405180910390fd5b60008381526013602090815260408083206001600160401b038616845290915290205460ff1615611a2f576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600e602052604081208054611a529061314c565b905003611a7257604051632aa5eff960e11b815260040160405180910390fd5b600083815260116020526040902054608011611aa15760405163bade3a7b60e01b815260040160405180910390fd5b60008381526013602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260118552928520805493840181558552929093206004820401805460039092166008026101000a808402199092169190930217909155811615611b4f576000838152600f602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b604080516001808252818301909252600091602080830190803683370190505090508381600081518110611b8557611b856131d5565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b83604051611bd491906131fe565b60405180910390a3610d24565b611beb338261261c565b610ccb57604051635d64832960e01b815260040160405180910390fd5b611c1383838361267b565b611c1e838383612723565b60008381526013602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b611c813382612771565b610ccb576040516302728a9d60e41b815260040160405180910390fd5b826001600160a01b0316611cb1826112ce565b6001600160a01b031614611cd85760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038216611cff576040516338f646ff60e21b815260040160405180910390fd5b611d0a8383836127dd565b600081815260196020908152604080832080546001600160a01b0319908116909155601b8352818420805490911690556001600160a01b038616835260189091528120805460019290611d5e908490613236565b90915550506001600160a01b0382166000908152601860205260408120805460019290611d8c9084906131eb565b909155505060008181526017602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6001600160401b038216611e14576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b0382166000908152600e602052604081208054611e379061314c565b90501115611e58576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152600e60205260409020611e7a828261328f565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611f00576040519150601f19603f3d011682016040523d82523d6000602084013e611f05565b606091505b5050905080610ca05760405162461bcd60e51b815260206004820152601060248201526f2a3930b739b332b9103330b4b632b21760811b604482015260640160405180910390fd5b60025460010361135b5760405163ed1fa96f60e01b815260040160405180910390fd5b600a5460ff16611f93576040516306d3818760e11b815260040160405180910390fd5b6006546003541061135b57604051635e91cdfb60e11b815260040160405180910390fd5b611fc183836127fa565b611fce6000848484612908565b610ca05760405163bcb5663760e01b815260040160405180910390fd5b6000611ff6826112ce565b9050612004816000846127dd565b61200f600083611913565b61201a60008361209d565b6001600160a01b0381166000908152601860205260408120805460019290612043908490613236565b909155505060008281526017602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b6000818152601b6020526040902080546001600160a01b0319166001600160a01b03841690811790915581906120d2826112ce565b6001600160a01b03167fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e60405160405180910390a45050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b612163611981565b60016002556040517f3e423347941b5c6e8c727e4071ffeb6869244ce75121d6a56ba8356086851c6c90600090a1565b816001600160a01b0316836001600160a01b0316036121c557604051630b7b99b960e21b815260040160405180910390fd5b6001600160a01b038381166000818152601a6020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b61223d848484611c9e565b61224984848484612908565b610d245760405163bcb5663760e01b815260040160405180910390fd5b6000838152601060205260409020548190811461229657604051633581be1d60e11b815260040160405180910390fd5b60008481526012602052604090206122af908484612b56565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610d24565b6000828152601160205260409020548181111561231057604051635134ce8960e01b815260040160405180910390fd5b60005b8181101561238e57600084815260116020526040812080548390811061233b5761233b6131d5565b60009182526020808320600483040154888452600f8252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101612313565b5060008381526011602052604081206123a691612c17565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6123e383838361267b565b6000838152600f602090815260408083206001600160401b038086168552925282205416908082156124bb576124b583601060008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156124a257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161245f5790505b5050505050612a0790919063ffffffff16565b90925090505b801561253c5760008681526010602052604090208054859190849081106124e4576124e46131d5565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526013835260408083209187168352925220805460ff191690556125c7565b6000868152601260209081526040808320601083529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6125d2868686612723565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a45b505050505050565b600080612628836112ce565b9050806001600160a01b0316846001600160a01b0316148061264f575061264f8185610ce1565b806126735750836001600160a01b0316612668846117ea565b6001600160a01b0316145b949350505050565b60008381526011602052604090205482106126a957604051630757d52160e01b815260040160405180910390fd5b60008381526011602052604090208054839081106126c9576126c96131d5565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610ca0576040516378eeeecf60e01b815260040160405180910390fd5b600083815260116020526040902061273b9083612a70565b6000928352600f602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b60008061277d836112ce565b9050806001600160a01b0316846001600160a01b031614806127c457506001600160a01b038082166000908152601a602090815260408083209388168352929052205460ff165b806126735750836001600160a01b031661266884610bf2565b6001600160a01b038216610ca05760048054600019019055505050565b6001600160a01b038216612821576040516325bd6bd360e01b815260040160405180910390fd5b6000818152601760205260409020546001600160a01b0316156128575760405163c5a8d37160e01b815260040160405180910390fd5b80612875576040516312c33ce360e01b815260040160405180910390fd5b612881600083836127dd565b6001600160a01b03821660009081526018602052604081208054600192906128aa9084906131eb565b909155505060008181526017602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60006001600160a01b0384163b156129fc57604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061294c90339089908890889060040161334e565b6020604051808303816000875af1925050508015612987575060408051601f3d908101601f191682019092526129849181019061338b565b60015b6129e2573d8080156129b5576040519150601f19603f3d011682016040523d82523d6000602084013e6129ba565b606091505b5080516129da5760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050612673565b506001949350505050565b81516000908190815b81811015612a5f57846001600160401b0316868281518110612a3457612a346131d5565b60200260200101516001600160401b031603612a5757925060019150612a699050565b600101612a10565b5060008092509250505b9250929050565b81548110612a7d57600080fd5b81548290612a8d90600190613236565b81548110612a9d57612a9d6131d5565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110612ad857612ad86131d5565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480612b2157612b216133a8565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b82805482825590600052602060002090600301600490048101928215612c075791602002820160005b83821115612bd25783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302612b7f565b8015612c055782816101000a8154906001600160401b030219169055600801602081600701049283019260010302612bd2565b505b50612c13929150612c38565b5090565b508054600082556003016004900490600052602060002090810190610ccb91905b5b80821115612c135760008155600101612c39565b6001600160e01b031981168114610ccb57600080fd5b600060208284031215612c7557600080fd5b8135612c8081612c4d565b9392505050565b6000815180845260005b81811015612cad57602081850181015186830182015201612c91565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000612c806020830184612c87565b600060208284031215612cf257600080fd5b5035919050565b80356001600160a01b0381168114610d9957600080fd5b60008060408385031215612d2357600080fd5b612d2c83612cf9565b946020939093013593505050565b600060208284031215612d4c57600080fd5b612c8082612cf9565b80356001600160401b0381168114610d9957600080fd5b600080600060608486031215612d8157600080fd5b83359250612d9160208501612d55565b9150612d9f60408501612d55565b90509250925092565b60008060408385031215612dbb57600080fd5b612dc483612cf9565b9150610d6c60208401612cf9565b600080600060608486031215612de757600080fd5b8335925060208401359150612d9f60408501612d55565b600080600060608486031215612e1357600080fd5b612e1c84612cf9565b9250612e2a60208501612cf9565b9150604084013590509250925092565b60008060408385031215612e4d57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b60006001600160401b0380841115612e8c57612e8c612e5c565b604051601f8501601f19908116603f01168101908282118183101715612eb457612eb4612e5c565b81604052809350858152868686011115612ecd57600080fd5b858560208301376000602087830101525050509392505050565b600060208284031215612ef957600080fd5b81356001600160401b03811115612f0f57600080fd5b8201601f81018413612f2057600080fd5b61267384823560208401612e72565b80358015158114610d9957600080fd5b60008060408385031215612f5257600080fd5b82359150610d6c60208401612f2f565b6020808252825182820181905260009190848201906040850190845b81811015612f9e57835161ffff1683529284019291840191600101612f7e565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612f9e5783516001600160401b031683529284019291840191600101612fc6565b60008060408385031215612ffe57600080fd5b82359150610d6c60208401612d55565b6000806040838503121561302157600080fd5b61302a83612cf9565b9150610d6c60208401612f2f565b6000806000806080858703121561304e57600080fd5b61305785612cf9565b935061306560208601612cf9565b92506040850135915060608501356001600160401b0381111561308757600080fd5b8501601f8101871361309857600080fd5b6130a787823560208401612e72565b91505092959194509250565b6000602082840312156130c557600080fd5b612c8082612f2f565b6000806000604084860312156130e357600080fd5b8335925060208401356001600160401b038082111561310157600080fd5b818601915086601f83011261311557600080fd5b81358181111561312457600080fd5b8760208260051b850101111561313957600080fd5b6020830194508093505050509250925092565b600181811c9082168061316057607f821691505b60208210810361318057634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610b5a57610b5a613186565b6000826131d057634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b80820180821115610b5a57610b5a613186565b6020808252825182820181905260009190848201906040850190845b81811015612f9e5783518352928401929184019160010161321a565b81810381811115610b5a57610b5a613186565b601f821115610ca057600081815260208120601f850160051c810160208610156132705750805b601f850160051c820191505b818110156126145782815560010161327c565b81516001600160401b038111156132a8576132a8612e5c565b6132bc816132b6845461314c565b84613249565b602080601f8311600181146132f157600084156132d95750858301515b600019600386901b1c1916600185901b178555612614565b600085815260208120601f198616915b8281101561332057888601518255948401946001909101908401613301565b508582101561333e5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061338190830184612c87565b9695505050505050565b60006020828403121561339d57600080fd5b8151612c8081612c4d565b634e487b7160e01b600052603160045260246000fdfea264697066735822122030042155a931f73c020fd80f7595313bbb6bb78e8d8fc3c28a99a769a90d789864736f6c63430008150033

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

00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000172450494e4b2078204576726c6f6f74205065726d697473000000000000000000000000000000000000000000000000000000000000000000000000000000000c50494e4b784576726c6f6f7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d576661764574444d7738664338744b6d7351325456596252704d4d70674e417a373471396d6d5a6e43624d3700000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5a457858796f5259594c74554e6e77554e737937735366644b535969324c79414258455567784b64647450500000000000000000000000000000000000000000000000cb5eb537bad9c54a4dae2e3526473d79a496456a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000950000000000000000000000000000000000000000000000055de6a779bbac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d595041386368476a75517a6171334a476b524b576932636a6d50734a4b316346594d54765a52524c736e38710000000000000000000000

-----Decoded View---------------
Arg [0] : name_ (string): $PINK x Evrloot Permits
Arg [1] : symbol_ (string): PINKxEvrloot
Arg [2] : collectionMetadata_ (string): ipfs://QmWfavEtDMw8fC8tKmsQ2TVYbRpMMpgNAz74q9mmZnCbM7
Arg [3] : tokenURI_ (string): ipfs://QmZExXyoRYYLtUNnwUNsy7sSfdKSYi2LyABXEUgxKddtPP
Arg [4] : data (tuple):
Arg [1] : royaltyRecipient (address): 0xCb5eB537bAd9C54A4Dae2E3526473D79A496456A
Arg [2] : royaltyPercentageBps (uint16): 500
Arg [3] : maxSupply (uint256): 1000
Arg [4] : maxVIPSupply (uint256): 149
Arg [5] : pricePerMintVIP (uint256): 99000000000000000000
Arg [6] : pricePerMintNonVIP (uint256): 0
Arg [7] : tokenURIVIP (string): ipfs://QmYPA8chGjuQzaq3JGkRKWi2cjmPsJK1cFYMTvZRRLsn8q


-----Encoded View---------------
25 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [4] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000017
Arg [6] : 2450494e4b2078204576726c6f6f74205065726d697473000000000000000000
Arg [7] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [8] : 50494e4b784576726c6f6f740000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [10] : 697066733a2f2f516d576661764574444d7738664338744b6d73513254565962
Arg [11] : 52704d4d70674e417a373471396d6d5a6e43624d370000000000000000000000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [13] : 697066733a2f2f516d5a457858796f5259594c74554e6e77554e737937735366
Arg [14] : 644b535969324c79414258455567784b64647450500000000000000000000000
Arg [15] : 000000000000000000000000cb5eb537bad9c54a4dae2e3526473d79a496456a
Arg [16] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [17] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000095
Arg [19] : 0000000000000000000000000000000000000000000000055de6a779bbac0000
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [21] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [23] : 697066733a2f2f516d595041386368476a75517a6171334a476b524b57693263
Arg [24] : 6a6d50734a4b316346594d54765a52524c736e38710000000000000000000000


Block Transaction Gas Used Reward
view all blocks collator

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

Validator Index Block Amount
View All Withdrawals

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

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