Token
Spacers (SPACERS)
ERC-721
Overview
Max Total Supply
220 SPACERS
Holders
32
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract
Balance
1 SPACERSLoading...
Loading
Loading...
Loading
Loading...
Loading
Contract Name:
Spacers
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "./SpacerBase.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; /** * @title Spacers * @author RMRK team */ contract Spacers is SpacerBase { using Strings for uint256; mapping(address => bool) private _autoAcceptCollection; constructor( string memory name_, string memory symbol_, string memory collectionMetadata_, InitData memory data ) SpacerBase(name_, symbol_, collectionMetadata_, data) {} function mintTokensWithNewAsset( address[] memory to, uint256[] memory tokenIds, address catalogAddress, string memory metadataURI, uint64[][] memory partIds ) public virtual notLocked onlyOwnerOrContributor { uint256 numToMint = tokenIds.length; if (numToMint == 0) revert RMRKMintZero(); if (numToMint + _nextId > _maxSupply) revert RMRKMintOverMax(); uint64 assetId; unchecked { _nextId += numToMint; _totalSupply += numToMint; } for (uint256 i; i < numToMint; ) { assetId = addEquippableAssetEntry( uint64(0), catalogAddress, string(abi.encodePacked(metadataURI, tokenIds[i].toString(), ".json")), partIds[i] ); _safeMint(to[i], tokenIds[i], ""); _addAssetToToken(tokenIds[i], assetId, 0); _acceptAsset(tokenIds[i], 0, assetId); unchecked { ++i; } } } function setAutoAcceptCollection( address collection ) public virtual onlyOwner { _autoAcceptCollection[collection] = true; } function _afterAddChild( uint256 tokenId, address childAddress, uint256 childId, bytes memory ) internal override { // Auto accept children if they are from known collections if (_autoAcceptCollection[childAddress]) { _acceptChild( tokenId, _pendingChildren[tokenId].length - 1, childAddress, childId ); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165.sol"; /** * @dev Interface for the NFT Royalty Standard. * * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal * support for royalty payments across all NFT marketplaces and ecosystem participants. * * _Available since v4.5._ */ interface IERC2981 is IERC165 { /** * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be paid in that same unit of exchange. */ function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /** * @title IRMRKInitData * @author RMRK team * @notice Interface representation of RMRK initialization data. * @dev This interface provides a struct used to pack data to avoid stack too deep error for too many arguments. */ interface IRMRKInitData { /** * @notice Used to provide initialization data without running into stack too deep errors. * @return erc20TokenAddress Address of the ERC20 token to be used when initializing a smart contract that supports * ERC20 pay module * @return tokenUriIsEnumerable Weather the token URI is enumerable or not * @return royaltyRecipient Recipient of resale royalties * @return royaltyPercentageBps The percentage to be paid from the sale of the token expressed in basis points * @return maxSupply The maximum supply of tokens * @return pricePerMint The price per single-token mint expressed in the lowest denomination of native currency */ struct InitData { address erc20TokenAddress; // 20 bytes bool tokenUriIsEnumerable; // 1 byte // --- new slot --- address royaltyRecipient; // 20 bytes uint16 royaltyPercentageBps; // 2 bytes // --- new slot --- uint256 maxSupply; // --- new slot --- uint256 pricePerMint; // another 32 bytes } }
// 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 Reverts if the lock flag is set to true. */ modifier notLocked() { _onlyNotLocked(); _; } /** * @notice Locks the operation. * @dev Once locked, functions using `notLocked` modifier cannot be executed. */ function setLock() public virtual onlyOwner { _lock = 1; } /** * @notice Used to retrieve the status of a lockable smart contract. * @return A boolean value signifying whether the smart contract has been locked */ function getLock() public view returns (bool) { return _lock == 1; } /** * @notice Used to verify that the operation of the smart contract is not locked. * @dev If the operation of the smart contract is locked, the execution will be reverted. */ function _onlyNotLocked() private view { if (_lock == 1) revert RMRKLocked(); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title IRMRKCatalog * @author RMRK team * @notice An interface Catalog for RMRK equippable module. */ interface IRMRKCatalog is IERC165 { /** * @notice Event to announce addition of a new part. * @dev It is emitted when a new part is added. * @param partId ID of the part that was added * @param itemType Enum value specifying whether the part is `None`, `Slot` and `Fixed` * @param zIndex An uint specifying the z value of the part. It is used to specify the depth which the part should * be rendered at * @param equippableAddresses An array of addresses that can equip this part * @param metadataURI The metadata URI of the part */ event AddedPart( uint64 indexed partId, ItemType indexed itemType, uint8 zIndex, address[] equippableAddresses, string metadataURI ); /** * @notice Event to announce new equippables to the part. * @dev It is emitted when new addresses are marked as equippable for `partId`. * @param partId ID of the part that had new equippable addresses added * @param equippableAddresses An array of the new addresses that can equip this part */ event AddedEquippables( uint64 indexed partId, address[] equippableAddresses ); /** * @notice Event to announce the overriding of equippable addresses of the part. * @dev It is emitted when the existing list of addresses marked as equippable for `partId` is overwritten by a new one. * @param partId ID of the part whose list of equippable addresses was overwritten * @param equippableAddresses The new, full, list of addresses that can equip this part */ event SetEquippables(uint64 indexed partId, address[] equippableAddresses); /** * @notice Event to announce that a given part can be equipped by any address. * @dev It is emitted when a given part is marked as equippable by any. * @param partId ID of the part marked as equippable by any address */ event SetEquippableToAll(uint64 indexed partId); /** * @notice Used to define a type of the item. Possible values are `None`, `Slot` or `Fixed`. * @dev Used for fixed and slot parts. */ enum ItemType { None, Slot, Fixed } /** * @notice The integral structure of a standard RMRK catalog item defining it. * @dev Requires a minimum of 3 storage slots per catalog item, equivalent to roughly 60,000 gas as of Berlin hard * fork (April 14, 2021), though 5-7 storage slots is more realistic, given the standard length of an IPFS URI. * This will result in between 25,000,000 and 35,000,000 gas per 250 assets--the maximum block size of Ethereum * mainnet is 30M at peak usage. * @return itemType The item type of the part * @return z The z value of the part defining how it should be rendered when presenting the full NFT * @return equippable The array of addresses allowed to be equipped in this part * @return metadataURI The metadata URI of the part */ struct Part { ItemType itemType; //1 byte uint8 z; //1 byte address[] equippable; //n Collections that can be equipped into this slot string metadataURI; //n bytes 32+ } /** * @notice The structure used to add a new `Part`. * @dev The part is added with specified ID, so you have to make sure that you are using an unused `partId`, * otherwise the addition of the part vill be reverted. * @dev The full `IntakeStruct` looks like this: * [ * partID, * [ * itemType, * z, * [ * permittedCollectionAddress0, * permittedCollectionAddress1, * permittedCollectionAddress2 * ], * metadataURI * ] * ] * @return partId ID to be assigned to the `Part` * @return part A `Part` to be added */ struct IntakeStruct { uint64 partId; Part part; } /** * @notice Used to return the metadata URI of the associated Catalog. * @return Catalog metadata URI */ function getMetadataURI() external view returns (string memory); /** * @notice Used to return the `itemType` of the associated Catalog * @return `itemType` of the associated Catalog */ function getType() external view returns (string memory); /** * @notice Used to check whether the given address is allowed to equip the desired `Part`. * @dev Returns true if a collection may equip asset with `partId`. * @param partId The ID of the part that we are checking * @param targetAddress The address that we are checking for whether the part can be equipped into it or not * @return The status indicating whether the `targetAddress` can be equipped into `Part` with `partId` or not */ function checkIsEquippable( uint64 partId, address targetAddress ) external view returns (bool); /** * @notice Used to check if the part is equippable by all addresses. * @dev Returns true if part is equippable to all. * @param partId ID of the part that we are checking * @return The status indicating whether the part with `partId` can be equipped by any address or not */ function checkIsEquippableToAll(uint64 partId) external view returns (bool); /** * @notice Used to retrieve a `Part` with id `partId` * @param partId ID of the part that we are retrieving * @return The `Part` struct associated with given `partId` */ function getPart(uint64 partId) external view returns (Part memory); /** * @notice Used to retrieve multiple parts at the same time. * @param partIds An array of part IDs that we want to retrieve * @return An array of `Part` structs associated with given `partIds` */ function getParts( uint64[] memory partIds ) external view returns (Part[] memory); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /** * @title IRMRKCore * @author RMRK team * @notice Interface smart contract for RMRK core module. */ interface IRMRKCore { /** * @notice Used to retrieve the collection name. * @return Name of the collection */ function name() external view returns (string memory); /** * @notice Used to retrieve the collection symbol. * @return Symbol of the collection */ function symbol() external view returns (string memory); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "./IRMRKCore.sol"; /** * @title RMRKCore * @author RMRK team * @notice Smart contract of the RMRK core module. * @dev This is currently just a passthrough contract which allows for granular editing of base-level ERC721 functions. */ contract RMRKCore is IRMRKCore { /** * @notice Version of the @rmrk-team/evm-contracts package * @return Version identifier of the smart contract */ string public constant VERSION = "1.0.1"; /** * @notice Used to initialize the smart contract. * @param name_ Name of the token collection * @param symbol_ Symbol of the token collection */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /// Token name string private _name; /// Token symbol string private _symbol; /** * @notice Used to retrieve the collection name. * @return Name of the collection */ function name() public view virtual override returns (string memory) { return _name; } /** * @notice Used to retrieve the collection symbol. * @return Symbol of the collection */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @notice Hook that is called before any token transfer. This includes minting and burning. * @dev Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be transferred to `to`. * - When `from` is zero, `tokenId` will be minted to `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` and `to` are never zero at the same time. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param from Address from which the token is being transferred * @param to Address to which the token is being transferred * @param tokenId ID of the token being transferred */ function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} /** * @notice Hook that is called after any transfer of tokens. This includes minting and burning. * @dev Calling conditions: * * - When `from` and `to` are both non-zero. * - `from` and `to` are never zero at the same time. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param from Address from which the token has been transferred * @param to Address to which the token has been transferred * @param tokenId ID of the token that has been transferred */ function _afterTokenTransfer( address from, address to, uint256 tokenId ) internal virtual {} }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "../multiasset/IERC5773.sol"; /** * @title IERC6220 * @author RMRK team * @notice Interface smart contract of the RMRK equippable module. */ interface IERC6220 is IERC5773 { /** * @notice Used to store the core structure of the `Equippable` RMRK lego. * @return assetId The ID of the asset equipping a child * @return childAssetId The ID of the asset used as equipment * @return childId The ID of token that is equipped * @return childEquippableAddress Address of the collection to which the child asset belongs to */ struct Equipment { uint64 assetId; uint64 childAssetId; uint256 childId; address childEquippableAddress; } /** * @notice Used to provide a struct for inputing equip data. * @dev Only used for input and not storage of data. * @return tokenId ID of the token we are managing * @return childIndex Index of a child in the list of token's active children * @return assetId ID of the asset that we are equipping into * @return slotPartId ID of the slot part that we are using to equip * @return childAssetId ID of the asset that we are equipping */ struct IntakeEquip { uint256 tokenId; uint256 childIndex; uint64 assetId; uint64 slotPartId; uint64 childAssetId; } /** * @notice Used to notify listeners that a child's asset has been equipped into one of its parent assets. * @param tokenId ID of the token that had an asset equipped * @param assetId ID of the asset associated with the token we are equipping into * @param slotPartId ID of the slot we are using to equip * @param childId ID of the child token we are equipping into the slot * @param childAddress Address of the child token's collection * @param childAssetId ID of the asset associated with the token we are equipping */ event ChildAssetEquipped( uint256 indexed tokenId, uint64 indexed assetId, uint64 indexed slotPartId, uint256 childId, address childAddress, uint64 childAssetId ); /** * @notice Used to notify listeners that a child's asset has been unequipped from one of its parent assets. * @param tokenId ID of the token that had an asset unequipped * @param assetId ID of the asset associated with the token we are unequipping out of * @param slotPartId ID of the slot we are unequipping from * @param childId ID of the token being unequipped * @param childAddress Address of the collection that a token that is being unequipped belongs to * @param childAssetId ID of the asset associated with the token we are unequipping */ event ChildAssetUnequipped( uint256 indexed tokenId, uint64 indexed assetId, uint64 indexed slotPartId, uint256 childId, address childAddress, uint64 childAssetId ); /** * @notice Used to notify listeners that the assets belonging to a `equippableGroupId` have been marked as * equippable into a given slot and parent * @param equippableGroupId ID of the equippable group being marked as equippable into the slot associated with * `slotPartId` of the `parentAddress` collection * @param slotPartId ID of the slot part of the catalog into which the parts belonging to the equippable group * associated with `equippableGroupId` can be equipped * @param parentAddress Address of the collection into which the parts belonging to `equippableGroupId` can be * equipped */ event ValidParentEquippableGroupIdSet( uint64 indexed equippableGroupId, uint64 indexed slotPartId, address parentAddress ); /** * @notice Used to equip a child into a token. * @dev The `IntakeEquip` stuct contains the following data: * [ * tokenId, * childIndex, * assetId, * slotPartId, * childAssetId * ] * @param data An `IntakeEquip` struct specifying the equip data */ function equip(IntakeEquip memory data) external; /** * @notice Used to unequip child from parent token. * @dev This can only be called by the owner of the token or by an account that has been granted permission to * manage the given token by the current owner. * @param tokenId ID of the parent from which the child is being unequipped * @param assetId ID of the parent's asset that contains the `Slot` into which the child is equipped * @param slotPartId ID of the `Slot` from which to unequip the child */ function unequip( uint256 tokenId, uint64 assetId, uint64 slotPartId ) external; /** * @notice Used to check whether the token has a given child equipped. * @dev This is used to prevent from transferring a child that is equipped. * @param tokenId ID of the parent token for which we are querying for * @param childAddress Address of the child token's smart contract * @param childId ID of the child token * @return A boolean value indicating whether the child token is equipped into the given token or not */ function isChildEquipped( uint256 tokenId, address childAddress, uint256 childId ) external view returns (bool); /** * @notice Used to verify whether a token can be equipped into a given parent's slot. * @param parent Address of the parent token's smart contract * @param tokenId ID of the token we want to equip * @param assetId ID of the asset associated with the token we want to equip * @param slotId ID of the slot that we want to equip the token into * @return A boolean indicating whether the token with the given asset can be equipped into the desired slot */ function canTokenBeEquippedWithAssetIntoSlot( address parent, uint256 tokenId, uint64 assetId, uint64 slotId ) external view returns (bool); /** * @notice Used to get the Equipment object equipped into the specified slot of the desired token. * @dev The `Equipment` struct consists of the following data: * [ * assetId, * childAssetId, * childId, * childEquippableAddress * ] * @param tokenId ID of the token for which we are retrieving the equipped object * @param targetCatalogAddress Address of the `Catalog` associated with the `Slot` part of the token * @param slotPartId ID of the `Slot` part that we are checking for equipped objects * @return The `Equipment` struct containing data about the equipped object */ function getEquipment( uint256 tokenId, address targetCatalogAddress, uint64 slotPartId ) external view returns (Equipment memory); /** * @notice Used to get the asset and equippable data associated with given `assetId`. * @param tokenId ID of the token for which to retrieve the asset * @param assetId ID of the asset of which we are retrieving * @return metadataURI The metadata URI of the asset * @return equippableGroupId ID of the equippable group this asset belongs to * @return catalogAddress The address of the catalog the part belongs to * @return partIds An array of IDs of parts included in the asset */ function getAssetAndEquippableData( uint256 tokenId, uint64 assetId ) external view returns ( string memory metadataURI, uint64 equippableGroupId, address catalogAddress, uint64[] memory partIds ); }
// SPDX-License-Identifier: Apache-2.0 //Generally all interactions should propagate downstream pragma solidity ^0.8.18; import "../catalog/IRMRKCatalog.sol"; import "../library/RMRKLib.sol"; import "../multiasset/AbstractMultiAsset.sol"; import "../nestable/RMRKNestable.sol"; import "../security/ReentrancyGuard.sol"; import "./IERC6220.sol"; /** * @title RMRKEquippable * @author RMRK team * @notice Smart contract of the RMRK Equippable module. */ contract RMRKEquippable is ReentrancyGuard, RMRKNestable, AbstractMultiAsset, IERC6220 { using RMRKLib for uint64[]; // ------------------- ASSETS -------------- // ------------------- ASSET APPROVALS -------------- /** * @notice Mapping from token ID to approver address to approved address for assets. * @dev The approver is necessary so approvals are invalidated for nested children on transfer. * @dev WARNING: If a child NFT returns the original root owner, old permissions would be active again. */ mapping(uint256 => mapping(address => address)) private _tokenApprovalsForAssets; // ------------------- EQUIPPABLE -------------- /// Mapping of uint64 asset ID to corresponding catalog address. mapping(uint64 => address) private _catalogAddresses; /// Mapping of uint64 ID to asset object. mapping(uint64 => uint64) private _equippableGroupIds; /// Mapping of assetId to catalog parts applicable to this asset, both fixed and slot mapping(uint64 => uint64[]) private _partIds; /// Mapping of token ID to catalog address to slot part ID to equipment information. Used to compose an NFT. mapping(uint256 => mapping(address => mapping(uint64 => Equipment))) private _equipments; /// Mapping of token ID to child (nestable) address to child ID to count of equipped items. Used to check if equipped. mapping(uint256 => mapping(address => mapping(uint256 => uint256))) private _equipCountPerChild; /// Mapping of `equippableGroupId` to parent contract address and valid `slotId`. mapping(uint64 => mapping(address => uint64)) private _validParentSlots; /** * @notice Used to verify that the caller is either the owner of the given token or approved to manage the token's assets * of the owner. * @param tokenId ID of the token that we are checking */ function _onlyApprovedForAssetsOrOwner(uint256 tokenId) private view { if (!_isApprovedForAssetsOrOwner(_msgSender(), tokenId)) revert RMRKNotApprovedForAssetsOrOwner(); } /** * @notice Used to ensure that the caller is either the owner of the given token or approved to manage the token's assets * of the owner. * @dev If that is not the case, the execution of the function will be reverted. * @param tokenId ID of the token that we are checking */ modifier onlyApprovedForAssetsOrOwner(uint256 tokenId) { _onlyApprovedForAssetsOrOwner(tokenId); _; } // ----------------------------- CONSTRUCTOR ------------------------------ /** * @notice Initializes the contract by setting a `name` and a `symbol` of the token collection. * @param name_ Name of the token collection * @param symbol_ Symbol of the token collection */ constructor( string memory name_, string memory symbol_ ) RMRKNestable(name_, symbol_) {} /** * @inheritdoc IERC165 */ function supportsInterface( bytes4 interfaceId ) public view virtual override(IERC165, RMRKNestable) returns (bool) { return RMRKNestable.supportsInterface(interfaceId) || interfaceId == type(IERC5773).interfaceId || interfaceId == type(IERC6220).interfaceId; } // ------------------------------- ASSETS ------------------------------ // --------------------------- ASSET HANDLERS ------------------------- /** * @notice Accepts a asset at from the pending array of given token. * @dev Migrates the asset from the token's pending asset array to the token's active asset array. * @dev Active assets cannot be removed by anyone, but can be replaced by a new asset. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * - `index` must be in range of the length of the pending asset array. * @dev Emits an {AssetAccepted} event. * @param tokenId ID of the token for which to accept the pending asset * @param index Index of the asset in the pending array to accept * @param assetId ID of the asset that is being accepted */ function acceptAsset( uint256 tokenId, uint256 index, uint64 assetId ) public virtual onlyApprovedForAssetsOrOwner(tokenId) { _acceptAsset(tokenId, index, assetId); } /** * @notice Rejects a asset from the pending array of given token. * @dev Removes the asset from the token's pending asset array. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * - `index` must be in range of the length of the pending asset array. * @dev Emits a {AssetRejected} event. * @param tokenId ID of the token that the asset is being rejected from * @param index Index of the asset in the pending array to be rejected * @param assetId ID of the asset that is being rejected */ function rejectAsset( uint256 tokenId, uint256 index, uint64 assetId ) public virtual onlyApprovedForAssetsOrOwner(tokenId) { _rejectAsset(tokenId, index, assetId); } /** * @notice Rejects all assets from the pending array of a given token. * @dev Effecitvely deletes the pending array. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * @dev Emits a {AssetRejected} event with assetId = 0. * @param tokenId ID of the token of which to clear the pending array. * @param maxRejections Maximum number of expected assets to reject, used to prevent from rejecting assets which * arrive just before this operation. */ function rejectAllAssets( uint256 tokenId, uint256 maxRejections ) public virtual onlyApprovedForAssetsOrOwner(tokenId) { _rejectAllAssets(tokenId, maxRejections); } /** * @notice Sets a new priority array for a given token. * @dev The priority array is a non-sequential list of `uint64`s, where the lowest value is considered highest * priority. * @dev Value `0` of a priority is a special case equivalent to unitialized. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * - The length of `priorities` must be equal the length of the active assets array. * @dev Emits a {AssetPrioritySet} event. * @param tokenId ID of the token to set the priorities for * @param priorities An array of priority values */ function setPriority( uint256 tokenId, uint64[] calldata priorities ) public virtual onlyApprovedForAssetsOrOwner(tokenId) { _setPriority(tokenId, priorities); } // --------------------------- ASSET INTERNALS ------------------------- /** * @notice Used to add a asset entry. * @dev This internal function warrants custom access control to be implemented when used. * @param id ID of the asset being added * @param equippableGroupId ID of the equippable group being marked as equippable into the slot associated with * `Parts` of the `Slot` type * @param catalogAddress Address of the `Catalog` associated with the asset * @param metadataURI The metadata URI of the asset * @param partIds An array of IDs of fixed and slot parts to be included in the asset */ function _addAssetEntry( uint64 id, uint64 equippableGroupId, address catalogAddress, string memory metadataURI, uint64[] memory partIds ) internal virtual { _addAssetEntry(id, metadataURI); if (catalogAddress == address(0) && partIds.length != 0) revert RMRKCatalogRequiredForParts(); _catalogAddresses[id] = catalogAddress; _equippableGroupIds[id] = equippableGroupId; _partIds[id] = partIds; } // ----------------------- ASSET APPROVALS ------------------------ /** * @notice Used to grant approvals for specific tokens to a specified address. * @dev This can only be called by the owner of the token or by an account that has been granted permission to * manage all of the owner's assets. * @param to Address of the account to receive the approval to the specified token * @param tokenId ID of the token for which we are granting the permission */ function approveForAssets(address to, uint256 tokenId) public virtual { address owner = ownerOf(tokenId); if (to == owner) revert RMRKApprovalForAssetsToCurrentOwner(); if ( _msgSender() != owner && !isApprovedForAllForAssets(owner, _msgSender()) ) revert RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll(); _approveForAssets(to, tokenId); } /** * @notice Used to get the address of the user that is approved to manage the specified token from the current * owner. * @param tokenId ID of the token we are checking * @return Address of the account that is approved to manage the token */ function getApprovedForAssets( uint256 tokenId ) public view virtual returns (address) { _requireMinted(tokenId); return _tokenApprovalsForAssets[tokenId][ownerOf(tokenId)]; } /** * @notice Internal function to check whether the queried user is either: * 1. The root owner of the token associated with `tokenId`. * 2. Is approved for all assets of the current owner via the `setApprovalForAllForAssets` function. * 3. Is granted approval for the specific tokenId for asset management via the `approveForAssets` function. * @param user Address of the user we are checking for permission * @param tokenId ID of the token to query for permission for a given `user` * @return A boolean value indicating whether the user is approved to manage the token or not */ function _isApprovedForAssetsOrOwner( address user, uint256 tokenId ) internal view virtual returns (bool) { address owner = ownerOf(tokenId); return (user == owner || isApprovedForAllForAssets(owner, user) || getApprovedForAssets(tokenId) == user); } /** * @notice Internal function for granting approvals for a specific token. * @param to Address of the account we are granting an approval to * @param tokenId ID of the token we are granting the approval for */ function _approveForAssets(address to, uint256 tokenId) internal virtual { address owner = ownerOf(tokenId); _tokenApprovalsForAssets[tokenId][owner] = to; emit ApprovalForAssets(owner, to, tokenId); } /** * @notice Used to clear the approvals on a given token. * @param tokenId ID of the token we are clearing the approvals of */ function _cleanApprovals(uint256 tokenId) internal virtual override { _approveForAssets(address(0), tokenId); } // ------------------------------- EQUIPPING ------------------------------ /** * @inheritdoc RMRKNestable */ function _transferChild( uint256 tokenId, address to, uint256 destinationId, uint256 childIndex, address childAddress, uint256 childId, bool isPending, bytes memory data ) internal virtual override { if (!isPending) { if (isChildEquipped(tokenId, childAddress, childId)) revert RMRKMustUnequipFirst(); } super._transferChild( tokenId, to, destinationId, childIndex, childAddress, childId, isPending, data ); } /** * @inheritdoc IERC6220 */ function equip( IntakeEquip memory data ) public virtual onlyApprovedOrOwner(data.tokenId) nonReentrant { _equip(data); } /** * @notice Private function used to equip a child into a token. * @dev If the `Slot` already has an item equipped, the execution will be reverted. * @dev If the child can't be used in the given `Slot`, the execution will be reverted. * @dev If the catalog doesn't allow this equip to happen, the execution will be reverted. * @dev The `IntakeEquip` stuct contains the following data: * [ * tokenId, * childIndex, * assetId, * slotPartId, * childAssetId * ] * @dev Emits ***ChildAssetEquipped*** event. * @param data An `IntakeEquip` struct specifying the equip data */ function _equip(IntakeEquip memory data) internal virtual { address catalogAddress = _catalogAddresses[data.assetId]; uint64 slotPartId = data.slotPartId; if ( _equipments[data.tokenId][catalogAddress][slotPartId] .childEquippableAddress != address(0) ) revert RMRKSlotAlreadyUsed(); // Check from parent's asset perspective: _checkAssetAcceptsSlot(data.assetId, slotPartId); IERC6059.Child memory child = childOf(data.tokenId, data.childIndex); // Check from child perspective intention to be used in part // We add reentrancy guard because of this call, it happens before updating state if ( !IERC6220(child.contractAddress) .canTokenBeEquippedWithAssetIntoSlot( address(this), child.tokenId, data.childAssetId, slotPartId ) ) revert RMRKTokenCannotBeEquippedWithAssetIntoSlot(); // Check from catalog perspective if ( !IRMRKCatalog(catalogAddress).checkIsEquippable( slotPartId, child.contractAddress ) ) revert RMRKEquippableEquipNotAllowedByCatalog(); _beforeEquip(data); Equipment memory newEquip = Equipment({ assetId: data.assetId, childAssetId: data.childAssetId, childId: child.tokenId, childEquippableAddress: child.contractAddress }); _equipments[data.tokenId][catalogAddress][slotPartId] = newEquip; _equipCountPerChild[data.tokenId][child.contractAddress][ child.tokenId ] += 1; emit ChildAssetEquipped( data.tokenId, data.assetId, slotPartId, child.tokenId, child.contractAddress, data.childAssetId ); _afterEquip(data); } /** * @notice Private function to check if a given asset accepts a given slot or not. * @dev Execution will be reverted if the `Slot` does not apply for the asset. * @param assetId ID of the asset * @param slotPartId ID of the `Slot` */ function _checkAssetAcceptsSlot( uint64 assetId, uint64 slotPartId ) private view { (, bool found) = _partIds[assetId].indexOf(slotPartId); if (!found) revert RMRKTargetAssetCannotReceiveSlot(); } /** * @inheritdoc IERC6220 */ function unequip( uint256 tokenId, uint64 assetId, uint64 slotPartId ) public virtual onlyApprovedOrOwner(tokenId) { _unequip(tokenId, assetId, slotPartId); } /** * @notice Private function used to unequip child from parent token. * @dev Emits ***ChildAssetUnequipped*** event. * @param tokenId ID of the parent from which the child is being unequipped * @param assetId ID of the parent's asset that contains the `Slot` into which the child is equipped * @param slotPartId ID of the `Slot` from which to unequip the child */ function _unequip( uint256 tokenId, uint64 assetId, uint64 slotPartId ) internal virtual { address targetCatalogAddress = _catalogAddresses[assetId]; Equipment memory equipment = _equipments[tokenId][targetCatalogAddress][ slotPartId ]; if (equipment.childEquippableAddress == address(0)) revert RMRKNotEquipped(); _beforeUnequip(tokenId, assetId, slotPartId); delete _equipments[tokenId][targetCatalogAddress][slotPartId]; _equipCountPerChild[tokenId][equipment.childEquippableAddress][ equipment.childId ] -= 1; emit ChildAssetUnequipped( tokenId, assetId, slotPartId, equipment.childId, equipment.childEquippableAddress, equipment.childAssetId ); _afterUnequip(tokenId, assetId, slotPartId); } /** * @inheritdoc IERC6220 */ function isChildEquipped( uint256 tokenId, address childAddress, uint256 childId ) public view virtual returns (bool) { return _equipCountPerChild[tokenId][childAddress][childId] != 0; } // --------------------- ADMIN VALIDATION --------------------- /** * @notice Internal function used to declare that the assets belonging to a given `equippableGroupId` are * equippable into the `Slot` associated with the `partId` of the collection at the specified `parentAddress`. * @dev Emits ***ValidParentEquippableGroupIdSet*** event. * @param equippableGroupId ID of the equippable group * @param parentAddress Address of the parent into which the equippable group can be equipped into * @param slotPartId ID of the `Slot` that the items belonging to the equippable group can be equipped into */ function _setValidParentForEquippableGroup( uint64 equippableGroupId, address parentAddress, uint64 slotPartId ) internal virtual { if (equippableGroupId == uint64(0) || slotPartId == uint64(0)) revert RMRKIdZeroForbidden(); _validParentSlots[equippableGroupId][parentAddress] = slotPartId; emit ValidParentEquippableGroupIdSet( equippableGroupId, slotPartId, parentAddress ); } /** * @inheritdoc IERC6220 */ function canTokenBeEquippedWithAssetIntoSlot( address parent, uint256 tokenId, uint64 assetId, uint64 slotId ) public view virtual returns (bool) { uint64 equippableGroupId = _equippableGroupIds[assetId]; uint64 equippableSlot = _validParentSlots[equippableGroupId][parent]; if (equippableSlot == slotId) { (, bool found) = getActiveAssets(tokenId).indexOf(assetId); return found; } return false; } // --------------------- Getting Extended Assets --------------------- /** * @inheritdoc IERC6220 */ function getAssetAndEquippableData( uint256 tokenId, uint64 assetId ) public view virtual returns (string memory, uint64, address, uint64[] memory) { return ( getAssetMetadata(tokenId, assetId), _equippableGroupIds[assetId], _catalogAddresses[assetId], _partIds[assetId] ); } //////////////////////////////////////// // UTILS //////////////////////////////////////// /** * @inheritdoc IERC6220 */ function getEquipment( uint256 tokenId, address targetCatalogAddress, uint64 slotPartId ) public view virtual returns (Equipment memory) { return _equipments[tokenId][targetCatalogAddress][slotPartId]; } // HOOKS /** * @notice A hook to be called before a equipping a asset to the token. * @dev The `IntakeEquip` struct consist of the following data: * [ * tokenId, * childIndex, * assetId, * slotPartId, * childAssetId * ] * @param data The `IntakeEquip` struct containing data of the asset that is being equipped */ function _beforeEquip(IntakeEquip memory data) internal virtual {} /** * @notice A hook to be called after equipping a asset to the token. * @dev The `IntakeEquip` struct consist of the following data: * [ * tokenId, * childIndex, * assetId, * slotPartId, * childAssetId * ] * @param data The `IntakeEquip` struct containing data of the asset that was equipped */ function _afterEquip(IntakeEquip memory data) internal virtual {} /** * @notice A hook to be called before unequipping a asset from the token. * @param tokenId ID of the token from which the asset is being unequipped * @param assetId ID of the asset being unequipped * @param slotPartId ID of the slot from which the asset is being unequipped */ function _beforeUnequip( uint256 tokenId, uint64 assetId, uint64 slotPartId ) internal virtual {} /** * @notice A hook to be called after unequipping a asset from the token. * @param tokenId ID of the token from which the asset was unequipped * @param assetId ID of the asset that was unequipped * @param slotPartId ID of the slot from which the asset was unequipped */ function _afterUnequip( uint256 tokenId, uint64 assetId, uint64 slotPartId ) internal virtual {} }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/interfaces/IERC2981.sol"; import "../library/RMRKErrors.sol"; /** * @title RMRKRoyalties * @author RMRK team * @notice Smart contract of the RMRK Royalties module. */ abstract contract RMRKRoyalties is IERC2981 { address private _royaltyRecipient; uint256 private _royaltyPercentageBps; /** * @notice Used to initiate the smart contract. * @dev `royaltyPercentageBps` is expressed in basis points, so 1 basis point equals 0.01% and 500 basis points * equal 5%. * @param royaltyRecipient Address to which royalties should be sent * @param royaltyPercentageBps The royalty percentage expressed in basis points */ constructor( address royaltyRecipient, uint256 royaltyPercentageBps //in basis points ) { _setRoyaltyRecipient(royaltyRecipient); if (royaltyPercentageBps >= 10000) revert RMRKRoyaltiesTooHigh(); _royaltyPercentageBps = royaltyPercentageBps; } /** * @notice Used to update recipient of royalties. * @dev Custom access control has to be implemented to ensure that only the intended actors can update the * beneficiary. * @param newRoyaltyRecipient Address of the new recipient of royalties */ function updateRoyaltyRecipient( address newRoyaltyRecipient ) external virtual; /** * @notice Used to update the royalty recipient. * @param newRoyaltyRecipient Address of the new recipient of royalties */ function _setRoyaltyRecipient(address newRoyaltyRecipient) internal { _royaltyRecipient = newRoyaltyRecipient; } /** * @notice Used to retrieve the recipient of royalties. * @return Address of the recipient of royalties */ function getRoyaltyRecipient() public view virtual returns (address) { return _royaltyRecipient; } /** * @notice Used to retrieve the specified royalty percentage. * @return The royalty percentage expressed in the basis points */ function getRoyaltyPercentage() public view virtual returns (uint256) { return _royaltyPercentageBps; } /** * @notice Used to retrieve the information about who shall receive royalties of a sale of the specified token and * how much they will be. * @param tokenId ID of the token for which the royalty info is being retrieved * @param salePrice Price of the token sale * @return receiver The beneficiary receiving royalties of the sale * @return royaltyAmount The value of the royalties recieved by the `receiver` from the sale */ function royaltyInfo( uint256 tokenId, uint256 salePrice ) external view virtual override returns (address receiver, uint256 royaltyAmount) { receiver = _royaltyRecipient; royaltyAmount = (salePrice * _royaltyPercentageBps) / 10000; } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title RMRKErrors /// @author RMRK team /// @notice A collection of errors used in the RMRK suite /// @dev Errors are kept in a centralised file in order to provide a central point of reference and to avoid error /// naming collisions due to inheritance /// Attempting to grant the token to 0x0 address error ERC721AddressZeroIsNotaValidOwner(); /// Attempting to grant approval to the current owner of the token error ERC721ApprovalToCurrentOwner(); /// Attempting to grant approval when not being owner or approved for all should not be permitted error ERC721ApproveCallerIsNotOwnerNorApprovedForAll(); /// Attempting to get approvals for a token owned by 0x0 (considered non-existent) error ERC721ApprovedQueryForNonexistentToken(); /// Attempting to grant approval to self error ERC721ApproveToCaller(); /// Attempting to use an invalid token ID error ERC721InvalidTokenId(); /// Attempting to mint to 0x0 address error ERC721MintToTheZeroAddress(); /// Attempting to manage a token without being its owner or approved by the owner error ERC721NotApprovedOrOwner(); /// Attempting to mint an already minted token error ERC721TokenAlreadyMinted(); /// Attempting to transfer the token from an address that is not the owner error ERC721TransferFromIncorrectOwner(); /// Attempting to safe transfer to an address that is unable to receive the token error ERC721TransferToNonReceiverImplementer(); /// Attempting to transfer the token to a 0x0 address error ERC721TransferToTheZeroAddress(); /// Attempting to grant approval of assets to their current owner error RMRKApprovalForAssetsToCurrentOwner(); /// Attempting to grant approval of assets without being the caller or approved for all error RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll(); /// Attempting to incorrectly configue a Catalog item error RMRKBadConfig(); /// Attempting to set the priorities with an array of length that doesn't match the length of active assets array error RMRKBadPriorityListLength(); /// Attempting to add an asset entry with `Part`s, without setting the `Catalog` address error RMRKCatalogRequiredForParts(); /// Attempting to transfer a soulbound (non-transferrable) token error RMRKCannotTransferSoulbound(); /// Attempting to accept a child that has already been accepted error RMRKChildAlreadyExists(); /// Attempting to interact with a child, using index that is higher than the number of children error RMRKChildIndexOutOfRange(); /// Attempting to find the index of a child token on a parent which does not own it. error RMRKChildNotFoundInParent(); /// Attempting to pass collaborator address array and collaborator permission array of different lengths error RMRKCollaboratorArraysNotEqualLength(); /// Attempting to register a collection that is already registered error RMRKCollectionAlreadyRegistered(); /// Attempting to manage or interact with colleciton that is not registered error RMRKCollectionNotRegistered(); /// Attempting to equip a `Part` with a child not approved by the Catalog error RMRKEquippableEquipNotAllowedByCatalog(); /// Attempting to use ID 0, which is not supported /// @dev The ID 0 in RMRK suite is reserved for empty values. Guarding against its use ensures the expected operation error RMRKIdZeroForbidden(); /// Attempting to interact with an asset, using index greater than number of assets error RMRKIndexOutOfRange(); /// Attempting to reclaim a child that can't be reclaimed error RMRKInvalidChildReclaim(); /// Attempting to interact with an end-user account when the contract account is expected error RMRKIsNotContract(); /// Attempting to interact with a contract that had its operation locked error RMRKLocked(); /// Attempting to add a pending child after the number of pending children has reached the limit (default limit is 128) error RMRKMaxPendingChildrenReached(); /// Attempting to add a pending asset after the number of pending assets has reached the limit (default limit is /// 128) error RMRKMaxPendingAssetsReached(); /// Attempting to burn a total number of recursive children higher than maximum set /// @param childContract Address of the collection smart contract in which the maximum number of recursive burns was reached /// @param childId ID of the child token at which the maximum number of recursive burns was reached error RMRKMaxRecursiveBurnsReached(address childContract, uint256 childId); /// Attempting to mint a number of tokens that would cause the total supply to be greater than maximum supply error RMRKMintOverMax(); /// Attempting to mint a nested token to a smart contract that doesn't support nesting error RMRKMintToNonRMRKNestableImplementer(); /// Attempting to transfer a child before it is unequipped error RMRKMustUnequipFirst(); /// Attempting to nest a child over the nestable limit (current limit is 100 levels of nesting) error RMRKNestableTooDeep(); /// Attempting to nest the token to own descendant, which would create a loop and leave the looped tokens in limbo error RMRKNestableTransferToDescendant(); /// Attempting to nest the token to a smart contract that doesn't support nesting error RMRKNestableTransferToNonRMRKNestableImplementer(); /// Attempting to nest the token into itself error RMRKNestableTransferToSelf(); /// Attempting to interact with an asset that can not be found error RMRKNoAssetMatchingId(); /// Attempting to manage an asset without owning it or having been granted permission by the owner to do so error RMRKNotApprovedForAssetsOrOwner(); /// Attempting to interact with a token without being its owner or having been granted permission by the /// owner to do so /// @dev When a token is nested, only the direct owner (NFT parent) can mange it. In that case, approved addresses are /// not allowed to manage it, in order to ensure the expected behaviour error RMRKNotApprovedOrDirectOwner(); /// Attempting to manage a collection without being the collection's collaborator error RMRKNotCollectionCollaborator(); /// Attemting to manage a collection without being the collection's issuer error RMRKNotCollectionIssuer(); /// Attempting to manage a collection without being the collection's issuer or collaborator error RMRKNotCollectionIssuerOrCollaborator(); /// Attempting to compose an asset wihtout having an associated Catalog error RMRKNotComposableAsset(); /// Attempting to unequip an item that isn't equipped error RMRKNotEquipped(); /// Attempting to interact with a management function without being the smart contract's owner error RMRKNotOwner(); /// Attempting to interact with a function without being the owner or contributor of the collection error RMRKNotOwnerOrContributor(); /// Attempting to manage a collection without being the specific address error RMRKNotSpecificAddress(); /// Attempting to manage a token without being its owner error RMRKNotTokenOwner(); /// Attempting to transfer the ownership to the 0x0 address error RMRKNewOwnerIsZeroAddress(); /// Attempting to assign a 0x0 address as a contributor error RMRKNewContributorIsZeroAddress(); /// Attemtping to use `Ownable` interface without implementing it error RMRKOwnableNotImplemented(); /// Attempting an operation requiring the token being nested, while it is not error RMRKParentIsNotNFT(); /// Attempting to add a `Part` with an ID that is already used error RMRKPartAlreadyExists(); /// Attempting to use a `Part` that doesn't exist error RMRKPartDoesNotExist(); /// Attempting to use a `Part` that is `Fixed` when `Slot` kind of `Part` should be used error RMRKPartIsNotSlot(); /// Attempting to interact with a pending child using an index greater than the size of pending array error RMRKPendingChildIndexOutOfRange(); /// Attempting to add an asset using an ID that has already been used error RMRKAssetAlreadyExists(); /// Attempting to equip an item into a slot that already has an item equipped error RMRKSlotAlreadyUsed(); /// Attempting to equip an item into a `Slot` that the target asset does not implement error RMRKTargetAssetCannotReceiveSlot(); /// Attempting to equip a child into a `Slot` and parent that the child's collection doesn't support error RMRKTokenCannotBeEquippedWithAssetIntoSlot(); /// Attempting to compose a NFT of a token without active assets error RMRKTokenDoesNotHaveAsset(); /// Attempting to determine the asset with the top priority on a token without assets error RMRKTokenHasNoAssets(); /// Attempting to accept or transfer a child which does not match the one at the specified index error RMRKUnexpectedChildId(); /// Attempting to reject all pending assets but more assets than expected are pending error RMRKUnexpectedNumberOfAssets(); /// Attempting to reject all pending children but children assets than expected are pending error RMRKUnexpectedNumberOfChildren(); /// Attempting to accept or reject an asset which does not match the one at the specified index error RMRKUnexpectedAssetId(); /// Attempting an operation expecting a parent to the token which is not the actual one error RMRKUnexpectedParent(); /// Attempting not to pass an empty array of equippable addresses when adding or setting the equippable addresses error RMRKZeroLengthIdsPassed(); /// Attempting to set the royalties to a value higher than 100% (10000 in base points) error RMRKRoyaltiesTooHigh();
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /** * @title RMRKLib * @author RMRK team * @notice RMRK library smart contract. */ library RMRKLib { /** * @notice Used to remove an item from the array using the specified index. * @dev The item is removed by replacing it with the last item and removing the last element. * @param array An array of items containing the item to be removed * @param index Index of the item to remove */ function removeItemByIndex(uint64[] storage array, uint256 index) internal { //Check to see if this is already gated by require in all calls require(index < array.length); array[index] = array[array.length - 1]; array.pop(); } /** * @notice Used to determine the index of the item in the array by spedifying its value. * @dev This was adapted from Cryptofin-Solidity `arrayUtils`. * @dev If the item is not found the index returned will equal `0`. * @param A The array containing the item to be found * @param a The value of the item to find the index of * @return The index of the item in the array * @return A boolean value specifying whether the item was found */ function indexOf( uint64[] memory A, uint64 a ) internal pure returns (uint256, bool) { uint256 length = A.length; for (uint256 i; i < length; ) { if (A[i] == a) { return (i, true); } unchecked { ++i; } } return (0, false); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "./IERC5773.sol"; import "../library/RMRKLib.sol"; import "@openzeppelin/contracts/utils/Context.sol"; import "../library/RMRKErrors.sol"; /** * @title AbstractMultiAsset * @author RMRK team * @notice Abstract Smart contract implementing most of the common logic for contracts implementing IERC5773 */ abstract contract AbstractMultiAsset is Context, IERC5773 { using RMRKLib for uint64[]; /// Mapping of uint64 Ids to asset metadata mapping(uint64 => string) private _assets; /// Mapping of tokenId to new asset, to asset to be replaced mapping(uint256 => mapping(uint64 => uint64)) private _assetReplacements; /// Mapping of tokenId to an array of active assets /// @dev Active recurses is unbounded, getting all would reach gas limit at around 30k items /// so we leave this as internal in case a custom implementation needs to implement pagination mapping(uint256 => uint64[]) internal _activeAssets; /// Mapping of tokenId to an array of pending assets mapping(uint256 => uint64[]) internal _pendingAssets; /// Mapping of tokenId to an array of priorities for active assets mapping(uint256 => uint64[]) internal _activeAssetPriorities; /// Mapping of tokenId to assetId to whether the token has this asset assigned mapping(uint256 => mapping(uint64 => bool)) private _tokenAssets; /// Mapping from owner to operator approvals for assets mapping(address => mapping(address => bool)) private _operatorApprovalsForAssets; /** * @inheritdoc IERC5773 */ function getAssetMetadata( uint256 tokenId, uint64 assetId ) public view virtual returns (string memory) { if (!_tokenAssets[tokenId][assetId]) revert RMRKTokenDoesNotHaveAsset(); return _assets[assetId]; } /** * @inheritdoc IERC5773 */ function getActiveAssets( uint256 tokenId ) public view virtual returns (uint64[] memory) { return _activeAssets[tokenId]; } /** * @inheritdoc IERC5773 */ function getPendingAssets( uint256 tokenId ) public view virtual returns (uint64[] memory) { return _pendingAssets[tokenId]; } /** * @inheritdoc IERC5773 */ function getActiveAssetPriorities( uint256 tokenId ) public view virtual returns (uint64[] memory) { return _activeAssetPriorities[tokenId]; } /** * @inheritdoc IERC5773 */ function getAssetReplacements( uint256 tokenId, uint64 newAssetId ) public view virtual returns (uint64) { return _assetReplacements[tokenId][newAssetId]; } /** * @inheritdoc IERC5773 */ function isApprovedForAllForAssets( address owner, address operator ) public view virtual returns (bool) { return _operatorApprovalsForAssets[owner][operator]; } /** * @inheritdoc IERC5773 */ function setApprovalForAllForAssets( address operator, bool approved ) public virtual { if (_msgSender() == operator) revert RMRKApprovalForAssetsToCurrentOwner(); _operatorApprovalsForAssets[_msgSender()][operator] = approved; emit ApprovalForAllForAssets(_msgSender(), operator, approved); } /** * @notice Used to accept a pending asset. * @dev The call is reverted if there is no pending asset at a given index. * @dev Emits ***AssetAccepted*** event. * @param tokenId ID of the token for which to accept the pending asset * @param index Index of the asset in the pending array to accept * @param assetId ID of the asset to accept in token's pending array */ function _acceptAsset( uint256 tokenId, uint256 index, uint64 assetId ) internal virtual { _validatePendingAssetAtIndex(tokenId, index, assetId); _beforeAcceptAsset(tokenId, index, assetId); uint64 replacesId = _assetReplacements[tokenId][assetId]; uint256 replaceIndex; bool replacefound; if (replacesId != uint64(0)) (replaceIndex, replacefound) = _activeAssets[tokenId].indexOf( replacesId ); if (replacefound) { // We don't want to remove and then push a new asset. // This way we also keep the priority of the original asset _activeAssets[tokenId][replaceIndex] = assetId; delete _tokenAssets[tokenId][replacesId]; } else { // We use the current size as next priority, by default priorities would be [0,1,2...] _activeAssetPriorities[tokenId].push( uint64(_activeAssets[tokenId].length) ); _activeAssets[tokenId].push(assetId); replacesId = uint64(0); } _removePendingAsset(tokenId, index, assetId); emit AssetAccepted(tokenId, assetId, replacesId); _afterAcceptAsset(tokenId, index, assetId); } /** * @notice Used to reject the specified asset from the pending array. * @dev The call is reverted if there is no pending asset at a given index. * @dev Emits ***AssetRejected*** event. * @param tokenId ID of the token that the asset is being rejected from * @param index Index of the asset in the pending array to be rejected * @param assetId ID of the asset expected to be in the index */ function _rejectAsset( uint256 tokenId, uint256 index, uint64 assetId ) internal virtual { _validatePendingAssetAtIndex(tokenId, index, assetId); _beforeRejectAsset(tokenId, index, assetId); _removePendingAsset(tokenId, index, assetId); delete _tokenAssets[tokenId][assetId]; emit AssetRejected(tokenId, assetId); _afterRejectAsset(tokenId, index, assetId); } /** * @notice Used to validate the index on the pending assets array * @dev The call is reverted if the index is out of range or the asset Id is not present at the index. * @param tokenId ID of the token that the asset is validated from * @param index Index of the asset in the pending array * @param assetId Id of the asset expected to be in the index */ function _validatePendingAssetAtIndex( uint256 tokenId, uint256 index, uint64 assetId ) private view { if (index >= _pendingAssets[tokenId].length) revert RMRKIndexOutOfRange(); if (assetId != _pendingAssets[tokenId][index]) revert RMRKUnexpectedAssetId(); } /** * @notice Used to remove the asset at the index on the pending assets array * @param tokenId ID of the token that the asset is being removed from * @param index Index of the asset in the pending array * @param assetId Id of the asset expected to be in the index */ function _removePendingAsset( uint256 tokenId, uint256 index, uint64 assetId ) private { _pendingAssets[tokenId].removeItemByIndex(index); delete _assetReplacements[tokenId][assetId]; } /** * @notice Used to reject all of the pending assets for the given token. * @dev When rejecting all assets, the pending array is indiscriminately cleared. * @dev If the number of pending assets is greater than the value of `maxRejections`, the exectuion will be * reverted. * @dev Emits ***AssetRejected*** event. * @param tokenId ID of the token to reject all of the pending assets. * @param maxRejections Maximum number of expected assets to reject, used to prevent from * rejecting assets which arrive just before this operation. */ function _rejectAllAssets( uint256 tokenId, uint256 maxRejections ) internal virtual { uint256 len = _pendingAssets[tokenId].length; if (len > maxRejections) revert RMRKUnexpectedNumberOfAssets(); _beforeRejectAllAssets(tokenId); for (uint256 i; i < len; ) { uint64 assetId = _pendingAssets[tokenId][i]; delete _assetReplacements[tokenId][assetId]; unchecked { ++i; } } delete (_pendingAssets[tokenId]); emit AssetRejected(tokenId, uint64(0)); _afterRejectAllAssets(tokenId); } /** * @notice Used to specify the priorities for a given token's active assets. * @dev If the length of the priorities array doesn't match the length of the active assets array, the execution * will be reverted. * @dev The position of the priority value in the array corresponds the position of the asset in the active * assets array it will be applied to. * @dev Emits ***AssetPrioritySet*** event. * @param tokenId ID of the token for which the priorities are being set * @param priorities Array of priorities for the assets */ function _setPriority( uint256 tokenId, uint64[] calldata priorities ) internal virtual { uint256 length = priorities.length; if (length != _activeAssets[tokenId].length) revert RMRKBadPriorityListLength(); _beforeSetPriority(tokenId, priorities); _activeAssetPriorities[tokenId] = priorities; emit AssetPrioritySet(tokenId); _afterSetPriority(tokenId, priorities); } /** * @notice Used to add an asset entry. * @dev If the specified ID is already used by another asset, the execution will be reverted. * @dev This internal function warrants custom access control to be implemented when used. * @dev Emits ***AssetSet*** event. * @param id ID of the asset to assign to the new asset * @param metadataURI Metadata URI of the asset */ function _addAssetEntry( uint64 id, string memory metadataURI ) internal virtual { if (id == uint64(0)) revert RMRKIdZeroForbidden(); if (bytes(_assets[id]).length > 0) revert RMRKAssetAlreadyExists(); _beforeAddAsset(id, metadataURI); _assets[id] = metadataURI; emit AssetSet(id); _afterAddAsset(id, metadataURI); } /** * @notice Used to add an asset to a token. * @dev If the given asset is already added to the token, the execution will be reverted. * @dev If the asset ID is invalid, the execution will be reverted. * @dev If the token already has the maximum amount of pending assets (128), the execution will be * reverted. * @dev Emits ***AssetAddedToTokens*** event. * @param tokenId ID of the token to add the asset to * @param assetId ID of the asset to add to the token * @param replacesAssetWithId ID of the asset to replace from the token's list of active assets */ function _addAssetToToken( uint256 tokenId, uint64 assetId, uint64 replacesAssetWithId ) internal virtual { if (_tokenAssets[tokenId][assetId]) revert RMRKAssetAlreadyExists(); if (bytes(_assets[assetId]).length == uint256(0)) revert RMRKNoAssetMatchingId(); if (_pendingAssets[tokenId].length >= 128) revert RMRKMaxPendingAssetsReached(); _beforeAddAssetToToken(tokenId, assetId, replacesAssetWithId); _tokenAssets[tokenId][assetId] = true; _pendingAssets[tokenId].push(assetId); if (replacesAssetWithId != uint64(0)) { _assetReplacements[tokenId][assetId] = replacesAssetWithId; } uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = tokenId; emit AssetAddedToTokens(tokenIds, assetId, replacesAssetWithId); _afterAddAssetToToken(tokenId, assetId, replacesAssetWithId); } /** * @notice Hook that is called before an asset is added. * @param id ID of the asset * @param metadataURI Metadata URI of the asset */ function _beforeAddAsset( uint64 id, string memory metadataURI ) internal virtual {} /** * @notice Hook that is called after an asset is added. * @param id ID of the asset * @param metadataURI Metadata URI of the asset */ function _afterAddAsset( uint64 id, string memory metadataURI ) internal virtual {} /** * @notice Hook that is called before adding an asset to a token's pending assets array. * @dev If the asset doesn't intend to replace another asset, the `replacesAssetWithId` value should be `0`. * @param tokenId ID of the token to which the asset is being added * @param assetId ID of the asset that is being added * @param replacesAssetWithId ID of the asset that this asset is attempting to replace */ function _beforeAddAssetToToken( uint256 tokenId, uint64 assetId, uint64 replacesAssetWithId ) internal virtual {} /** * @notice Hook that is called after an asset has been added to a token's pending assets array. * @dev If the asset doesn't intend to replace another asset, the `replacesAssetWithId` value should be `0`. * @param tokenId ID of the token to which the asset is has been added * @param assetId ID of the asset that is has been added * @param replacesAssetWithId ID of the asset that this asset is attempting to replace */ function _afterAddAssetToToken( uint256 tokenId, uint64 assetId, uint64 replacesAssetWithId ) internal virtual {} /** * @notice Hook that is called before an asset is accepted to a token's active assets array. * @param tokenId ID of the token for which the asset is being accepted * @param index Index of the asset in the token's pending assets array * @param assetId ID of the asset expected to be located at the specified `index` */ function _beforeAcceptAsset( uint256 tokenId, uint256 index, uint64 assetId ) internal virtual {} /** * @notice Hook that is called after an asset is accepted to a token's active assets array. * @param tokenId ID of the token for which the asset has been accepted * @param index Index of the asset in the token's pending assets array * @param assetId ID of the asset expected to have been located at the specified `index` */ function _afterAcceptAsset( uint256 tokenId, uint256 index, uint64 assetId ) internal virtual {} /** * @notice Hook that is called before rejecting an asset. * @param tokenId ID of the token from which the asset is being rejected * @param index Index of the asset in the token's pending assets array * @param assetId ID of the asset expected to be located at the specified `index` */ function _beforeRejectAsset( uint256 tokenId, uint256 index, uint64 assetId ) internal virtual {} /** * @notice Hook that is called after rejecting an asset. * @param tokenId ID of the token from which the asset has been rejected * @param index Index of the asset in the token's pending assets array * @param assetId ID of the asset expected to have been located at the specified `index` */ function _afterRejectAsset( uint256 tokenId, uint256 index, uint64 assetId ) internal virtual {} /** * @notice Hook that is called before rejecting all assets of a token. * @param tokenId ID of the token from which all of the assets are being rejected */ function _beforeRejectAllAssets(uint256 tokenId) internal virtual {} /** * @notice Hook that is called after rejecting all assets of a token. * @param tokenId ID of the token from which all of the assets have been rejected */ function _afterRejectAllAssets(uint256 tokenId) internal virtual {} /** * @notice Hook that is called before the priorities for token's assets is set. * @param tokenId ID of the token for which the asset priorities are being set * @param priorities[] An array of priorities for token's active resources */ function _beforeSetPriority( uint256 tokenId, uint64[] calldata priorities ) internal virtual {} /** * @notice Hook that is called after the priorities for token's assets is set. * @param tokenId ID of the token for which the asset priorities have been set * @param priorities[] An array of priorities for token's active resources */ function _afterSetPriority( uint256 tokenId, uint64[] calldata priorities ) internal virtual {} }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title IERC5773 * @author RMRK team * @notice Interface smart contract of the RMRK multi asset module. */ interface IERC5773 is IERC165 { /** * @notice Used to notify listeners that an asset object is initialized at `assetId`. * @param assetId ID of the asset that was initialized */ event AssetSet(uint64 indexed assetId); /** * @notice Used to notify listeners that an asset object at `assetId` is added to token's pending asset * array. * @param tokenIds An array of token IDs that received a new pending asset * @param assetId ID of the asset that has been added to the token's pending assets array * @param replacesId ID of the asset that would be replaced */ event AssetAddedToTokens( uint256[] tokenIds, uint64 indexed assetId, uint64 indexed replacesId ); /** * @notice Used to notify listeners that an asset object at `assetId` is accepted by the token and migrated * from token's pending assets array to active assets array of the token. * @param tokenId ID of the token that had a new asset accepted * @param assetId ID of the asset that was accepted * @param replacesId ID of the asset that was replaced */ event AssetAccepted( uint256 indexed tokenId, uint64 indexed assetId, uint64 indexed replacesId ); /** * @notice Used to notify listeners that an asset object at `assetId` is rejected from token and is dropped * from the pending assets array of the token. * @param tokenId ID of the token that had an asset rejected * @param assetId ID of the asset that was rejected */ event AssetRejected(uint256 indexed tokenId, uint64 indexed assetId); /** * @notice Used to notify listeners that token's prioritiy array is reordered. * @param tokenId ID of the token that had the asset priority array updated */ event AssetPrioritySet(uint256 indexed tokenId); /** * @notice Used to notify listeners that owner has granted an approval to the user to manage the assets of a * given token. * @dev Approvals must be cleared on transfer * @param owner Address of the account that has granted the approval for all token's assets * @param approved Address of the account that has been granted approval to manage the token's assets * @param tokenId ID of the token on which the approval was granted */ event ApprovalForAssets( address indexed owner, address indexed approved, uint256 indexed tokenId ); /** * @notice Used to notify listeners that owner has granted approval to the user to manage assets of all of their * tokens. * @param owner Address of the account that has granted the approval for all assets on all of their tokens * @param operator Address of the account that has been granted the approval to manage the token's assets on all of * the tokens * @param approved Boolean value signifying whether the permission has been granted (`true`) or revoked (`false`) */ event ApprovalForAllForAssets( address indexed owner, address indexed operator, bool approved ); /** * @notice Accepts an asset at from the pending array of given token. * @dev Migrates the asset from the token's pending asset array to the token's active asset array. * @dev Active assets cannot be removed by anyone, but can be replaced by a new asset. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * - `index` must be in range of the length of the pending asset array. * @dev Emits an {AssetAccepted} event. * @param tokenId ID of the token for which to accept the pending asset * @param index Index of the asset in the pending array to accept * @param assetId ID of the asset expected to be in the index */ function acceptAsset( uint256 tokenId, uint256 index, uint64 assetId ) external; /** * @notice Rejects an asset from the pending array of given token. * @dev Removes the asset from the token's pending asset array. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * - `index` must be in range of the length of the pending asset array. * @dev Emits a {AssetRejected} event. * @param tokenId ID of the token that the asset is being rejected from * @param index Index of the asset in the pending array to be rejected * @param assetId ID of the asset expected to be in the index */ function rejectAsset( uint256 tokenId, uint256 index, uint64 assetId ) external; /** * @notice Rejects all assets from the pending array of a given token. * @dev Effecitvely deletes the pending array. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * @dev Emits a {AssetRejected} event with assetId = 0. * @param tokenId ID of the token of which to clear the pending array. * @param maxRejections Maximum number of expected assets to reject, used to prevent from rejecting assets which * arrive just before this operation. */ function rejectAllAssets(uint256 tokenId, uint256 maxRejections) external; /** * @notice Sets a new priority array for a given token. * @dev The priority array is a non-sequential list of `uint64`s, where the lowest value is considered highest * priority. * @dev Value `0` of a priority is a special case equivalent to unitialized. * @dev Requirements: * * - The caller must own the token or be approved to manage the token's assets * - `tokenId` must exist. * - The length of `priorities` must be equal the length of the active assets array. * @dev Emits a {AssetPrioritySet} event. * @param tokenId ID of the token to set the priorities for * @param priorities An array of priorities of active assets. The succesion of items in the priorities array * matches that of the succesion of items in the active array */ function setPriority( uint256 tokenId, uint64[] calldata priorities ) external; /** * @notice Used to retrieve IDs of the active assets of given token. * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call * `getAssetMetadata(tokenId, assetId)`. * @dev You can safely get 10k * @param tokenId ID of the token to retrieve the IDs of the active assets * @return An array of active asset IDs of the given token */ function getActiveAssets( uint256 tokenId ) external view returns (uint64[] memory); /** * @notice Used to retrieve IDs of the pending assets of given token. * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call * `getAssetMetadata(tokenId, assetId)`. * @param tokenId ID of the token to retrieve the IDs of the pending assets * @return An array of pending asset IDs of the given token */ function getPendingAssets( uint256 tokenId ) external view returns (uint64[] memory); /** * @notice Used to retrieve the priorities of the active resoources of a given token. * @dev Asset priorities are a non-sequential array of uint64 values with an array size equal to active asset * priorites. * @param tokenId ID of the token for which to retrieve the priorities of the active assets * @return An array of priorities of the active assets of the given token */ function getActiveAssetPriorities( uint256 tokenId ) external view returns (uint64[] memory); /** * @notice Used to retrieve the asset that will be replaced if a given asset from the token's pending array * is accepted. * @dev Asset data is stored by reference, in order to access the data corresponding to the ID, call * `getAssetMetadata(tokenId, assetId)`. * @param tokenId ID of the token to check * @param newAssetId ID of the pending asset which will be accepted * @return ID of the asset which will be replaced */ function getAssetReplacements( uint256 tokenId, uint64 newAssetId ) external view returns (uint64); /** * @notice Used to fetch the asset metadata of the specified token's active asset with the given index. * @dev Assets are stored by reference mapping `_assets[assetId]`. * @dev Can be overriden to implement enumerate, fallback or other custom logic. * @param tokenId ID of the token from which to retrieve the asset metadata * @param assetId Asset Id, must be in the active assets array * @return The metadata of the asset belonging to the specified index in the token's active assets * array */ function getAssetMetadata( uint256 tokenId, uint64 assetId ) external view returns (string memory); // Approvals /** * @notice Used to grant permission to the user to manage token's assets. * @dev This differs from transfer approvals, as approvals are not cleared when the approved party accepts or * rejects an asset, or sets asset priorities. This approval is cleared on token transfer. * @dev Only a single account can be approved at a time, so approving the `0x0` address clears previous approvals. * @dev Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * @dev Emits an {ApprovalForAssets} event. * @param to Address of the account to grant the approval to * @param tokenId ID of the token for which the approval to manage the assets is granted */ function approveForAssets(address to, uint256 tokenId) external; /** * @notice Used to retrieve the address of the account approved to manage assets of a given token. * @dev Requirements: * * - `tokenId` must exist. * @param tokenId ID of the token for which to retrieve the approved address * @return Address of the account that is approved to manage the specified token's assets */ function getApprovedForAssets( uint256 tokenId ) external view returns (address); /** * @notice Used to add or remove an operator of assets for the caller. * @dev Operators can call {acceptAsset}, {rejectAsset}, {rejectAllAssets} or {setPriority} for any token * owned by the caller. * @dev Requirements: * * - The `operator` cannot be the caller. * @dev Emits an {ApprovalForAllForAssets} event. * @param operator Address of the account to which the operator role is granted or revoked from * @param approved The boolean value indicating whether the operator role is being granted (`true`) or revoked * (`false`) */ function setApprovalForAllForAssets( address operator, bool approved ) external; /** * @notice Used to check whether the address has been granted the operator role by a given address or not. * @dev See {setApprovalForAllForAssets}. * @param owner Address of the account that we are checking for whether it has granted the operator role * @param operator Address of the account that we are checking whether it has the operator role or not * @return A boolean value indicating wehter the account we are checking has been granted the operator role */ function isApprovedForAllForAssets( address owner, address operator ) external view returns (bool); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title IERC6059 * @author RMRK team * @notice Interface smart contract of the RMRK nestable module. */ interface IERC6059 is IERC165 { /** * @notice The core struct of RMRK ownership. * @dev The `DirectOwner` struct is used to store information of the next immediate owner, be it the parent token or * the externally owned account. * @dev If the token is owned by the externally owned account, the `tokenId` should equal `0`. * @param tokenId ID of the parent token * @param ownerAddress Address of the owner of the token. If the owner is another token, then the address should be * the one of the parent token's collection smart contract. If the owner is externally owned account, the address * should be the address of this account * @param isNft A boolean value signifying whether the token is owned by another token (`true`) or by an externally * owned account (`false`) */ struct DirectOwner { uint256 tokenId; address ownerAddress; } /** * @notice Used to notify listeners that the token is being transferred. * @dev Emitted when `tokenId` token is transferred from `from` to `to`. * @param from Address of the previous immediate owner, which is a smart contract if the token was nested. * @param to Address of the new immediate owner, which is a smart contract if the token is being nested. * @param fromTokenId ID of the previous parent token. If the token was not nested before, the value should be `0` * @param toTokenId ID of the new parent token. If the token is not being nested, the value should be `0` * @param tokenId ID of the token being transferred */ event NestTransfer( address indexed from, address indexed to, uint256 fromTokenId, uint256 toTokenId, uint256 indexed tokenId ); /** * @notice Used to notify listeners that a new token has been added to a given token's pending children array. * @dev Emitted when a child NFT is added to a token's pending array. * @param tokenId ID of the token that received a new pending child token * @param childIndex Index of the proposed child token in the parent token's pending children array * @param childAddress Address of the proposed child token's collection smart contract * @param childId ID of the child token in the child token's collection smart contract */ event ChildProposed( uint256 indexed tokenId, uint256 childIndex, address indexed childAddress, uint256 indexed childId ); /** * @notice Used to notify listeners that a new child token was accepted by the parent token. * @dev Emitted when a parent token accepts a token from its pending array, migrating it to the active array. * @param tokenId ID of the token that accepted a new child token * @param childIndex Index of the newly accepted child token in the parent token's active children array * @param childAddress Address of the child token's collection smart contract * @param childId ID of the child token in the child token's collection smart contract */ event ChildAccepted( uint256 indexed tokenId, uint256 childIndex, address indexed childAddress, uint256 indexed childId ); /** * @notice Used to notify listeners that all pending child tokens of a given token have been rejected. * @dev Emitted when a token removes all a child tokens from its pending array. * @param tokenId ID of the token that rejected all of the pending children */ event AllChildrenRejected(uint256 indexed tokenId); /** * @notice Used to notify listeners a child token has been transferred from parent token. * @dev Emitted when a token transfers a child from itself, transferring ownership to the root owner. * @param tokenId ID of the token that transferred a child token * @param childIndex Index of a child in the array from which it is being transferred * @param childAddress Address of the child token's collection smart contract * @param childId ID of the child token in the child token's collection smart contract * @param fromPending A boolean value signifying whether the token was in the pending child tokens array (`true`) or * in the active child tokens array (`false`) * @param toZero A boolean value signifying whether the token is being transferred to the `0x0` address (`true`) or * not (`false`) */ event ChildTransferred( uint256 indexed tokenId, uint256 childIndex, address indexed childAddress, uint256 indexed childId, bool fromPending, bool toZero ); /** * @notice The core child token struct, holding the information about the child tokens. * @return tokenId ID of the child token in the child token's collection smart contract * @return contractAddress Address of the child token's smart contract */ struct Child { uint256 tokenId; address contractAddress; } /** * @notice Used to retrieve the *root* owner of a given token. * @dev The *root* owner of the token is an externally owned account (EOA). If the given token is child of another * NFT, this will return an EOA address. Otherwise, if the token is owned by an EOA, this EOA wil be returned. * @param tokenId ID of the token for which the *root* owner has been retrieved * @return owner The *root* owner of the token */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @notice Used to retrieve the immediate owner of the given token. * @dev If the immediate owner is another token, the address returned, should be the one of the parent token's * collection smart contract. * @param tokenId ID of the token for which the RMRK owner is being retrieved * @return Address of the given token's owner * @return The ID of the parent token. Should be `0` if the owner is an externally owned account * @return The boolean value signifying whether the owner is an NFT or not */ function directOwnerOf( uint256 tokenId ) external view returns (address, uint256, bool); /** * @notice Used to burn a given token. * @dev When a token is burned, all of its child tokens are recursively burned as well. * @dev When specifying the maximum recursive burns, the execution will be reverted if there are more children to be * burned. * @dev Setting the `maxRecursiveBurn` value to 0 will only attempt to burn the specified token and revert if there * are any child tokens present. * @dev The approvals are cleared when the token is burned. * @dev Requirements: * * - `tokenId` must exist. * @dev Emits a {Transfer} event. * @param tokenId ID of the token to burn * @param maxRecursiveBurns Maximum number of tokens to recursively burn * @return Number of recursively burned children */ function burn( uint256 tokenId, uint256 maxRecursiveBurns ) external returns (uint256); /** * @notice Used to add a child token to a given parent token. * @dev This adds the child token into the given parent token's pending child tokens array. * @dev Requirements: * * - `directOwnerOf` on the child contract must resolve to the called contract. * - the pending array of the parent contract must not be full. * @param parentId ID of the parent token to receive the new child token * @param childId ID of the new proposed child token * @param data Additional data with no specified format */ function addChild( uint256 parentId, uint256 childId, bytes memory data ) external; /** * @notice Used to accept a pending child token for a given parent token. * @dev This moves the child token from parent token's pending child tokens array into the active child tokens * array. * @param parentId ID of the parent token for which the child token is being accepted * @param childIndex Index of a child tokem in the given parent's pending children array * @param childAddress Address of the collection smart contract of the child token expected to be located at the * specified index of the given parent token's pending children array * @param childId ID of the child token expected to be located at the specified index of the given parent token's * pending children array */ function acceptChild( uint256 parentId, uint256 childIndex, address childAddress, uint256 childId ) external; /** * @notice Used to reject all pending children of a given parent token. * @dev Removes the children from the pending array mapping. * @dev This does not update the ownership storage data on children. If necessary, ownership can be reclaimed by the * rootOwner of the previous parent. * @dev Requirements: * * Requirements: * * - `parentId` must exist * @param parentId ID of the parent token for which to reject all of the pending tokens. * @param maxRejections Maximum number of expected children to reject, used to prevent from rejecting children which * arrive just before this operation. */ function rejectAllChildren( uint256 parentId, uint256 maxRejections ) external; /** * @notice Used to transfer a child token from a given parent token. * @dev When transferring a child token, the owner of the token is set to `to`, or is not updated in the event of * `to` being the `0x0` address. * @param tokenId ID of the parent token from which the child token is being transferred * @param to Address to which to transfer the token to * @param destinationId ID of the token to receive this child token (MUST be 0 if the destination is not a token) * @param childIndex Index of a token we are transferring, in the array it belongs to (can be either active array or * pending array) * @param childAddress Address of the child token's collection smart contract. * @param childId ID of the child token in its own collection smart contract. * @param isPending A boolean value indicating whether the child token being transferred is in the pending array of * the parent token (`true`) or in the active array (`false`) * @param data Additional data with no specified format, sent in call to `_to` */ function transferChild( uint256 tokenId, address to, uint256 destinationId, uint256 childIndex, address childAddress, uint256 childId, bool isPending, bytes memory data ) external; /** * @notice Used to retrieve the active child tokens of a given parent token. * @dev Returns array of Child structs existing for parent token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @param parentId ID of the parent token for which to retrieve the active child tokens * @return An array of Child structs containing the parent token's active child tokens */ function childrenOf( uint256 parentId ) external view returns (Child[] memory); /** * @notice Used to retrieve the pending child tokens of a given parent token. * @dev Returns array of pending Child structs existing for given parent. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @param parentId ID of the parent token for which to retrieve the pending child tokens * @return An array of Child structs containing the parent token's pending child tokens */ function pendingChildrenOf( uint256 parentId ) external view returns (Child[] memory); /** * @notice Used to retrieve a specific active child token for a given parent token. * @dev Returns a single Child struct locating at `index` of parent token's active child tokens array. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @param parentId ID of the parent token for which the child is being retrieved * @param index Index of the child token in the parent token's active child tokens array * @return A Child struct containing data about the specified child */ function childOf( uint256 parentId, uint256 index ) external view returns (Child memory); /** * @notice Used to retrieve a specific pending child token from a given parent token. * @dev Returns a single Child struct locating at `index` of parent token's active child tokens array. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @param parentId ID of the parent token for which the pending child token is being retrieved * @param index Index of the child token in the parent token's pending child tokens array * @return A Child struct containting data about the specified child */ function pendingChildOf( uint256 parentId, uint256 index ) external view returns (Child memory); /** * @notice Used to transfer the token into another token. * @param from Address of the direct owner of the token to be transferred * @param to Address of the receiving token's collection smart contract * @param tokenId ID of the token being transferred * @param destinationId ID of the token to receive the token being transferred * @param data Additional data with no specified format, sent in the addChild call */ function nestTransferFrom( address from, address to, uint256 tokenId, uint256 destinationId, bytes memory data ) external; }
// SPDX-License-Identifier: Apache-2.0 //Generally all interactions should propagate downstream pragma solidity ^0.8.18; import "./IERC6059.sol"; import "../core/RMRKCore.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import "../library/RMRKErrors.sol"; /** * @title RMRKNestable * @author RMRK team * @notice Smart contract of the RMRK Nestable module. * @dev This contract is hierarchy agnostic and can support an arbitrary number of nested levels up and down, as long as * gas limits allow it. */ contract RMRKNestable is Context, IERC165, IERC721, IERC6059, RMRKCore { using Address for address; uint256 private constant _MAX_LEVELS_TO_CHECK_FOR_INHERITANCE_LOOP = 100; // Mapping owner address to token count mapping(address => uint256) private _balances; // Mapping from token ID to approver address to approved address // The approver is necessary so approvals are invalidated for nested children on transfer // WARNING: If a child NFT returns to a previous root owner, old permissions would be active again mapping(uint256 => mapping(address => address)) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; // ------------------- NESTABLE -------------- // Mapping from token ID to DirectOwner struct mapping(uint256 => DirectOwner) private _RMRKOwners; // Mapping of tokenId to array of active children structs mapping(uint256 => Child[]) internal _activeChildren; // Mapping of tokenId to array of pending children structs mapping(uint256 => Child[]) internal _pendingChildren; // Mapping of child token address to child token ID to whether they are pending or active on any token // We might have a first extra mapping from token ID, but since the same child cannot be nested into multiple tokens // we can strip it for size/gas savings. mapping(address => mapping(uint256 => uint256)) internal _childIsInActive; // -------------------------- MODIFIERS ---------------------------- /** * @notice Used to verify that the caller is either the owner of the token or approved to manage it by its owner. * @dev If the caller is not the owner of the token or approved to manage it by its owner, the execution will be * reverted. * @param tokenId ID of the token to check */ function _onlyApprovedOrOwner(uint256 tokenId) private view { if (!_isApprovedOrOwner(_msgSender(), tokenId)) revert ERC721NotApprovedOrOwner(); } /** * @notice Used to verify that the caller is either the owner of the token or approved to manage it by its owner. * @param tokenId ID of the token to check */ modifier onlyApprovedOrOwner(uint256 tokenId) { _onlyApprovedOrOwner(tokenId); _; } /** * @notice Used to verify that the caller is approved to manage the given token or it its direct owner. * @dev This does not delegate to ownerOf, which returns the root owner, but rater uses an owner from DirectOwner * struct. * @dev The execution is reverted if the caller is not immediate owner or approved to manage the given token. * @dev Used for parent-scoped transfers. * @param tokenId ID of the token to check. */ function _onlyApprovedOrDirectOwner(uint256 tokenId) private view { if (!_isApprovedOrDirectOwner(_msgSender(), tokenId)) revert RMRKNotApprovedOrDirectOwner(); } /** * @notice Used to verify that the caller is approved to manage the given token or is its direct owner. * @param tokenId ID of the token to check */ modifier onlyApprovedOrDirectOwner(uint256 tokenId) { _onlyApprovedOrDirectOwner(tokenId); _; } // ----------------------------- CONSTRUCTOR ------------------------------ /** * @notice Initializes the contract by setting a `name` and a `symbol` to the token collection. * @param name_ Name of the token collection * @param symbol_ Symbol of the token collection */ constructor( string memory name_, string memory symbol_ ) RMRKCore(name_, symbol_) {} // ------------------------------- ERC721 --------------------------------- /** * @inheritdoc IERC165 */ function supportsInterface( bytes4 interfaceId ) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId || interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || interfaceId == type(IERC6059).interfaceId; } /** * @notice Used to retrieve the number of tokens in `owner`'s account. * @param owner Address of the account being checked * @return The balance of the given account */ function balanceOf(address owner) public view virtual returns (uint256) { if (owner == address(0)) revert ERC721AddressZeroIsNotaValidOwner(); return _balances[owner]; } //////////////////////////////////////// // TRANSFERS //////////////////////////////////////// /** * @notice Transfers a given token from `from` to `to`. * @dev Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * @dev Emits a {Transfer} event. * @param from Address from which to transfer the token from * @param to Address to which to transfer the token to * @param tokenId ID of the token to transfer */ function transferFrom( address from, address to, uint256 tokenId ) public virtual onlyApprovedOrDirectOwner(tokenId) { _transfer(from, to, tokenId, ""); } /** * @notice Used to safely transfer a given token token from `from` to `to`. * @dev Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * @dev Emits a {Transfer} event. * @param from Address to transfer the tokens from * @param to Address to transfer the tokens to * @param tokenId ID of the token to transfer */ function safeTransferFrom( address from, address to, uint256 tokenId ) public virtual { safeTransferFrom(from, to, tokenId, ""); } /** * @notice Used to safely transfer a given token token from `from` to `to`. * @dev Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * @dev Emits a {Transfer} event. * @param from Address to transfer the tokens from * @param to Address to transfer the tokens to * @param tokenId ID of the token to transfer * @param data Additional data without a specified format to be sent along with the token transaction */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory data ) public virtual onlyApprovedOrDirectOwner(tokenId) { _safeTransfer(from, to, tokenId, data); } /** * @inheritdoc IERC6059 */ function nestTransferFrom( address from, address to, uint256 tokenId, uint256 destinationId, bytes memory data ) public virtual onlyApprovedOrDirectOwner(tokenId) { _nestTransfer(from, to, tokenId, destinationId, data); } /** * @notice Used to safely transfer the token form `from` to `to`. * @dev The function checks that contract recipients are aware of the ERC721 protocol to prevent tokens from being * forever locked. * @dev This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. implement alternative * mechanisms to perform token transfer, such as signature-based. * @dev Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * @dev Emits a {Transfer} event. * @param from Address of the account currently owning the given token * @param to Address to transfer the token to * @param tokenId ID of the token to transfer * @param data Additional data with no specified format, sent in call to `to` */ function _safeTransfer( address from, address to, uint256 tokenId, bytes memory data ) internal virtual { _transfer(from, to, tokenId, data); if (!_checkOnERC721Received(from, to, tokenId, data)) revert ERC721TransferToNonReceiverImplementer(); } /** * @notice Used to transfer the token from `from` to `to`. * @dev As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * @dev Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * @dev Emits a {Transfer} event. * @param from Address of the account currently owning the given token * @param to Address to transfer the token to * @param tokenId ID of the token to transfer * @param data Additional data with no specified format, sent in call to `to` */ function _transfer( address from, address to, uint256 tokenId, bytes memory data ) internal virtual { (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId); if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner(); if (to == address(0)) revert ERC721TransferToTheZeroAddress(); _beforeTokenTransfer(from, to, tokenId); _beforeNestedTokenTransfer( immediateOwner, to, parentId, 0, tokenId, data ); _balances[from] -= 1; _updateOwnerAndClearApprovals(tokenId, 0, to); _balances[to] += 1; emit Transfer(from, to, tokenId); emit NestTransfer(immediateOwner, to, parentId, 0, tokenId); _afterTokenTransfer(from, to, tokenId); _afterNestedTokenTransfer( immediateOwner, to, parentId, 0, tokenId, data ); } /** * @notice Used to transfer a token into another token. * @dev Attempting to nest a token into `0x0` address will result in reverted transaction. * @dev Attempting to nest a token into itself will result in reverted transaction. * @param from Address of the account currently owning the given token * @param to Address of the receiving token's collection smart contract * @param tokenId ID of the token to transfer * @param destinationId ID of the token receiving the given token * @param data Additional data with no specified format, sent in the addChild call */ function _nestTransfer( address from, address to, uint256 tokenId, uint256 destinationId, bytes memory data ) internal virtual { (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId); if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner(); if (to == address(0)) revert ERC721TransferToTheZeroAddress(); if (to == address(this) && tokenId == destinationId) revert RMRKNestableTransferToSelf(); // Destination contract checks: // It seems redundant, but otherwise it would revert with no error if (!to.isContract()) revert RMRKIsNotContract(); if (!IERC165(to).supportsInterface(type(IERC6059).interfaceId)) revert RMRKNestableTransferToNonRMRKNestableImplementer(); _checkForInheritanceLoop(tokenId, to, destinationId); _beforeTokenTransfer(from, to, tokenId); _beforeNestedTokenTransfer( immediateOwner, to, parentId, destinationId, tokenId, data ); _balances[from] -= 1; _updateOwnerAndClearApprovals(tokenId, destinationId, to); _balances[to] += 1; // Sending to NFT: _sendToNFT(immediateOwner, to, parentId, destinationId, tokenId, data); } /** * @notice Used to send a token to another token. * @dev If the token being sent is currently owned by an externally owned account, the `parentId` should equal `0`. * @dev Emits {Transfer} event. * @dev Emits {NestTransfer} event. * @param from Address from which the token is being sent * @param to Address of the collection smart contract of the token to receive the given token * @param parentId ID of the current parent token of the token being sent * @param destinationId ID of the tokento receive the token being sent * @param tokenId ID of the token being sent * @param data Additional data with no specified format, sent in the addChild call */ function _sendToNFT( address from, address to, uint256 parentId, uint256 destinationId, uint256 tokenId, bytes memory data ) private { IERC6059 destContract = IERC6059(to); destContract.addChild(destinationId, tokenId, data); emit Transfer(from, to, tokenId); emit NestTransfer(from, to, parentId, destinationId, tokenId); _afterTokenTransfer(from, to, tokenId); _afterNestedTokenTransfer( from, to, parentId, destinationId, tokenId, data ); } /** * @notice Used to check if nesting a given token into a specified token would create an inheritance loop. * @dev If a loop would occur, the tokens would be unmanageable, so the execution is reverted if one is detected. * @dev The check for inheritance loop is bounded to guard against too much gas being consumed. * @param currentId ID of the token that would be nested * @param targetContract Address of the collection smart contract of the token into which the given token would be * nested * @param targetId ID of the token into which the given token would be nested */ function _checkForInheritanceLoop( uint256 currentId, address targetContract, uint256 targetId ) private view { for (uint256 i; i < _MAX_LEVELS_TO_CHECK_FOR_INHERITANCE_LOOP; ) { ( address nextOwner, uint256 nextOwnerTokenId, bool isNft ) = IERC6059(targetContract).directOwnerOf(targetId); // If there's a final address, we're good. There's no loop. if (!isNft) { return; } // Ff the current nft is an ancestor at some point, there is an inheritance loop if (nextOwner == address(this) && nextOwnerTokenId == currentId) { revert RMRKNestableTransferToDescendant(); } // We reuse the parameters to save some contract size targetContract = nextOwner; targetId = nextOwnerTokenId; unchecked { ++i; } } revert RMRKNestableTooDeep(); } //////////////////////////////////////// // MINTING //////////////////////////////////////// /** * @notice Used to safely mint the token to the specified address while passing the additional data to contract * recipients. * @param to Address to which to mint the token * @param tokenId ID of the token to mint * @param data Additional data to send with the tokens */ function _safeMint( address to, uint256 tokenId, bytes memory data ) internal virtual { _mint(to, tokenId, data); if (!_checkOnERC721Received(address(0), to, tokenId, data)) revert ERC721TransferToNonReceiverImplementer(); } /** * @notice Used to mint a specified token to a given address. * @dev WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible. * @dev Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * @dev Emits a {Transfer} event. * @dev Emits a {NestTransfer} event. * @param to Address to mint the token to * @param tokenId ID of the token to mint * @param data Additional data with no specified format, sent in call to `to` */ function _mint( address to, uint256 tokenId, bytes memory data ) internal virtual { _innerMint(to, tokenId, 0, data); emit Transfer(address(0), to, tokenId); emit NestTransfer(address(0), to, 0, 0, tokenId); _afterTokenTransfer(address(0), to, tokenId); _afterNestedTokenTransfer(address(0), to, 0, 0, tokenId, data); } /** * @notice Used to mint a child token to a given parent token. * @param to Address of the collection smart contract of the token into which to mint the child token * @param tokenId ID of the token to mint * @param destinationId ID of the token into which to mint the new child token * @param data Additional data with no specified format, sent in the addChild call */ function _nestMint( address to, uint256 tokenId, uint256 destinationId, bytes memory data ) internal virtual { // It seems redundant, but otherwise it would revert with no error if (!to.isContract()) revert RMRKIsNotContract(); if (!IERC165(to).supportsInterface(type(IERC6059).interfaceId)) revert RMRKMintToNonRMRKNestableImplementer(); _innerMint(to, tokenId, destinationId, data); _sendToNFT(address(0), to, 0, destinationId, tokenId, data); } /** * @notice Used to mint a child token into a given parent token. * @dev Requirements: * * - `to` cannot be the zero address. * - `tokenId` must not exist. * - `tokenId` must not be `0`. * @param to Address of the collection smart contract of the token into which to mint the child token * @param tokenId ID of the token to mint * @param destinationId ID of the token into which to mint the new token * @param data Additional data with no specified format, sent in call to `to` */ function _innerMint( address to, uint256 tokenId, uint256 destinationId, bytes memory data ) private { if (to == address(0)) revert ERC721MintToTheZeroAddress(); if (_exists(tokenId)) revert ERC721TokenAlreadyMinted(); if (tokenId == uint256(0)) revert RMRKIdZeroForbidden(); _beforeTokenTransfer(address(0), to, tokenId); _beforeNestedTokenTransfer( address(0), to, 0, destinationId, tokenId, data ); _balances[to] += 1; _RMRKOwners[tokenId] = DirectOwner({ ownerAddress: to, tokenId: destinationId }); } //////////////////////////////////////// // Ownership //////////////////////////////////////// /** * @inheritdoc IERC6059 */ function ownerOf( uint256 tokenId ) public view virtual override(IERC6059, IERC721) returns (address) { (address owner, uint256 ownerTokenId, bool isNft) = directOwnerOf( tokenId ); if (isNft) { owner = IERC6059(owner).ownerOf(ownerTokenId); } return owner; } /** * @inheritdoc IERC6059 */ function directOwnerOf( uint256 tokenId ) public view virtual returns (address, uint256, bool) { DirectOwner memory owner = _RMRKOwners[tokenId]; if (owner.ownerAddress == address(0)) revert ERC721InvalidTokenId(); return (owner.ownerAddress, owner.tokenId, owner.tokenId != 0); } //////////////////////////////////////// // BURNING //////////////////////////////////////// /** * @notice Used to burn a given token. * @dev In case the token has any child tokens, the execution will be reverted. * @param tokenId ID of the token to burn */ function burn(uint256 tokenId) public virtual { burn(tokenId, 0); } /** * @inheritdoc IERC6059 */ function burn( uint256 tokenId, uint256 maxChildrenBurns ) public virtual onlyApprovedOrDirectOwner(tokenId) returns (uint256) { return _burn(tokenId, maxChildrenBurns); } /** * @notice Used to burn a token. * @dev When a token is burned, its children are recursively burned as well. * @dev The approvals are cleared when the token is burned. * @dev Requirements: * * - `tokenId` must exist. * @dev Emits a {Transfer} event. * @dev Emits a {NestTransfer} event. * @param tokenId ID of the token to burn * @param maxChildrenBurns Maximum children to recursively burn * @return The number of recursive burns it took to burn all of the children */ function _burn( uint256 tokenId, uint256 maxChildrenBurns ) internal virtual returns (uint256) { (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId); address owner = ownerOf(tokenId); _balances[immediateOwner] -= 1; _beforeTokenTransfer(owner, address(0), tokenId); _beforeNestedTokenTransfer( immediateOwner, address(0), parentId, 0, tokenId, "" ); _approve(address(0), tokenId); _cleanApprovals(tokenId); Child[] memory children = childrenOf(tokenId); delete _activeChildren[tokenId]; delete _pendingChildren[tokenId]; delete _tokenApprovals[tokenId][owner]; uint256 pendingRecursiveBurns; uint256 totalChildBurns; uint256 length = children.length; //gas savings for (uint256 i; i < length; ) { if (totalChildBurns >= maxChildrenBurns) revert RMRKMaxRecursiveBurnsReached( children[i].contractAddress, children[i].tokenId ); delete _childIsInActive[children[i].contractAddress][ children[i].tokenId ]; unchecked { // At this point we know pendingRecursiveBurns must be at least 1 pendingRecursiveBurns = maxChildrenBurns - totalChildBurns; } // We substract one to the next level to count for the token being burned, then add it again on returns // This is to allow the behavior of 0 recursive burns meaning only the current token is deleted. totalChildBurns += IERC6059(children[i].contractAddress).burn( children[i].tokenId, pendingRecursiveBurns - 1 ) + 1; unchecked { ++i; } } // Can't remove before burning child since child will call back to get root owner delete _RMRKOwners[tokenId]; emit Transfer(owner, address(0), tokenId); emit NestTransfer(immediateOwner, address(0), parentId, 0, tokenId); _afterTokenTransfer(owner, address(0), tokenId); _afterNestedTokenTransfer( immediateOwner, address(0), parentId, 0, tokenId, "" ); return totalChildBurns; } //////////////////////////////////////// // APPROVALS //////////////////////////////////////// /** * @notice Used to grant a one-time approval to manage one's token. * @dev Gives permission to `to` to transfer `tokenId` token to another account. * @dev The approval is cleared when the token is transferred. * @dev Only a single account can be approved at a time, so approving the zero address clears previous approvals. * @dev Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * @dev Emits an {Approval} event. * @param to Address receiving the approval * @param tokenId ID of the token for which the approval is being granted */ function approve(address to, uint256 tokenId) public virtual { address owner = ownerOf(tokenId); if (to == owner) revert ERC721ApprovalToCurrentOwner(); if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender())) revert ERC721ApproveCallerIsNotOwnerNorApprovedForAll(); _approve(to, tokenId); } /** * @notice Used to retrieve the account approved to manage given token. * @dev Requirements: * * - `tokenId` must exist. * @param tokenId ID of the token to check for approval * @return Address of the account approved to manage the token */ function getApproved( uint256 tokenId ) public view virtual returns (address) { _requireMinted(tokenId); return _tokenApprovals[tokenId][ownerOf(tokenId)]; } /** * @notice Used to approve or remove `operator` as an operator for the caller. * @dev Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * @dev Requirements: * * - The `operator` cannot be the caller. * @dev Emits an {ApprovalForAll} event. * @param operator Address of the operator being managed * @param approved A boolean value signifying whether the approval is being granted (`true`) or (`revoked`) */ function setApprovalForAll(address operator, bool approved) public virtual { if (_msgSender() == operator) revert ERC721ApproveToCaller(); _operatorApprovals[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } /** * @notice Used to check if the given address is allowed to manage the tokens of the specified address. * @param owner Address of the owner of the tokens * @param operator Address being checked for approval * @return A boolean value signifying whether the *operator* is allowed to manage the tokens of the *owner* (`true`) * or not (`false`) */ function isApprovedForAll( address owner, address operator ) public view virtual returns (bool) { return _operatorApprovals[owner][operator]; } /** * @notice Used to grant an approval to manage a given token. * @dev Emits an {Approval} event. * @param to Address to which the approval is being granted * @param tokenId ID of the token for which the approval is being granted */ function _approve(address to, uint256 tokenId) internal virtual { address owner = ownerOf(tokenId); _tokenApprovals[tokenId][owner] = to; emit Approval(owner, to, tokenId); } /** * @notice Used to update the owner of the token and clear the approvals associated with the previous owner. * @dev The `destinationId` should equal `0` if the new owner is an externally owned account. * @param tokenId ID of the token being updated * @param destinationId ID of the token to receive the given token * @param to Address of account to receive the token */ function _updateOwnerAndClearApprovals( uint256 tokenId, uint256 destinationId, address to ) internal { _RMRKOwners[tokenId] = DirectOwner({ ownerAddress: to, tokenId: destinationId }); // Clear approvals from the previous owner _approve(address(0), tokenId); _cleanApprovals(tokenId); } /** * @notice Used to remove approvals for the current owner of the given token. * @param tokenId ID of the token to clear the approvals for */ function _cleanApprovals(uint256 tokenId) internal virtual {} //////////////////////////////////////// // UTILS //////////////////////////////////////// /** * @notice Used to check whether the given account is allowed to manage the given token. * @dev Requirements: * * - `tokenId` must exist. * @param spender Address that is being checked for approval * @param tokenId ID of the token being checked * @return A boolean value indicating whether the `spender` is approved to manage the given token */ function _isApprovedOrOwner( address spender, uint256 tokenId ) internal view virtual returns (bool) { address owner = ownerOf(tokenId); return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); } /** * @notice Used to check whether the account is approved to manage the token or its direct owner. * @param spender Address that is being checked for approval or direct ownership * @param tokenId ID of the token being checked * @return A boolean value indicating whether the `spender` is approved to manage the given token or its * direct owner */ function _isApprovedOrDirectOwner( address spender, uint256 tokenId ) internal view virtual returns (bool) { (address owner, uint256 parentId, ) = directOwnerOf(tokenId); // When the parent is an NFT, only it can do operations if (parentId != 0) { return (spender == owner); } // Otherwise, the owner or approved address can return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); } /** * @notice Used to enforce that the given token has been minted. * @dev Reverts if the `tokenId` has not been minted yet. * @dev The validation checks whether the owner of a given token is a `0x0` address and considers it not minted if * it is. This means that both tokens that haven't been minted yet as well as the ones that have already been * burned will cause the transaction to be reverted. * @param tokenId ID of the token to check */ function _requireMinted(uint256 tokenId) internal view virtual { if (!_exists(tokenId)) revert ERC721InvalidTokenId(); } /** * @notice Used to check whether the given token exists. * @dev Tokens start existing when they are minted (`_mint`) and stop existing when they are burned (`_burn`). * @param tokenId ID of the token being checked * @return A boolean value signifying whether the token exists */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _RMRKOwners[tokenId].ownerAddress != address(0); } /** * @notice Used to invoke {IERC721Receiver-onERC721Received} on a target address. * @dev The call is not executed if the target address is not a contract. * @param from Address representing the previous owner of the given token * @param to Yarget address that will receive the tokens * @param tokenId ID of the token to be transferred * @param data Optional data to send along with the call * @return Boolean value signifying whether the call correctly returned the expected magic value */ function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory data ) private returns (bool) { if (to.isContract()) { try IERC721Receiver(to).onERC721Received( _msgSender(), from, tokenId, data ) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == uint256(0)) { revert ERC721TransferToNonReceiverImplementer(); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } //////////////////////////////////////// // CHILD MANAGEMENT PUBLIC //////////////////////////////////////// /** * @inheritdoc IERC6059 */ function addChild( uint256 parentId, uint256 childId, bytes memory data ) public virtual { _requireMinted(parentId); address childAddress = _msgSender(); if (!childAddress.isContract()) revert RMRKIsNotContract(); Child memory child = Child({ contractAddress: childAddress, tokenId: childId }); _beforeAddChild(parentId, childAddress, childId, data); uint256 length = pendingChildrenOf(parentId).length; if (length < 128) { _pendingChildren[parentId].push(child); } else { revert RMRKMaxPendingChildrenReached(); } // Previous length matches the index for the new child emit ChildProposed(parentId, length, childAddress, childId); _afterAddChild(parentId, childAddress, childId, data); } /** * @inheritdoc IERC6059 */ function acceptChild( uint256 parentId, uint256 childIndex, address childAddress, uint256 childId ) public virtual onlyApprovedOrOwner(parentId) { _acceptChild(parentId, childIndex, childAddress, childId); } /** * @notice Used to accept a pending child token for a given parent token. * @dev This moves the child token from parent token's pending child tokens array into the active child tokens * array. * @dev Requirements: * * - `tokenId` must exist * - `index` must be in range of the pending children array * @dev Emits ***ChildAccepted*** event. * @param parentId ID of the parent token for which the child token is being accepted * @param childIndex Index of a child tokem in the given parent's pending children array * @param childAddress Address of the collection smart contract of the child token expected to be located at the * specified index of the given parent token's pending children array * @param childId ID of the child token expected to be located at the specified index of the given parent token's * pending children array */ function _acceptChild( uint256 parentId, uint256 childIndex, address childAddress, uint256 childId ) internal virtual { Child memory child = pendingChildOf(parentId, childIndex); _checkExpectedChild(child, childAddress, childId); if (_childIsInActive[childAddress][childId] != 0) revert RMRKChildAlreadyExists(); _beforeAcceptChild(parentId, childIndex, childAddress, childId); // Remove from pending: _removeChildByIndex(_pendingChildren[parentId], childIndex); // Add to active: _activeChildren[parentId].push(child); _childIsInActive[childAddress][childId] = 1; // We use 1 as true emit ChildAccepted(parentId, childIndex, childAddress, childId); _afterAcceptChild(parentId, childIndex, childAddress, childId); } /** * @inheritdoc IERC6059 */ function rejectAllChildren( uint256 tokenId, uint256 maxRejections ) public virtual onlyApprovedOrOwner(tokenId) { _rejectAllChildren(tokenId, maxRejections); } /** * @notice Used to reject all pending children of a given parent token. * @dev Removes the children from the pending array mapping. * @dev This does not update the ownership storage data on children. If necessary, ownership can be reclaimed by the * rootOwner of the previous parent. * @dev Requirements: * * - `tokenId` must exist * @dev Emits ***AllChildrenRejected*** event. * @param tokenId ID of the parent token for which to reject all of the pending tokens. * @param maxRejections Maximum number of expected children to reject, used to prevent from rejecting children which * arrive just before this operation. */ function _rejectAllChildren( uint256 tokenId, uint256 maxRejections ) internal virtual { if (_pendingChildren[tokenId].length > maxRejections) revert RMRKUnexpectedNumberOfChildren(); _beforeRejectAllChildren(tokenId); delete _pendingChildren[tokenId]; emit AllChildrenRejected(tokenId); _afterRejectAllChildren(tokenId); } /** * @inheritdoc IERC6059 */ function transferChild( uint256 tokenId, address to, uint256 destinationId, uint256 childIndex, address childAddress, uint256 childId, bool isPending, bytes memory data ) public virtual onlyApprovedOrOwner(tokenId) { _transferChild( tokenId, to, destinationId, childIndex, childAddress, childId, isPending, data ); } /** * @notice Used to transfer a child token from a given parent token. * @dev When transferring a child token, the owner of the token is set to `to`, or is not updated in the event of * `to` being the `0x0` address. * @dev Requirements: * * - `tokenId` must exist. * @dev Emits {ChildTransferred} event. * @param tokenId ID of the parent token from which the child token is being transferred * @param to Address to which to transfer the token to * @param destinationId ID of the token to receive this child token (MUST be 0 if the destination is not a token) * @param childIndex Index of a token we are transferring, in the array it belongs to (can be either active array or * pending array) * @param childAddress Address of the child token's collection smart contract. * @param childId ID of the child token in its own collection smart contract. * @param isPending A boolean value indicating whether the child token being transferred is in the pending array of * the parent token (`true`) or in the active array (`false`) * @param data Additional data with no specified format, sent in call to `_to` */ function _transferChild( uint256 tokenId, address to, uint256 destinationId, // newParentId uint256 childIndex, address childAddress, uint256 childId, bool isPending, bytes memory data ) internal virtual { Child memory child; if (isPending) { child = pendingChildOf(tokenId, childIndex); } else { child = childOf(tokenId, childIndex); } _checkExpectedChild(child, childAddress, childId); _beforeTransferChild( tokenId, childIndex, childAddress, childId, isPending, data ); if (isPending) { _removeChildByIndex(_pendingChildren[tokenId], childIndex); } else { delete _childIsInActive[childAddress][childId]; _removeChildByIndex(_activeChildren[tokenId], childIndex); } if (to != address(0)) { if (destinationId == uint256(0)) { IERC721(childAddress).safeTransferFrom( address(this), to, childId, data ); } else { // Destination is an NFT IERC6059(child.contractAddress).nestTransferFrom( address(this), to, child.tokenId, destinationId, data ); } } emit ChildTransferred( tokenId, childIndex, childAddress, childId, isPending, to == address(0) ); _afterTransferChild( tokenId, childIndex, childAddress, childId, isPending, data ); } /** * @notice Used to verify that the child being accessed is the intended child. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @param child A Child struct of a child being accessed * @param expectedAddress The address expected to be the one of the child * @param expectedId The token ID expected to be the one of the child */ function _checkExpectedChild( Child memory child, address expectedAddress, uint256 expectedId ) private pure { if ( expectedAddress != child.contractAddress || expectedId != child.tokenId ) revert RMRKUnexpectedChildId(); } //////////////////////////////////////// // CHILD MANAGEMENT GETTERS //////////////////////////////////////// /** * @inheritdoc IERC6059 */ function childrenOf( uint256 parentId ) public view virtual returns (Child[] memory) { Child[] memory children = _activeChildren[parentId]; return children; } /** * @inheritdoc IERC6059 */ function pendingChildrenOf( uint256 parentId ) public view virtual returns (Child[] memory) { Child[] memory pendingChildren = _pendingChildren[parentId]; return pendingChildren; } /** * @inheritdoc IERC6059 */ function childOf( uint256 parentId, uint256 index ) public view virtual returns (Child memory) { if (childrenOf(parentId).length <= index) revert RMRKChildIndexOutOfRange(); Child memory child = _activeChildren[parentId][index]; return child; } /** * @inheritdoc IERC6059 */ function pendingChildOf( uint256 parentId, uint256 index ) public view virtual returns (Child memory) { if (pendingChildrenOf(parentId).length <= index) revert RMRKPendingChildIndexOutOfRange(); Child memory child = _pendingChildren[parentId][index]; return child; } // HOOKS /** * @notice Hook that is called before nested token transfer. * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param from Address from which the token is being transferred * @param to Address to which the token is being transferred * @param fromTokenId ID of the token from which the given token is being transferred * @param toTokenId ID of the token to which the given token is being transferred * @param tokenId ID of the token being transferred * @param data Additional data with no specified format, sent in the addChild call */ function _beforeNestedTokenTransfer( address from, address to, uint256 fromTokenId, uint256 toTokenId, uint256 tokenId, bytes memory data ) internal virtual {} /** * @notice Hook that is called after nested token transfer. * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param from Address from which the token was transferred * @param to Address to which the token was transferred * @param fromTokenId ID of the token from which the given token was transferred * @param toTokenId ID of the token to which the given token was transferred * @param tokenId ID of the token that was transferred * @param data Additional data with no specified format, sent in the addChild call */ function _afterNestedTokenTransfer( address from, address to, uint256 fromTokenId, uint256 toTokenId, uint256 tokenId, bytes memory data ) internal virtual {} /** * @notice Hook that is called before a child is added to the pending tokens array of a given token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param tokenId ID of the token that will receive a new pending child token * @param childAddress Address of the collection smart contract of the child token expected to be located at the * specified index of the given parent token's pending children array * @param childId ID of the child token expected to be located at the specified index of the given parent token's * pending children array * @param data Additional data with no specified format */ function _beforeAddChild( uint256 tokenId, address childAddress, uint256 childId, bytes memory data ) internal virtual {} /** * @notice Hook that is called after a child is added to the pending tokens array of a given token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param tokenId ID of the token that has received a new pending child token * @param childAddress Address of the collection smart contract of the child token expected to be located at the * specified index of the given parent token's pending children array * @param childId ID of the child token expected to be located at the specified index of the given parent token's * pending children array * @param data Additional data with no specified format */ function _afterAddChild( uint256 tokenId, address childAddress, uint256 childId, bytes memory data ) internal virtual {} /** * @notice Hook that is called before a child is accepted to the active tokens array of a given token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param parentId ID of the token that will accept a pending child token * @param childIndex Index of the child token to accept in the given parent token's pending children array * @param childAddress Address of the collection smart contract of the child token expected to be located at the * specified index of the given parent token's pending children array * @param childId ID of the child token expected to be located at the specified index of the given parent token's * pending children array */ function _beforeAcceptChild( uint256 parentId, uint256 childIndex, address childAddress, uint256 childId ) internal virtual {} /** * @notice Hook that is called after a child is accepted to the active tokens array of a given token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param parentId ID of the token that has accepted a pending child token * @param childIndex Index of the child token that was accpeted in the given parent token's pending children array * @param childAddress Address of the collection smart contract of the child token that was expected to be located * at the specified index of the given parent token's pending children array * @param childId ID of the child token that was expected to be located at the specified index of the given parent * token's pending children array */ function _afterAcceptChild( uint256 parentId, uint256 childIndex, address childAddress, uint256 childId ) internal virtual {} /** * @notice Hook that is called before a child is transferred from a given child token array of a given token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param tokenId ID of the token that will transfer a child token * @param childIndex Index of the child token that will be transferred from the given parent token's children array * @param childAddress Address of the collection smart contract of the child token that is expected to be located * at the specified index of the given parent token's children array * @param childId ID of the child token that is expected to be located at the specified index of the given parent * token's children array * @param isPending A boolean value signifying whether the child token is being transferred from the pending child * tokens array (`true`) or from the active child tokens array (`false`) * @param data Additional data with no specified format, sent in the addChild call */ function _beforeTransferChild( uint256 tokenId, uint256 childIndex, address childAddress, uint256 childId, bool isPending, bytes memory data ) internal virtual {} /** * @notice Hook that is called after a child is transferred from a given child token array of a given token. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param tokenId ID of the token that has transferred a child token * @param childIndex Index of the child token that was transferred from the given parent token's children array * @param childAddress Address of the collection smart contract of the child token that was expected to be located * at the specified index of the given parent token's children array * @param childId ID of the child token that was expected to be located at the specified index of the given parent * token's children array * @param isPending A boolean value signifying whether the child token was transferred from the pending child tokens * array (`true`) or from the active child tokens array (`false`) * @param data Additional data with no specified format, sent in the addChild call */ function _afterTransferChild( uint256 tokenId, uint256 childIndex, address childAddress, uint256 childId, bool isPending, bytes memory data ) internal virtual {} /** * @notice Hook that is called before a pending child tokens array of a given token is cleared. * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param tokenId ID of the token that will reject all of the pending child tokens */ function _beforeRejectAllChildren(uint256 tokenId) internal virtual {} /** * @notice Hook that is called after a pending child tokens array of a given token is cleared. * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * @param tokenId ID of the token that has rejected all of the pending child tokens */ function _afterRejectAllChildren(uint256 tokenId) internal virtual {} // HELPERS /** * @notice Used to remove a specified child token form an array using its index within said array. * @dev The caller must ensure that the length of the array is valid compared to the index passed. * @dev The Child struct consists of the following values: * [ * tokenId, * contractAddress * ] * @param array An array od Child struct containing info about the child tokens in a given child tokens array * @param index An index of the child token to remove in the accompanying array */ function _removeChildByIndex(Child[] storage array, uint256 index) private { array[index] = array[array.length - 1]; array.pop(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.18; error RentrantCall(); /** * @title ReentrancyGuard * @notice Smart contract used to guard against potential reentrancy exploits. * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; /** * @notice Initializes the ReentrancyGuard with the `_status` of `_NOT_ENTERED`. */ constructor() { _status = _NOT_ENTERED; } /** * @notice Used to ensure that the function it is applied to cannot be reentered. * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantIn(); _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @notice Used to ensure that the current call is not a reentrant call. * @dev If reentrant call is detected, the execution will be reverted. */ function _nonReentrantIn() private { // On the first call to nonReentrant, _notEntered will be true if (_status == _ENTERED) revert RentrantCall(); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /** * @title RMRKCollectionMetadata * @author RMRK team * @notice Smart contract of the RMRK Collection metadata module. */ contract RMRKCollectionMetadata { string private _collectionMetadata; /** * @notice Used to initialize the contract with the given metadata. * @param collectionMetadata_ The collection metadata with which to initialize the smart contract */ constructor(string memory collectionMetadata_) { _setCollectionMetadata(collectionMetadata_); } /** * @notice Used to set the metadata of the collection. * @param newMetadata The new metadata of the collection */ function _setCollectionMetadata(string memory newMetadata) internal { _collectionMetadata = newMetadata; } /** * @notice Used to retrieve the metadata of the collection. * @return string The metadata URI of the collection */ function collectionMetadata() public view returns (string memory) { return _collectionMetadata; } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "../access/OwnableLock.sol"; import "../library/RMRKErrors.sol"; /** * @title RMRKMintingUtils * @author RMRK team * @notice Smart contract of the RMRK minting utils module. * @dev This smart contract includes the top-level utilities for managing minting and implements OwnableLock by default. * @dev Max supply-related and pricing variables are immutable after deployment. */ contract RMRKMintingUtils is OwnableLock { uint256 internal _nextId; uint256 internal _totalSupply; uint256 internal _maxSupply; uint256 internal immutable _pricePerMint; /** * @notice Initializes the smart contract with a given maximum supply and minting price. * @param maxSupply_ The maximum supply of tokens to initialize the smart contract with * @param pricePerMint_ The minting price to initialize the smart contract with, expressed in the smallest * denomination of the native currency of the chain to which the smart contract is deployed to */ constructor(uint256 maxSupply_, uint256 pricePerMint_) { _maxSupply = maxSupply_; _pricePerMint = pricePerMint_; } /** * @notice Used to verify that the sale of the given token is still available. * @dev If the maximum supply is reached, the execution will be reverted. */ modifier saleIsOpen() { _checkSaleIsOpen(); _; } /** * @inheritdoc OwnableLock */ function setLock() public virtual override onlyOwner { super.setLock(); _maxSupply = _totalSupply; } /** * @notice Used to retrieve the total supply of the tokens in a collection. * @return The number of tokens in a collection */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @notice Used to retrieve the maximum supply of the collection. * @return The maximum supply of tokens in the collection */ function maxSupply() public view returns (uint256) { return _maxSupply; } /** * @notice Used to retrieve the price per mint. * @return The price per mint of a single token expressed in the lowest denomination of a native currency */ function pricePerMint() public view returns (uint256) { return _pricePerMint; } /** * @notice Used to withdraw the minting proceedings to a specified address. * @dev This function can only be called by the owner. * @param to Address to receive the given amount of minting proceedings * @param amount The amount to withdraw */ function withdrawRaised(address to, uint256 amount) external onlyOwner { _withdraw(to, amount); } /** * @notice Used to withdraw the minting proceedings to a specified address. * @param _address Address to receive the given amount of minting proceedings * @param _amount The amount to withdraw */ function _withdraw(address _address, uint256 _amount) private { (bool success, ) = _address.call{value: _amount}(""); require(success, "Transfer failed."); } /** * @notice Used to verify that the sale is still open. * @dev In case the maximum supply of the collection is reached, the execution is reverted. */ function _checkSaleIsOpen() private view { if (_nextId >= _maxSupply) revert RMRKMintOverMax(); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; import "@rmrk-team/evm-contracts/contracts/RMRK/equippable/RMRKEquippable.sol"; import "@rmrk-team/evm-contracts/contracts/RMRK/extension/RMRKRoyalties.sol"; import "@rmrk-team/evm-contracts/contracts/RMRK/utils/RMRKCollectionMetadata.sol"; import "@rmrk-team/evm-contracts/contracts/RMRK/utils/RMRKMintingUtils.sol"; import "@rmrk-team/evm-contracts/contracts/implementations/IRMRKInitData.sol"; error RMRKMintZero(); /** * @title SpacerItems * @author RMRK team */ abstract contract SpacerBase is IRMRKInitData, RMRKMintingUtils, RMRKCollectionMetadata, RMRKRoyalties, RMRKEquippable { uint256 private _totalAssets; uint64 private constant _LOWEST_POSSIBLE_PRIORITY = 2 ^ (64 - 1); constructor( string memory name_, string memory symbol_, string memory collectionMetadata_, InitData memory data ) RMRKMintingUtils(data.maxSupply, data.pricePerMint) RMRKCollectionMetadata(collectionMetadata_) RMRKRoyalties(data.royaltyRecipient, data.royaltyPercentageBps) RMRKEquippable(name_, symbol_) {} function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual override { super._beforeTokenTransfer(from, to, tokenId); if (to == address(0)) { unchecked { _totalSupply -= 1; } } } function addAssetsToTokens( uint256[] memory tokenIds, uint64[] memory assetIds, uint64 replacesAssetWithId ) public virtual onlyOwnerOrContributor { uint256 len = tokenIds.length; for (uint256 i; i < len; ) { _addAssetToToken(tokenIds[0], assetIds[0], replacesAssetWithId); unchecked { ++i; } } } function addAssetToToken( uint256 tokenId, uint64 assetId, uint64 replacesAssetWithId ) public virtual onlyOwnerOrContributor { _addAssetToToken(tokenId, assetId, replacesAssetWithId); } function addAssetEntry( string memory metadataURI ) public virtual onlyOwnerOrContributor returns (uint256) { unchecked { _totalAssets += 1; } _addAssetEntry(uint64(_totalAssets), metadataURI); return _totalAssets; } function addEquippableAssetEntries( uint64 equippableGroupId, address catalogAddress, string[] memory metadataURI, uint64[] memory partIds ) public virtual onlyOwnerOrContributor { uint256 len = metadataURI.length; for (uint256 i; i < len; ) { addEquippableAssetEntry( equippableGroupId, catalogAddress, metadataURI[i], partIds ); unchecked { ++i; } } } function addEquippableAssetEntry( uint64 equippableGroupId, address catalogAddress, string memory metadataURI, uint64[] memory partIds ) public virtual onlyOwnerOrContributor returns (uint64) { unchecked { _totalAssets += 1; } _addAssetEntry( uint64(_totalAssets), equippableGroupId, catalogAddress, metadataURI, partIds ); return uint64(_totalAssets); } function setValidParentForEquippableGroup( uint64 equippableGroupId, address parentAddress, uint64 partId ) public virtual onlyOwnerOrContributor { _setValidParentForEquippableGroup( equippableGroupId, parentAddress, partId ); } function totalAssets() public view virtual returns (uint256) { return _totalAssets; } function updateRoyaltyRecipient( address newRoyaltyRecipient ) public virtual override onlyOwner { _setRoyaltyRecipient(newRoyaltyRecipient); } function tokenURI( uint256 tokenId ) public view virtual returns (string memory) { _requireMinted(tokenId); uint64[] memory priorities = getActiveAssetPriorities(tokenId); // This will always be at least 1, since we can only mint with an asset directly uint256 len = priorities.length; uint64 maxPriority = _LOWEST_POSSIBLE_PRIORITY; uint64 maxPriorityIndex; for (uint64 i; i < len; ) { uint64 currentPrio = priorities[i]; if (currentPrio < maxPriority) { maxPriority = currentPrio; maxPriorityIndex = i; } unchecked { ++i; } } uint64 maxPriorityAssetId = getActiveAssets(tokenId)[maxPriorityIndex]; return getAssetMetadata(tokenId, maxPriorityAssetId); } }
{ "optimizer": { "enabled": true, "runs": 1000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"collectionMetadata_","type":"string"},{"components":[{"internalType":"address","name":"erc20TokenAddress","type":"address"},{"internalType":"bool","name":"tokenUriIsEnumerable","type":"bool"},{"internalType":"address","name":"royaltyRecipient","type":"address"},{"internalType":"uint16","name":"royaltyPercentageBps","type":"uint16"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"pricePerMint","type":"uint256"}],"internalType":"struct IRMRKInitData.InitData","name":"data","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ERC721AddressZeroIsNotaValidOwner","type":"error"},{"inputs":[],"name":"ERC721ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ERC721ApproveCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"ERC721ApproveToCaller","type":"error"},{"inputs":[],"name":"ERC721InvalidTokenId","type":"error"},{"inputs":[],"name":"ERC721MintToTheZeroAddress","type":"error"},{"inputs":[],"name":"ERC721NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"ERC721TokenAlreadyMinted","type":"error"},{"inputs":[],"name":"ERC721TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"ERC721TransferToNonReceiverImplementer","type":"error"},{"inputs":[],"name":"ERC721TransferToTheZeroAddress","type":"error"},{"inputs":[],"name":"RMRKApprovalForAssetsToCurrentOwner","type":"error"},{"inputs":[],"name":"RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"RMRKAssetAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKBadPriorityListLength","type":"error"},{"inputs":[],"name":"RMRKCatalogRequiredForParts","type":"error"},{"inputs":[],"name":"RMRKChildAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKEquippableEquipNotAllowedByCatalog","type":"error"},{"inputs":[],"name":"RMRKIdZeroForbidden","type":"error"},{"inputs":[],"name":"RMRKIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKIsNotContract","type":"error"},{"inputs":[],"name":"RMRKLocked","type":"error"},{"inputs":[],"name":"RMRKMaxPendingAssetsReached","type":"error"},{"inputs":[],"name":"RMRKMaxPendingChildrenReached","type":"error"},{"inputs":[{"internalType":"address","name":"childContract","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"RMRKMaxRecursiveBurnsReached","type":"error"},{"inputs":[],"name":"RMRKMintOverMax","type":"error"},{"inputs":[],"name":"RMRKMintZero","type":"error"},{"inputs":[],"name":"RMRKMustUnequipFirst","type":"error"},{"inputs":[],"name":"RMRKNestableTooDeep","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToDescendant","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToNonRMRKNestableImplementer","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToSelf","type":"error"},{"inputs":[],"name":"RMRKNewContributorIsZeroAddress","type":"error"},{"inputs":[],"name":"RMRKNewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"RMRKNoAssetMatchingId","type":"error"},{"inputs":[],"name":"RMRKNotApprovedForAssetsOrOwner","type":"error"},{"inputs":[],"name":"RMRKNotApprovedOrDirectOwner","type":"error"},{"inputs":[],"name":"RMRKNotEquipped","type":"error"},{"inputs":[],"name":"RMRKNotOwner","type":"error"},{"inputs":[],"name":"RMRKNotOwnerOrContributor","type":"error"},{"inputs":[],"name":"RMRKPendingChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKRoyaltiesTooHigh","type":"error"},{"inputs":[],"name":"RMRKSlotAlreadyUsed","type":"error"},{"inputs":[],"name":"RMRKTargetAssetCannotReceiveSlot","type":"error"},{"inputs":[],"name":"RMRKTokenCannotBeEquippedWithAssetIntoSlot","type":"error"},{"inputs":[],"name":"RMRKTokenDoesNotHaveAsset","type":"error"},{"inputs":[],"name":"RMRKUnexpectedAssetId","type":"error"},{"inputs":[],"name":"RMRKUnexpectedChildId","type":"error"},{"inputs":[],"name":"RMRKUnexpectedNumberOfAssets","type":"error"},{"inputs":[],"name":"RMRKUnexpectedNumberOfChildren","type":"error"},{"inputs":[],"name":"RentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AllChildrenRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAllForAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ApprovalForAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"replacesId","type":"uint64"}],"name":"AssetAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"replacesId","type":"uint64"}],"name":"AssetAddedToTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AssetPrioritySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"AssetRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"AssetSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"}],"name":"ChildAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"address","name":"childAddress","type":"address"},{"indexed":false,"internalType":"uint64","name":"childAssetId","type":"uint64"}],"name":"ChildAssetEquipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"assetId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"address","name":"childAddress","type":"address"},{"indexed":false,"internalType":"uint64","name":"childAssetId","type":"uint64"}],"name":"ChildAssetUnequipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"}],"name":"ChildProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"fromPending","type":"bool"},{"indexed":false,"internalType":"bool","name":"toZero","type":"bool"}],"name":"ChildTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"bool","name":"isContributor","type":"bool"}],"name":"ContributorUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NestTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"address","name":"parentAddress","type":"address"}],"name":"ValidParentEquippableGroupIdSet","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"acceptAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"acceptChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"metadataURI","type":"string"}],"name":"addAssetEntry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"replacesAssetWithId","type":"uint64"}],"name":"addAssetToToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint64[]","name":"assetIds","type":"uint64[]"},{"internalType":"uint64","name":"replacesAssetWithId","type":"uint64"}],"name":"addAssetsToTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"addChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"internalType":"address","name":"catalogAddress","type":"address"},{"internalType":"string[]","name":"metadataURI","type":"string[]"},{"internalType":"uint64[]","name":"partIds","type":"uint64[]"}],"name":"addEquippableAssetEntries","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"internalType":"address","name":"catalogAddress","type":"address"},{"internalType":"string","name":"metadataURI","type":"string"},{"internalType":"uint64[]","name":"partIds","type":"uint64[]"}],"name":"addEquippableAssetEntry","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approveForAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxChildrenBurns","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"parent","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"slotId","type":"uint64"}],"name":"canTokenBeEquippedWithAssetIntoSlot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"childOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"}],"name":"childrenOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectionMetadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"directOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"slotPartId","type":"uint64"},{"internalType":"uint64","name":"childAssetId","type":"uint64"}],"internalType":"struct IERC6220.IntakeEquip","name":"data","type":"tuple"}],"name":"equip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getActiveAssetPriorities","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getActiveAssets","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApprovedForAssets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"getAssetAndEquippableData","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"getAssetMetadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"newAssetId","type":"uint64"}],"name":"getAssetReplacements","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"targetCatalogAddress","type":"address"},{"internalType":"uint64","name":"slotPartId","type":"uint64"}],"name":"getEquipment","outputs":[{"components":[{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"childAssetId","type":"uint64"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"address","name":"childEquippableAddress","type":"address"}],"internalType":"struct IERC6220.Equipment","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getPendingAssets","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAllForAssets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"isChildEquipped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"isContributor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"},{"internalType":"bool","name":"grantRole","type":"bool"}],"name":"manageContributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"to","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"catalogAddress","type":"address"},{"internalType":"string","name":"metadataURI","type":"string"},{"internalType":"uint64[][]","name":"partIds","type":"uint64[][]"}],"name":"mintTokensWithNewAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"nestTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"pendingChildOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"}],"name":"pendingChildrenOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IERC6059.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxRejections","type":"uint256"}],"name":"rejectAllAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxRejections","type":"uint256"}],"name":"rejectAllChildren","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"}],"name":"rejectAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAllForAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"setAutoAcceptCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64[]","name":"priorities","type":"uint64[]"}],"name":"setPriority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"internalType":"address","name":"parentAddress","type":"address"},{"internalType":"uint64","name":"partId","type":"uint64"}],"name":"setValidParentForEquippableGroup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"bool","name":"isPending","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint64","name":"assetId","type":"uint64"},{"internalType":"uint64","name":"slotPartId","type":"uint64"}],"name":"unequip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRoyaltyRecipient","type":"address"}],"name":"updateRoyaltyRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawRaised","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a06040523480156200001157600080fd5b506040516200638638038062006386833981016040819052620000349162000288565b838383838383818181818660400151876060015161ffff168989608001518a60a00151600160008190555062000079620000736200010260201b60201c565b62000106565b6006919091556080526200008d8162000158565b50600880546001600160a01b0319166001600160a01b0384161790556127108110620000cc57604051634006185d60e11b815260040160405180910390fd5b60095550600a620000de83826200043a565b50600b620000ed82826200043a565b50505050505050505050505050505062000506565b3390565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60076200016682826200043a565b5050565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715620001a557620001a56200016a565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620001d657620001d66200016a565b604052919050565b600082601f830112620001f057600080fd5b81516001600160401b038111156200020c576200020c6200016a565b602062000222601f8301601f19168201620001ab565b82815285828487010111156200023757600080fd5b60005b83811015620002575785810183015182820184015282016200023a565b506000928101909101919091529392505050565b80516001600160a01b03811681146200028357600080fd5b919050565b600080600080848603610120811215620002a157600080fd5b85516001600160401b0380821115620002b957600080fd5b620002c789838a01620001de565b96506020880151915080821115620002de57600080fd5b620002ec89838a01620001de565b955060408801519150808211156200030357600080fd5b506200031288828901620001de565b93505060c0605f19820112156200032857600080fd5b506200033362000180565b62000341606087016200026b565b8152608086015180151581146200035757600080fd5b60208201526200036a60a087016200026b565b604082015260c086015161ffff811681146200038557600080fd5b606082015260e086015160808201526101009095015160a0860152509194909350909190565b600181811c90821680620003c057607f821691505b602082108103620003e157634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200043557600081815260208120601f850160051c81016020861015620004105750805b601f850160051c820191505b8181101562000431578281556001016200041c565b5050505b505050565b81516001600160401b038111156200045657620004566200016a565b6200046e81620004678454620003ab565b84620003e7565b602080601f831160018114620004a657600084156200048d5750858301515b600019600386901b1c1916600185901b17855562000431565b600085815260208120601f198616915b82811015620004d757888601518255948401946001909101908401620004b6565b5085821015620004f65787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b608051615e646200052260003960006109fd0152615e646000f3fe608060405234801561001057600080fd5b50600436106104725760003560e01c80636f19951c11610250578063b390c0ab11610150578063e467a48f116100c8578063ee1dffcf11610097578063fb25fb7a1161007c578063fb25fb7a14610b60578063fc3517c814610b9a578063ffa1ad7414610bad57600080fd5b8063ee1dffcf14610b0e578063f2fde38b14610b4d57600080fd5b8063e467a48f14610a99578063e7de4de414610aac578063e97ceaa814610abf578063e985e9c514610ad257600080fd5b8063c87b56dd1161011f578063de8e602c11610104578063de8e602c14610a3c578063defa80c314610a4f578063df6f556b14610a6257600080fd5b8063c87b56dd14610a21578063d5abeb0114610a3457600080fd5b8063b390c0ab146109c2578063b88d4fde146109d5578063c259a988146109e8578063c5610a29146109fb57600080fd5b80638507dc28116101e357806395edc18c116101b2578063a22cb46511610197578063a22cb46514610989578063a898e3641461099c578063ace61e00146109af57600080fd5b806395edc18c14610965578063979613361461097657600080fd5b80638507dc281461093157806389ed2edf146109445780638da5cb5b1461094c57806395d89b411461095d57600080fd5b80637507e2ae1161021f5780637507e2ae1461081b57806379e8ca9e1461090b5780637aba6f371461091e5780637e5852d91461092657600080fd5b80636f19951c146107da57806370a08231146107ed578063715018a6146108005780637280281e1461080857600080fd5b80632f32f937116103765780634e60edba116102ee5780635e94354a116102bd5780636352211e116102a25780636352211e146107a1578063635490cc146107b457806365b67eb9146107c757600080fd5b80635e94354a1461077b5780635ea72f361461078e57600080fd5b80634e60edba1461071d57806351532e5a1461074057806359c8b7dd146107605780635e2e32921461076857600080fd5b8063362b67f41161034557806342842e0e1161032a57806342842e0e146106d757806342966c68146106ea57806344ec9344146106fd57600080fd5b8063362b67f4146106b157806338dcf74c146106c457600080fd5b80632f32f9371461065857806330ffb1d614610678578063326d526d1461068b57806333ed28451461069e57600080fd5b80630fc499f51161040957806322e6d160116103d857806323b872dd116103bd57806323b872dd146105e85780632452cd91146105fb5780632a55205a1461062657600080fd5b806322e6d1601461059957806322f6da9c146105d557600080fd5b80630fc499f51461053f57806318160ddd146105525780631c7bb4611461055a5780631d0d35f51461056d57600080fd5b806306fdde031161044557806306fdde03146104d9578063074334fb146104ee578063081812fc14610501578063095ea7b31461052c57600080fd5b806301e1d1141461047757806301ffc9a71461048e57806302549760146104b1578063064c0a3a146104c6575b600080fd5b6021545b6040519081526020015b60405180910390f35b6104a161049c366004614dc7565b610be9565b6040519015158152602001610485565b6104c46104bf366004614f6c565b610c62565b005b6104c46104d4366004615064565b610cad565b6104e1610cc4565b6040516104859190615129565b6104a16104fc36600461513c565b610d56565b61051461050f36600461518b565b610dcf565b6040516001600160a01b039091168152602001610485565b6104c461053a3660046151a4565b610e15565b6104c461054d3660046151d0565b610ed3565b60055461047b565b6104c46105683660046151ed565b610efc565b6104a161057b3660046151d0565b6001600160a01b031660009081526002602052604090205460011490565b6104a16105a7366004615229565b6001600160a01b03918216600090815260196020908152604080832093909416825291909152205460ff1690565b6104c46105e3366004615262565b610f0f565b6104c46105f636600461528e565b610f2a565b61060e6106093660046152cf565b610f4f565b6040516001600160401b039091168152602001610485565b610639610634366004615349565b610f7d565b604080516001600160a01b039093168352602083019190915201610485565b61066b610666366004615349565b610fb3565b604051610485919061536b565b61047b61068636600461538b565b611065565b6104c46106993660046151d0565b61108e565b6104c46106ac3660046151a4565b6110ba565b6104c46106bf36600461541a565b6110d0565b6104c46106d2366004615484565b611133565b6104c46106e536600461528e565b611158565b6104c46106f836600461518b565b611173565b61071061070b36600461518b565b61117e565b6040516104859190615505565b61073061072b366004615565565b6111fd565b60405161048594939291906155cc565b61075361074e36600461518b565b6112da565b604051610485919061560f565b60095461047b565b6104e1610776366004615565565b61136e565b61075361078936600461518b565b611476565b6104c461079c3660046151a4565b6114df565b6105146107af36600461518b565b61159c565b6104c46107c2366004615622565b611645565b6104c46107d5366004615671565b611777565b6107106107e836600461518b565b61178a565b61047b6107fb3660046151d0565b6117fa565b6104c4611858565b61075361081636600461518b565b61186c565b6108bd61082936600461569f565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601e87528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352680100000000000000009004909416958101959095526001830154918501919091526002909101549091169082015290565b604051610485919060006080820190506001600160401b0380845116835280602085015116602084015250604083015160408301526001600160a01b03606084015116606083015292915050565b6104c46109193660046156d4565b6118d5565b6104c46119ab565b6003546001146104a1565b6104c461093f3660046156d4565b6119c3565b6104e1611a59565b6001546001600160a01b0316610514565b6104e1611a68565b6008546001600160a01b0316610514565b6104c4610984366004615349565b611a77565b6104c46109973660046156d4565b611a8b565b61066b6109aa366004615349565b611b32565b6104c46109bd366004615781565b611ba8565b61047b6109d0366004615349565b611d6b565b6104c46109e336600461588f565b611d81565b6104c46109f63660046151ed565b611d97565b7f000000000000000000000000000000000000000000000000000000000000000061047b565b6104e1610a2f36600461518b565b611dac565b60065461047b565b6104c4610a4a3660046158ee565b611e74565b6104c4610a5d36600461596c565b611e89565b61060e610a70366004615565565b60009182526014602090815260408084206001600160401b039384168552909152909120541690565b610514610aa736600461518b565b611eae565b6104c4610aba366004615349565b611ed1565b6104c4610acd366004615a07565b611ee5565b6104a1610ae0366004615229565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205460ff1690565b6104a1610b1c366004615a46565b6000928352601f602090815260408085206001600160a01b0394909416855292815282842091845252902054151590565b6104c4610b5b3660046151d0565b611efb565b610b73610b6e36600461518b565b611f4c565b604080516001600160a01b0390941684526020840192909252151590820152606001610485565b6104c4610ba8366004615262565b611fb4565b6104e16040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b6000610bf482611fc9565b80610c2857506001600160e01b031982167f06b4329a00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982167f28bc9ae400000000000000000000000000000000000000000000000000000000145b92915050565b610c6a612067565b815160005b81811015610ca557610c9c8686868481518110610c8e57610c8e615a6d565b602002602001015186610f4f565b50600101610c6f565b505050505050565b82610cb7816120bf565b610ca586868686866120ff565b6060600a8054610cd390615a83565b80601f0160208091040260200160405190810160405280929190818152602001828054610cff90615a83565b8015610d4c5780601f10610d2157610100808354040283529160200191610d4c565b820191906000526020600020905b815481529060010190602001808311610d2f57829003601f168201915b5050505050905090565b6001600160401b038083166000908152601c602090815260408083205484168084528280528184206001600160a01b038a1685529092528220549192909181169084168103610dc0576000610db486610dae8961186c565b9061231a565b9450610dc79350505050565b6000925050505b949350505050565b6000610dda82612383565b6000828152600d6020526040812090610df28461159c565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6000610e208261159c565b9050806001600160a01b0316836001600160a01b031603610e6d576040517f0591db6d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03821614801590610e8d5750610e8b8133610ae0565b155b15610ec4576040517f982462b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece83836123bb565b505050565b610edb612434565b600880546001600160a01b0319166001600160a01b03831617905550565b50565b610f04612067565b610ece838383612478565b82610f19816126a0565b610f248484846126e0565b50505050565b80610f34816120bf565b610f248484846040518060200160405280600081525061274f565b6000610f59612067565b6021805460010190819055610f7190868686866128c5565b50602154949350505050565b6008546009546001600160a01b039091169060009061271090610fa09085615ad3565b610faa9190615aea565b90509250929050565b604080518082019091526000808252602082015281610fd18461178a565b5111611009576040517fca7cc84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040812080548490811061102957611029615a6d565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b600061106f612067565b60218054600101908190556110849083612985565b506021545b919050565b611096612434565b6001600160a01b03166000908152602260205260409020805460ff19166001179055565b6110c2612434565b6110cc8282612a4b565b5050565b6110d8612067565b825160005b8181101561112c57611124856000815181106110fb576110fb615a6d565b60200260200101518560008151811061111657611116615a6d565b602002602001015185612478565b6001016110dd565b5050505050565b805161113e81612b0d565b611146612b4d565b61114f82612b90565b50506001600055565b610ece83838360405180602001604052806000815250611d81565b6110cc816000611d6b565b6000818152601160209081526040808320805482518185028101850190935280835260609493849084015b828210156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b50929695505050505050565b6060600080606061120e868661136e565b6001600160401b038087166000908152601c6020908152604080832054601b835281842054601d845293829020805483518186028101860190945280845291909516946001600160a01b0390941693928391908301828280156112c257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161127f5790505b50505050509050935093509350935092959194509250565b60008181526016602090815260409182902080548351818402810184019094528084526060939283018282801561136257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161131f5790505b50505050509050919050565b60008281526018602090815260408083206001600160401b038516845290915290205460609060ff166113cd576040517fdcc947e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038216600090815260136020526040902080546113f090615a83565b80601f016020809104026020016040519081016040528092919081815260200182805461141c90615a83565b80156114695780601f1061143e57610100808354040283529160200191611469565b820191906000526020600020905b81548152906001019060200180831161144c57829003601f168201915b5050505050905092915050565b60008181526017602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b60006114ea8261159c565b9050806001600160a01b0316836001600160a01b03160361151e576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b0382161480159061155b57506001600160a01b038116600090815260196020908152604080832033845290915290205460ff16155b15611592576040517faf45d29200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece8383612f87565b6000806000806115ab85611f4c565b925092509250801561163c576040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015611615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116399190615b0c565b92505b50909392505050565b61164e83612383565b33803b61166e5760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b038216602082015260006116958661117e565b51905060808110156116f4576000868152601160209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055611726565b6040517fa53c8c0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd8460405161176391815260200190565b60405180910390a4610ca586848787613000565b61177f612067565b610ece838383613049565b60008181526010602090815260408083208054825181850281018501909352808352606094938490840182156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b60006001600160a01b03821661183c576040517f7772286600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b611860612434565b61186a60006130fb565b565b60008181526015602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b6118dd612434565b6001600160a01b03821661191d576040517fb5c0938000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611942576001600160a01b0382166000908152600260205260408120819055611961565b6001600160a01b03821660009081526002602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac28260405161199f911515815260200190565b60405180910390a25050565b6119b3612434565b6119bb61314d565b600554600655565b6001600160a01b03821633036119ec576040516375f45abd60e01b815260040160405180910390fd5b3360008181526019602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060078054610cd390615a83565b6060600b8054610cd390615a83565b81611a8181612b0d565b610ece838361315c565b6001600160a01b0382163303611acd576040517f2dee66e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611a4d565b604080518082019091526000808252602082015281611b508461117e565b5111611b88576040517fda22687f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260116020526040812080548490811061102957611029615a6d565b611bb06131ea565b611bb8612067565b83516000819003611bf5576040517f376bec4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600654600454611c059083615b29565b1115611c3d576040517fbd239bf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480548201905560058054820190556000805b82811015611d6157611cc160008787611c828b8681518110611c7557611c75615a6d565b6020026020010151613226565b604051602001611c93929190615b3c565b604051602081830303815290604052878581518110611cb457611cb4615a6d565b6020026020010151610f4f565b9150611d0f888281518110611cd857611cd8615a6d565b6020026020010151888381518110611cf257611cf2615a6d565b6020026020010151604051806020016040528060008152506132c5565b611d34878281518110611d2457611d24615a6d565b6020026020010151836000612478565b611d59878281518110611d4957611d49615a6d565b60200260200101516000846132fa565b600101611c51565b5050505050505050565b600082611d77816120bf565b610dc7848461353a565b81611d8b816120bf565b61112c858585856138d0565b82611da181612b0d565b610f24848484613905565b6060611db782612383565b6000611dc283611476565b8051909150603d6000805b83816001600160401b03161015611e2f57600085826001600160401b031681518110611dfb57611dfb615a6d565b60200260200101519050836001600160401b0316816001600160401b03161015611e26578093508192505b50600101611dcd565b506000611e3b8761186c565b826001600160401b031681518110611e5557611e55615a6d565b60200260200101519050611e69878261136e565b979650505050505050565b82611e7e816126a0565b610f24848484613ade565b87611e9381612b0d565b611ea38989898989898989613b71565b505050505050505050565b6000611eb982612383565b6000828152601a6020526040812090610df28461159c565b81611edb816126a0565b610ece8383613be8565b83611eef81612b0d565b61112c85858585613cf9565b611f03612434565b6001600160a01b038116611f43576040517f4ece6ecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ef9816130fb565b6000818152600f60209081526040808320815180830190925280548252600101546001600160a01b03169181018290528291829190611f9e5760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b82611fbe816126a0565b610f248484846132fa565b60006001600160e01b031982166301ffc9a760e01b148061201357506001600160e01b031982167f80ac58cd00000000000000000000000000000000000000000000000000000000145b8061204757506001600160e01b031982167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982166342b0e56f60e01b1492915050565b6001546001600160a01b0316331480159061208857506120863361057b565b155b1561186a576040517f1eca167000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120c93382613e18565b610ef9576040517f8be7930600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061210b85611f4c565b5091509150866001600160a01b0316826001600160a01b0316146121425760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616612169576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561218057508385145b156121b7576040517f3d76b10700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0386163b6121df5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa15801561222a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224e9190615b93565b612284576040517f5e13b1cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61228f858786613ebb565b61229a878787613fde565b6001600160a01b0387166000908152600c602052604081208054600192906122c3908490615bb0565b909155506122d49050858588613ffb565b6001600160a01b0386166000908152600c602052604081208054600192906122fd908490615b29565b90915550612311905082878387898861405a565b50505050505050565b81516000908190815b8181101561237257846001600160401b031686828151811061234757612347615a6d565b60200260200101516001600160401b03160361236a5792506001915061237c9050565b600101612323565b5060008092509250505b9250929050565b6000818152600f60205260409020600101546001600160a01b0316610ef95760405163089ba7e160e41b815260040160405180910390fd5b60006123c68261159c565b6000838152600d602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6001546001600160a01b0316331461186a576040517f38c5ab1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038616845290915290205460ff16156124bc576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546124df90615a83565b905003612518576040517f554bdff200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902054608011612560576040517fbade3a7b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260168552928520805493840181558552929093206004820401805460039092166008026101000a80840219909216919093021790915581161561260e5760008381526014602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061264457612644615a6d565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516126939190615bc3565b60405180910390a3610f24565b6126aa3382614174565b610ef9576040517f5d64832900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126eb8383836141f2565b6126f68383836142cc565b60008381526018602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b60008061275b84611f4c565b5091509150856001600160a01b0316826001600160a01b0316146127925760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166127b9576040516338f646ff60e21b815260040160405180910390fd5b6127c4868686613fde565b6001600160a01b0386166000908152600c602052604081208054600192906127ed908490615bb0565b909155506127ff905084600087613ffb565b6001600160a01b0385166000908152600c60205260408120805460019290612828908490615b29565b909155505060405184906001600160a01b0380881691908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a483856001600160a01b0316836001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8460006040516128b8929190918252602082015260400190565b60405180910390a4610ca5565b6128cf8583612985565b6001600160a01b0383161580156128e65750805115155b1561291d576040517f206b5a0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038581166000908152601b6020908152604080832080546001600160a01b0319166001600160a01b038916179055601c8252808320805467ffffffffffffffff191694891694909417909355601d81529190208251610ca592840190614bff565b6001600160401b0382166129ac576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546129cf90615a83565b905011156129f0576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152601360205260409020612a128282615c4d565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612a98576040519150601f19603f3d011682016040523d82523d6000602084013e612a9d565b606091505b5050905080610ece576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5472616e73666572206661696c65642e0000000000000000000000000000000060448201526064015b60405180910390fd5b612b17338261431a565b610ef9576040517f2728a9d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600260005403612b89576040517fc57fd5d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600055565b6040808201516001600160401b039081166000908152601b602090815283822054606086015186518452601e83528584206001600160a01b039283168086529084528685209582168552949092529390912060020154919290911615612c22576040517fbd0650ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c30836040015182614386565b6000612c4484600001518560200151610fb3565b6020810151815160808701516040517f074334fb00000000000000000000000000000000000000000000000000000000815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015612cce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf29190615b93565b612d28576040517f2c36cd3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516040517fdcb79d560000000000000000000000000000000000000000000000000000000081526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa158015612d9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc09190615b93565b612df6576040517fe451dfea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808082018352868301516001600160401b0390811683529087015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528c516000908152601e86528981208d8416825286528981208c8916825286528981208951815498518a1668010000000000000000026fffffffffffffffffffffffffffffffff1990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558a518452601f835286842090519094168352928152848220865183529052928320805492939192909190612ef4908490615b29565b92505081905550826001600160401b031685604001516001600160401b031686600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518a60800151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a461112c565b6000612f928261159c565b6000838152601a602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160a01b03831660009081526022602052604090205460ff1615610f2457600084815260116020526040902054610f2490859061304290600190615bb0565b8585613cf9565b6001600160401b038316158061306657506001600160401b038116155b15613084576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152602080805260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b613155612434565b6001600355565b6000828152601160205260409020548110156131a4576040517f3ce62f1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526011602052604081206131bb91614cb7565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b60035460010361186a576040517fed1fa96f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606060006132338361443f565b60010190506000816001600160401b0381111561325257613252614e22565b6040519080825280601f01601f19166020018201604052801561327c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461328657509392505050565b6132d0838383614521565b6132dd60008484846145b3565b610ece5760405163bcb5663760e01b815260040160405180910390fd5b6133058383836141f2565b60008381526014602090815260408083206001600160401b038086168552925282205416908082156133dd576133d783601560008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156133c457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116133815790505b505050505061231a90919063ffffffff16565b90925090505b801561345e57600086815260156020526040902080548591908490811061340657613406615a6d565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526018835260408083209187168352925220805460ff191690556134e9565b6000868152601760209081526040808320601583529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6134f48686866142cc565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4610ca5565b600080600061354885611f4c565b509150915060006135588661159c565b6001600160a01b0384166000908152600c602052604081208054929350600192909190613586908490615bb0565b90915550613598905081600088613fde565b6040805160208101909152600090526135b26000876123bb565b6135bb866146af565b60006135c68761178a565b60008881526010602052604081209192506135e19190614cb7565b60008781526011602052604081206135f891614cb7565b6000878152600d602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015613812578983106136bc5784818151811061364f5761364f615a6d565b60200260200101516020015185828151811061366d5761366d615a6d565b6020908102919091010151516040517f6177b2500000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526024820152604401612b04565b601260008683815181106136d2576136d2615a6d565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061371257613712615a6d565b602002602001015160000151815260200190815260200160002060009055828a03935084818151811061374757613747615a6d565b6020026020010151602001516001600160a01b031663b390c0ab86838151811061377357613773615a6d565b60200260200101516000015160018761378c9190615bb0565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156137cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f39190615d0c565b6137fe906001615b29565b6138089084615b29565b925060010161362e565b5060008a8152600f602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b038816907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a4604080518781526000602082018190528c9290916001600160a01b038b16917f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a46040805160208101909152600090525098975050505050505050565b6138dc8484848461274f565b6138e8848484846145b3565b610f245760405163bcb5663760e01b815260040160405180910390fd5b6001600160401b038083166000908152601b6020908152604080832054878452601e83528184206001600160a01b0391821680865290845282852087871686528452938290208251608081018452815480881682526801000000000000000090049096169386019390935260018301549185019190915260029091015416606083018190529091906139c3576040517f5f79f75c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601e602090815260408083206001600160a01b0380871685529083528184206001600160401b0388168552835281842080546fffffffffffffffffffffffffffffffff191681556001808201869055600290910180546001600160a01b0319169055898552601f8452828520606087015190921685529083528184208583015185529092528220805491929091613a60908490615bb0565b92505081905550826001600160401b0316846001600160401b0316867f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da13846040015185606001518660200151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60008381526015602052604090205481908114613b27576040517f6b037c3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152601760205260409020613b40908484614cd8565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610f24565b81613bd8576000888152601f602090815260408083206001600160a01b0388168452825280832086845290915290205415613bd8576040517f1867727400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d6188888888888888886146ba565b60008281526016602052604090205481811115613c31576040517f5134ce8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015613caf576000848152601660205260408120805483908110613c5c57613c5c615a6d565b6000918252602080832060048304015488845260148252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101613c34565b506000838152601660205260408120613cc791614d54565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6000613d058585611b32565b9050613d128184846148cd565b6001600160a01b038316600090815260126020908152604080832085845290915290205415613d6d576040517f188a497300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601160205260409020613d85908561492a565b600085815260106020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260128452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a99101612f7a565b6000806000613e2684611f4c565b509150915080600014613e4957506001600160a01b038481169116149050610c5c565b816001600160a01b0316856001600160a01b03161480613e8e57506001600160a01b038083166000908152600e602090815260408083209389168352929052205460ff165b80613eb25750846001600160a01b0316613ea785610dcf565b6001600160a01b0316145b95945050505050565b60005b6064811015613fab576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b8152600401613efa91815260200190565b606060405180830381865afa158015613f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f3b9190615d25565b92509250925080613f4f5750505050505050565b6001600160a01b03831630148015613f6657508682145b15613f9d576040517f48a87cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509093509150600101613ebe565b506040517f6934d7a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216610ece5760058054600019019055505050565b6040805180820182528381526001600160a01b0383811660208084019182526000888152600f9091529384209251835551600190920180546001600160a01b0319169290911691909117905561405190846123bb565b610ece836146af565b6040517f635490cc00000000000000000000000000000000000000000000000000000000815285906001600160a01b0382169063635490cc906140a590879087908790600401615d68565b600060405180830381600087803b1580156140bf57600080fd5b505af11580156140d3573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a482866001600160a01b0316886001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8888604051614167929190918252602082015260400190565b60405180910390a4612311565b6000806141808361159c565b9050806001600160a01b0316846001600160a01b031614806141c757506001600160a01b0380821660009081526019602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084611eae565b6001600160a01b031614949350505050565b6000838152601660205260409020548210614239576040517f0757d52100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902080548390811061425957614259615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610ece576040517f78eeeecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526016602052604090206142e490836149e4565b60009283526014602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b6000806143268361159c565b9050806001600160a01b0316846001600160a01b0316148061436d57506001600160a01b038082166000908152600e602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084610dcf565b6001600160401b0382166000908152601d6020908152604080832080548251818502810185019093528083526144059386939291908301828280156133c457600091825260209182902080546001600160401b03168452908202830192909160089101808411613381579050505050505061231a90919063ffffffff16565b91505080610ece576040517f4ef44ed500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310614488577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106144b4576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144d257662386f26fc10000830492506010015b6305f5e10083106144ea576305f5e100830492506008015b61271083106144fe57612710830492506004015b60648310614510576064830492506002015b600a8310610c5c5760010192915050565b61452e8383600084614aca565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46040805160008082526020820181905284926001600160a01b038716927f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a4505050565b60006001600160a01b0384163b156146a757604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906145f7903390899088908890600401615d87565b6020604051808303816000875af1925050508015614632575060408051601f3d908101601f1916820190925261462f91810190615dc3565b60015b61468d573d808015614660576040519150601f19603f3d011682016040523d82523d6000602084013e614665565b606091505b5080516146855760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610dc7565b506001610dc7565b610ef9600082612f87565b604080518082019091526000808252602082015282156146e5576146de8987611b32565b90506146f2565b6146ef8987610fb3565b90505b6146fd8186866148cd565b821561472057600089815260116020526040902061471b908761492a565b61475a565b6001600160a01b038516600090815260126020908152604080832087845282528083208390558b83526010909152902061475a908761492a565b6001600160a01b0388161561487657866147f0576040517fb88d4fde0000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063b88d4fde906147b99030908c9089908890600401615d87565b600060405180830381600087803b1580156147d357600080fd5b505af11580156147e7573d6000803e3d6000fd5b50505050614876565b602081015181516040517f064c0a3a0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163064c0a3a916148439130918d918d908990600401615de0565b600060405180830381600087803b15801561485d57600080fd5b505af1158015614871573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a4611ea3565b82602001516001600160a01b0316826001600160a01b03161415806148f3575082518114155b15610ece576040517fe707e58200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8154829061493a90600190615bb0565b8154811061494a5761494a615a6d565b906000526020600020906002020182828154811061496a5761496a615a6d565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b0390921691909117905581548290806149b5576149b5615e18565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b815481106149f157600080fd5b81548290614a0190600190615bb0565b81548110614a1157614a11615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110614a4c57614a4c615a6d565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480614a9557614a95615e18565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b6001600160a01b038416614b0a576040517f25bd6bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600f60205260409020600101546001600160a01b031615614b5c576040517fc5a8d37100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82614b7a576040516312c33ce360e01b815260040160405180910390fd5b614b8660008585613fde565b6001600160a01b0384166000908152600c60205260408120805460019290614baf908490615b29565b90915550506040805180820182529283526001600160a01b0394851660208085019182526000958652600f9052932091518255509051600190910180546001600160a01b03191691909216179055565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c7257835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614c28565b8015614ca55782816101000a8154906001600160401b030219169055600801602081600701049283019260010302614c72565b505b50614cb3929150614d75565b5090565b5080546000825560020290600052602060002090810190610ef99190614d8a565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c725783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614d01565b508054600082556003016004900490600052602060002090810190610ef991905b5b80821115614cb35760008155600101614d76565b5b80821115614cb357600081556001810180546001600160a01b0319169055600201614d8b565b6001600160e01b031981168114610ef957600080fd5b600060208284031215614dd957600080fd5b8135614de481614db1565b9392505050565b80356001600160401b038116811461108957600080fd5b6001600160a01b0381168114610ef957600080fd5b803561108981614e02565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e6057614e60614e22565b604052919050565b60006001600160401b03821115614e8157614e81614e22565b5060051b60200190565b600082601f830112614e9c57600080fd5b81356001600160401b03811115614eb557614eb5614e22565b614ec8601f8201601f1916602001614e38565b818152846020838601011115614edd57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614f0b57600080fd5b81356020614f20614f1b83614e68565b614e38565b82815260059290921b84018101918181019086841115614f3f57600080fd5b8286015b84811015614f6157614f5481614deb565b8352918301918301614f43565b509695505050505050565b60008060008060808587031215614f8257600080fd5b614f8b85614deb565b9350602080860135614f9c81614e02565b935060408601356001600160401b0380821115614fb857600080fd5b818801915088601f830112614fcc57600080fd5b8135614fda614f1b82614e68565b81815260059190911b8301840190848101908b831115614ff957600080fd5b8585015b83811015615031578035858111156150155760008081fd5b6150238e89838a0101614e8b565b845250918601918601614ffd565b5096505050606088013592508083111561504a57600080fd5b505061505887828801614efa565b91505092959194509250565b600080600080600060a0868803121561507c57600080fd5b853561508781614e02565b9450602086013561509781614e02565b9350604086013592506060860135915060808601356001600160401b038111156150c057600080fd5b6150cc88828901614e8b565b9150509295509295909350565b60005b838110156150f45781810151838201526020016150dc565b50506000910152565b600081518084526151158160208601602086016150d9565b601f01601f19169290920160200192915050565b602081526000614de460208301846150fd565b6000806000806080858703121561515257600080fd5b843561515d81614e02565b93506020850135925061517260408601614deb565b915061518060608601614deb565b905092959194509250565b60006020828403121561519d57600080fd5b5035919050565b600080604083850312156151b757600080fd5b82356151c281614e02565b946020939093013593505050565b6000602082840312156151e257600080fd5b8135614de481614e02565b60008060006060848603121561520257600080fd5b8335925061521260208501614deb565b915061522060408501614deb565b90509250925092565b6000806040838503121561523c57600080fd5b823561524781614e02565b9150602083013561525781614e02565b809150509250929050565b60008060006060848603121561527757600080fd5b833592506020840135915061522060408501614deb565b6000806000606084860312156152a357600080fd5b83356152ae81614e02565b925060208401356152be81614e02565b929592945050506040919091013590565b600080600080608085870312156152e557600080fd5b6152ee85614deb565b935060208501356152fe81614e02565b925060408501356001600160401b038082111561531a57600080fd5b61532688838901614e8b565b9350606087013591508082111561533c57600080fd5b5061505887828801614efa565b6000806040838503121561535c57600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610c5c565b60006020828403121561539d57600080fd5b81356001600160401b038111156153b357600080fd5b610dc784828501614e8b565b600082601f8301126153d057600080fd5b813560206153e0614f1b83614e68565b82815260059290921b840181019181810190868411156153ff57600080fd5b8286015b84811015614f615780358352918301918301615403565b60008060006060848603121561542f57600080fd5b83356001600160401b038082111561544657600080fd5b615452878388016153bf565b9450602086013591508082111561546857600080fd5b5061547586828701614efa565b92505061522060408501614deb565b600060a0828403121561549657600080fd5b60405160a081018181106001600160401b03821117156154b8576154b8614e22565b806040525082358152602083013560208201526154d760408401614deb565b60408201526154e860608401614deb565b60608201526154f960808401614deb565b60808201529392505050565b602080825282518282018190526000919060409081850190868401855b8281101561555857615548848351805182526020908101516001600160a01b0316910152565b9284019290850190600101615522565b5091979650505050505050565b6000806040838503121561557857600080fd5b82359150610faa60208401614deb565b600081518084526020808501945080840160005b838110156155c15781516001600160401b03168752958201959082019060010161559c565b509495945050505050565b6080815260006155df60808301876150fd565b6001600160401b03861660208401526001600160a01b03851660408401528281036060840152611e698185615588565b602081526000614de46020830184615588565b60008060006060848603121561563757600080fd5b833592506020840135915060408401356001600160401b0381111561565b57600080fd5b61566786828701614e8b565b9150509250925092565b60008060006060848603121561568657600080fd5b61568f84614deb565b9250602084013561521281614e02565b6000806000606084860312156156b457600080fd5b83359250602084013561521281614e02565b8015158114610ef957600080fd5b600080604083850312156156e757600080fd5b82356156f281614e02565b91506020830135615257816156c6565b600082601f83011261571357600080fd5b81356020615723614f1b83614e68565b82815260059290921b8401810191818101908684111561574257600080fd5b8286015b84811015614f615780356001600160401b038111156157655760008081fd5b6157738986838b0101614efa565b845250918301918301615746565b600080600080600060a0868803121561579957600080fd5b85356001600160401b03808211156157b057600080fd5b818801915088601f8301126157c457600080fd5b813560206157d4614f1b83614e68565b82815260059290921b8401810191818101908c8411156157f357600080fd5b948201945b8386101561581a57853561580b81614e02565b825294820194908201906157f8565b9950508901359250508082111561583057600080fd5b61583c89838a016153bf565b955061584a60408901614e17565b9450606088013591508082111561586057600080fd5b61586c89838a01614e8b565b9350608088013591508082111561588257600080fd5b506150cc88828901615702565b600080600080608085870312156158a557600080fd5b84356158b081614e02565b935060208501356158c081614e02565b92506040850135915060608501356001600160401b038111156158e257600080fd5b61505887828801614e8b565b60008060006040848603121561590357600080fd5b8335925060208401356001600160401b038082111561592157600080fd5b818601915086601f83011261593557600080fd5b81358181111561594457600080fd5b8760208260051b850101111561595957600080fd5b6020830194508093505050509250925092565b600080600080600080600080610100898b03121561598957600080fd5b88359750602089013561599b81614e02565b9650604089013595506060890135945060808901356159b981614e02565b935060a0890135925060c08901356159d0816156c6565b915060e08901356001600160401b038111156159eb57600080fd5b6159f78b828c01614e8b565b9150509295985092959890939650565b60008060008060808587031215615a1d57600080fd5b84359350602085013592506040850135615a3681614e02565b9396929550929360600135925050565b600080600060608486031215615a5b57600080fd5b8335925060208401356152be81614e02565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680615a9757607f821691505b602082108103615ab757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610c5c57610c5c615abd565b600082615b0757634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615b1e57600080fd5b8151614de481614e02565b80820180821115610c5c57610c5c615abd565b60008351615b4e8184602088016150d9565b835190830190615b628183602088016150d9565b7f2e6a736f6e0000000000000000000000000000000000000000000000000000009101908152600501949350505050565b600060208284031215615ba557600080fd5b8151614de4816156c6565b81810381811115610c5c57610c5c615abd565b6020808252825182820181905260009190848201906040850190845b81811015615bfb57835183529284019291840191600101615bdf565b50909695505050505050565b601f821115610ece57600081815260208120601f850160051c81016020861015615c2e5750805b601f850160051c820191505b81811015610ca557828155600101615c3a565b81516001600160401b03811115615c6657615c66614e22565b615c7a81615c748454615a83565b84615c07565b602080601f831160018114615caf5760008415615c975750858301515b600019600386901b1c1916600185901b178555610ca5565b600085815260208120601f198616915b82811015615cde57888601518255948401946001909101908401615cbf565b5085821015615cfc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615d1e57600080fd5b5051919050565b600080600060608486031215615d3a57600080fd5b8351615d4581614e02565b602085015160408601519194509250615d5d816156c6565b809150509250925092565b838152826020820152606060408201526000613eb260608301846150fd565b60006001600160a01b03808716835280861660208401525083604083015260806060830152615db960808301846150fd565b9695505050505050565b600060208284031215615dd557600080fd5b8151614de481614db1565b60006001600160a01b03808816835280871660208401525084604083015283606083015260a06080830152611e6960a08301846150fd565b634e487b7160e01b600052603160045260246000fdfea264697066735822122047b1c52c6a661e31a9451ea2f083f2a14788bea6dac65656fb85b2f841f3641f64736f6c634300081200330000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b32153aa5b3dd930d265bc800d2f7d657c994828000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000075370616365727300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000753504143455253000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f516d506f7268456a4479337348753548564d465735426277743835335378384132436667614845737a556d5643342f537061636572732e6a736f6e000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104725760003560e01c80636f19951c11610250578063b390c0ab11610150578063e467a48f116100c8578063ee1dffcf11610097578063fb25fb7a1161007c578063fb25fb7a14610b60578063fc3517c814610b9a578063ffa1ad7414610bad57600080fd5b8063ee1dffcf14610b0e578063f2fde38b14610b4d57600080fd5b8063e467a48f14610a99578063e7de4de414610aac578063e97ceaa814610abf578063e985e9c514610ad257600080fd5b8063c87b56dd1161011f578063de8e602c11610104578063de8e602c14610a3c578063defa80c314610a4f578063df6f556b14610a6257600080fd5b8063c87b56dd14610a21578063d5abeb0114610a3457600080fd5b8063b390c0ab146109c2578063b88d4fde146109d5578063c259a988146109e8578063c5610a29146109fb57600080fd5b80638507dc28116101e357806395edc18c116101b2578063a22cb46511610197578063a22cb46514610989578063a898e3641461099c578063ace61e00146109af57600080fd5b806395edc18c14610965578063979613361461097657600080fd5b80638507dc281461093157806389ed2edf146109445780638da5cb5b1461094c57806395d89b411461095d57600080fd5b80637507e2ae1161021f5780637507e2ae1461081b57806379e8ca9e1461090b5780637aba6f371461091e5780637e5852d91461092657600080fd5b80636f19951c146107da57806370a08231146107ed578063715018a6146108005780637280281e1461080857600080fd5b80632f32f937116103765780634e60edba116102ee5780635e94354a116102bd5780636352211e116102a25780636352211e146107a1578063635490cc146107b457806365b67eb9146107c757600080fd5b80635e94354a1461077b5780635ea72f361461078e57600080fd5b80634e60edba1461071d57806351532e5a1461074057806359c8b7dd146107605780635e2e32921461076857600080fd5b8063362b67f41161034557806342842e0e1161032a57806342842e0e146106d757806342966c68146106ea57806344ec9344146106fd57600080fd5b8063362b67f4146106b157806338dcf74c146106c457600080fd5b80632f32f9371461065857806330ffb1d614610678578063326d526d1461068b57806333ed28451461069e57600080fd5b80630fc499f51161040957806322e6d160116103d857806323b872dd116103bd57806323b872dd146105e85780632452cd91146105fb5780632a55205a1461062657600080fd5b806322e6d1601461059957806322f6da9c146105d557600080fd5b80630fc499f51461053f57806318160ddd146105525780631c7bb4611461055a5780631d0d35f51461056d57600080fd5b806306fdde031161044557806306fdde03146104d9578063074334fb146104ee578063081812fc14610501578063095ea7b31461052c57600080fd5b806301e1d1141461047757806301ffc9a71461048e57806302549760146104b1578063064c0a3a146104c6575b600080fd5b6021545b6040519081526020015b60405180910390f35b6104a161049c366004614dc7565b610be9565b6040519015158152602001610485565b6104c46104bf366004614f6c565b610c62565b005b6104c46104d4366004615064565b610cad565b6104e1610cc4565b6040516104859190615129565b6104a16104fc36600461513c565b610d56565b61051461050f36600461518b565b610dcf565b6040516001600160a01b039091168152602001610485565b6104c461053a3660046151a4565b610e15565b6104c461054d3660046151d0565b610ed3565b60055461047b565b6104c46105683660046151ed565b610efc565b6104a161057b3660046151d0565b6001600160a01b031660009081526002602052604090205460011490565b6104a16105a7366004615229565b6001600160a01b03918216600090815260196020908152604080832093909416825291909152205460ff1690565b6104c46105e3366004615262565b610f0f565b6104c46105f636600461528e565b610f2a565b61060e6106093660046152cf565b610f4f565b6040516001600160401b039091168152602001610485565b610639610634366004615349565b610f7d565b604080516001600160a01b039093168352602083019190915201610485565b61066b610666366004615349565b610fb3565b604051610485919061536b565b61047b61068636600461538b565b611065565b6104c46106993660046151d0565b61108e565b6104c46106ac3660046151a4565b6110ba565b6104c46106bf36600461541a565b6110d0565b6104c46106d2366004615484565b611133565b6104c46106e536600461528e565b611158565b6104c46106f836600461518b565b611173565b61071061070b36600461518b565b61117e565b6040516104859190615505565b61073061072b366004615565565b6111fd565b60405161048594939291906155cc565b61075361074e36600461518b565b6112da565b604051610485919061560f565b60095461047b565b6104e1610776366004615565565b61136e565b61075361078936600461518b565b611476565b6104c461079c3660046151a4565b6114df565b6105146107af36600461518b565b61159c565b6104c46107c2366004615622565b611645565b6104c46107d5366004615671565b611777565b6107106107e836600461518b565b61178a565b61047b6107fb3660046151d0565b6117fa565b6104c4611858565b61075361081636600461518b565b61186c565b6108bd61082936600461569f565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601e87528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352680100000000000000009004909416958101959095526001830154918501919091526002909101549091169082015290565b604051610485919060006080820190506001600160401b0380845116835280602085015116602084015250604083015160408301526001600160a01b03606084015116606083015292915050565b6104c46109193660046156d4565b6118d5565b6104c46119ab565b6003546001146104a1565b6104c461093f3660046156d4565b6119c3565b6104e1611a59565b6001546001600160a01b0316610514565b6104e1611a68565b6008546001600160a01b0316610514565b6104c4610984366004615349565b611a77565b6104c46109973660046156d4565b611a8b565b61066b6109aa366004615349565b611b32565b6104c46109bd366004615781565b611ba8565b61047b6109d0366004615349565b611d6b565b6104c46109e336600461588f565b611d81565b6104c46109f63660046151ed565b611d97565b7f0000000000000000000000000000000000000000000000000de0b6b3a764000061047b565b6104e1610a2f36600461518b565b611dac565b60065461047b565b6104c4610a4a3660046158ee565b611e74565b6104c4610a5d36600461596c565b611e89565b61060e610a70366004615565565b60009182526014602090815260408084206001600160401b039384168552909152909120541690565b610514610aa736600461518b565b611eae565b6104c4610aba366004615349565b611ed1565b6104c4610acd366004615a07565b611ee5565b6104a1610ae0366004615229565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205460ff1690565b6104a1610b1c366004615a46565b6000928352601f602090815260408085206001600160a01b0394909416855292815282842091845252902054151590565b6104c4610b5b3660046151d0565b611efb565b610b73610b6e36600461518b565b611f4c565b604080516001600160a01b0390941684526020840192909252151590820152606001610485565b6104c4610ba8366004615262565b611fb4565b6104e16040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b6000610bf482611fc9565b80610c2857506001600160e01b031982167f06b4329a00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982167f28bc9ae400000000000000000000000000000000000000000000000000000000145b92915050565b610c6a612067565b815160005b81811015610ca557610c9c8686868481518110610c8e57610c8e615a6d565b602002602001015186610f4f565b50600101610c6f565b505050505050565b82610cb7816120bf565b610ca586868686866120ff565b6060600a8054610cd390615a83565b80601f0160208091040260200160405190810160405280929190818152602001828054610cff90615a83565b8015610d4c5780601f10610d2157610100808354040283529160200191610d4c565b820191906000526020600020905b815481529060010190602001808311610d2f57829003601f168201915b5050505050905090565b6001600160401b038083166000908152601c602090815260408083205484168084528280528184206001600160a01b038a1685529092528220549192909181169084168103610dc0576000610db486610dae8961186c565b9061231a565b9450610dc79350505050565b6000925050505b949350505050565b6000610dda82612383565b6000828152600d6020526040812090610df28461159c565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6000610e208261159c565b9050806001600160a01b0316836001600160a01b031603610e6d576040517f0591db6d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03821614801590610e8d5750610e8b8133610ae0565b155b15610ec4576040517f982462b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece83836123bb565b505050565b610edb612434565b600880546001600160a01b0319166001600160a01b03831617905550565b50565b610f04612067565b610ece838383612478565b82610f19816126a0565b610f248484846126e0565b50505050565b80610f34816120bf565b610f248484846040518060200160405280600081525061274f565b6000610f59612067565b6021805460010190819055610f7190868686866128c5565b50602154949350505050565b6008546009546001600160a01b039091169060009061271090610fa09085615ad3565b610faa9190615aea565b90509250929050565b604080518082019091526000808252602082015281610fd18461178a565b5111611009576040517fca7cc84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040812080548490811061102957611029615a6d565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b600061106f612067565b60218054600101908190556110849083612985565b506021545b919050565b611096612434565b6001600160a01b03166000908152602260205260409020805460ff19166001179055565b6110c2612434565b6110cc8282612a4b565b5050565b6110d8612067565b825160005b8181101561112c57611124856000815181106110fb576110fb615a6d565b60200260200101518560008151811061111657611116615a6d565b602002602001015185612478565b6001016110dd565b5050505050565b805161113e81612b0d565b611146612b4d565b61114f82612b90565b50506001600055565b610ece83838360405180602001604052806000815250611d81565b6110cc816000611d6b565b6000818152601160209081526040808320805482518185028101850190935280835260609493849084015b828210156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b50929695505050505050565b6060600080606061120e868661136e565b6001600160401b038087166000908152601c6020908152604080832054601b835281842054601d845293829020805483518186028101860190945280845291909516946001600160a01b0390941693928391908301828280156112c257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161127f5790505b50505050509050935093509350935092959194509250565b60008181526016602090815260409182902080548351818402810184019094528084526060939283018282801561136257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161131f5790505b50505050509050919050565b60008281526018602090815260408083206001600160401b038516845290915290205460609060ff166113cd576040517fdcc947e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038216600090815260136020526040902080546113f090615a83565b80601f016020809104026020016040519081016040528092919081815260200182805461141c90615a83565b80156114695780601f1061143e57610100808354040283529160200191611469565b820191906000526020600020905b81548152906001019060200180831161144c57829003601f168201915b5050505050905092915050565b60008181526017602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b60006114ea8261159c565b9050806001600160a01b0316836001600160a01b03160361151e576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b0382161480159061155b57506001600160a01b038116600090815260196020908152604080832033845290915290205460ff16155b15611592576040517faf45d29200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ece8383612f87565b6000806000806115ab85611f4c565b925092509250801561163c576040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015611615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116399190615b0c565b92505b50909392505050565b61164e83612383565b33803b61166e5760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b038216602082015260006116958661117e565b51905060808110156116f4576000868152601160209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055611726565b6040517fa53c8c0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd8460405161176391815260200190565b60405180910390a4610ca586848787613000565b61177f612067565b610ece838383613049565b60008181526010602090815260408083208054825181850281018501909352808352606094938490840182156111f15760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016111a9565b60006001600160a01b03821661183c576040517f7772286600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b611860612434565b61186a60006130fb565b565b60008181526015602090815260409182902080548351818402810184019094528084526060939283018282801561136257600091825260209182902080546001600160401b0316845290820283019290916008910180841161131f575094979650505050505050565b6118dd612434565b6001600160a01b03821661191d576040517fb5c0938000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611942576001600160a01b0382166000908152600260205260408120819055611961565b6001600160a01b03821660009081526002602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac28260405161199f911515815260200190565b60405180910390a25050565b6119b3612434565b6119bb61314d565b600554600655565b6001600160a01b03821633036119ec576040516375f45abd60e01b815260040160405180910390fd5b3360008181526019602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060078054610cd390615a83565b6060600b8054610cd390615a83565b81611a8181612b0d565b610ece838361315c565b6001600160a01b0382163303611acd576040517f2dee66e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611a4d565b604080518082019091526000808252602082015281611b508461117e565b5111611b88576040517fda22687f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260116020526040812080548490811061102957611029615a6d565b611bb06131ea565b611bb8612067565b83516000819003611bf5576040517f376bec4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600654600454611c059083615b29565b1115611c3d576040517fbd239bf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480548201905560058054820190556000805b82811015611d6157611cc160008787611c828b8681518110611c7557611c75615a6d565b6020026020010151613226565b604051602001611c93929190615b3c565b604051602081830303815290604052878581518110611cb457611cb4615a6d565b6020026020010151610f4f565b9150611d0f888281518110611cd857611cd8615a6d565b6020026020010151888381518110611cf257611cf2615a6d565b6020026020010151604051806020016040528060008152506132c5565b611d34878281518110611d2457611d24615a6d565b6020026020010151836000612478565b611d59878281518110611d4957611d49615a6d565b60200260200101516000846132fa565b600101611c51565b5050505050505050565b600082611d77816120bf565b610dc7848461353a565b81611d8b816120bf565b61112c858585856138d0565b82611da181612b0d565b610f24848484613905565b6060611db782612383565b6000611dc283611476565b8051909150603d6000805b83816001600160401b03161015611e2f57600085826001600160401b031681518110611dfb57611dfb615a6d565b60200260200101519050836001600160401b0316816001600160401b03161015611e26578093508192505b50600101611dcd565b506000611e3b8761186c565b826001600160401b031681518110611e5557611e55615a6d565b60200260200101519050611e69878261136e565b979650505050505050565b82611e7e816126a0565b610f24848484613ade565b87611e9381612b0d565b611ea38989898989898989613b71565b505050505050505050565b6000611eb982612383565b6000828152601a6020526040812090610df28461159c565b81611edb816126a0565b610ece8383613be8565b83611eef81612b0d565b61112c85858585613cf9565b611f03612434565b6001600160a01b038116611f43576040517f4ece6ecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ef9816130fb565b6000818152600f60209081526040808320815180830190925280548252600101546001600160a01b03169181018290528291829190611f9e5760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b82611fbe816126a0565b610f248484846132fa565b60006001600160e01b031982166301ffc9a760e01b148061201357506001600160e01b031982167f80ac58cd00000000000000000000000000000000000000000000000000000000145b8061204757506001600160e01b031982167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610c5c57506001600160e01b031982166342b0e56f60e01b1492915050565b6001546001600160a01b0316331480159061208857506120863361057b565b155b1561186a576040517f1eca167000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120c93382613e18565b610ef9576040517f8be7930600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061210b85611f4c565b5091509150866001600160a01b0316826001600160a01b0316146121425760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616612169576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561218057508385145b156121b7576040517f3d76b10700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0386163b6121df5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa15801561222a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224e9190615b93565b612284576040517f5e13b1cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61228f858786613ebb565b61229a878787613fde565b6001600160a01b0387166000908152600c602052604081208054600192906122c3908490615bb0565b909155506122d49050858588613ffb565b6001600160a01b0386166000908152600c602052604081208054600192906122fd908490615b29565b90915550612311905082878387898861405a565b50505050505050565b81516000908190815b8181101561237257846001600160401b031686828151811061234757612347615a6d565b60200260200101516001600160401b03160361236a5792506001915061237c9050565b600101612323565b5060008092509250505b9250929050565b6000818152600f60205260409020600101546001600160a01b0316610ef95760405163089ba7e160e41b815260040160405180910390fd5b60006123c68261159c565b6000838152600d602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6001546001600160a01b0316331461186a576040517f38c5ab1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038616845290915290205460ff16156124bc576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546124df90615a83565b905003612518576040517f554bdff200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902054608011612560576040517fbade3a7b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260168552928520805493840181558552929093206004820401805460039092166008026101000a80840219909216919093021790915581161561260e5760008381526014602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061264457612644615a6d565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516126939190615bc3565b60405180910390a3610f24565b6126aa3382614174565b610ef9576040517f5d64832900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126eb8383836141f2565b6126f68383836142cc565b60008381526018602090815260408083206001600160401b0385168085529252808320805460ff1916905551909185917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a3505050565b60008061275b84611f4c565b5091509150856001600160a01b0316826001600160a01b0316146127925760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166127b9576040516338f646ff60e21b815260040160405180910390fd5b6127c4868686613fde565b6001600160a01b0386166000908152600c602052604081208054600192906127ed908490615bb0565b909155506127ff905084600087613ffb565b6001600160a01b0385166000908152600c60205260408120805460019290612828908490615b29565b909155505060405184906001600160a01b0380881691908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a483856001600160a01b0316836001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8460006040516128b8929190918252602082015260400190565b60405180910390a4610ca5565b6128cf8583612985565b6001600160a01b0383161580156128e65750805115155b1561291d576040517f206b5a0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160401b038581166000908152601b6020908152604080832080546001600160a01b0319166001600160a01b038916179055601c8252808320805467ffffffffffffffff191694891694909417909355601d81529190208251610ca592840190614bff565b6001600160401b0382166129ac576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038216600090815260136020526040812080546129cf90615a83565b905011156129f0576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152601360205260409020612a128282615c4d565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612a98576040519150601f19603f3d011682016040523d82523d6000602084013e612a9d565b606091505b5050905080610ece576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5472616e73666572206661696c65642e0000000000000000000000000000000060448201526064015b60405180910390fd5b612b17338261431a565b610ef9576040517f2728a9d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600260005403612b89576040517fc57fd5d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600055565b6040808201516001600160401b039081166000908152601b602090815283822054606086015186518452601e83528584206001600160a01b039283168086529084528685209582168552949092529390912060020154919290911615612c22576040517fbd0650ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c30836040015182614386565b6000612c4484600001518560200151610fb3565b6020810151815160808701516040517f074334fb00000000000000000000000000000000000000000000000000000000815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015612cce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf29190615b93565b612d28576040517f2c36cd3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208101516040517fdcb79d560000000000000000000000000000000000000000000000000000000081526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa158015612d9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc09190615b93565b612df6576040517fe451dfea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808082018352868301516001600160401b0390811683529087015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528c516000908152601e86528981208d8416825286528981208c8916825286528981208951815498518a1668010000000000000000026fffffffffffffffffffffffffffffffff1990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558a518452601f835286842090519094168352928152848220865183529052928320805492939192909190612ef4908490615b29565b92505081905550826001600160401b031685604001516001600160401b031686600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518a60800151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a461112c565b6000612f928261159c565b6000838152601a602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160a01b03831660009081526022602052604090205460ff1615610f2457600084815260116020526040902054610f2490859061304290600190615bb0565b8585613cf9565b6001600160401b038316158061306657506001600160401b038116155b15613084576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152602080805260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b613155612434565b6001600355565b6000828152601160205260409020548110156131a4576040517f3ce62f1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526011602052604081206131bb91614cb7565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b60035460010361186a576040517fed1fa96f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606060006132338361443f565b60010190506000816001600160401b0381111561325257613252614e22565b6040519080825280601f01601f19166020018201604052801561327c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461328657509392505050565b6132d0838383614521565b6132dd60008484846145b3565b610ece5760405163bcb5663760e01b815260040160405180910390fd5b6133058383836141f2565b60008381526014602090815260408083206001600160401b038086168552925282205416908082156133dd576133d783601560008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156133c457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116133815790505b505050505061231a90919063ffffffff16565b90925090505b801561345e57600086815260156020526040902080548591908490811061340657613406615a6d565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526018835260408083209187168352925220805460ff191690556134e9565b6000868152601760209081526040808320601583529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b6134f48686866142cc565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4610ca5565b600080600061354885611f4c565b509150915060006135588661159c565b6001600160a01b0384166000908152600c602052604081208054929350600192909190613586908490615bb0565b90915550613598905081600088613fde565b6040805160208101909152600090526135b26000876123bb565b6135bb866146af565b60006135c68761178a565b60008881526010602052604081209192506135e19190614cb7565b60008781526011602052604081206135f891614cb7565b6000878152600d602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015613812578983106136bc5784818151811061364f5761364f615a6d565b60200260200101516020015185828151811061366d5761366d615a6d565b6020908102919091010151516040517f6177b2500000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526024820152604401612b04565b601260008683815181106136d2576136d2615a6d565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600086838151811061371257613712615a6d565b602002602001015160000151815260200190815260200160002060009055828a03935084818151811061374757613747615a6d565b6020026020010151602001516001600160a01b031663b390c0ab86838151811061377357613773615a6d565b60200260200101516000015160018761378c9190615bb0565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156137cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f39190615d0c565b6137fe906001615b29565b6138089084615b29565b925060010161362e565b5060008a8152600f602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b038816907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a4604080518781526000602082018190528c9290916001600160a01b038b16917f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a46040805160208101909152600090525098975050505050505050565b6138dc8484848461274f565b6138e8848484846145b3565b610f245760405163bcb5663760e01b815260040160405180910390fd5b6001600160401b038083166000908152601b6020908152604080832054878452601e83528184206001600160a01b0391821680865290845282852087871686528452938290208251608081018452815480881682526801000000000000000090049096169386019390935260018301549185019190915260029091015416606083018190529091906139c3576040517f5f79f75c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601e602090815260408083206001600160a01b0380871685529083528184206001600160401b0388168552835281842080546fffffffffffffffffffffffffffffffff191681556001808201869055600290910180546001600160a01b0319169055898552601f8452828520606087015190921685529083528184208583015185529092528220805491929091613a60908490615bb0565b92505081905550826001600160401b0316846001600160401b0316867f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da13846040015185606001518660200151604051612f7a939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60008381526015602052604090205481908114613b27576040517f6b037c3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152601760205260409020613b40908484614cd8565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610f24565b81613bd8576000888152601f602090815260408083206001600160a01b0388168452825280832086845290915290205415613bd8576040517f1867727400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d6188888888888888886146ba565b60008281526016602052604090205481811115613c31576040517f5134ce8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015613caf576000848152601660205260408120805483908110613c5c57613c5c615a6d565b6000918252602080832060048304015488845260148252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff1916905550600101613c34565b506000838152601660205260408120613cc791614d54565b60405160009084907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3505050565b6000613d058585611b32565b9050613d128184846148cd565b6001600160a01b038316600090815260126020908152604080832085845290915290205415613d6d576040517f188a497300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152601160205260409020613d85908561492a565b600085815260106020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260128452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a99101612f7a565b6000806000613e2684611f4c565b509150915080600014613e4957506001600160a01b038481169116149050610c5c565b816001600160a01b0316856001600160a01b03161480613e8e57506001600160a01b038083166000908152600e602090815260408083209389168352929052205460ff165b80613eb25750846001600160a01b0316613ea785610dcf565b6001600160a01b0316145b95945050505050565b60005b6064811015613fab576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b8152600401613efa91815260200190565b606060405180830381865afa158015613f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f3b9190615d25565b92509250925080613f4f5750505050505050565b6001600160a01b03831630148015613f6657508682145b15613f9d576040517f48a87cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509093509150600101613ebe565b506040517f6934d7a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216610ece5760058054600019019055505050565b6040805180820182528381526001600160a01b0383811660208084019182526000888152600f9091529384209251835551600190920180546001600160a01b0319169290911691909117905561405190846123bb565b610ece836146af565b6040517f635490cc00000000000000000000000000000000000000000000000000000000815285906001600160a01b0382169063635490cc906140a590879087908790600401615d68565b600060405180830381600087803b1580156140bf57600080fd5b505af11580156140d3573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a482866001600160a01b0316886001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d8888604051614167929190918252602082015260400190565b60405180910390a4612311565b6000806141808361159c565b9050806001600160a01b0316846001600160a01b031614806141c757506001600160a01b0380821660009081526019602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084611eae565b6001600160a01b031614949350505050565b6000838152601660205260409020548210614239576040517f0757d52100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260166020526040902080548390811061425957614259615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610ece576040517f78eeeecf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526016602052604090206142e490836149e4565b60009283526014602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b6000806143268361159c565b9050806001600160a01b0316846001600160a01b0316148061436d57506001600160a01b038082166000908152600e602090815260408083209388168352929052205460ff165b80610dc75750836001600160a01b03166141e084610dcf565b6001600160401b0382166000908152601d6020908152604080832080548251818502810185019093528083526144059386939291908301828280156133c457600091825260209182902080546001600160401b03168452908202830192909160089101808411613381579050505050505061231a90919063ffffffff16565b91505080610ece576040517f4ef44ed500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310614488577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106144b4576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144d257662386f26fc10000830492506010015b6305f5e10083106144ea576305f5e100830492506008015b61271083106144fe57612710830492506004015b60648310614510576064830492506002015b600a8310610c5c5760010192915050565b61452e8383600084614aca565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46040805160008082526020820181905284926001600160a01b038716927f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a4505050565b60006001600160a01b0384163b156146a757604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906145f7903390899088908890600401615d87565b6020604051808303816000875af1925050508015614632575060408051601f3d908101601f1916820190925261462f91810190615dc3565b60015b61468d573d808015614660576040519150601f19603f3d011682016040523d82523d6000602084013e614665565b606091505b5080516146855760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610dc7565b506001610dc7565b610ef9600082612f87565b604080518082019091526000808252602082015282156146e5576146de8987611b32565b90506146f2565b6146ef8987610fb3565b90505b6146fd8186866148cd565b821561472057600089815260116020526040902061471b908761492a565b61475a565b6001600160a01b038516600090815260126020908152604080832087845282528083208390558b83526010909152902061475a908761492a565b6001600160a01b0388161561487657866147f0576040517fb88d4fde0000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063b88d4fde906147b99030908c9089908890600401615d87565b600060405180830381600087803b1580156147d357600080fd5b505af11580156147e7573d6000803e3d6000fd5b50505050614876565b602081015181516040517f064c0a3a0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163064c0a3a916148439130918d918d908990600401615de0565b600060405180830381600087803b15801561485d57600080fd5b505af1158015614871573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a4611ea3565b82602001516001600160a01b0316826001600160a01b03161415806148f3575082518114155b15610ece576040517fe707e58200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8154829061493a90600190615bb0565b8154811061494a5761494a615a6d565b906000526020600020906002020182828154811061496a5761496a615a6d565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b0390921691909117905581548290806149b5576149b5615e18565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b815481106149f157600080fd5b81548290614a0190600190615bb0565b81548110614a1157614a11615a6d565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316828281548110614a4c57614a4c615a6d565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555081805480614a9557614a95615e18565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b6001600160a01b038416614b0a576040517f25bd6bd300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600f60205260409020600101546001600160a01b031615614b5c576040517fc5a8d37100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82614b7a576040516312c33ce360e01b815260040160405180910390fd5b614b8660008585613fde565b6001600160a01b0384166000908152600c60205260408120805460019290614baf908490615b29565b90915550506040805180820182529283526001600160a01b0394851660208085019182526000958652600f9052932091518255509051600190910180546001600160a01b03191691909216179055565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c7257835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614c28565b8015614ca55782816101000a8154906001600160401b030219169055600801602081600701049283019260010302614c72565b505b50614cb3929150614d75565b5090565b5080546000825560020290600052602060002090810190610ef99190614d8a565b82805482825590600052602060002090600301600490048101928215614ca75791602002820160005b83821115614c725783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614d01565b508054600082556003016004900490600052602060002090810190610ef991905b5b80821115614cb35760008155600101614d76565b5b80821115614cb357600081556001810180546001600160a01b0319169055600201614d8b565b6001600160e01b031981168114610ef957600080fd5b600060208284031215614dd957600080fd5b8135614de481614db1565b9392505050565b80356001600160401b038116811461108957600080fd5b6001600160a01b0381168114610ef957600080fd5b803561108981614e02565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614e6057614e60614e22565b604052919050565b60006001600160401b03821115614e8157614e81614e22565b5060051b60200190565b600082601f830112614e9c57600080fd5b81356001600160401b03811115614eb557614eb5614e22565b614ec8601f8201601f1916602001614e38565b818152846020838601011115614edd57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112614f0b57600080fd5b81356020614f20614f1b83614e68565b614e38565b82815260059290921b84018101918181019086841115614f3f57600080fd5b8286015b84811015614f6157614f5481614deb565b8352918301918301614f43565b509695505050505050565b60008060008060808587031215614f8257600080fd5b614f8b85614deb565b9350602080860135614f9c81614e02565b935060408601356001600160401b0380821115614fb857600080fd5b818801915088601f830112614fcc57600080fd5b8135614fda614f1b82614e68565b81815260059190911b8301840190848101908b831115614ff957600080fd5b8585015b83811015615031578035858111156150155760008081fd5b6150238e89838a0101614e8b565b845250918601918601614ffd565b5096505050606088013592508083111561504a57600080fd5b505061505887828801614efa565b91505092959194509250565b600080600080600060a0868803121561507c57600080fd5b853561508781614e02565b9450602086013561509781614e02565b9350604086013592506060860135915060808601356001600160401b038111156150c057600080fd5b6150cc88828901614e8b565b9150509295509295909350565b60005b838110156150f45781810151838201526020016150dc565b50506000910152565b600081518084526151158160208601602086016150d9565b601f01601f19169290920160200192915050565b602081526000614de460208301846150fd565b6000806000806080858703121561515257600080fd5b843561515d81614e02565b93506020850135925061517260408601614deb565b915061518060608601614deb565b905092959194509250565b60006020828403121561519d57600080fd5b5035919050565b600080604083850312156151b757600080fd5b82356151c281614e02565b946020939093013593505050565b6000602082840312156151e257600080fd5b8135614de481614e02565b60008060006060848603121561520257600080fd5b8335925061521260208501614deb565b915061522060408501614deb565b90509250925092565b6000806040838503121561523c57600080fd5b823561524781614e02565b9150602083013561525781614e02565b809150509250929050565b60008060006060848603121561527757600080fd5b833592506020840135915061522060408501614deb565b6000806000606084860312156152a357600080fd5b83356152ae81614e02565b925060208401356152be81614e02565b929592945050506040919091013590565b600080600080608085870312156152e557600080fd5b6152ee85614deb565b935060208501356152fe81614e02565b925060408501356001600160401b038082111561531a57600080fd5b61532688838901614e8b565b9350606087013591508082111561533c57600080fd5b5061505887828801614efa565b6000806040838503121561535c57600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610c5c565b60006020828403121561539d57600080fd5b81356001600160401b038111156153b357600080fd5b610dc784828501614e8b565b600082601f8301126153d057600080fd5b813560206153e0614f1b83614e68565b82815260059290921b840181019181810190868411156153ff57600080fd5b8286015b84811015614f615780358352918301918301615403565b60008060006060848603121561542f57600080fd5b83356001600160401b038082111561544657600080fd5b615452878388016153bf565b9450602086013591508082111561546857600080fd5b5061547586828701614efa565b92505061522060408501614deb565b600060a0828403121561549657600080fd5b60405160a081018181106001600160401b03821117156154b8576154b8614e22565b806040525082358152602083013560208201526154d760408401614deb565b60408201526154e860608401614deb565b60608201526154f960808401614deb565b60808201529392505050565b602080825282518282018190526000919060409081850190868401855b8281101561555857615548848351805182526020908101516001600160a01b0316910152565b9284019290850190600101615522565b5091979650505050505050565b6000806040838503121561557857600080fd5b82359150610faa60208401614deb565b600081518084526020808501945080840160005b838110156155c15781516001600160401b03168752958201959082019060010161559c565b509495945050505050565b6080815260006155df60808301876150fd565b6001600160401b03861660208401526001600160a01b03851660408401528281036060840152611e698185615588565b602081526000614de46020830184615588565b60008060006060848603121561563757600080fd5b833592506020840135915060408401356001600160401b0381111561565b57600080fd5b61566786828701614e8b565b9150509250925092565b60008060006060848603121561568657600080fd5b61568f84614deb565b9250602084013561521281614e02565b6000806000606084860312156156b457600080fd5b83359250602084013561521281614e02565b8015158114610ef957600080fd5b600080604083850312156156e757600080fd5b82356156f281614e02565b91506020830135615257816156c6565b600082601f83011261571357600080fd5b81356020615723614f1b83614e68565b82815260059290921b8401810191818101908684111561574257600080fd5b8286015b84811015614f615780356001600160401b038111156157655760008081fd5b6157738986838b0101614efa565b845250918301918301615746565b600080600080600060a0868803121561579957600080fd5b85356001600160401b03808211156157b057600080fd5b818801915088601f8301126157c457600080fd5b813560206157d4614f1b83614e68565b82815260059290921b8401810191818101908c8411156157f357600080fd5b948201945b8386101561581a57853561580b81614e02565b825294820194908201906157f8565b9950508901359250508082111561583057600080fd5b61583c89838a016153bf565b955061584a60408901614e17565b9450606088013591508082111561586057600080fd5b61586c89838a01614e8b565b9350608088013591508082111561588257600080fd5b506150cc88828901615702565b600080600080608085870312156158a557600080fd5b84356158b081614e02565b935060208501356158c081614e02565b92506040850135915060608501356001600160401b038111156158e257600080fd5b61505887828801614e8b565b60008060006040848603121561590357600080fd5b8335925060208401356001600160401b038082111561592157600080fd5b818601915086601f83011261593557600080fd5b81358181111561594457600080fd5b8760208260051b850101111561595957600080fd5b6020830194508093505050509250925092565b600080600080600080600080610100898b03121561598957600080fd5b88359750602089013561599b81614e02565b9650604089013595506060890135945060808901356159b981614e02565b935060a0890135925060c08901356159d0816156c6565b915060e08901356001600160401b038111156159eb57600080fd5b6159f78b828c01614e8b565b9150509295985092959890939650565b60008060008060808587031215615a1d57600080fd5b84359350602085013592506040850135615a3681614e02565b9396929550929360600135925050565b600080600060608486031215615a5b57600080fd5b8335925060208401356152be81614e02565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680615a9757607f821691505b602082108103615ab757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610c5c57610c5c615abd565b600082615b0757634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615b1e57600080fd5b8151614de481614e02565b80820180821115610c5c57610c5c615abd565b60008351615b4e8184602088016150d9565b835190830190615b628183602088016150d9565b7f2e6a736f6e0000000000000000000000000000000000000000000000000000009101908152600501949350505050565b600060208284031215615ba557600080fd5b8151614de4816156c6565b81810381811115610c5c57610c5c615abd565b6020808252825182820181905260009190848201906040850190845b81811015615bfb57835183529284019291840191600101615bdf565b50909695505050505050565b601f821115610ece57600081815260208120601f850160051c81016020861015615c2e5750805b601f850160051c820191505b81811015610ca557828155600101615c3a565b81516001600160401b03811115615c6657615c66614e22565b615c7a81615c748454615a83565b84615c07565b602080601f831160018114615caf5760008415615c975750858301515b600019600386901b1c1916600185901b178555610ca5565b600085815260208120601f198616915b82811015615cde57888601518255948401946001909101908401615cbf565b5085821015615cfc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215615d1e57600080fd5b5051919050565b600080600060608486031215615d3a57600080fd5b8351615d4581614e02565b602085015160408601519194509250615d5d816156c6565b809150509250925092565b838152826020820152606060408201526000613eb260608301846150fd565b60006001600160a01b03808716835280861660208401525083604083015260806060830152615db960808301846150fd565b9695505050505050565b600060208284031215615dd557600080fd5b8151614de481614db1565b60006001600160a01b03808816835280871660208401525084604083015283606083015260a06080830152611e6960a08301846150fd565b634e487b7160e01b600052603160045260246000fdfea264697066735822122047b1c52c6a661e31a9451ea2f083f2a14788bea6dac65656fb85b2f841f3641f64736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b32153aa5b3dd930d265bc800d2f7d657c994828000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000075370616365727300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000753504143455253000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f516d506f7268456a4479337348753548564d465735426277743835335378384132436667614845737a556d5643342f537061636572732e6a736f6e000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : name_ (string): Spacers
Arg [1] : symbol_ (string): SPACERS
Arg [2] : collectionMetadata_ (string): ipfs://QmPorhEjDy3sHu5HVMFW5Bbwt853Sx8A2CfgaHEszUmVC4/Spacers.json
Arg [3] : data (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [2] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 000000000000000000000000b32153aa5b3dd930d265bc800d2f7d657c994828
Arg [6] : 000000000000000000000000000000000000000000000000000000000000012c
Arg [7] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [8] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [10] : 5370616365727300000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [12] : 5350414345525300000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000042
Arg [14] : 697066733a2f2f516d506f7268456a4479337348753548564d46573542627774
Arg [15] : 3835335378384132436667614845737a556d5643342f537061636572732e6a73
Arg [16] : 6f6e000000000000000000000000000000000000000000000000000000000000
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.