Source Code
Overview
GLMR Balance
GLMR Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 84 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Mint With Assets | 10783478 | 266 days ago | IN | 0 GLMR | 0.0344955 | ||||
| Set External Per... | 10783473 | 266 days ago | IN | 0 GLMR | 0.022354 | ||||
| Mint With Assets | 10678918 | 274 days ago | IN | 0 GLMR | 0.01724817 | ||||
| Set External Per... | 10678916 | 274 days ago | IN | 0 GLMR | 0.00145353 | ||||
| Mint With Assets | 10678916 | 274 days ago | IN | 0 GLMR | 0.01724775 | ||||
| Set External Per... | 10678916 | 274 days ago | IN | 0 GLMR | 0.00145353 | ||||
| Mint With Assets | 10678916 | 274 days ago | IN | 0 GLMR | 0.01724775 | ||||
| Set External Per... | 10678909 | 274 days ago | IN | 0 GLMR | 0.011177 | ||||
| Mint With Assets | 10678907 | 274 days ago | IN | 0 GLMR | 0.01724775 | ||||
| Set External Per... | 10678907 | 274 days ago | IN | 0 GLMR | 0.011177 | ||||
| Mint With Assets | 10678903 | 274 days ago | IN | 0 GLMR | 0.01724775 | ||||
| Set External Per... | 10678903 | 274 days ago | IN | 0 GLMR | 0.011177 | ||||
| Mint With Assets | 10678899 | 274 days ago | IN | 0 GLMR | 0.01724775 | ||||
| Set External Per... | 10678895 | 274 days ago | IN | 0 GLMR | 0.011177 | ||||
| Mint With Assets | 10678893 | 274 days ago | IN | 0 GLMR | 0.01724775 | ||||
| Set External Per... | 10678892 | 274 days ago | IN | 0 GLMR | 0.011177 | ||||
| Set External Per... | 10678887 | 274 days ago | IN | 0 GLMR | 0.011177 | ||||
| Manage Contribut... | 9758011 | 339 days ago | IN | 0 GLMR | 0.022362 | ||||
| Mint With Assets | 9363735 | 367 days ago | IN | 0 GLMR | 0.137982 | ||||
| Set External Per... | 9363731 | 367 days ago | IN | 0 GLMR | 0.08932 | ||||
| Add Equippable A... | 9363728 | 367 days ago | IN | 0 GLMR | 0.093128 | ||||
| Add Asset Entry | 9363722 | 367 days ago | IN | 0 GLMR | 0.091224 | ||||
| Manage Contribut... | 9363228 | 367 days ago | IN | 0 GLMR | 0.089448 | ||||
| Mint With Assets | 9320838 | 370 days ago | IN | 0 GLMR | 0.137982 | ||||
| Set External Per... | 9320833 | 370 days ago | IN | 0 GLMR | 0.08932 |
View more zero value Internal Transactions in Advanced View mode
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LunaRightHand
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 200 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0
/***********************************************/
/** Using software by RMRK.app **/
/** [email protected] **/
/***********************************************/
pragma solidity ^0.8.21;
import {
RMRKAbstractEquippable
} from "@rmrk-team/evm-contracts/contracts/implementations/abstract/RMRKAbstractEquippable.sol";
import {
RMRKImplementationBase
} from "@rmrk-team/evm-contracts/contracts/implementations/utils/RMRKImplementationBase.sol";
import {
IERC721Enumerable
} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
error ERC721OutOfBoundsIndex(address owner, uint256 index);
error LengthMismatch();
contract LunaRightHand is RMRKAbstractEquippable {
// Events
/**
* @notice From ERC4906 This event emits when the metadata of a token is changed.
* So that the third-party platforms such as NFT market could
* get notified when the metadata of a token is changed.
*/
event MetadataUpdate(uint256 _tokenId);
// Variables
// IERC721Enumerable
mapping(address owner => mapping(uint256 index => uint256))
private _ownedTokens;
mapping(uint256 tokenId => uint256) private _ownedTokensIndex;
uint256[] private _allTokens;
mapping(uint256 tokenId => uint256) private _allTokensIndex;
mapping(address => bool) private _autoAcceptCollection;
mapping(address => bool) private _externalPermission;
// Constructor
constructor(
string memory collectionMetadata,
uint256 maxSupply,
address royaltyRecipient,
uint16 royaltyPercentageBps
)
RMRKImplementationBase(
"RIGHT HAND - Luna Stronghold",
"RHLS",
collectionMetadata,
maxSupply,
royaltyRecipient,
royaltyPercentageBps
)
{}
// Methods
function tokenURI(uint256 tokenId) public view returns (string memory) {
_requireMinted(tokenId);
// This will revert if the token has not assets, only use if at least an asset is assigned on mint to every token
return getAssetMetadata(tokenId, _activeAssets[tokenId][0]);
}
/**
* @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
* @param replacedAssetId ID of the asset that has been replaced by the accepted asset
*/
function _afterAcceptAsset(
uint256 tokenId,
uint256 index,
uint64 assetId,
uint64 replacedAssetId
) internal virtual override {
if (replacedAssetId != 0) {
emit MetadataUpdate(tokenId);
}
}
/**
* @notice Mints a new token with the specified assets.
* @param to The address to mint the token to.
* @param assetIds The IDs of the assets to assign to the token.
*/
function mintWithAssets(
address to,
uint64[] memory assetIds
) public virtual {
require(_externalPermission[msg.sender], "Permission denied");
require(assetIds.length > 0, "No assets to mint");
(uint256 tokenId, ) = _prepareMint(1);
_safeMint(to, tokenId, "");
for (uint256 i; i < assetIds.length; ) {
_addAssetToToken(tokenId, assetIds[i], 0);
// Only first asset or assets added by token owner are auto-accepted, so we might need to accept for the rest of cases
if (_pendingAssets[tokenId].length != 0) {
_acceptAsset(tokenId, 0, assetIds[i]);
}
unchecked {
++i;
}
}
_externalPermission[msg.sender] = false;
}
/**
* @notice Grants or revokes minting permission for an external account.
* @dev This function can only be called by an authorized contract.
* @param account The address of the account to grant or revoke permission.
* @param permission Boolean value indicating whether to grant or revoke permission.
*/
function setExternalPermission(
address account,
bool permission
) public onlyOwnerOrContributor {
_externalPermission[account] = permission;
}
/**
* @notice Verifies if an external account has permission.
* @param account The address of the account to check permission.
* @return Boolean value indicating if the account has permission.
*/
function hasExternalPermission(address account) public view returns (bool) {
return _externalPermission[account];
}
/**
* @inheritdoc IERC165
*/
function supportsInterface(
bytes4 interfaceId
) public view override returns (bool) {
return
type(IERC721Enumerable).interfaceId == interfaceId ||
RMRKAbstractEquippable.supportsInterface(interfaceId);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override {
super._beforeTokenTransfer(from, to, tokenId);
if (from == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (from != to) {
_removeTokenFromOwnerEnumeration(from, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (from != to) {
_addTokenToOwnerEnumeration(to, tokenId);
}
}
// IERC721Enumerable (Based on OpenZeppelin implementation)
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(
address owner,
uint256 index
) public view virtual returns (uint256) {
if (index >= balanceOf(owner)) {
revert ERC721OutOfBoundsIndex(owner, index);
}
return _ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual returns (uint256) {
if (index >= totalSupply()) {
revert ERC721OutOfBoundsIndex(address(0), index);
}
return _allTokens[index];
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(
address from,
uint256 tokenId
) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = balanceOf(from) - 1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
function setAutoAcceptCollection(
address collection,
bool autoAccept
) public virtual onlyOwnerOrContributor {
_autoAcceptCollection[collection] = autoAccept;
}
function _afterAddChild(
uint256 tokenId,
address childAddress,
uint256 childId,
bytes memory
) internal virtual 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 v5.0.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../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.
*/
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 (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../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 v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../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 address zero.
*
* 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 v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @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 v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";
import {
IERC721Metadata
} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {
RMRKMinifiedEquippable
} from "../../RMRK/equippable/RMRKMinifiedEquippable.sol";
import {RMRKImplementationBase} from "../utils/RMRKImplementationBase.sol";
/**
* @title RMRKAbstractEquippable
* @author RMRK team
* @notice Abstract implementation of RMRK equipable module.
*/
abstract contract RMRKAbstractEquippable is
RMRKImplementationBase,
RMRKMinifiedEquippable
{
/**
* @notice Used to add an asset to a token.
* @dev If the given asset is already added to the token, the execution will be reverted.
* @dev If the asset ID is invalid, the execution will be reverted.
* @dev If the token already has the maximum amount of pending assets (128), the execution will be
* reverted.
* @param tokenId ID of the token to add the asset to
* @param assetId ID of the asset to add to the token
* @param replacesAssetWithId ID of the asset to replace from the token's list of active assets
*/
function addAssetToToken(
uint256 tokenId,
uint64 assetId,
uint64 replacesAssetWithId
) public virtual onlyOwnerOrContributor {
_addAssetToToken(tokenId, assetId, replacesAssetWithId);
}
/**
* @notice Used to add an equippable asset entry.
* @dev The ID of the asset is automatically assigned to be the next available asset ID.
* @param equippableGroupId ID of the equippable group
* @param catalogAddress Address of the `Catalog` smart contract this asset belongs to
* @param metadataURI Metadata URI of the asset
* @param partIds An array of IDs of fixed and slot parts to be included in the asset
* @return assetId The ID of the newly added asset
*/
function addEquippableAssetEntry(
uint64 equippableGroupId,
address catalogAddress,
string memory metadataURI,
uint64[] memory partIds
) public virtual onlyOwnerOrContributor returns (uint256 assetId) {
unchecked {
++_totalAssets;
}
_addAssetEntry(
uint64(_totalAssets),
equippableGroupId,
catalogAddress,
metadataURI,
partIds
);
assetId = _totalAssets;
}
/**
* @notice Used to add a asset entry.
* @dev The ID of the asset is automatically assigned to be the next available asset ID.
* @param metadataURI Metadata URI of the asset
* @return assetId ID of the newly added asset
*/
function addAssetEntry(
string memory metadataURI
) public virtual onlyOwnerOrContributor returns (uint256 assetId) {
unchecked {
++_totalAssets;
}
_addAssetEntry(uint64(_totalAssets), metadataURI);
assetId = _totalAssets;
}
/**
* @notice Used to declare that the assets belonging to a given `equippableGroupId` are equippable into the `Slot`
* associated with the `partId` of the collection at the specified `parentAddress`
* @param equippableGroupId ID of the equippable group
* @param parentAddress Address of the parent into which the equippable group can be equipped into
* @param partId ID of the `Slot` that the items belonging to the equippable group can be equipped into
*/
function setValidParentForEquippableGroup(
uint64 equippableGroupId,
address parentAddress,
uint64 partId
) public virtual onlyOwnerOrContributor {
_setValidParentForEquippableGroup(
equippableGroupId,
parentAddress,
partId
);
}
/**
* @inheritdoc IERC165
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
super.supportsInterface(interfaceId) ||
interfaceId == type(IERC2981).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
interfaceId == RMRK_INTERFACE();
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override {
super._beforeTokenTransfer(from, to, tokenId);
if (to == address(0)) {
unchecked {
_totalSupply -= 1;
}
}
}
function _afterAddAssetToToken(
uint256 tokenId,
uint64 assetId,
uint64 replacesAssetWithId
) internal virtual override {
super._afterAddAssetToToken(tokenId, assetId, replacesAssetWithId);
// This relies on no other auto accept mechanism being in place.
// We auto accept the first ever asset or any asset added by the token owner.
// This is done to allow a meta factory to mint, add assets and accept them in one transaction.
if (
_activeAssets[tokenId].length == 0 ||
_msgSender() == ownerOf(tokenId)
) {
_acceptAsset(tokenId, _pendingAssets[tokenId].length - 1, assetId);
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
import {Ownable} from "../../RMRK/access/Ownable.sol";
import "../../RMRK/library/RMRKErrors.sol";
import {RMRKRoyalties} from "../../RMRK/extension/RMRKRoyalties.sol";
/**
* @title RMRKImplementationBase
* @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 Ownable by default.
*/
abstract contract RMRKImplementationBase is RMRKRoyalties, Ownable {
string internal _contractURI;
string private _name;
string private _symbol;
uint256 private _nextId;
uint256 internal _totalSupply;
uint256 internal _maxSupply;
uint256 internal _totalAssets;
/**
* @notice Initializes the smart contract with a given maximum supply and minting price.
* @param name_ Name of the token collection
* @param symbol_ Symbol of the token collection
* @param contractURI_ The collection metadata URI
* @param maxSupply_ The maximum supply of tokens
* @param royaltyRecipient Address to which royalties should be sent
* @param royaltyPercentageBps The royalty percentage expressed in basis points
*/
constructor(
string memory name_,
string memory symbol_,
string memory contractURI_,
uint256 maxSupply_,
address royaltyRecipient,
uint256 royaltyPercentageBps
) RMRKRoyalties(royaltyRecipient, royaltyPercentageBps) {
_name = name_;
_symbol = symbol_;
_contractURI = contractURI_;
_maxSupply = maxSupply_;
}
/**
* @notice Used to retrieve the total supply of the tokens in a collection.
* @return totalSupply_ The number of tokens in a collection
*/
function totalSupply() public view virtual returns (uint256 totalSupply_) {
totalSupply_ = _totalSupply;
}
/**
* @notice Used to retrieve the maximum supply of the collection.
* @return maxSupply_ The maximum supply of tokens in the collection
*/
function maxSupply() public view virtual returns (uint256 maxSupply_) {
maxSupply_ = _maxSupply;
}
/**
* @notice Used to retrieve the total number of assets.
* @return totalAssets_ The total number of assets
*/
function totalAssets() public view virtual returns (uint256 totalAssets_) {
totalAssets_ = _totalAssets;
}
/**
* @notice Used to retrieve the metadata URI of the collection.
* @return contractURI_ string The metadata URI of the collection
*/
function contractURI()
public
view
virtual
returns (string memory contractURI_)
{
contractURI_ = _contractURI;
}
/**
* @notice Used to retrieve the collection name.
* @return name_ Name of the collection
*/
function name() public view virtual returns (string memory name_) {
name_ = _name;
}
/**
* @notice Used to retrieve the collection symbol.
* @return symbol_ Symbol of the collection
*/
function symbol() public view virtual returns (string memory symbol_) {
symbol_ = _symbol;
}
/**
* @inheritdoc RMRKRoyalties
*/
function updateRoyaltyRecipient(
address newRoyaltyRecipient
) public virtual override onlyOwner {
_setRoyaltyRecipient(newRoyaltyRecipient);
}
/**
* @notice Used to calculate the token IDs of tokens to be minted.
* @param numToMint Amount of tokens to be minted
* @return nextToken The ID of the first token to be minted in the current minting cycle
* @return totalSupplyOffset The ID of the last token to be minted in the current minting cycle
*/
function _prepareMint(
uint256 numToMint
) internal virtual returns (uint256 nextToken, uint256 totalSupplyOffset) {
if (numToMint == uint256(0)) revert RMRKMintZero();
if (numToMint + _nextId > _maxSupply) revert RMRKMintOverMax();
unchecked {
nextToken = _nextId + 1;
_nextId += numToMint;
_totalSupply += numToMint;
totalSupplyOffset = _nextId + 1;
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import "../library/RMRKErrors.sol";
/**
* @title Ownable
* @author RMRK team
* @notice A minimal ownable smart contract 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 owner_ Address of the current owner
*/
function owner() public view virtual returns (address owner_) {
owner_ = _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 isContributor_ Boolean value indicating whether the address is a contributor or not
*/
function isContributor(
address contributor
) public view returns (bool isContributor_) {
isContributor_ = _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.21;
import {IERC165} from "@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 isEquippable 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 isEquippable);
/**
* @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 isEquippableToAll The status indicating whether the part with `partId` can be equipped by any address or not
*/
function checkIsEquippableToAll(
uint64 partId
) external view returns (bool isEquippableToAll);
/**
* @notice Used to retrieve a `Part` with id `partId`
* @param partId ID of the part that we are retrieving
* @return part The `Part` struct associated with given `partId`
*/
function getPart(uint64 partId) external view returns (Part memory part);
/**
* @notice Used to retrieve multiple parts at the same time.
* @param partIds An array of part IDs that we want to retrieve
* @return part An array of `Part` structs associated with given `partIds`
*/
function getParts(
uint64[] memory partIds
) external view returns (Part[] memory part);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
/**
* @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 {
string private constant _VERSION = "2.5.5";
bytes4 private constant _RMRK_INTERFACE = 0x524D524B; // "RMRK" in ASCII hex
/**
* @notice Version of the @rmrk-team/evm-contracts package
* @return version Version identifier for implementations of the @rmrk-team/evm-contracts package
*/
function VERSION() public pure returns (string memory version) {
version = _VERSION;
}
/**
* @notice Interface identifier of the @rmrk-team/evm-contracts package
* @return rmrkInterface Interface identifier for implementations of the @rmrk-team/evm-contracts package
*/
function RMRK_INTERFACE() public pure returns (bytes4 rmrkInterface) {
rmrkInterface = _RMRK_INTERFACE;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
import {IERC5773} from "../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 isEquipped 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 isEquipped);
/**
* @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 canBeEquipped 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 canBeEquipped);
/**
* @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 equipment The `Equipment` struct containing data about the equipped object
*/
function getEquipment(
uint256 tokenId,
address targetCatalogAddress,
uint64 slotPartId
) external view returns (Equipment memory equipment);
/**
* @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
pragma solidity ^0.8.21;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC5773} from "../multiasset/IERC5773.sol";
import {IERC6220} from "../equippable/IERC6220.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {
IERC721Receiver
} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC7401} from "../nestable/IERC7401.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IRMRKCatalog} from "../catalog/IRMRKCatalog.sol";
import {RMRKCore} from "../core/RMRKCore.sol";
import {RMRKLib} from "../library/RMRKLib.sol";
import {ReentrancyGuard} from "../security/ReentrancyGuard.sol";
import "../library/RMRKErrors.sol";
/**
* @title RMRKMinifiedEquippable
* @author RMRK team
* @notice Smart contract of the RMRK Equippable module, without utility internal functions.
* @dev This includes all the code for MultiAsset, Nestable and Equippable.
* @dev Most of the code is duplicated from the other legos, this version is created to save size.
*/
contract RMRKMinifiedEquippable is
ReentrancyGuard,
Context,
IERC165,
IERC721,
IERC7401,
IERC6220,
RMRKCore
{
using RMRKLib for uint64[];
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) internal view {
address owner = ownerOf(tokenId);
if (
!(_msgSender() == owner ||
isApprovedForAll(owner, _msgSender()) ||
getApproved(tokenId) == _msgSender())
) 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) internal view {
(address owner, uint256 parentId, ) = directOwnerOf(tokenId);
// When the parent is an NFT, only it can do operations. Otherwise, the owner or approved address can
if (
(parentId != 0 && _msgSender() != owner) ||
!(_msgSender() == owner ||
isApprovedForAll(owner, _msgSender()) ||
getApproved(tokenId) == _msgSender())
) {
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);
_;
}
// ------------------------------- ERC721 ---------------------------------
/**
* @notice Used to retrieve the number of tokens in `owner`'s account.
* @param owner Address of the account being checked
* @return balance The balance of the given account
*/
function balanceOf(
address owner
) public view virtual returns (uint256 balance) {
if (owner == address(0)) revert ERC721AddressZeroIsNotaValidOwner();
balance = _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) {
_transfer(from, to, tokenId, data);
if (!_checkOnERC721Received(from, to, tokenId, data))
revert ERC721TransferToNonReceiverImplementer();
}
/**
* @inheritdoc IERC7401
*/
function nestTransferFrom(
address from,
address to,
uint256 tokenId,
uint256 destinationId,
bytes memory data
) public virtual onlyApprovedOrDirectOwner(tokenId) {
(address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner();
if (to == address(this) && tokenId == destinationId)
revert RMRKNestableTransferToSelf();
_checkDestination(to);
_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 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(from, to, parentId, 0, tokenId, data);
_balances[from] -= 1;
_updateOwnerAndClearApprovals(tokenId, 0, to);
_balances[to] += 1;
emit Transfer(from, to, tokenId);
emit NestTransfer(from, to, parentId, 0, tokenId);
_afterTokenTransfer(from, to, tokenId);
_afterNestedTokenTransfer(from, to, parentId, 0, 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 {
IERC7401 destContract = IERC7401(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
) = IERC7401(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 {
_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);
if (!_checkOnERC721Received(address(0), to, tokenId, data))
revert ERC721TransferToNonReceiverImplementer();
}
/**
* @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 {
_checkDestination(to);
_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 IERC7401
*/
function ownerOf(
uint256 tokenId
) public view virtual override(IERC7401, IERC721) returns (address owner_) {
(address owner, uint256 ownerTokenId, bool isNft) = directOwnerOf(
tokenId
);
if (isNft) {
owner = IERC7401(owner).ownerOf(ownerTokenId);
}
owner_ = owner;
}
/**
* @inheritdoc IERC7401
*/
function directOwnerOf(
uint256 tokenId
)
public
view
virtual
returns (address owner_, uint256 parentId, bool isNFT)
{
DirectOwner memory owner = _RMRKOwners[tokenId];
if (owner.ownerAddress == address(0)) revert ERC721InvalidTokenId();
owner_ = owner.ownerAddress;
parentId = owner.tokenId;
isNFT = 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 IERC7401
*/
function burn(
uint256 tokenId,
uint256 maxChildrenBurns
)
public
virtual
onlyApprovedOrDirectOwner(tokenId)
returns (uint256 burnedChildren)
{
(address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
address rootOwner = ownerOf(tokenId);
_beforeTokenTransfer(immediateOwner, address(0), tokenId);
_beforeNestedTokenTransfer(
immediateOwner,
address(0),
parentId,
0,
tokenId,
""
);
_balances[immediateOwner] -= 1;
_approve(address(0), tokenId);
_approveForAssets(address(0), tokenId);
Child[] memory children = childrenOf(tokenId);
delete _activeChildren[tokenId];
delete _pendingChildren[tokenId];
delete _tokenApprovals[tokenId][rootOwner];
uint256 pendingRecursiveBurns;
uint256 length = children.length; //gas savings
for (uint256 i; i < length; ) {
if (burnedChildren >= 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 - burnedChildren;
}
// 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.
burnedChildren +=
IERC7401(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(immediateOwner, address(0), tokenId);
emit NestTransfer(immediateOwner, address(0), parentId, 0, tokenId);
_afterTokenTransfer(immediateOwner, address(0), tokenId);
_afterNestedTokenTransfer(
immediateOwner,
address(0),
parentId,
0,
tokenId,
""
);
}
////////////////////////////////////////
// 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 approved Address of the account approved to manage the token
*/
function getApproved(
uint256 tokenId
) public view virtual returns (address approved) {
_requireMinted(tokenId);
approved = _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 isApproved 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 isApproved) {
isApproved = _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);
_approveForAssets(address(0), tokenId);
}
////////////////////////////////////////
// UTILS
////////////////////////////////////////
/**
* @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 exists A boolean value signifying whether the token exists
*/
function _exists(
uint256 tokenId
) internal view virtual returns (bool exists) {
exists = _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 valid 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 valid) {
if (to.code.length != 0) {
try
IERC721Receiver(to).onERC721Received(
_msgSender(),
from,
tokenId,
data
)
returns (bytes4 retval) {
valid = 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 {
valid = true;
}
}
////////////////////////////////////////
// CHILD MANAGEMENT PUBLIC
////////////////////////////////////////
/**
* @inheritdoc IERC7401
*/
function addChild(
uint256 parentId,
uint256 childId,
bytes memory data
) public virtual {
_requireMinted(parentId);
address childAddress = _msgSender();
if (childAddress.code.length == 0) 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 IERC7401
*/
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 IERC7401
*/
function rejectAllChildren(
uint256 tokenId,
uint256 maxRejections
) public virtual onlyApprovedOrOwner(tokenId) {
if (_pendingChildren[tokenId].length > maxRejections)
revert RMRKUnexpectedNumberOfChildren();
_beforeRejectAllChildren(tokenId);
delete _pendingChildren[tokenId];
emit AllChildrenRejected(tokenId);
_afterRejectAllChildren(tokenId);
}
/**
* @inheritdoc IERC7401
*/
function transferChild(
uint256 tokenId,
address to,
uint256 destinationId,
uint256 childIndex,
address childAddress,
uint256 childId,
bool isPending,
bytes memory data
) public virtual onlyApprovedOrOwner(tokenId) {
Child memory child;
if (isPending) {
child = pendingChildOf(tokenId, childIndex);
} else {
if (isChildEquipped(tokenId, childAddress, childId))
revert RMRKMustUnequipFirst();
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
IERC7401(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 IERC7401
*/
function childrenOf(
uint256 parentId
) public view virtual returns (Child[] memory children) {
children = _activeChildren[parentId];
}
/**
* @inheritdoc IERC7401
*/
function pendingChildrenOf(
uint256 parentId
) public view virtual returns (Child[] memory children) {
children = _pendingChildren[parentId];
}
/**
* @inheritdoc IERC7401
*/
function childOf(
uint256 parentId,
uint256 index
) public view virtual returns (Child memory child) {
if (childrenOf(parentId).length <= index)
revert RMRKChildIndexOutOfRange();
child = _activeChildren[parentId][index];
}
/**
* @inheritdoc IERC7401
*/
function pendingChildOf(
uint256 parentId,
uint256 index
) public view virtual returns (Child memory child) {
if (pendingChildrenOf(parentId).length <= index)
revert RMRKPendingChildIndexOutOfRange();
child = _pendingChildren[parentId][index];
}
// HOOKS
/**
* @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 {}
/**
* @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();
}
/// Mapping of uint64 Ids to asset metadata
mapping(uint64 => string) internal _assets;
/// Mapping of tokenId to new asset, to asset to be replaced
mapping(uint256 => mapping(uint64 => uint64)) internal _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)) internal _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 override returns (string memory metadata) {
// Allow to get the asset metadata without a token having it:
if (tokenId != 0 && !_tokenAssets[tokenId][assetId])
revert RMRKTokenDoesNotHaveAsset();
metadata = _assets[assetId];
}
/**
* @inheritdoc IERC5773
*/
function getActiveAssets(
uint256 tokenId
) public view virtual returns (uint64[] memory assetIds) {
assetIds = _activeAssets[tokenId];
}
/**
* @inheritdoc IERC5773
*/
function getPendingAssets(
uint256 tokenId
) public view virtual returns (uint64[] memory assetIds) {
assetIds = _pendingAssets[tokenId];
}
/**
* @inheritdoc IERC5773
*/
function getActiveAssetPriorities(
uint256 tokenId
) public view virtual returns (uint64[] memory priorities) {
priorities = _activeAssetPriorities[tokenId];
}
/**
* @inheritdoc IERC5773
*/
function getAssetReplacements(
uint256 tokenId,
uint64 newAssetId
) public view virtual returns (uint64 replacedAssetId) {
replacedAssetId = _assetReplacements[tokenId][newAssetId];
}
/**
* @inheritdoc IERC5773
*/
function isApprovedForAllForAssets(
address owner,
address operator
) public view virtual returns (bool isApproved) {
isApproved = _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 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 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`
* @param replacedAssetId ID of the asset that has been replaced by the accepted asset
*/
function _afterAcceptAsset(
uint256 tokenId,
uint256 index,
uint64 assetId,
uint64 replacedAssetId
) 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[] memory 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[] memory priorities
) internal virtual {}
// ------------------- 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) internal _catalogAddresses;
/// Mapping of uint64 ID to asset object.
mapping(uint64 => uint64) internal _equippableGroupIds;
/// Mapping of assetId to catalog parts applicable to this asset, both fixed and slot
mapping(uint64 => uint64[]) internal _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)))
internal _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)))
internal _equipCountPerChild;
/// Mapping of `equippableGroupId` to parent contract address and valid `slotId`.
mapping(uint64 => mapping(address => uint64)) internal _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) internal view {
address owner = ownerOf(tokenId);
if (
!(_msgSender() == owner ||
isApprovedForAllForAssets(owner, _msgSender()) ||
getApprovedForAssets(tokenId) == _msgSender())
) 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);
_;
}
/**
* @inheritdoc IERC165
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
interfaceId == type(IERC165).interfaceId ||
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC7401).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 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, replacesId);
}
/**
* @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) {
_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 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) {
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 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[] memory priorities
) public virtual onlyApprovedForAssetsOrOwner(tokenId) {
uint256 length = priorities.length;
if (length != _activeAssets[tokenId].length)
revert RMRKBadPriorityListLength();
_beforeSetPriority(tokenId, priorities);
_activeAssetPriorities[tokenId] = priorities;
emit AssetPrioritySet(tokenId);
_afterSetPriority(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 approved Address of the account that is approved to manage the token
*/
function getApprovedForAssets(
uint256 tokenId
) public view virtual returns (address approved) {
_requireMinted(tokenId);
approved = _tokenApprovalsForAssets[tokenId][ownerOf(tokenId)];
}
/**
* @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);
}
// ------------------------------- EQUIPPING ------------------------------
/**
* @inheritdoc IERC6220
*/
function equip(
IntakeEquip memory data
) public virtual onlyApprovedForAssetsOrOwner(data.tokenId) nonReentrant {
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);
IERC7401.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 onlyApprovedForAssetsOrOwner(tokenId) {
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 isEquipped) {
isEquipped = _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 canBeEquipped) {
uint64 equippableGroupId = _equippableGroupIds[assetId];
uint64 equippableSlot = _validParentSlots[equippableGroupId][parent];
if (equippableSlot == slotId) {
(, bool found) = getActiveAssets(tokenId).indexOf(assetId);
canBeEquipped = found;
}
}
// --------------------- Getting Extended Assets ---------------------
/**
* @inheritdoc IERC6220
*/
function getAssetAndEquippableData(
uint256 tokenId,
uint64 assetId
)
public
view
virtual
returns (
string memory metadataURI,
uint64 equippableGroupId,
address catalogAddress,
uint64[] memory partIds
)
{
metadataURI = getAssetMetadata(tokenId, assetId);
equippableGroupId = _equippableGroupIds[assetId];
catalogAddress = _catalogAddresses[assetId];
partIds = _partIds[assetId];
}
////////////////////////////////////////
// UTILS
////////////////////////////////////////
/**
* @inheritdoc IERC6220
*/
function getEquipment(
uint256 tokenId,
address targetCatalogAddress,
uint64 slotPartId
) public view virtual returns (Equipment memory equipment) {
equipment = _equipments[tokenId][targetCatalogAddress][slotPartId];
}
/**
* @notice Checks the destination is valid for a Nest Transfer/Mint.
* @dev The destination must be a contract that implements the IERC7401 interface.
* @param to Address of the destination
*/
function _checkDestination(address to) internal view {
// Checking if it is a contract before calling it seems redundant, but otherwise it would revert with no error
if (to.code.length == 0) revert RMRKIsNotContract();
if (!IERC165(to).supportsInterface(type(IERC7401).interfaceId))
revert RMRKNestableTransferToNonRMRKNestableImplementer();
}
// 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.21;
import {IERC2981} from "@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 **/ // Inheritance is commmnted to prevent linearization issues
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) {
_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 recipient Address of the recipient of royalties
*/
function getRoyaltyRecipient()
public
view
virtual
returns (address recipient)
{
recipient = _royaltyRecipient;
}
/**
* @notice Used to retrieve the specified royalty percentage.
* @return royaltyPercentageBps The royalty percentage expressed in the basis points
*/
function getRoyaltyPercentage()
public
view
virtual
returns (uint256 royaltyPercentageBps)
{
royaltyPercentageBps = _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 returns (address receiver, uint256 royaltyAmount) {
uint256(tokenId); // Silence the warning about unused variable, needed for docs generation
receiver = _royaltyRecipient;
royaltyAmount = (salePrice * _royaltyPercentageBps) / 10000;
}
}// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.21; /// @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 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 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 zero tokens error RMRKMintZero(); /// Attempting to pass complementary arrays of different lengths error RMRKMismachedArrayLength(); /// Attempting to transfer a child before it is unequipped error RMRKMustUnequipFirst(); /// Attempting to nest a child over the nestable limit (current limit is 100 levels of nesting) error RMRKNestableTooDeep(); /// Attempting to nest the token to own descendant, which would create a loop and leave the looped tokens in limbo error RMRKNestableTransferToDescendant(); /// Attempting to nest the token to a smart contract that doesn't support nesting error RMRKNestableTransferToNonRMRKNestableImplementer(); /// Attempting to nest the token into itself error RMRKNestableTransferToSelf(); /// Attempting to interact with an asset that can not be found error RMRKNoAssetMatchingId(); /// Attempting to manage an asset without owning it or having been granted permission by the owner to do so error RMRKNotApprovedForAssetsOrOwner(); /// Attempting to interact with a token without being its owner or having been granted permission by the /// owner to do so /// @dev When a token is nested, only the direct owner (NFT parent) can mange it. In that case, approved addresses are /// not allowed to manage it, in order to ensure the expected behaviour error RMRKNotApprovedOrDirectOwner(); /// Attempting to 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 transfer the ownership to the 0x0 address error RMRKNewOwnerIsZeroAddress(); /// Attempting to assign a 0x0 address as a contributor error RMRKNewContributorIsZeroAddress(); /// 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 basis points) error RMRKRoyaltiesTooHigh(); /// Attempting to do a bulk operation on a token that is not owned by the caller error RMRKCanOnlyDoBulkOperationsOnOwnedTokens(); /// Attempting to do a bulk operation with multiple tokens at a time error RMRKCanOnlyDoBulkOperationsWithOneTokenAtATime(); /// Attempting to pay with native token with a value different than expected error RMRKWrongValueSent(); // Attempting to send native token to a recipient that is unable to receive it error TransferFailed();
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
/**
* @title RMRKLib
* @author RMRK team
* @notice RMRK library smart contract.
*/
library RMRKLib {
error IndexOutOfBounds();
/**
* @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
if (index >= array.length) revert IndexOutOfBounds();
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.21;
import {IERC165} from "@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 assetIds An array of active asset IDs of the given token
*/
function getActiveAssets(
uint256 tokenId
) external view returns (uint64[] memory assetIds);
/**
* @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 assetIds An array of pending asset IDs of the given token
*/
function getPendingAssets(
uint256 tokenId
) external view returns (uint64[] memory assetIds);
/**
* @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 priorities An array of priorities of the active assets of the given token
*/
function getActiveAssetPriorities(
uint256 tokenId
) external view returns (uint64[] memory priorities);
/**
* @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 replacesAssetWithId ID of the asset which will be replaced
*/
function getAssetReplacements(
uint256 tokenId,
uint64 newAssetId
) external view returns (uint64 replacesAssetWithId);
/**
* @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 metadata 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 metadata);
// 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 approved Address of the account that is approved to manage the specified token's assets
*/
function getApprovedForAssets(
uint256 tokenId
) external view returns (address approved);
/**
* @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 isApproved A boolean value indicating whether the account we are checking has been granted the operator role
*/
function isApprovedForAllForAssets(
address owner,
address operator
) external view returns (bool isApproved);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.21;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/**
* @title IERC7401
* @author RMRK team
* @notice Interface smart contract of the RMRK nestable module.
*/
interface IERC7401 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 will 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 will be the parent token's collection address.
* @param tokenId ID of the token for which the RMRK owner is being retrieved
* @return owner Address of the given token's owner
* @return parentId The ID of the parent token. Should be `0` if the owner is an externally owned account
* @return isNFT The boolean value signifying whether the owner is an NFT or not
*/
function directOwnerOf(
uint256 tokenId
) external view returns (address owner, uint256 parentId, bool isNFT);
/**
* @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 burnedChildren Number of recursively burned children
*/
function burn(
uint256 tokenId,
uint256 maxRecursiveBurns
) external returns (uint256 burnedChildren);
/**
* @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 children An array of Child structs containing the parent token's active child tokens
*/
function childrenOf(
uint256 parentId
) external view returns (Child[] memory children);
/**
* @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 children An array of Child structs containing the parent token's pending child tokens
*/
function pendingChildrenOf(
uint256 parentId
) external view returns (Child[] memory children);
/**
* @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 child A Child struct containing data about the specified child
*/
function childOf(
uint256 parentId,
uint256 index
) external view returns (Child memory child);
/**
* @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 child A Child struct containting data about the specified child
*/
function pendingChildOf(
uint256 parentId,
uint256 index
) external view returns (Child memory child);
/**
* @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: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.21;
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;
}
}{
"evmVersion": "london",
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"collectionMetadata","type":"string"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"address","name":"royaltyRecipient","type":"address"},{"internalType":"uint16","name":"royaltyPercentageBps","type":"uint16"}],"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":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ERC721OutOfBoundsIndex","type":"error"},{"inputs":[],"name":"ERC721TokenAlreadyMinted","type":"error"},{"inputs":[],"name":"ERC721TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"ERC721TransferToNonReceiverImplementer","type":"error"},{"inputs":[],"name":"ERC721TransferToTheZeroAddress","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[],"name":"RMRKApprovalForAssetsToCurrentOwner","type":"error"},{"inputs":[],"name":"RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"RMRKAssetAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKBadPriorityListLength","type":"error"},{"inputs":[],"name":"RMRKCatalogRequiredForParts","type":"error"},{"inputs":[],"name":"RMRKChildAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKEquippableEquipNotAllowedByCatalog","type":"error"},{"inputs":[],"name":"RMRKIdZeroForbidden","type":"error"},{"inputs":[],"name":"RMRKIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKIsNotContract","type":"error"},{"inputs":[],"name":"RMRKMaxPendingAssetsReached","type":"error"},{"inputs":[],"name":"RMRKMaxPendingChildrenReached","type":"error"},{"inputs":[{"internalType":"address","name":"childContract","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"RMRKMaxRecursiveBurnsReached","type":"error"},{"inputs":[],"name":"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":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NestTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"indexed":true,"internalType":"uint64","name":"slotPartId","type":"uint64"},{"indexed":false,"internalType":"address","name":"parentAddress","type":"address"}],"name":"ValidParentEquippableGroupIdSet","type":"event"},{"inputs":[],"name":"RMRK_INTERFACE","outputs":[{"internalType":"bytes4","name":"rmrkInterface","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"version","type":"string"}],"stateMutability":"pure","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":"assetId","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":"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":"addEquippableAssetEntry","outputs":[{"internalType":"uint256","name":"assetId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approveForAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","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":"burnedChildren","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":"canBeEquipped","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 IERC7401.Child","name":"child","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 IERC7401.Child[]","name":"children","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"contractURI_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"directOwnerOf","outputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"bool","name":"isNFT","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":"priorities","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getActiveAssets","outputs":[{"internalType":"uint64[]","name":"assetIds","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"approved","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApprovedForAssets","outputs":[{"internalType":"address","name":"approved","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":"metadataURI","type":"string"},{"internalType":"uint64","name":"equippableGroupId","type":"uint64"},{"internalType":"address","name":"catalogAddress","type":"address"},{"internalType":"uint64[]","name":"partIds","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":"metadata","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":"replacedAssetId","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":"equipment","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getPendingAssets","outputs":[{"internalType":"uint64[]","name":"assetIds","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyPercentage","outputs":[{"internalType":"uint256","name":"royaltyPercentageBps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRecipient","outputs":[{"internalType":"address","name":"recipient","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"hasExternalPermission","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"isApproved","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":"isApproved","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":"isEquipped","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"isContributor","outputs":[{"internalType":"bool","name":"isContributor_","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":"maxSupply_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint64[]","name":"assetIds","type":"uint64[]"}],"name":"mintWithAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"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":"owner_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner_","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 IERC7401.Child","name":"child","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 IERC7401.Child[]","name":"children","type":"tuple[]"}],"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"},{"internalType":"bool","name":"autoAccept","type":"bool"}],"name":"setAutoAcceptCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"permission","type":"bool"}],"name":"setExternalPermission","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":"symbol_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"totalAssets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"totalSupply_","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"}]Contract Creation Code
60806040523480156200001157600080fd5b50604051620057e0380380620057e08339810160408190526200003491620001d3565b6040518060400160405280601c81526020017f52494748542048414e44202d204c756e61205374726f6e67686f6c64000000008152506040518060400160405280600481526020016352484c5360e01b8152508585858561ffff168181620000a2826200011960201b60201c565b6127108110620000c557604051634006185d60e11b815260040160405180910390fd5b600190815560025550620000d9336200013b565b6006620000e7878262000366565b506007620000f6868262000366565b50600562000105858262000366565b505050600a55506200043295505050505050565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b0381168114620001bb57600080fd5b919050565b805161ffff81168114620001bb57600080fd5b60008060008060808587031215620001ea57600080fd5b84516001600160401b03808211156200020257600080fd5b818701915087601f8301126200021757600080fd5b8151818111156200022c576200022c6200018d565b604051601f8201601f19908116603f011681019083821181831017156200025757620002576200018d565b81604052828152602093508a848487010111156200027457600080fd5b600091505b8282101562000298578482018401518183018501529083019062000279565b60008484830101528098505050508087015194505050620002bc60408601620001a3565b9150620002cc60608601620001c0565b905092959194509250565b600181811c90821680620002ec57607f821691505b6020821081036200030d57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200036157600081815260208120601f850160051c810160208610156200033c5750805b601f850160051c820191505b818110156200035d5782815560010162000348565b5050505b505050565b81516001600160401b038111156200038257620003826200018d565b6200039a81620003938454620002d7565b8462000313565b602080601f831160018114620003d25760008415620003b95750858301515b600019600386901b1c1916600185901b1785556200035d565b600085815260208120601f198616915b828110156200040357888601518255948401946001909101908401620003e2565b5085821015620004225787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61539e80620004426000396000f3fe608060405234801561001057600080fd5b50600436106104075760003560e01c8063635490cc11610220578063b390c0ab11610130578063e7de4de4116100b8578063ee1dffcf11610087578063ee1dffcf14610aa2578063f2fde38b14610ae0578063fb25fb7a14610af3578063fc3517c814610b2d578063ffa1ad7414610b4057600080fd5b8063e7de4de414610a38578063e8a3d48514610a4b578063e97ceaa814610a53578063e985e9c514610a6657600080fd5b8063d5abeb01116100ff578063d5abeb01146109a8578063de8e602c146109b0578063defa80c3146109c3578063df6f556b146109d6578063e467a48f14610a2557600080fd5b8063b390c0ab1461095c578063b88d4fde1461096f578063c259a98814610982578063c87b56dd1461099557600080fd5b80637dd540b2116101b357806395d89b411161018257806395d89b411461090a57806395edc18c146109125780639796133614610923578063a22cb46514610936578063a898e3641461094957600080fd5b80637dd540b2146108be5780638507dc28146108d15780638d4f3bf5146108e45780638da5cb5b146108f957600080fd5b8063715018a6116101ef578063715018a6146107a95780637280281e146107b15780637507e2ae146107c457806379e8ca9e146108ab57600080fd5b8063635490cc1461075d57806365b67eb9146107705780636f19951c1461078357806370a082311461079657600080fd5b80632f745c591161031b5780634e60edba116102ae5780635b453bbd1161027d5780635b453bbd146106e55780635e2e3292146107115780635e94354a146107245780635ea72f36146107375780636352211e1461074a57600080fd5b80634e60edba146106875780634f6ccce7146106aa57806351532e5a146106bd57806359c8b7dd146106dd57600080fd5b806342842e0e116102ea57806342842e0e1461062e57806342966c681461064157806344ec9344146106545780634cc482c11461067457600080fd5b80632f745c59146105e257806330ffb1d6146105f557806338dcf74c146106085780633ce67a4a1461061b57600080fd5b806318160ddd1161039e57806322f6da9c1161036d57806322f6da9c1461055757806323b872dd1461056a5780632452cd911461057d5780632a55205a146105905780632f32f937146105c257600080fd5b806318160ddd146104d45780631c7bb461146104dc5780631d0d35f5146104ef57806322e6d1601461051b57600080fd5b8063074334fb116103da578063074334fb14610470578063081812fc14610483578063095ea7b3146104ae5780630fc499f5146104c157600080fd5b806301e1d1141461040c57806301ffc9a714610423578063064c0a3a1461044657806306fdde031461045b575b600080fd5b600b545b6040519081526020015b60405180910390f35b610436610431366004614668565b610b61565b604051901515815260200161041a565b610459610454366004614756565b610b8c565b005b610463610cae565b60405161041a9190614811565b61043661047e366004614840565b610d40565b61049661049136600461488f565b610dad565b6040516001600160a01b03909116815260200161041a565b6104596104bc3660046148a8565b610df3565b6104596104cf3660046148d4565b610e7f565b600954610410565b6104596104ea3660046148f1565b610ea8565b6104366104fd3660046148d4565b6001600160a01b031660009081526004602052604090205460011490565b61043661052936600461492d565b6001600160a01b03918216600090815260196020908152604080832093909416825291909152205460ff1690565b610459610565366004614966565b610ebb565b610459610578366004614992565b610f36565b61041061058b366004614a59565b610f5b565b6105a361059e366004614adf565b610f89565b604080516001600160a01b03909316835260208301919091520161041a565b6105d56105d0366004614adf565b610fbe565b60405161041a9190614b01565b6104106105f03660046148a8565b611055565b610410610603366004614b21565b6110bf565b610459610616366004614b55565b6110e6565b610459610629366004614be4565b61146d565b61045961063c366004614992565b6114a0565b61045961064f36600461488f565b6114bb565b61066761066236600461488f565b6114ca565b60405161041a9190614c12565b610459610682366004614be4565b611552565b61069a610695366004614c72565b611585565b60405161041a9493929190614cd9565b6104106106b836600461488f565b61165c565b6106d06106cb36600461488f565b6116b5565b60405161041a9190614d1c565b600154610410565b6104366106f33660046148d4565b6001600160a01b031660009081526026602052604090205460ff1690565b61046361071f366004614c72565b611749565b6106d061073236600461488f565b611844565b6104596107453660046148a8565b6118ad565b61049661075836600461488f565b611934565b61045961076b366004614d2f565b6119c4565b61045961077e366004614d7e565b611ae8565b61066761079136600461488f565b611afb565b6104106107a43660046148d4565b611b6e565b610459611bb3565b6106d06107bf36600461488f565b611bc7565b6108616107d2366004614dac565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601e87528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352600160401b9004909416958101959095526001830154918501919091526002909101549091169082015290565b60405161041a919081516001600160401b03908116825260208084015190911690820152604080830151908201526060918201516001600160a01b03169181019190915260800190565b6104596108b9366004614be4565b611c30565b6104596108cc366004614dd3565b611ced565b6104596108df366004614be4565b611e3d565b60405163524d524b60e01b815260200161041a565b6003546001600160a01b0316610496565b610463611ed3565b6000546001600160a01b0316610496565b610459610931366004614adf565b611ee2565b610459610944366004614be4565b611f62565b6105d5610957366004614adf565b611ff0565b61041061096a366004614adf565b61204d565b61045961097d366004614e22565b6123ad565b6104596109903660046148f1565b6123f3565b6104636109a336600461488f565b6125bc565b600a54610410565b6104596109be366004614e81565b612618565b6104596109d1366004614eb1565b6126a2565b610a0d6109e4366004614c72565b60009182526014602090815260408084206001600160401b039384168552909152909120541690565b6040516001600160401b03909116815260200161041a565b610496610a3336600461488f565b6128dd565b610459610a46366004614adf565b612900565b610463612a02565b610459610a61366004614f4c565b612a11565b610436610a7436600461492d565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205460ff1690565b610436610ab0366004614f8b565b6000928352601f602090815260408085206001600160a01b03909416855292815282842091845252902054151590565b610459610aee3660046148d4565b612a27565b610b06610b0136600461488f565b612a5f565b604080516001600160a01b039094168452602084019290925215159082015260600161041a565b610459610b3b366004614966565b612ac7565b604080518082019091526005815264322e352e3560d81b6020820152610463565b600063780e9d6360e01b6001600160e01b031983161480610b865750610b8682612adc565b92915050565b82610b9681612b3a565b600080610ba286612a5f565b5091509150876001600160a01b0316826001600160a01b031614610bd95760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b03871630148015610bf057508486145b15610c0e57604051633d76b10760e01b815260040160405180910390fd5b610c1787612bc2565b610c22868887612c79565b610c2d888888612d6a565b6001600160a01b0388166000908152600c60205260408120805460019290610c56908490614fc8565b90915550610c679050868689612e2d565b6001600160a01b0387166000908152600c60205260408120805460019290610c90908490614fdb565b90915550610ca49050828883888a89612e8e565b5050505050505050565b606060068054610cbd90614fee565b80601f0160208091040260200160405190810160405280929190818152602001828054610ce990614fee565b8015610d365780601f10610d0b57610100808354040283529160200191610d36565b820191906000526020600020905b815481529060010190602001808311610d1957829003601f168201915b5050505050905090565b6001600160401b038083166000908152601c602090815260408083205484168084528280528184206001600160a01b038a1685529092528220549192909181169084168103610da3576000610d9e86610d9889611bc7565b90612f6f565b945050505b5050949350505050565b6000610db882612fd8565b6000828152600d6020526040812090610dd084611934565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6000610dfe82611934565b9050806001600160a01b0316836001600160a01b031603610e3257604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b03821614801590610e525750610e508133610a74565b155b15610e7057604051634c12315960e11b815260040160405180910390fd5b610e7a8383613010565b505050565b610e87613089565b600080546001600160a01b0319166001600160a01b03831617905550565b50565b610eb06130b4565b610e7a8383836130f3565b82610ec5816132ef565b610ed0848484613350565b610edb8484846133f8565b60008481526018602090815260408083206001600160401b0386168085529252808320805460ff1916905551909186917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a35b50505050565b80610f4081612b3a565b610f3084848460405180602001604052806000815250613446565b6000610f656130b4565b600b805460010190819055610f7d908686868661358b565b5050600b549392505050565b600080546001546001600160a01b03909116919061271090610fab9085615028565b610fb5919061503f565b90509250929050565b604080518082019091526000808252602082015281610fdc84611afb565b5111610ffb5760405163653e642560e11b815260040160405180910390fd5b600083815260106020526040902080548390811061101b5761101b615061565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b0316918101919091529392505050565b600061106083611b6e565b82106110965760405163295f44f760e21b81526001600160a01b0384166004820152602481018390526044015b60405180910390fd5b506001600160a01b03919091166000908152602160209081526040808320938352929052205490565b60006110c96130b4565b600b8054600101908190556110de9083613632565b5050600b5490565b80516110f1816132ef565b6110f96136f8565b6040808301516001600160401b039081166000908152601b602090815283822054606087015187518452601e83528584206001600160a01b0392831680865290845286852095821685529490925293909120600201549192909116156111725760405163bd0650ab60e01b815260040160405180910390fd5b611180846040015182613720565b600061119485600001518660200151610fbe565b60208101518151608088015160405163074334fb60e01b815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190615077565b61124657604051632c36cd3360e01b815260040160405180910390fd5b6020810151604051636e5bceab60e11b81526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa1580156112a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c59190615077565b6112e257604051637228eff560e11b815260040160405180910390fd5b6040805160808082018352878301516001600160401b0390811683529088015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528d516000908152601e86528981208d8416825286528981208c8916825286528981208951815498518a16600160401b026001600160801b031990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558b518452601f8352868420905190941683529281528482208651835290529283208054929391929091906113d2908490614fdb565b92505081905550826001600160401b031686604001516001600160401b031687600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518b60800151604051611458939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a45050600160025550505050565b6114756130b4565b6001600160a01b03919091166000908152602560205260409020805460ff1916911515919091179055565b610e7a838383604051806020016040528060008152506123ad565b6114c681600061204d565b5050565b606060116000838152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156115475760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ff565b505050509050919050565b61155a6130b4565b6001600160a01b03919091166000908152602660205260409020805460ff1916911515919091179055565b606060008060606115968686611749565b6001600160401b038087166000908152601c6020908152604080832054601b835281842054601d8452938290208054835181860281018601909452808452969a50941697506001600160a01b03909216955090929083018282801561164c57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116116095790505b5050505050905092959194509250565b600061166760095490565b82106116905760405163295f44f760e21b8152600060048201526024810183905260440161108d565b602382815481106116a3576116a3615061565b90600052602060002001549050919050565b60008181526016602090815260409182902080548351818402810184019094528084526060939283018282801561173d57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116116fa5790505b50505050509050919050565b6060821580159061177d575060008381526018602090815260408083206001600160401b038616845290915290205460ff16155b1561179b57604051631b9928fd60e31b815260040160405180910390fd5b6001600160401b038216600090815260136020526040902080546117be90614fee565b80601f01602080910402602001604051908101604052809291908181526020018280546117ea90614fee565b80156118375780601f1061180c57610100808354040283529160200191611837565b820191906000526020600020905b81548152906001019060200180831161181a57829003601f168201915b5050505050905092915050565b60008181526017602090815260409182902080548351818402810184019094528084526060939283018282801561173d57600091825260209182902080546001600160401b031684529082028301929091600891018084116116fa575094979650505050505050565b60006118b882611934565b9050806001600160a01b0316836001600160a01b0316036118ec576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b0382161480159061190c575061190a8133610529565b155b1561192a576040516357a2e94960e11b815260040160405180910390fd5b610e7a83836137ea565b60008060008061194385612a5f565b92509250925080156119bb576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015611994573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b89190615094565b92505b50909392505050565b6119cd83612fd8565b33803b6000036119f05760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b03821660208201526000611a17866114ca565b5190506080811015611a76576000868152601160209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055611a8f565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd84604051611acc91815260200190565b60405180910390a4611ae086848787613863565b505050505050565b611af06130b4565b610e7a8383836138ac565b60008181526010602090815260408083208054825181850281018501909352808352606094929391929091840182156115475760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ff565b60006001600160a01b038216611b9757604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b611bbb613089565b611bc5600061395e565b565b60008181526015602090815260409182902080548351818402810184019094528084526060939283018282801561173d57600091825260209182902080546001600160401b031684529082028301929091600891018084116116fa575094979650505050505050565b611c38613089565b6001600160a01b038216611c5f5760405163016b812760e71b815260040160405180910390fd5b80611c84576001600160a01b0382166000908152600460205260408120819055611ca3565b6001600160a01b03821660009081526004602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac282604051611ce1911515815260200190565b60405180910390a25050565b3360009081526026602052604090205460ff16611d405760405162461bcd60e51b815260206004820152601160248201527014195c9b5a5cdcda5bdb8819195b9a5959607a1b604482015260640161108d565b6000815111611d855760405162461bcd60e51b8152602060048201526011602482015270139bc8185cdcd95d1cc81d1bc81b5a5b9d607a1b604482015260640161108d565b6000611d9160016139b0565b509050611dae838260405180602001604052806000815250613a22565b60005b8251811015611e2057611ddf82848381518110611dd057611dd0615061565b602002602001015160006130f3565b60008281526016602052604090205415611e1857611e18826000858481518110611e0b57611e0b615061565b6020026020010151613ab5565b600101611db1565b5050336000908152602660205260409020805460ff191690555050565b6001600160a01b0382163303611e66576040516375f45abd60e01b815260040160405180910390fd5b3360008181526019602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060078054610cbd90614fee565b81611eec81613cd2565b600083815260116020526040902054821015611f1b57604051631e73178b60e11b815260040160405180910390fd5b6000838152601160205260408120611f3291614518565b60405183907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a2505050565b6001600160a01b0382163303611f8b57604051630b7b99b960e21b815260040160405180910390fd5b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611ec7565b60408051808201909152600080825260208201528161200e846114ca565b511161202d5760405163da22687f60e01b815260040160405180910390fd5b600083815260116020526040902080548390811061101b5761101b615061565b60008261205981612b3a565b60008061206586612a5f565b5091509150600061207587611934565b905061208383600089612d6a565b6040805160208101909152600090526001600160a01b0383166000908152600c602052604081208054600192906120bb908490614fc8565b909155506120cc9050600088613010565b6120d76000886137ea565b60006120e288611afb565b60008981526010602052604081209192506120fd9190614518565b600088815260116020526040812061211491614518565b6000888152600d602090815260408083206001600160a01b0386168452909152812080546001600160a01b03191690558151815b81811015612313578989106121bd5783818151811061216957612169615061565b60200260200101516020015184828151811061218757612187615061565b6020908102919091010151516040516306177b2560e41b81526001600160a01b039092166004830152602482015260440161108d565b601260008583815181106121d3576121d3615061565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600085838151811061221357612213615061565b602002602001015160000151815260200190815260200160002060009055888a03925083818151811061224857612248615061565b6020026020010151602001516001600160a01b031663b390c0ab85838151811061227457612274615061565b60200260200101516000015160018661228d9190614fc8565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156122d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122f491906150b1565b6122ff906001614fdb565b612309908a614fdb565b9850600101612148565b5060008a8152600f602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b03891690600080516020615349833981519152908390a4604080518681526000602082018190528c9290916001600160a01b038a1691600080516020615329833981519152910160405180910390a46040805160208101909152600090525050505050505092915050565b816123b781612b3a565b6123c385858585613446565b6123cf85858585613d33565b6123ec5760405163bcb5663760e01b815260040160405180910390fd5b5050505050565b826123fd816132ef565b6001600160401b038084166000908152601b6020908152604080832054888452601e83528184206001600160a01b039182168086529084528285208887168652845293829020825160808101845281548088168252600160401b900490961693860193909352600183015491850191909152600290910154166060830181905290919061249d576040516317de7dd760e21b815260040160405180910390fd5b6000868152601e602090815260408083206001600160a01b0380871685529083528184206001600160401b0389168552835281842080546001600160801b03191681556001808201869055600290910180546001600160a01b03191690558a8552601f8452828520606087015190921685529083528184208583015185529092528220805491929091612531908490614fc8565b92505081905550836001600160401b0316856001600160401b0316877f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da138460400151856060015186602001516040516125af939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a4611ae0565b60606125c782612fd8565b60008281526015602052604081208054610b86928592916125ea576125ea615061565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316611749565b81612622816132ef565b8151600084815260156020526040902054811461265257604051633581be1d60e11b815260040160405180910390fd5b6000848152601760209081526040909120845161267192860190614539565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610f30565b876126ac81613cd2565b604080518082019091526000808252602082015283156126d7576126d08a88611ff0565b905061272d565b60008a8152601f602090815260408083206001600160a01b038a16845282528083208884529091529020541561272057604051630619dc9d60e21b815260040160405180910390fd5b61272a8a88610fbe565b90505b612738818787613e33565b831561275b5760008a81526011602052604090206127569088613e77565b612795565b6001600160a01b038616600090815260126020908152604080832088845282528083208390558c8352601090915290206127959088613e77565b6001600160a01b0389161561287f578761281257604051635c46a7ef60e11b81526001600160a01b0387169063b88d4fde906127db9030908d908a9089906004016150ca565b600060405180830381600087803b1580156127f557600080fd5b505af1158015612809573d6000803e3d6000fd5b5050505061287f565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a9161284c9130918e918e908a90600401615107565b600060405180830381600087803b15801561286657600080fd5b505af115801561287a573d6000803e3d6000fd5b505050505b6040805188815285151560208201526001600160a01b038b81161582840152915187928916918d917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a450505050505050505050565b60006128e882612fd8565b6000828152601a6020526040812090610dd084611934565b8161290a816132ef565b6000838152601660205260409020548281111561293a57604051635134ce8960e01b815260040160405180910390fd5b60005b818110156129b857600085815260166020526040812080548390811061296557612965615061565b6000918252602080832060048304015489845260148252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff191690555060010161293d565b5060008481526016602052604081206129d0916145f1565b60405160009085907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3610f30565b606060058054610cbd90614fee565b83612a1b81613cd2565b6123ec85858585613f31565b612a2f613089565b6001600160a01b038116612a5657604051634ece6ecf60e01b815260040160405180910390fd5b610ea58161395e565b6000818152600f60209081526040808320815180830190925280548252600101546001600160a01b03169181018290528291829190612ab15760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b82612ad1816132ef565b610f30848484613ab5565b6000612ae78261403f565b80612b0257506001600160e01b0319821663152a902d60e11b145b80612b1d57506001600160e01b03198216635b5e139f60e01b145b80610b865750506001600160e01b03191663524d524b60e01b1490565b600080612b4683612a5f565b509150915080600014158015612b655750336001600160a01b03831614155b80612ba45750336001600160a01b0383161480612b875750612b878233610a74565b80612ba2575033612b9784610dad565b6001600160a01b0316145b155b15610e7a576040516345f3c98360e11b815260040160405180910390fd5b806001600160a01b03163b600003612bed5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015612c38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5c9190615077565b610ea557604051631784ec7360e21b815260040160405180910390fd5b60005b6064811015612d50576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b8152600401612cb891815260200190565b606060405180830381865afa158015612cd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf99190615141565b92509250925080612d0d5750505050505050565b6001600160a01b03831630148015612d2457508682145b15612d42576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101612c7c565b50604051630349a6bd60e51b815260040160405180910390fd5b612d758383836140c6565b6001600160a01b038316612dd057612dcb81602380546000838152602460205260408120829055600182018355919091527fd57b2b5166478fd4318d2acc6cc2c704584312bdd8781b32d5d06abda57f42300155565b612df3565b816001600160a01b0316836001600160a01b031614612df357612df383826140e3565b6001600160a01b038216612e0a57610e7a81614180565b816001600160a01b0316836001600160a01b031614610e7a57610e7a828261422f565b6040805180820182528381526001600160a01b0383811660208084019182526000888152600f9091529384209251835551600190920180546001600160a01b03191692909116919091179055612e839084613010565b610e7a6000846137ea565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc90612ec090879087908790600401615184565b600060405180830381600087803b158015612eda57600080fd5b505af1158015612eee573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b031660008051602061534983398151915260405160405180910390a482866001600160a01b0316886001600160a01b03166000805160206153298339815191528888604051612f5e929190918252602082015260400190565b60405180910390a450505050505050565b81516000908190815b81811015612fc757846001600160401b0316868281518110612f9c57612f9c615061565b60200260200101516001600160401b031603612fbf57925060019150612fd19050565b600101612f78565b5060008092509250505b9250929050565b6000818152600f60205260409020600101546001600160a01b0316610ea55760405163089ba7e160e41b815260040160405180910390fd5b600061301b82611934565b6000838152600d602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6003546001600160a01b03163314611bc557604051631c62d58f60e11b815260040160405180910390fd5b6003546001600160a01b031633148015906130d557506130d3336104fd565b155b15611bc5576040516301eca16760e41b815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038616845290915290205460ff1615613137576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152601360205260408120805461315a90614fee565b90500361317a57604051632aa5eff960e11b815260040160405180910390fd5b6000838152601660205260409020546080116131a95760405163bade3a7b60e01b815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260168552928520805493840181558552929093206004820401805460039092166008026101000a8084021990921691909302179091558116156132575760008381526014602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061328d5761328d615061565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516132dc91906151ac565b60405180910390a3610f30848484614273565b60006132fa82611934565b9050336001600160a01b038216148061331857506133188133610529565b80613333575033613328836128dd565b6001600160a01b0316145b6114c657604051635d64832960e01b815260040160405180910390fd5b600083815260166020526040902054821061337e57604051630757d52160e01b815260040160405180910390fd5b600083815260166020526040902080548390811061339e5761339e615061565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610e7a576040516378eeeecf60e01b815260040160405180910390fd5b600083815260166020526040902061341090836142d3565b60009283526014602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b60008061345284612a5f565b5091509150856001600160a01b0316826001600160a01b0316146134895760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166134b0576040516338f646ff60e21b815260040160405180910390fd5b6134bb868686612d6a565b6001600160a01b0386166000908152600c602052604081208054600192906134e4908490614fc8565b909155506134f6905084600087612e2d565b6001600160a01b0385166000908152600c6020526040812080546001929061351f908490614fdb565b909155505060405184906001600160a01b03808816919089169060008051602061534983398151915290600090a483856001600160a01b0316876001600160a01b03166000805160206153298339815191528460006040516125af929190918252602082015260400190565b6135958583613632565b6001600160a01b0383161580156135ac5750805115155b156135ca57604051631035ad0760e11b815260040160405180910390fd5b6001600160401b038581166000908152601b6020908152604080832080546001600160a01b0319166001600160a01b038916179055601c8252808320805467ffffffffffffffff191694891694909417909355601d81529190208251611ae092840190614539565b6001600160401b038216613659576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b0382166000908152601360205260408120805461367c90614fee565b9050111561369d576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b03821660009081526013602052604090206136bf8282615236565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b600280540361371a576040516362bfeae960e11b815260040160405180910390fd5b60028055565b6001600160401b0382166000908152601d6020908152604080832080548251818502810185019093528083526137c99386939291908301828280156137b657602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116137735790505b5050505050612f6f90919063ffffffff16565b91505080610e7a57604051634ef44ed560e01b815260040160405180910390fd5b60006137f582611934565b6000838152601a602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160a01b03831660009081526025602052604090205460ff1615610f3057600084815260116020526040902054610f309085906138a590600190614fc8565b8585613f31565b6001600160401b03831615806138c957506001600160401b038116155b156138e7576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152602080805260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600080826139d15760405163376bec4d60e01b815260040160405180910390fd5b600a546008546139e19085614fdb565b1115613a0057604051635e91cdfb60e11b815260040160405180910390fd5b5050600880548281019182905560098054909301909255600191820192910190565b613a2f83836000846143cd565b60405182906001600160a01b03851690600090600080516020615349833981519152908290a46040805160008082526020820181905284926001600160a01b03871692600080516020615329833981519152910160405180910390a4613a986000848484613d33565b610e7a5760405163bcb5663760e01b815260040160405180910390fd5b613ac0838383613350565b60008381526014602090815260408083206001600160401b03808616855292528220541690808215613b6e57613b6883601560008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156137b657600091825260209182902080546001600160401b031684529082028301929091600891018084116137735790505050505050612f6f90919063ffffffff16565b90925090505b8015613bef576000868152601560205260409020805485919084908110613b9757613b97615061565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526018835260408083209187168352925220805460ff19169055613c7a565b6000868152601760209081526040808320601583529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b613c858686866133f8565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4611ae0868686866144d0565b6000613cdd82611934565b9050336001600160a01b0382161480613cfb5750613cfb8133610a74565b80613d16575033613d0b83610dad565b6001600160a01b0316145b6114c6576040516302728a9d60e41b815260040160405180910390fd5b60006001600160a01b0384163b15613e2757604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290613d779033908990889088906004016150ca565b6020604051808303816000875af1925050508015613db2575060408051601f3d908101601f19168201909252613daf918101906152f5565b60015b613e0d573d808015613de0576040519150601f19603f3d011682016040523d82523d6000602084013e613de5565b606091505b508051613e055760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050613e2b565b5060015b949350505050565b82602001516001600160a01b0316826001600160a01b0316141580613e59575082518114155b15610e7a57604051637383f2c160e11b815260040160405180910390fd5b81548290613e8790600190614fc8565b81548110613e9757613e97615061565b9060005260206000209060020201828281548110613eb757613eb7615061565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b039092169190911790558154829080613f0257613f02615312565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b6000613f3d8585611ff0565b9050613f4a818484613e33565b6001600160a01b038316600090815260126020908152604080832085845290915290205415613f8c5760405163188a497360e01b815260040160405180910390fd5b6000858152601160205260409020613fa49085613e77565b600085815260106020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260128452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a9910160405180910390a46123ec565b60006001600160e01b031982166301ffc9a760e01b148061407057506001600160e01b031982166380ac58cd60e01b145b8061408b57506001600160e01b031982166342b0e56f60e01b145b806140a657506001600160e01b0319821663035a194d60e11b145b80610b8657506001600160e01b03198216630a2f26b960e21b1492915050565b6001600160a01b038216610e7a5760098054600019019055505050565b600060016140f084611b6e565b6140fa9190614fc8565b60008381526022602052604090205490915080821461414d576001600160a01b03841660009081526021602090815260408083208584528252808320548484528184208190558352602290915290208190555b5060009182526022602090815260408084208490556001600160a01b039094168352602181528383209183525290812055565b60235460009061419290600190614fc8565b600083815260246020526040812054602380549394509092849081106141ba576141ba615061565b9060005260206000200154905080602383815481106141db576141db615061565b600091825260208083209091019290925582815260249091526040808220849055858252812055602380548061421357614213615312565b6001900381819060005260206000200160009055905550505050565b600061423a83611b6e565b6001600160a01b039093166000908152602160209081526040808320868452825280832085905593825260229052919091209190915550565b60008381526015602052604090205415806142a7575061429283611934565b6001600160a01b0316336001600160a01b0316145b15610e7a57600083815260166020526040902054610e7a9084906142cd90600190614fc8565b84613ab5565b815481106142f457604051634e23d03560e01b815260040160405180910390fd5b8154829061430490600190614fc8565b8154811061431457614314615061565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b031682828154811061434f5761434f615061565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b031602179055508180548061439857614398615312565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b6001600160a01b0384166143f4576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600f60205260409020600101546001600160a01b03161561442d5760405163c5a8d37160e01b815260040160405180910390fd5b8261444b576040516312c33ce360e01b815260040160405180910390fd5b61445760008585612d6a565b6001600160a01b0384166000908152600c60205260408120805460019290614480908490614fdb565b90915550506040805180820182529283526001600160a01b0394851660208085019182526000958652600f9052932091518255509051600190910180546001600160a01b03191691909216179055565b6001600160401b03811615610f30576040518481527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a150505050565b5080546000825560020290600052602060002090810190610ea59190614616565b828054828255906000526020600020906003016004900481019282156145e15791602002820160005b838211156145ac57835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614562565b80156145df5782816101000a8154906001600160401b0302191690556008016020816007010492830192600103026145ac565b505b506145ed92915061463d565b5090565b508054600082556003016004900490600052602060002090810190610ea5919061463d565b5b808211156145ed57600081556001810180546001600160a01b0319169055600201614617565b5b808211156145ed576000815560010161463e565b6001600160e01b031981168114610ea557600080fd5b60006020828403121561467a57600080fd5b813561468581614652565b9392505050565b6001600160a01b0381168114610ea557600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156146df576146df6146a1565b604052919050565b600082601f8301126146f857600080fd5b81356001600160401b03811115614711576147116146a1565b614724601f8201601f19166020016146b7565b81815284602083860101111561473957600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a0868803121561476e57600080fd5b85356147798161468c565b945060208601356147898161468c565b9350604086013592506060860135915060808601356001600160401b038111156147b257600080fd5b6147be888289016146e7565b9150509295509295909350565b6000815180845260005b818110156147f1576020818501810151868301820152016147d5565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061468560208301846147cb565b80356001600160401b038116811461483b57600080fd5b919050565b6000806000806080858703121561485657600080fd5b84356148618161468c565b93506020850135925061487660408601614824565b915061488460608601614824565b905092959194509250565b6000602082840312156148a157600080fd5b5035919050565b600080604083850312156148bb57600080fd5b82356148c68161468c565b946020939093013593505050565b6000602082840312156148e657600080fd5b81356146858161468c565b60008060006060848603121561490657600080fd5b8335925061491660208501614824565b915061492460408501614824565b90509250925092565b6000806040838503121561494057600080fd5b823561494b8161468c565b9150602083013561495b8161468c565b809150509250929050565b60008060006060848603121561497b57600080fd5b833592506020840135915061492460408501614824565b6000806000606084860312156149a757600080fd5b83356149b28161468c565b925060208401356149c28161468c565b929592945050506040919091013590565b600082601f8301126149e457600080fd5b813560206001600160401b038211156149ff576149ff6146a1565b8160051b614a0e8282016146b7565b9283528481018201928281019087851115614a2857600080fd5b83870192505b84831015614a4e57614a3f83614824565b82529183019190830190614a2e565b979650505050505050565b60008060008060808587031215614a6f57600080fd5b614a7885614824565b93506020850135614a888161468c565b925060408501356001600160401b0380821115614aa457600080fd5b614ab0888389016146e7565b93506060870135915080821115614ac657600080fd5b50614ad3878288016149d3565b91505092959194509250565b60008060408385031215614af257600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610b86565b600060208284031215614b3357600080fd5b81356001600160401b03811115614b4957600080fd5b613e2b848285016146e7565b600060a08284031215614b6757600080fd5b60405160a081018181106001600160401b0382111715614b8957614b896146a1565b80604052508235815260208301356020820152614ba860408401614824565b6040820152614bb960608401614824565b6060820152614bca60808401614824565b60808201529392505050565b8015158114610ea557600080fd5b60008060408385031215614bf757600080fd5b8235614c028161468c565b9150602083013561495b81614bd6565b602080825282518282018190526000919060409081850190868401855b82811015614c6557614c55848351805182526020908101516001600160a01b0316910152565b9284019290850190600101614c2f565b5091979650505050505050565b60008060408385031215614c8557600080fd5b82359150610fb560208401614824565b600081518084526020808501945080840160005b83811015614cce5781516001600160401b031687529582019590820190600101614ca9565b509495945050505050565b608081526000614cec60808301876147cb565b6001600160401b03861660208401526001600160a01b03851660408401528281036060840152614a4e8185614c95565b6020815260006146856020830184614c95565b600080600060608486031215614d4457600080fd5b833592506020840135915060408401356001600160401b03811115614d6857600080fd5b614d74868287016146e7565b9150509250925092565b600080600060608486031215614d9357600080fd5b614d9c84614824565b925060208401356149168161468c565b600080600060608486031215614dc157600080fd5b8335925060208401356149168161468c565b60008060408385031215614de657600080fd5b8235614df18161468c565b915060208301356001600160401b03811115614e0c57600080fd5b614e18858286016149d3565b9150509250929050565b60008060008060808587031215614e3857600080fd5b8435614e438161468c565b93506020850135614e538161468c565b92506040850135915060608501356001600160401b03811115614e7557600080fd5b614ad3878288016146e7565b60008060408385031215614e9457600080fd5b8235915060208301356001600160401b03811115614e0c57600080fd5b600080600080600080600080610100898b031215614ece57600080fd5b883597506020890135614ee08161468c565b965060408901359550606089013594506080890135614efe8161468c565b935060a0890135925060c0890135614f1581614bd6565b915060e08901356001600160401b03811115614f3057600080fd5b614f3c8b828c016146e7565b9150509295985092959890939650565b60008060008060808587031215614f6257600080fd5b84359350602085013592506040850135614f7b8161468c565b9396929550929360600135925050565b600080600060608486031215614fa057600080fd5b8335925060208401356149c28161468c565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b8657610b86614fb2565b80820180821115610b8657610b86614fb2565b600181811c9082168061500257607f821691505b60208210810361502257634e487b7160e01b600052602260045260246000fd5b50919050565b8082028115828204841417610b8657610b86614fb2565b60008261505c57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561508957600080fd5b815161468581614bd6565b6000602082840312156150a657600080fd5b81516146858161468c565b6000602082840312156150c357600080fd5b5051919050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906150fd908301846147cb565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a060808201819052600090614a4e908301846147cb565b60008060006060848603121561515657600080fd5b83516151618161468c565b60208501516040860151919450925061517981614bd6565b809150509250925092565b8381528260208201526060604082015260006151a360608301846147cb565b95945050505050565b6020808252825182820181905260009190848201906040850190845b818110156151e4578351835292840192918401916001016151c8565b50909695505050505050565b601f821115610e7a57600081815260208120601f850160051c810160208610156152175750805b601f850160051c820191505b81811015611ae057828155600101615223565b81516001600160401b0381111561524f5761524f6146a1565b6152638161525d8454614fee565b846151f0565b602080601f83116001811461529857600084156152805750858301515b600019600386901b1c1916600185901b178555611ae0565b600085815260208120601f198616915b828110156152c7578886015182559484019460019091019084016152a8565b50858210156152e55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561530757600080fd5b815161468581614652565b634e487b7160e01b600052603160045260246000fdfe04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552dddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220ccb7c5d0edfefd52e6997cccc3d729c67534b9ef32787b939580e52f4927b29f64736f6c634300081500330000000000000000000000000000000000000000000000000000000000000080ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000e150519ae293922cfe6217feba3add4726f5e85100000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d515a7735767951476e765a4637327a6243616a326f327236706a453361416a373345356952725177766e656e0000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104075760003560e01c8063635490cc11610220578063b390c0ab11610130578063e7de4de4116100b8578063ee1dffcf11610087578063ee1dffcf14610aa2578063f2fde38b14610ae0578063fb25fb7a14610af3578063fc3517c814610b2d578063ffa1ad7414610b4057600080fd5b8063e7de4de414610a38578063e8a3d48514610a4b578063e97ceaa814610a53578063e985e9c514610a6657600080fd5b8063d5abeb01116100ff578063d5abeb01146109a8578063de8e602c146109b0578063defa80c3146109c3578063df6f556b146109d6578063e467a48f14610a2557600080fd5b8063b390c0ab1461095c578063b88d4fde1461096f578063c259a98814610982578063c87b56dd1461099557600080fd5b80637dd540b2116101b357806395d89b411161018257806395d89b411461090a57806395edc18c146109125780639796133614610923578063a22cb46514610936578063a898e3641461094957600080fd5b80637dd540b2146108be5780638507dc28146108d15780638d4f3bf5146108e45780638da5cb5b146108f957600080fd5b8063715018a6116101ef578063715018a6146107a95780637280281e146107b15780637507e2ae146107c457806379e8ca9e146108ab57600080fd5b8063635490cc1461075d57806365b67eb9146107705780636f19951c1461078357806370a082311461079657600080fd5b80632f745c591161031b5780634e60edba116102ae5780635b453bbd1161027d5780635b453bbd146106e55780635e2e3292146107115780635e94354a146107245780635ea72f36146107375780636352211e1461074a57600080fd5b80634e60edba146106875780634f6ccce7146106aa57806351532e5a146106bd57806359c8b7dd146106dd57600080fd5b806342842e0e116102ea57806342842e0e1461062e57806342966c681461064157806344ec9344146106545780634cc482c11461067457600080fd5b80632f745c59146105e257806330ffb1d6146105f557806338dcf74c146106085780633ce67a4a1461061b57600080fd5b806318160ddd1161039e57806322f6da9c1161036d57806322f6da9c1461055757806323b872dd1461056a5780632452cd911461057d5780632a55205a146105905780632f32f937146105c257600080fd5b806318160ddd146104d45780631c7bb461146104dc5780631d0d35f5146104ef57806322e6d1601461051b57600080fd5b8063074334fb116103da578063074334fb14610470578063081812fc14610483578063095ea7b3146104ae5780630fc499f5146104c157600080fd5b806301e1d1141461040c57806301ffc9a714610423578063064c0a3a1461044657806306fdde031461045b575b600080fd5b600b545b6040519081526020015b60405180910390f35b610436610431366004614668565b610b61565b604051901515815260200161041a565b610459610454366004614756565b610b8c565b005b610463610cae565b60405161041a9190614811565b61043661047e366004614840565b610d40565b61049661049136600461488f565b610dad565b6040516001600160a01b03909116815260200161041a565b6104596104bc3660046148a8565b610df3565b6104596104cf3660046148d4565b610e7f565b600954610410565b6104596104ea3660046148f1565b610ea8565b6104366104fd3660046148d4565b6001600160a01b031660009081526004602052604090205460011490565b61043661052936600461492d565b6001600160a01b03918216600090815260196020908152604080832093909416825291909152205460ff1690565b610459610565366004614966565b610ebb565b610459610578366004614992565b610f36565b61041061058b366004614a59565b610f5b565b6105a361059e366004614adf565b610f89565b604080516001600160a01b03909316835260208301919091520161041a565b6105d56105d0366004614adf565b610fbe565b60405161041a9190614b01565b6104106105f03660046148a8565b611055565b610410610603366004614b21565b6110bf565b610459610616366004614b55565b6110e6565b610459610629366004614be4565b61146d565b61045961063c366004614992565b6114a0565b61045961064f36600461488f565b6114bb565b61066761066236600461488f565b6114ca565b60405161041a9190614c12565b610459610682366004614be4565b611552565b61069a610695366004614c72565b611585565b60405161041a9493929190614cd9565b6104106106b836600461488f565b61165c565b6106d06106cb36600461488f565b6116b5565b60405161041a9190614d1c565b600154610410565b6104366106f33660046148d4565b6001600160a01b031660009081526026602052604090205460ff1690565b61046361071f366004614c72565b611749565b6106d061073236600461488f565b611844565b6104596107453660046148a8565b6118ad565b61049661075836600461488f565b611934565b61045961076b366004614d2f565b6119c4565b61045961077e366004614d7e565b611ae8565b61066761079136600461488f565b611afb565b6104106107a43660046148d4565b611b6e565b610459611bb3565b6106d06107bf36600461488f565b611bc7565b6108616107d2366004614dac565b6040805160808082018352600080835260208084018290528385018290526060938401829052968152601e87528381206001600160a01b03968716825287528381206001600160401b03958616825287528390208351918201845280548086168352600160401b9004909416958101959095526001830154918501919091526002909101549091169082015290565b60405161041a919081516001600160401b03908116825260208084015190911690820152604080830151908201526060918201516001600160a01b03169181019190915260800190565b6104596108b9366004614be4565b611c30565b6104596108cc366004614dd3565b611ced565b6104596108df366004614be4565b611e3d565b60405163524d524b60e01b815260200161041a565b6003546001600160a01b0316610496565b610463611ed3565b6000546001600160a01b0316610496565b610459610931366004614adf565b611ee2565b610459610944366004614be4565b611f62565b6105d5610957366004614adf565b611ff0565b61041061096a366004614adf565b61204d565b61045961097d366004614e22565b6123ad565b6104596109903660046148f1565b6123f3565b6104636109a336600461488f565b6125bc565b600a54610410565b6104596109be366004614e81565b612618565b6104596109d1366004614eb1565b6126a2565b610a0d6109e4366004614c72565b60009182526014602090815260408084206001600160401b039384168552909152909120541690565b6040516001600160401b03909116815260200161041a565b610496610a3336600461488f565b6128dd565b610459610a46366004614adf565b612900565b610463612a02565b610459610a61366004614f4c565b612a11565b610436610a7436600461492d565b6001600160a01b039182166000908152600e6020908152604080832093909416825291909152205460ff1690565b610436610ab0366004614f8b565b6000928352601f602090815260408085206001600160a01b03909416855292815282842091845252902054151590565b610459610aee3660046148d4565b612a27565b610b06610b0136600461488f565b612a5f565b604080516001600160a01b039094168452602084019290925215159082015260600161041a565b610459610b3b366004614966565b612ac7565b604080518082019091526005815264322e352e3560d81b6020820152610463565b600063780e9d6360e01b6001600160e01b031983161480610b865750610b8682612adc565b92915050565b82610b9681612b3a565b600080610ba286612a5f565b5091509150876001600160a01b0316826001600160a01b031614610bd95760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b03871630148015610bf057508486145b15610c0e57604051633d76b10760e01b815260040160405180910390fd5b610c1787612bc2565b610c22868887612c79565b610c2d888888612d6a565b6001600160a01b0388166000908152600c60205260408120805460019290610c56908490614fc8565b90915550610c679050868689612e2d565b6001600160a01b0387166000908152600c60205260408120805460019290610c90908490614fdb565b90915550610ca49050828883888a89612e8e565b5050505050505050565b606060068054610cbd90614fee565b80601f0160208091040260200160405190810160405280929190818152602001828054610ce990614fee565b8015610d365780601f10610d0b57610100808354040283529160200191610d36565b820191906000526020600020905b815481529060010190602001808311610d1957829003601f168201915b5050505050905090565b6001600160401b038083166000908152601c602090815260408083205484168084528280528184206001600160a01b038a1685529092528220549192909181169084168103610da3576000610d9e86610d9889611bc7565b90612f6f565b945050505b5050949350505050565b6000610db882612fd8565b6000828152600d6020526040812090610dd084611934565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6000610dfe82611934565b9050806001600160a01b0316836001600160a01b031603610e3257604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b03821614801590610e525750610e508133610a74565b155b15610e7057604051634c12315960e11b815260040160405180910390fd5b610e7a8383613010565b505050565b610e87613089565b600080546001600160a01b0319166001600160a01b03831617905550565b50565b610eb06130b4565b610e7a8383836130f3565b82610ec5816132ef565b610ed0848484613350565b610edb8484846133f8565b60008481526018602090815260408083206001600160401b0386168085529252808320805460ff1916905551909186917f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb1249190a35b50505050565b80610f4081612b3a565b610f3084848460405180602001604052806000815250613446565b6000610f656130b4565b600b805460010190819055610f7d908686868661358b565b5050600b549392505050565b600080546001546001600160a01b03909116919061271090610fab9085615028565b610fb5919061503f565b90509250929050565b604080518082019091526000808252602082015281610fdc84611afb565b5111610ffb5760405163653e642560e11b815260040160405180910390fd5b600083815260106020526040902080548390811061101b5761101b615061565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b0316918101919091529392505050565b600061106083611b6e565b82106110965760405163295f44f760e21b81526001600160a01b0384166004820152602481018390526044015b60405180910390fd5b506001600160a01b03919091166000908152602160209081526040808320938352929052205490565b60006110c96130b4565b600b8054600101908190556110de9083613632565b5050600b5490565b80516110f1816132ef565b6110f96136f8565b6040808301516001600160401b039081166000908152601b602090815283822054606087015187518452601e83528584206001600160a01b0392831680865290845286852095821685529490925293909120600201549192909116156111725760405163bd0650ab60e01b815260040160405180910390fd5b611180846040015182613720565b600061119485600001518660200151610fbe565b60208101518151608088015160405163074334fb60e01b815230600482015260248101929092526001600160401b039081166044830152851660648201529192506001600160a01b03169063074334fb90608401602060405180830381865afa158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190615077565b61124657604051632c36cd3360e01b815260040160405180910390fd5b6020810151604051636e5bceab60e11b81526001600160401b03841660048201526001600160a01b0391821660248201529084169063dcb79d5690604401602060405180830381865afa1580156112a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c59190615077565b6112e257604051637228eff560e11b815260040160405180910390fd5b6040805160808082018352878301516001600160401b0390811683529088015181166020808401918252855184860190815286820180516001600160a01b03908116606088019081528d516000908152601e86528981208d8416825286528981208c8916825286528981208951815498518a16600160401b026001600160801b031990991699169890981796909617875592516001808801919091559251600290960180549682166001600160a01b0319909716969096179095558b518452601f8352868420905190941683529281528482208651835290529283208054929391929091906113d2908490614fdb565b92505081905550826001600160401b031686604001516001600160401b031687600001517f1f5de02b1d9c93ca468f54630e1daf13f6dc458a63f8061ff73e85bf9bc38884856000015186602001518b60800151604051611458939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a45050600160025550505050565b6114756130b4565b6001600160a01b03919091166000908152602560205260409020805460ff1916911515919091179055565b610e7a838383604051806020016040528060008152506123ad565b6114c681600061204d565b5050565b606060116000838152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156115475760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ff565b505050509050919050565b61155a6130b4565b6001600160a01b03919091166000908152602660205260409020805460ff1916911515919091179055565b606060008060606115968686611749565b6001600160401b038087166000908152601c6020908152604080832054601b835281842054601d8452938290208054835181860281018601909452808452969a50941697506001600160a01b03909216955090929083018282801561164c57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116116095790505b5050505050905092959194509250565b600061166760095490565b82106116905760405163295f44f760e21b8152600060048201526024810183905260440161108d565b602382815481106116a3576116a3615061565b90600052602060002001549050919050565b60008181526016602090815260409182902080548351818402810184019094528084526060939283018282801561173d57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116116fa5790505b50505050509050919050565b6060821580159061177d575060008381526018602090815260408083206001600160401b038616845290915290205460ff16155b1561179b57604051631b9928fd60e31b815260040160405180910390fd5b6001600160401b038216600090815260136020526040902080546117be90614fee565b80601f01602080910402602001604051908101604052809291908181526020018280546117ea90614fee565b80156118375780601f1061180c57610100808354040283529160200191611837565b820191906000526020600020905b81548152906001019060200180831161181a57829003601f168201915b5050505050905092915050565b60008181526017602090815260409182902080548351818402810184019094528084526060939283018282801561173d57600091825260209182902080546001600160401b031684529082028301929091600891018084116116fa575094979650505050505050565b60006118b882611934565b9050806001600160a01b0316836001600160a01b0316036118ec576040516375f45abd60e01b815260040160405180910390fd5b336001600160a01b0382161480159061190c575061190a8133610529565b155b1561192a576040516357a2e94960e11b815260040160405180910390fd5b610e7a83836137ea565b60008060008061194385612a5f565b92509250925080156119bb576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015611994573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b89190615094565b92505b50909392505050565b6119cd83612fd8565b33803b6000036119f05760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b03821660208201526000611a17866114ca565b5190506080811015611a76576000868152601160209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055611a8f565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd84604051611acc91815260200190565b60405180910390a4611ae086848787613863565b505050505050565b611af06130b4565b610e7a8383836138ac565b60008181526010602090815260408083208054825181850281018501909352808352606094929391929091840182156115475760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b03168284015290835290920191016114ff565b60006001600160a01b038216611b9757604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b611bbb613089565b611bc5600061395e565b565b60008181526015602090815260409182902080548351818402810184019094528084526060939283018282801561173d57600091825260209182902080546001600160401b031684529082028301929091600891018084116116fa575094979650505050505050565b611c38613089565b6001600160a01b038216611c5f5760405163016b812760e71b815260040160405180910390fd5b80611c84576001600160a01b0382166000908152600460205260408120819055611ca3565b6001600160a01b03821660009081526004602052604090206001908190555b50816001600160a01b03167f4b5657e84cf8a17ac5587bbeb3cc2bab9826c4c67b8bad81b4849de49d37aac282604051611ce1911515815260200190565b60405180910390a25050565b3360009081526026602052604090205460ff16611d405760405162461bcd60e51b815260206004820152601160248201527014195c9b5a5cdcda5bdb8819195b9a5959607a1b604482015260640161108d565b6000815111611d855760405162461bcd60e51b8152602060048201526011602482015270139bc8185cdcd95d1cc81d1bc81b5a5b9d607a1b604482015260640161108d565b6000611d9160016139b0565b509050611dae838260405180602001604052806000815250613a22565b60005b8251811015611e2057611ddf82848381518110611dd057611dd0615061565b602002602001015160006130f3565b60008281526016602052604090205415611e1857611e18826000858481518110611e0b57611e0b615061565b6020026020010151613ab5565b600101611db1565b5050336000908152602660205260409020805460ff191690555050565b6001600160a01b0382163303611e66576040516375f45abd60e01b815260040160405180910390fd5b3360008181526019602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f0cff4fcf777050010027190b8061fd8bfd1de16d81b1f94e9752df1427a2623591015b60405180910390a35050565b606060078054610cbd90614fee565b81611eec81613cd2565b600083815260116020526040902054821015611f1b57604051631e73178b60e11b815260040160405180910390fd5b6000838152601160205260408120611f3291614518565b60405183907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a2505050565b6001600160a01b0382163303611f8b57604051630b7b99b960e21b815260040160405180910390fd5b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319101611ec7565b60408051808201909152600080825260208201528161200e846114ca565b511161202d5760405163da22687f60e01b815260040160405180910390fd5b600083815260116020526040902080548390811061101b5761101b615061565b60008261205981612b3a565b60008061206586612a5f565b5091509150600061207587611934565b905061208383600089612d6a565b6040805160208101909152600090526001600160a01b0383166000908152600c602052604081208054600192906120bb908490614fc8565b909155506120cc9050600088613010565b6120d76000886137ea565b60006120e288611afb565b60008981526010602052604081209192506120fd9190614518565b600088815260116020526040812061211491614518565b6000888152600d602090815260408083206001600160a01b0386168452909152812080546001600160a01b03191690558151815b81811015612313578989106121bd5783818151811061216957612169615061565b60200260200101516020015184828151811061218757612187615061565b6020908102919091010151516040516306177b2560e41b81526001600160a01b039092166004830152602482015260440161108d565b601260008583815181106121d3576121d3615061565b6020026020010151602001516001600160a01b03166001600160a01b03168152602001908152602001600020600085838151811061221357612213615061565b602002602001015160000151815260200190815260200160002060009055888a03925083818151811061224857612248615061565b6020026020010151602001516001600160a01b031663b390c0ab85838151811061227457612274615061565b60200260200101516000015160018661228d9190614fc8565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af11580156122d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122f491906150b1565b6122ff906001614fdb565b612309908a614fdb565b9850600101612148565b5060008a8152600f602052604080822082815560010180546001600160a01b0319169055518b91906001600160a01b03891690600080516020615349833981519152908390a4604080518681526000602082018190528c9290916001600160a01b038a1691600080516020615329833981519152910160405180910390a46040805160208101909152600090525050505050505092915050565b816123b781612b3a565b6123c385858585613446565b6123cf85858585613d33565b6123ec5760405163bcb5663760e01b815260040160405180910390fd5b5050505050565b826123fd816132ef565b6001600160401b038084166000908152601b6020908152604080832054888452601e83528184206001600160a01b039182168086529084528285208887168652845293829020825160808101845281548088168252600160401b900490961693860193909352600183015491850191909152600290910154166060830181905290919061249d576040516317de7dd760e21b815260040160405180910390fd5b6000868152601e602090815260408083206001600160a01b0380871685529083528184206001600160401b0389168552835281842080546001600160801b03191681556001808201869055600290910180546001600160a01b03191690558a8552601f8452828520606087015190921685529083528184208583015185529092528220805491929091612531908490614fc8565b92505081905550836001600160401b0316856001600160401b0316877f438e039ebbba8f290f3b5d41aaf3295eccc9b5e6b0e1d52ace700772afb7da138460400151856060015186602001516040516125af939291909283526001600160a01b039190911660208301526001600160401b0316604082015260600190565b60405180910390a4611ae0565b60606125c782612fd8565b60008281526015602052604081208054610b86928592916125ea576125ea615061565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b0316611749565b81612622816132ef565b8151600084815260156020526040902054811461265257604051633581be1d60e11b815260040160405180910390fd5b6000848152601760209081526040909120845161267192860190614539565b5060405184907ff0bfd70b0068f973d58178846ca67112671ec45e060838f7de5662036bcf801790600090a2610f30565b876126ac81613cd2565b604080518082019091526000808252602082015283156126d7576126d08a88611ff0565b905061272d565b60008a8152601f602090815260408083206001600160a01b038a16845282528083208884529091529020541561272057604051630619dc9d60e21b815260040160405180910390fd5b61272a8a88610fbe565b90505b612738818787613e33565b831561275b5760008a81526011602052604090206127569088613e77565b612795565b6001600160a01b038616600090815260126020908152604080832088845282528083208390558c8352601090915290206127959088613e77565b6001600160a01b0389161561287f578761281257604051635c46a7ef60e11b81526001600160a01b0387169063b88d4fde906127db9030908d908a9089906004016150ca565b600060405180830381600087803b1580156127f557600080fd5b505af1158015612809573d6000803e3d6000fd5b5050505061287f565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a9161284c9130918e918e908a90600401615107565b600060405180830381600087803b15801561286657600080fd5b505af115801561287a573d6000803e3d6000fd5b505050505b6040805188815285151560208201526001600160a01b038b81161582840152915187928916918d917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a450505050505050505050565b60006128e882612fd8565b6000828152601a6020526040812090610dd084611934565b8161290a816132ef565b6000838152601660205260409020548281111561293a57604051635134ce8960e01b815260040160405180910390fd5b60005b818110156129b857600085815260166020526040812080548390811061296557612965615061565b6000918252602080832060048304015489845260148252604080852060039094166008026101000a9091046001600160401b031684529190529020805467ffffffffffffffff191690555060010161293d565b5060008481526016602052604081206129d0916145f1565b60405160009085907f1010837a46db9510cad56c9b63e97183557a136e9d4ddbec309ce52c99afb124908390a3610f30565b606060058054610cbd90614fee565b83612a1b81613cd2565b6123ec85858585613f31565b612a2f613089565b6001600160a01b038116612a5657604051634ece6ecf60e01b815260040160405180910390fd5b610ea58161395e565b6000818152600f60209081526040808320815180830190925280548252600101546001600160a01b03169181018290528291829190612ab15760405163089ba7e160e41b815260040160405180910390fd5b6020810151905190959094508415159350915050565b82612ad1816132ef565b610f30848484613ab5565b6000612ae78261403f565b80612b0257506001600160e01b0319821663152a902d60e11b145b80612b1d57506001600160e01b03198216635b5e139f60e01b145b80610b865750506001600160e01b03191663524d524b60e01b1490565b600080612b4683612a5f565b509150915080600014158015612b655750336001600160a01b03831614155b80612ba45750336001600160a01b0383161480612b875750612b878233610a74565b80612ba2575033612b9784610dad565b6001600160a01b0316145b155b15610e7a576040516345f3c98360e11b815260040160405180910390fd5b806001600160a01b03163b600003612bed5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015612c38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5c9190615077565b610ea557604051631784ec7360e21b815260040160405180910390fd5b60005b6064811015612d50576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b8152600401612cb891815260200190565b606060405180830381865afa158015612cd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cf99190615141565b92509250925080612d0d5750505050505050565b6001600160a01b03831630148015612d2457508682145b15612d42576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101612c7c565b50604051630349a6bd60e51b815260040160405180910390fd5b612d758383836140c6565b6001600160a01b038316612dd057612dcb81602380546000838152602460205260408120829055600182018355919091527fd57b2b5166478fd4318d2acc6cc2c704584312bdd8781b32d5d06abda57f42300155565b612df3565b816001600160a01b0316836001600160a01b031614612df357612df383826140e3565b6001600160a01b038216612e0a57610e7a81614180565b816001600160a01b0316836001600160a01b031614610e7a57610e7a828261422f565b6040805180820182528381526001600160a01b0383811660208084019182526000888152600f9091529384209251835551600190920180546001600160a01b03191692909116919091179055612e839084613010565b610e7a6000846137ea565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc90612ec090879087908790600401615184565b600060405180830381600087803b158015612eda57600080fd5b505af1158015612eee573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b031660008051602061534983398151915260405160405180910390a482866001600160a01b0316886001600160a01b03166000805160206153298339815191528888604051612f5e929190918252602082015260400190565b60405180910390a450505050505050565b81516000908190815b81811015612fc757846001600160401b0316868281518110612f9c57612f9c615061565b60200260200101516001600160401b031603612fbf57925060019150612fd19050565b600101612f78565b5060008092509250505b9250929050565b6000818152600f60205260409020600101546001600160a01b0316610ea55760405163089ba7e160e41b815260040160405180910390fd5b600061301b82611934565b6000838152600d602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6003546001600160a01b03163314611bc557604051631c62d58f60e11b815260040160405180910390fd5b6003546001600160a01b031633148015906130d557506130d3336104fd565b155b15611bc5576040516301eca16760e41b815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038616845290915290205460ff1615613137576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b0382166000908152601360205260408120805461315a90614fee565b90500361317a57604051632aa5eff960e11b815260040160405180910390fd5b6000838152601660205260409020546080116131a95760405163bade3a7b60e01b815260040160405180910390fd5b60008381526018602090815260408083206001600160401b038681168086529184528285208054600160ff19909116811790915588865260168552928520805493840181558552929093206004820401805460039092166008026101000a8084021990921691909302179091558116156132575760008381526014602090815260408083206001600160401b0386811685529252909120805467ffffffffffffffff19169183169190911790555b60408051600180825281830190925260009160208083019080368337019050509050838160008151811061328d5761328d615061565b602002602001018181525050816001600160401b0316836001600160401b03167f4a85a0221f784dbe75db7c29c422f474c15bde9211a98e50a30018fa8dfa937b836040516132dc91906151ac565b60405180910390a3610f30848484614273565b60006132fa82611934565b9050336001600160a01b038216148061331857506133188133610529565b80613333575033613328836128dd565b6001600160a01b0316145b6114c657604051635d64832960e01b815260040160405180910390fd5b600083815260166020526040902054821061337e57604051630757d52160e01b815260040160405180910390fd5b600083815260166020526040902080548390811061339e5761339e615061565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03166001600160401b0316816001600160401b031614610e7a576040516378eeeecf60e01b815260040160405180910390fd5b600083815260166020526040902061341090836142d3565b60009283526014602090815260408085206001600160401b039093168552919052909120805467ffffffffffffffff1916905550565b60008061345284612a5f565b5091509150856001600160a01b0316826001600160a01b0316146134895760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166134b0576040516338f646ff60e21b815260040160405180910390fd5b6134bb868686612d6a565b6001600160a01b0386166000908152600c602052604081208054600192906134e4908490614fc8565b909155506134f6905084600087612e2d565b6001600160a01b0385166000908152600c6020526040812080546001929061351f908490614fdb565b909155505060405184906001600160a01b03808816919089169060008051602061534983398151915290600090a483856001600160a01b0316876001600160a01b03166000805160206153298339815191528460006040516125af929190918252602082015260400190565b6135958583613632565b6001600160a01b0383161580156135ac5750805115155b156135ca57604051631035ad0760e11b815260040160405180910390fd5b6001600160401b038581166000908152601b6020908152604080832080546001600160a01b0319166001600160a01b038916179055601c8252808320805467ffffffffffffffff191694891694909417909355601d81529190208251611ae092840190614539565b6001600160401b038216613659576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b0382166000908152601360205260408120805461367c90614fee565b9050111561369d576040516308fe3c3160e41b815260040160405180910390fd5b6001600160401b03821660009081526013602052604090206136bf8282615236565b506040516001600160401b038316907f3cd061096eaf881067d936308fbd8b81d060c45ab2ec910c02b953162befc10990600090a25050565b600280540361371a576040516362bfeae960e11b815260040160405180910390fd5b60028055565b6001600160401b0382166000908152601d6020908152604080832080548251818502810185019093528083526137c99386939291908301828280156137b657602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116137735790505b5050505050612f6f90919063ffffffff16565b91505080610e7a57604051634ef44ed560e01b815260040160405180910390fd5b60006137f582611934565b6000838152601a602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917fb90cc0d925ac3511ab6af2d7ca73ffcf7ec4bd871fff36b958ecf440079c463e9190a4505050565b6001600160a01b03831660009081526025602052604090205460ff1615610f3057600084815260116020526040902054610f309085906138a590600190614fc8565b8585613f31565b6001600160401b03831615806138c957506001600160401b038116155b156138e7576040516312c33ce360e01b815260040160405180910390fd5b6001600160401b038381166000818152602080805260408083206001600160a01b03881680855290835292819020805467ffffffffffffffff19169587169586179055519182527f5b5af0622001a9b735a56357ddc1abd65e6a640126498674daf9d2fb05160725910160405180910390a3505050565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600080826139d15760405163376bec4d60e01b815260040160405180910390fd5b600a546008546139e19085614fdb565b1115613a0057604051635e91cdfb60e11b815260040160405180910390fd5b5050600880548281019182905560098054909301909255600191820192910190565b613a2f83836000846143cd565b60405182906001600160a01b03851690600090600080516020615349833981519152908290a46040805160008082526020820181905284926001600160a01b03871692600080516020615329833981519152910160405180910390a4613a986000848484613d33565b610e7a5760405163bcb5663760e01b815260040160405180910390fd5b613ac0838383613350565b60008381526014602090815260408083206001600160401b03808616855292528220541690808215613b6e57613b6883601560008981526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156137b657600091825260209182902080546001600160401b031684529082028301929091600891018084116137735790505050505050612f6f90919063ffffffff16565b90925090505b8015613bef576000868152601560205260409020805485919084908110613b9757613b97615061565b600091825260208083206004830401805460039093166008026101000a6001600160401b038181021990941695841602949094179093558882526018835260408083209187168352925220805460ff19169055613c7a565b6000868152601760209081526040808320601583529083208054825460018181018555938652848620600480830490910180546001600160401b0394851660086003958616810261010090810a9283029288021990931691909117909255855496870186559488529587209085040180548b84169590921690950290920a9283029202191617905592505b613c858686866133f8565b826001600160401b0316846001600160401b0316877f3f2709a99f6c06b4e57bbb38eb0134332f96f51a3da314f41a515adbb28b17cc60405160405180910390a4611ae0868686866144d0565b6000613cdd82611934565b9050336001600160a01b0382161480613cfb5750613cfb8133610a74565b80613d16575033613d0b83610dad565b6001600160a01b0316145b6114c6576040516302728a9d60e41b815260040160405180910390fd5b60006001600160a01b0384163b15613e2757604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290613d779033908990889088906004016150ca565b6020604051808303816000875af1925050508015613db2575060408051601f3d908101601f19168201909252613daf918101906152f5565b60015b613e0d573d808015613de0576040519150601f19603f3d011682016040523d82523d6000602084013e613de5565b606091505b508051613e055760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050613e2b565b5060015b949350505050565b82602001516001600160a01b0316826001600160a01b0316141580613e59575082518114155b15610e7a57604051637383f2c160e11b815260040160405180910390fd5b81548290613e8790600190614fc8565b81548110613e9757613e97615061565b9060005260206000209060020201828281548110613eb757613eb7615061565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b039092169190911790558154829080613f0257613f02615312565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b6000613f3d8585611ff0565b9050613f4a818484613e33565b6001600160a01b038316600090815260126020908152604080832085845290915290205415613f8c5760405163188a497360e01b815260040160405180910390fd5b6000858152601160205260409020613fa49085613e77565b600085815260106020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260128452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a9910160405180910390a46123ec565b60006001600160e01b031982166301ffc9a760e01b148061407057506001600160e01b031982166380ac58cd60e01b145b8061408b57506001600160e01b031982166342b0e56f60e01b145b806140a657506001600160e01b0319821663035a194d60e11b145b80610b8657506001600160e01b03198216630a2f26b960e21b1492915050565b6001600160a01b038216610e7a5760098054600019019055505050565b600060016140f084611b6e565b6140fa9190614fc8565b60008381526022602052604090205490915080821461414d576001600160a01b03841660009081526021602090815260408083208584528252808320548484528184208190558352602290915290208190555b5060009182526022602090815260408084208490556001600160a01b039094168352602181528383209183525290812055565b60235460009061419290600190614fc8565b600083815260246020526040812054602380549394509092849081106141ba576141ba615061565b9060005260206000200154905080602383815481106141db576141db615061565b600091825260208083209091019290925582815260249091526040808220849055858252812055602380548061421357614213615312565b6001900381819060005260206000200160009055905550505050565b600061423a83611b6e565b6001600160a01b039093166000908152602160209081526040808320868452825280832085905593825260229052919091209190915550565b60008381526015602052604090205415806142a7575061429283611934565b6001600160a01b0316336001600160a01b0316145b15610e7a57600083815260166020526040902054610e7a9084906142cd90600190614fc8565b84613ab5565b815481106142f457604051634e23d03560e01b815260040160405180910390fd5b8154829061430490600190614fc8565b8154811061431457614314615061565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b031682828154811061434f5761434f615061565b90600052602060002090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b031602179055508180548061439857614398615312565b60008281526020902060046000199092019182040180546001600160401b03600860038516026101000a021916905590555050565b6001600160a01b0384166143f4576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600f60205260409020600101546001600160a01b03161561442d5760405163c5a8d37160e01b815260040160405180910390fd5b8261444b576040516312c33ce360e01b815260040160405180910390fd5b61445760008585612d6a565b6001600160a01b0384166000908152600c60205260408120805460019290614480908490614fdb565b90915550506040805180820182529283526001600160a01b0394851660208085019182526000958652600f9052932091518255509051600190910180546001600160a01b03191691909216179055565b6001600160401b03811615610f30576040518481527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a150505050565b5080546000825560020290600052602060002090810190610ea59190614616565b828054828255906000526020600020906003016004900481019282156145e15791602002820160005b838211156145ac57835183826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302614562565b80156145df5782816101000a8154906001600160401b0302191690556008016020816007010492830192600103026145ac565b505b506145ed92915061463d565b5090565b508054600082556003016004900490600052602060002090810190610ea5919061463d565b5b808211156145ed57600081556001810180546001600160a01b0319169055600201614617565b5b808211156145ed576000815560010161463e565b6001600160e01b031981168114610ea557600080fd5b60006020828403121561467a57600080fd5b813561468581614652565b9392505050565b6001600160a01b0381168114610ea557600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156146df576146df6146a1565b604052919050565b600082601f8301126146f857600080fd5b81356001600160401b03811115614711576147116146a1565b614724601f8201601f19166020016146b7565b81815284602083860101111561473957600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a0868803121561476e57600080fd5b85356147798161468c565b945060208601356147898161468c565b9350604086013592506060860135915060808601356001600160401b038111156147b257600080fd5b6147be888289016146e7565b9150509295509295909350565b6000815180845260005b818110156147f1576020818501810151868301820152016147d5565b506000602082860101526020601f19601f83011685010191505092915050565b60208152600061468560208301846147cb565b80356001600160401b038116811461483b57600080fd5b919050565b6000806000806080858703121561485657600080fd5b84356148618161468c565b93506020850135925061487660408601614824565b915061488460608601614824565b905092959194509250565b6000602082840312156148a157600080fd5b5035919050565b600080604083850312156148bb57600080fd5b82356148c68161468c565b946020939093013593505050565b6000602082840312156148e657600080fd5b81356146858161468c565b60008060006060848603121561490657600080fd5b8335925061491660208501614824565b915061492460408501614824565b90509250925092565b6000806040838503121561494057600080fd5b823561494b8161468c565b9150602083013561495b8161468c565b809150509250929050565b60008060006060848603121561497b57600080fd5b833592506020840135915061492460408501614824565b6000806000606084860312156149a757600080fd5b83356149b28161468c565b925060208401356149c28161468c565b929592945050506040919091013590565b600082601f8301126149e457600080fd5b813560206001600160401b038211156149ff576149ff6146a1565b8160051b614a0e8282016146b7565b9283528481018201928281019087851115614a2857600080fd5b83870192505b84831015614a4e57614a3f83614824565b82529183019190830190614a2e565b979650505050505050565b60008060008060808587031215614a6f57600080fd5b614a7885614824565b93506020850135614a888161468c565b925060408501356001600160401b0380821115614aa457600080fd5b614ab0888389016146e7565b93506060870135915080821115614ac657600080fd5b50614ad3878288016149d3565b91505092959194509250565b60008060408385031215614af257600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b03169082015260408101610b86565b600060208284031215614b3357600080fd5b81356001600160401b03811115614b4957600080fd5b613e2b848285016146e7565b600060a08284031215614b6757600080fd5b60405160a081018181106001600160401b0382111715614b8957614b896146a1565b80604052508235815260208301356020820152614ba860408401614824565b6040820152614bb960608401614824565b6060820152614bca60808401614824565b60808201529392505050565b8015158114610ea557600080fd5b60008060408385031215614bf757600080fd5b8235614c028161468c565b9150602083013561495b81614bd6565b602080825282518282018190526000919060409081850190868401855b82811015614c6557614c55848351805182526020908101516001600160a01b0316910152565b9284019290850190600101614c2f565b5091979650505050505050565b60008060408385031215614c8557600080fd5b82359150610fb560208401614824565b600081518084526020808501945080840160005b83811015614cce5781516001600160401b031687529582019590820190600101614ca9565b509495945050505050565b608081526000614cec60808301876147cb565b6001600160401b03861660208401526001600160a01b03851660408401528281036060840152614a4e8185614c95565b6020815260006146856020830184614c95565b600080600060608486031215614d4457600080fd5b833592506020840135915060408401356001600160401b03811115614d6857600080fd5b614d74868287016146e7565b9150509250925092565b600080600060608486031215614d9357600080fd5b614d9c84614824565b925060208401356149168161468c565b600080600060608486031215614dc157600080fd5b8335925060208401356149168161468c565b60008060408385031215614de657600080fd5b8235614df18161468c565b915060208301356001600160401b03811115614e0c57600080fd5b614e18858286016149d3565b9150509250929050565b60008060008060808587031215614e3857600080fd5b8435614e438161468c565b93506020850135614e538161468c565b92506040850135915060608501356001600160401b03811115614e7557600080fd5b614ad3878288016146e7565b60008060408385031215614e9457600080fd5b8235915060208301356001600160401b03811115614e0c57600080fd5b600080600080600080600080610100898b031215614ece57600080fd5b883597506020890135614ee08161468c565b965060408901359550606089013594506080890135614efe8161468c565b935060a0890135925060c0890135614f1581614bd6565b915060e08901356001600160401b03811115614f3057600080fd5b614f3c8b828c016146e7565b9150509295985092959890939650565b60008060008060808587031215614f6257600080fd5b84359350602085013592506040850135614f7b8161468c565b9396929550929360600135925050565b600080600060608486031215614fa057600080fd5b8335925060208401356149c28161468c565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b8657610b86614fb2565b80820180821115610b8657610b86614fb2565b600181811c9082168061500257607f821691505b60208210810361502257634e487b7160e01b600052602260045260246000fd5b50919050565b8082028115828204841417610b8657610b86614fb2565b60008261505c57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561508957600080fd5b815161468581614bd6565b6000602082840312156150a657600080fd5b81516146858161468c565b6000602082840312156150c357600080fd5b5051919050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906150fd908301846147cb565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a060808201819052600090614a4e908301846147cb565b60008060006060848603121561515657600080fd5b83516151618161468c565b60208501516040860151919450925061517981614bd6565b809150509250925092565b8381528260208201526060604082015260006151a360608301846147cb565b95945050505050565b6020808252825182820181905260009190848201906040850190845b818110156151e4578351835292840192918401916001016151c8565b50909695505050505050565b601f821115610e7a57600081815260208120601f850160051c810160208610156152175750805b601f850160051c820191505b81811015611ae057828155600101615223565b81516001600160401b0381111561524f5761524f6146a1565b6152638161525d8454614fee565b846151f0565b602080601f83116001811461529857600084156152805750858301515b600019600386901b1c1916600185901b178555611ae0565b600085815260208120601f198616915b828110156152c7578886015182559484019460019091019084016152a8565b50858210156152e55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561530757600080fd5b815161468581614652565b634e487b7160e01b600052603160045260246000fdfe04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552dddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220ccb7c5d0edfefd52e6997cccc3d729c67534b9ef32787b939580e52f4927b29f64736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000080ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000e150519ae293922cfe6217feba3add4726f5e85100000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d515a7735767951476e765a4637327a6243616a326f327236706a453361416a373345356952725177766e656e0000000000000000000000
-----Decoded View---------------
Arg [0] : collectionMetadata (string): ipfs://QmQZw5vyQGnvZF72zbCaj2o2r6pjE3aAj73E5iRrQwvnen
Arg [1] : maxSupply (uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935
Arg [2] : royaltyRecipient (address): 0xe150519ae293922cfE6217FEba3AdD4726f5E851
Arg [3] : royaltyPercentageBps (uint16): 1000
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Arg [2] : 000000000000000000000000e150519ae293922cfe6217feba3add4726f5e851
Arg [3] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [5] : 697066733a2f2f516d515a7735767951476e765a4637327a6243616a326f3272
Arg [6] : 36706a453361416a373345356952725177766e656e0000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in GLMR
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.