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