Contract Name:
MintingManager
Contract Source Code:
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import '@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol';
import './cns/IResolver.sol';
import './cns/IMintingController.sol';
import './cns/IURIPrefixController.sol';
import './IMintingManager.sol';
import './IUNSRegistry.sol';
import './metatx/Relayer.sol';
import './roles/MinterRole.sol';
/**
* @title MintingManager
* @dev Defines the functions for distribution of Second Level Domains (SLD)s.
*/
contract MintingManager is Initializable, ContextUpgradeable, OwnableUpgradeable, MinterRole, Relayer, IMintingManager {
string public constant NAME = 'UNS: Minting Manager';
string public constant VERSION = '0.1.0';
IUNSRegistry public unsRegistry;
IMintingController public cnsMintingController;
IURIPrefixController public cnsURIPrefixController;
IResolver public cnsResolver;
/**
* @dev Mapping TLD `namehash` to TLD label
*
* `namehash` = uint256(keccak256(abi.encodePacked(uint256(0x0), keccak256(abi.encodePacked(label)))))
*/
mapping(uint256 => string) internal _tlds;
/**
* @dev bytes4(keccak256('mintSLD(address,uint256,string)')) == 0xae2ad903
*/
bytes4 private constant _SIG_MINT = 0xae2ad903;
/**
* @dev bytes4(keccak256('safeMintSLD(address,uint256,string)')) == 0x4c1819e0
*/
bytes4 private constant _SIG_SAFE_MINT = 0x4c1819e0;
/**
* @dev bytes4(keccak256('safeMintSLD(address,uint256,string,bytes)')) == 0x58839d6b
*/
bytes4 private constant _SIG_SAFE_MINT_DATA = 0x58839d6b;
/**
* @dev bytes4(keccak256('mintSLDWithRecords(address,uint256,string,string[],string[])')) == 0x39ccf4d0
*/
bytes4 private constant _SIG_MINT_WITH_RECORDS = 0x39ccf4d0;
/**
* @dev bytes4(keccak256('safeMintSLDWithRecords(address,uint256,string,string[],string[])')) == 0x27bbd225
*/
bytes4 private constant _SIG_SAFE_MINT_WITH_RECORDS = 0x27bbd225;
/**
* @dev bytes4(keccak256('safeMintSLDWithRecords(address,uint256,string,string[],string[],bytes)')) == 0x6a2d2256
*/
bytes4 private constant _SIG_SAFE_MINT_WITH_RECORDS_DATA = 0x6a2d2256;
modifier onlyRegisteredTld(uint256 tld) {
require(bytes(_tlds[tld]).length > 0, 'MintingManager: TLD_NOT_REGISTERED');
_;
}
function initialize(
IUNSRegistry unsRegistry_,
IMintingController cnsMintingController_,
IURIPrefixController cnsURIPrefixController_,
IResolver cnsResolver_
) public initializer {
unsRegistry = unsRegistry_;
cnsMintingController = cnsMintingController_;
cnsURIPrefixController = cnsURIPrefixController_;
cnsResolver = cnsResolver_;
__Ownable_init_unchained();
__MinterRole_init_unchained();
// Relayer is required to be a minter
_addMinter(address(this));
_tlds[0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f] = 'crypto';
string[8] memory tlds = ['wallet', 'coin', 'x', 'nft', 'blockchain', 'bitcoin', '888', 'dao'];
for (uint256 i = 0; i < tlds.length; i++) {
uint256 namehash = uint256(keccak256(abi.encodePacked(uint256(0x0), keccak256(abi.encodePacked(tlds[i])))));
_tlds[namehash] = tlds[i];
if (!unsRegistry.exists(namehash)) {
unsRegistry.mint(address(0xdead), namehash, tlds[i]);
}
}
}
function mintSLD(
address to,
uint256 tld,
string calldata label
) external override onlyMinter onlyRegisteredTld(tld) {
_mintSLD(to, tld, label);
}
function safeMintSLD(
address to,
uint256 tld,
string calldata label
) external override onlyMinter onlyRegisteredTld(tld) {
_safeMintSLD(to, tld, label, '');
}
function safeMintSLD(
address to,
uint256 tld,
string calldata label,
bytes calldata data
) external override onlyMinter onlyRegisteredTld(tld) {
_safeMintSLD(to, tld, label, data);
}
function mintSLDWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values
) external override onlyMinter onlyRegisteredTld(tld) {
_mintSLDWithRecords(to, tld, label, keys, values);
}
function safeMintSLDWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values
) external override onlyMinter onlyRegisteredTld(tld) {
_safeMintSLDWithRecords(to, tld, label, keys, values, '');
}
function safeMintSLDWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values,
bytes calldata data
) external override onlyMinter onlyRegisteredTld(tld) {
_safeMintSLDWithRecords(to, tld, label, keys, values, data);
}
function claim(uint256 tld, string calldata label) external override onlyRegisteredTld(tld) {
_mintSLD(_msgSender(), tld, _freeSLDLabel(label));
}
function claimTo(
address to,
uint256 tld,
string calldata label
) external override onlyRegisteredTld(tld) {
_mintSLD(to, tld, _freeSLDLabel(label));
}
function claimToWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values
) external override onlyRegisteredTld(tld) {
_mintSLDWithRecords(to, tld, _freeSLDLabel(label), keys, values);
}
function setResolver(address resolver) external onlyOwner {
cnsResolver = IResolver(resolver);
}
function setTokenURIPrefix(string calldata prefix) external override onlyOwner {
unsRegistry.setTokenURIPrefix(prefix);
if (address(cnsURIPrefixController) != address(0x0)) {
cnsURIPrefixController.setTokenURIPrefix(prefix);
}
}
function _verifyRelaySigner(address signer) internal view override {
super._verifyRelaySigner(signer);
require(isMinter(signer), 'MintingManager: SIGNER_IS_NOT_MINTER');
}
function _verifyRelayCall(bytes4 funcSig, bytes calldata) internal pure override {
bool isSupported =
funcSig == _SIG_MINT ||
funcSig == _SIG_SAFE_MINT ||
funcSig == _SIG_SAFE_MINT_DATA ||
funcSig == _SIG_MINT_WITH_RECORDS ||
funcSig == _SIG_SAFE_MINT_WITH_RECORDS ||
funcSig == _SIG_SAFE_MINT_WITH_RECORDS_DATA;
require(isSupported, 'MintingManager: UNSUPPORTED_RELAY_CALL');
}
function _mintSLD(
address to,
uint256 tld,
string memory label
) private {
if (tld == 0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f) {
cnsMintingController.mintSLDWithResolver(to, label, address(cnsResolver));
} else {
unsRegistry.mint(to, _childId(tld, label), _uri(tld, label));
}
}
function _safeMintSLD(
address to,
uint256 tld,
string calldata label,
bytes memory data
) private {
if (tld == 0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f) {
cnsMintingController.safeMintSLDWithResolver(to, label, address(cnsResolver), data);
} else {
unsRegistry.safeMint(to, _childId(tld, label), _uri(tld, label), data);
}
}
function _mintSLDWithRecords(
address to,
uint256 tld,
string memory label,
string[] calldata keys,
string[] calldata values
) private {
uint256 tokenId = _childId(tld, label);
if (tld == 0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f) {
cnsMintingController.mintSLDWithResolver(to, label, address(cnsResolver));
if (keys.length > 0) {
cnsResolver.preconfigure(keys, values, tokenId);
}
} else {
unsRegistry.mintWithRecords(to, tokenId, _uri(tld, label), keys, values);
}
}
function _safeMintSLDWithRecords(
address to,
uint256 tld,
string memory label,
string[] calldata keys,
string[] calldata values,
bytes memory data
) private {
uint256 tokenId = _childId(tld, label);
if (tld == 0x0f4a10a4f46c288cea365fcf45cccf0e9d901b945b9829ccdb54c10dc3cb7a6f) {
cnsMintingController.safeMintSLDWithResolver(to, label, address(cnsResolver), data);
if (keys.length > 0) {
cnsResolver.preconfigure(keys, values, tokenId);
}
} else {
unsRegistry.safeMintWithRecords(to, tokenId, _uri(tld, label), keys, values, data);
}
}
function _childId(uint256 tokenId, string memory label) internal pure returns (uint256) {
require(bytes(label).length != 0, 'MintingManager: LABEL_EMPTY');
return uint256(keccak256(abi.encodePacked(tokenId, keccak256(abi.encodePacked(label)))));
}
function _freeSLDLabel(string calldata label) private pure returns (string memory) {
return string(abi.encodePacked('udtestdev-', label));
}
function _uri(uint256 tld, string memory label) private view returns (string memory) {
return string(abi.encodePacked(label, '.', _tlds[tld]));
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal initializer {
__Context_init_unchained();
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal initializer {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: 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 {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity ^0.8.0;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/*
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSAUpgradeable {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
} else if (signature.length == 64) {
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
let vs := mload(add(signature, 0x40))
r := mload(add(signature, 0x20))
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
} else {
revert("ECDSA: invalid signature length");
}
return recover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
interface IResolver {
function preconfigure(
string[] memory keys,
string[] memory values,
uint256 tokenId
) external;
function get(string calldata key, uint256 tokenId) external view returns (string memory);
function getMany(string[] calldata keys, uint256 tokenId) external view returns (string[] memory);
function getByHash(uint256 keyHash, uint256 tokenId) external view returns (string memory key, string memory value);
function getManyByHash(uint256[] calldata keyHashes, uint256 tokenId)
external
view
returns (string[] memory keys, string[] memory values);
function set(
string calldata key,
string calldata value,
uint256 tokenId
) external;
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
interface IMintingController {
function mintSLD(address to, string calldata label) external;
function safeMintSLD(address to, string calldata label) external;
function safeMintSLD(
address to,
string calldata label,
bytes calldata data
) external;
function mintSLDWithResolver(
address to,
string memory label,
address resolver
) external;
function safeMintSLDWithResolver(
address to,
string calldata label,
address resolver
) external;
function safeMintSLDWithResolver(
address to,
string calldata label,
address resolver,
bytes calldata data
) external;
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
interface IURIPrefixController {
function setTokenURIPrefix(string calldata prefix) external;
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
interface IMintingManager {
/**
* @dev Mints a Second Level Domain (SLD).
* @param to address to mint the new SLD to.
* @param tld id of parent token.
* @param label SLD label to mint.
*/
function mintSLD(
address to,
uint256 tld,
string calldata label
) external;
/**
* @dev Safely mints a Second Level Domain (SLD).
* Implements a ERC721Reciever check unlike mintSLD.
* @param to address to mint the new SLD to.
* @param tld id of parent token.
* @param label SLD label to mint.
*/
function safeMintSLD(
address to,
uint256 tld,
string calldata label
) external;
/**
* @dev Safely mints a Second Level Domain (SLD).
* Implements a ERC721Reciever check unlike mintSLD.
* @param to address to mint the new SLD to.
* @param tld id of parent token.
* @param label SLD label to mint.
* @param data bytes data to send along with a safe transfer check.
*/
function safeMintSLD(
address to,
uint256 tld,
string calldata label,
bytes calldata data
) external;
/**
* @dev Mints a Second Level Domain (SLD) with records.
* @param to address to mint the new SLD to.
* @param tld id of parent token.
* @param label SLD label to mint.
* @param keys Record keys.
* @param values Record values.
*/
function mintSLDWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values
) external;
/**
* @dev Mints a Second Level Domain (SLD) with records.
* Implements a ERC721Reciever check unlike mintSLD.
* @param to address to mint the new SLD to.
* @param tld id of parent token.
* @param label SLD label to mint.
* @param keys Record keys.
* @param values Record values.
*/
function safeMintSLDWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values
) external;
/**
* @dev Mints a Second Level Domain (SLD) with records.
* Implements a ERC721Reciever check unlike mintSLD.
* @param to address to mint the new SLD to.
* @param tld id of parent token.
* @param label SLD label to mint.
* @param keys Record keys.
* @param values Record values.
* @param data bytes data to send along with a safe transfer check.
*/
function safeMintSLDWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values,
bytes calldata data
) external;
/**
* @dev Claims free domain. The fuction adds prefix `udtestdev-` to label.
* @param tld id of parent token
* @param label SLD label to mint
*/
function claim(uint256 tld, string calldata label) external;
/**
* @dev Claims free domain. The fuction adds prefix `udtestdev-` to label.
* @param to address to mint the new SLD to
* @param tld id of parent token
* @param label SLD label to mint
*/
function claimTo(
address to,
uint256 tld,
string calldata label
) external;
/**
* @dev Claims free domain. The fuction adds prefix `udtestdev-` to label.
* @param to address to mint the new SLD to
* @param tld id of parent token
* @param label SLD label to mint
*/
function claimToWithRecords(
address to,
uint256 tld,
string calldata label,
string[] calldata keys,
string[] calldata values
) external;
/**
* @dev Function to set the token URI Prefix for all tokens.
* @param prefix string URI to assign
*/
function setTokenURIPrefix(string calldata prefix) external;
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
import '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol';
import './IRecordStorage.sol';
interface IUNSRegistry is IERC721MetadataUpgradeable, IRecordStorage {
event NewURI(uint256 indexed tokenId, string uri);
event NewURIPrefix(string prefix);
/**
* @dev Function to set the token URI Prefix for all tokens.
* @param prefix string URI to assign
*/
function setTokenURIPrefix(string calldata prefix) external;
/**
* @dev Returns whether the given spender can transfer a given token ID.
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool);
/**
* @dev Gets the resolver of the specified token ID.
* @param tokenId uint256 ID of the token to query the resolver of
* @return address currently marked as the resolver of the given token ID
*/
function resolverOf(uint256 tokenId) external view returns (address);
/**
* @dev Provides child token (subdomain) of provided tokenId.
* @param tokenId uint256 ID of the token
* @param label label of subdomain (for `aaa.bbb.crypto` it will be `aaa`)
*/
function childIdOf(uint256 tokenId, string calldata label) external pure returns (uint256);
/**
* @dev Existence of token.
* @param tokenId uint256 ID of the token
*/
function exists(uint256 tokenId) external view returns (bool);
/**
* @dev Transfer domain ownership without resetting domain records.
* @param to address of new domain owner
* @param tokenId uint256 ID of the token to be transferred
*/
function setOwner(address to, uint256 tokenId) external;
/**
* @dev Burns `tokenId`. See {ERC721-_burn}.
*
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/
function burn(uint256 tokenId) external;
/**
* @dev Mints token.
* @param to address to mint the new SLD to.
* @param tokenId id of token.
* @param uri domain URI.
*/
function mint(
address to,
uint256 tokenId,
string calldata uri
) external;
/**
* @dev Safely mints token.
* Implements a ERC721Reciever check unlike mint.
* @param to address to mint the new SLD to.
* @param tokenId id of token.
* @param uri domain URI.
*/
function safeMint(
address to,
uint256 tokenId,
string calldata uri
) external;
/**
* @dev Safely mints token.
* Implements a ERC721Reciever check unlike mint.
* @param to address to mint the new SLD to.
* @param tokenId id of token.
* @param uri domain URI.
* @param data bytes data to send along with a safe transfer check
*/
function safeMint(
address to,
uint256 tokenId,
string calldata uri,
bytes calldata data
) external;
/**
* @dev Mints token with records
* @param to address to mint the new SLD to
* @param tokenId id of token
* @param keys New record keys
* @param values New record values
* @param uri domain URI
*/
function mintWithRecords(
address to,
uint256 tokenId,
string calldata uri,
string[] calldata keys,
string[] calldata values
) external;
/**
* @dev Safely mints token with records
* @param to address to mint the new SLD to
* @param tokenId id of token
* @param keys New record keys
* @param values New record values
* @param uri domain URI
*/
function safeMintWithRecords(
address to,
uint256 tokenId,
string calldata uri,
string[] calldata keys,
string[] calldata values
) external;
/**
* @dev Safely mints token with records
* @param to address to mint the new SLD to
* @param tokenId id of token
* @param keys New record keys
* @param values New record values
* @param uri domain URI
* @param data bytes data to send along with a safe transfer check
*/
function safeMintWithRecords(
address to,
uint256 tokenId,
string calldata uri,
string[] calldata keys,
string[] calldata values,
bytes calldata data
) external;
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
import '@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol';
abstract contract Relayer is ContextUpgradeable {
using ECDSAUpgradeable for bytes32;
event Relayed(address indexed sender, address indexed signer, bytes4 indexed funcSig, bytes32 digest);
/**
* Relay allows execute transaction on behalf of whitelisted minter.
* The function verify signature of call data parameter before execution.
* It allows anybody send transaction on-chain when minter has provided proper parameters.
* The function allows to relaying calls of fixed functions. The restriction defined in function `verifyCall`
*/
function relay(bytes calldata data, bytes calldata signature) external returns (bytes memory) {
bytes32 digest = keccak256(data);
address signer = keccak256(abi.encodePacked(digest, address(this))).toEthSignedMessageHash().recover(signature);
bytes4 funcSig;
bytes memory _data = data;
/* solium-disable-next-line security/no-inline-assembly */
assembly {
funcSig := mload(add(_data, add(0x20, 0)))
}
_verifyRelaySigner(signer);
_verifyRelayCall(funcSig, data);
/* solium-disable-next-line security/no-low-level-calls */
(bool success, bytes memory result) = address(this).call(data);
if (success == false) {
/* solium-disable-next-line security/no-inline-assembly */
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
emit Relayed(_msgSender(), signer, funcSig, digest);
return result;
}
function _verifyRelaySigner(address signer) internal view virtual {
require(signer != address(0), 'Relayer: SIGNATURE_IS_INVALID');
}
function _verifyRelayCall(bytes4 funcSig, bytes calldata data) internal pure virtual {}
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
abstract contract MinterRole is OwnableUpgradeable, AccessControlUpgradeable {
bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE');
modifier onlyMinter() {
require(isMinter(_msgSender()), 'MinterRole: CALLER_IS_NOT_MINTER');
_;
}
// solhint-disable-next-line func-name-mixedcase
function __MinterRole_init() internal initializer {
__Ownable_init_unchained();
__AccessControl_init_unchained();
__MinterRole_init_unchained();
}
// solhint-disable-next-line func-name-mixedcase
function __MinterRole_init_unchained() internal initializer {}
function isMinter(address account) public view returns (bool) {
return hasRole(MINTER_ROLE, account);
}
function addMinter(address account) public onlyOwner {
_addMinter(account);
}
function addMinters(address[] memory accounts) public onlyOwner {
for (uint256 index = 0; index < accounts.length; index++) {
_addMinter(accounts[index]);
}
}
function removeMinter(address account) public onlyOwner {
_removeMinter(account);
}
function removeMinters(address[] memory accounts) public onlyOwner {
for (uint256 index = 0; index < accounts.length; index++) {
_removeMinter(accounts[index]);
}
}
function renounceMinter() public {
_removeMinter(_msgSender());
}
/**
* Renounce minter account with funds' forwarding
*/
function closeMinter(address payable receiver) external payable onlyMinter {
require(receiver != address(0x0), 'MinterRole: RECEIVER_IS_EMPTY');
renounceMinter();
receiver.transfer(msg.value);
}
/**
* Replace minter account by new account with funds' forwarding
*/
function rotateMinter(address payable receiver) external payable onlyMinter {
require(receiver != address(0x0), 'MinterRole: RECEIVER_IS_EMPTY');
_addMinter(receiver);
renounceMinter();
receiver.transfer(msg.value);
}
function _addMinter(address account) internal {
_setupRole(MINTER_ROLE, account);
}
function _removeMinter(address account) internal {
renounceRole(MINTER_ROLE, account);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC721Upgradeable.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721MetadataUpgradeable is IERC721Upgradeable {
/**
* @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);
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
import './IRecordReader.sol';
interface IRecordStorage is IRecordReader {
event Set(uint256 indexed tokenId, string indexed keyIndex, string indexed valueIndex, string key, string value);
event NewKey(uint256 indexed tokenId, string indexed keyIndex, string key);
event ResetRecords(uint256 indexed tokenId);
/**
* @dev Set record by key
* @param key The key set the value of
* @param value The value to set key to
* @param tokenId ERC-721 token id to set
*/
function set(
string calldata key,
string calldata value,
uint256 tokenId
) external;
/**
* @dev Set records by keys
* @param keys The keys set the values of
* @param values Records values
* @param tokenId ERC-721 token id of the domain
*/
function setMany(
string[] memory keys,
string[] memory values,
uint256 tokenId
) external;
/**
* @dev Set record by key hash
* @param keyHash The key hash set the value of
* @param value The value to set key to
* @param tokenId ERC-721 token id to set
*/
function setByHash(
uint256 keyHash,
string calldata value,
uint256 tokenId
) external;
/**
* @dev Set records by key hashes
* @param keyHashes The key hashes set the values of
* @param values Records values
* @param tokenId ERC-721 token id of the domain
*/
function setManyByHash(
uint256[] calldata keyHashes,
string[] calldata values,
uint256 tokenId
) external;
/**
* @dev Reset all domain records and set new ones
* @param keys New record keys
* @param values New record values
* @param tokenId ERC-721 token id of the domain
*/
function reconfigure(
string[] memory keys,
string[] memory values,
uint256 tokenId
) external;
/**
* @dev Function to reset all existing records on a domain.
* @param tokenId ERC-721 token id to set.
*/
function reset(uint256 tokenId) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721Upgradeable is IERC165Upgradeable {
/**
* @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`, 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 be 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: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* 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 Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @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 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);
/**
* @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;
}
// SPDX-License-Identifier: MIT
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 IERC165Upgradeable {
/**
* @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);
}
// @author Unstoppable Domains, Inc.
// @date June 16th, 2021
pragma solidity ^0.8.0;
interface IRecordReader {
/**
* @dev Function to get record.
* @param key The key to query the value of.
* @param tokenId The token id to fetch.
* @return The value string.
*/
function get(string calldata key, uint256 tokenId) external view returns (string memory);
/**
* @dev Function to get multiple record.
* @param keys The keys to query the value of.
* @param tokenId The token id to fetch.
* @return The values.
*/
function getMany(string[] calldata keys, uint256 tokenId) external view returns (string[] memory);
/**
* @dev Function get value by provied key hash.
* @param keyHash The key to query the value of.
* @param tokenId The token id to set.
*/
function getByHash(uint256 keyHash, uint256 tokenId) external view returns (string memory key, string memory value);
/**
* @dev Function get values by provied key hashes.
* @param keyHashes The key to query the value of.
* @param tokenId The token id to set.
*/
function getManyByHash(uint256[] calldata keyHashes, uint256 tokenId)
external
view
returns (string[] memory keys, string[] memory values);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
function __AccessControl_init() internal initializer {
__Context_init_unchained();
__ERC165_init_unchained();
__AccessControl_init_unchained();
}
function __AccessControl_init_unchained() internal initializer {
}
struct RoleData {
mapping (address => bool) members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlUpgradeable).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
*/
function _checkRole(bytes32 role, address account) internal view {
if(!hasRole(role, account)) {
revert(string(abi.encodePacked(
"AccessControl: account ",
StringsUpgradeable.toHexString(uint160(account), 20),
" is missing role ",
StringsUpgradeable.toHexString(uint256(role), 32)
)));
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library StringsUpgradeable {
bytes16 private constant alphabet = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = alphabet[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal initializer {
__ERC165_init_unchained();
}
function __ERC165_init_unchained() internal initializer {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
uint256[50] private __gap;
}