EnsMapper Contract
EnsMapper Contract is a custom ENS Resolver contract created by PCC's wizard hodl.pcc.eth
. This contract made PCC the first project that links ERC-721 NFTs to ENS subdomains possible.
The contract itself says feel free to reuse it for other projects to use!
Source Code
The EnsMapper contract is deployed to 0x9B6d20F524367D7E98ED849d37Fc662402DCa7FB
, and source code is public on Etherscan. Here's a copy of the contract's source code:
View Contract Source Code
- 1/8
- 2/8
- 3/8
- 4/8
- 5/8
- 6/8
- 7/8
- 8/8
EnsMapper.sol
/*
▓▓█
▒██▒▒█
█▓▓▓░▒▓▓
▒█▓▒█░▒▒▒█
▒█▒▒▒█▒▒▒▒▓▒
▓▓▒░ ▓█▒▒▒▓██▓▒░▒█
█▓▓██▓░ ▓█▒▒▒▒████▒▒▒█
▓█▓▒▒▓██▓░ ▒█▒▒▒▒▒██▓█▓░░▓▒
▓▒▓▒▒▒▒▒▓█▓░ ░▒▒▓▓██▒▒▒▒▒▒█████▒▒▒▓
▓░█▒▒▒▒▒▒▒▓▓█▓█▓▓▓▓▒▒▒▒▒▒▒▒██▓██▒░▒█
▓░▓█▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓████▒▒▒█
▓░▓██▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▒▒▓██░░░█
▓░▓███▒▒▒▒▒▒▒▒▒▒▒▓█▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▓▓
▒▒▒██▓▒▓█▓▒▒▒▒▒▒▒▓▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▓▒█
▓▒█▓▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▓█▓▓▓▓█▓▒▒▒▒▒▒▒▓▒
▓▒█▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ ▓█▓▒▒▒▒▒▓█
▒▒▓▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▓▓ ░▓▓ █▓▒▒▒▒▒█
█▒▒▓▓▓▒▒▓▓▒▒▒▒▒▒▓▓ █████▓ █▓▒▒▒▒▓▒
▓▓█▒ ▒▓▒▒▒▒▒█ ░██████ ░█▒▒▒▒▓▓
▓█▒ ▒███ ▒▓▒▒▒▒█ ██████ ▓▒▒▒▒▒▓
██ █████ █▒▒▒▒█ ███▓ ▓▓▒▒▒▒▓
█▓ █████ ▒▓▒▒▒█ █▓▓▓▒▒▓
█▓ ░███ ░▓▒▒▒▓█ ░█▓▒▒▒▓▒▓
██ ▒▓▒▒▒▒▓▓ ░▒▓█▓ ░▓▓
▓█░ █▓██▓▒▒▓█▓▓▓▓██▓▓▒▓▒░░▒▓▒▓
▒██░ ▓▒███▓▒▒▒▒▓▓▓▓▒▒▒▒▒▒▓▓▓▓▒▓
█▓█▓▓▒▒▓█▓▒░██▒▒▓▓█▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓█▒
▓ ░▓▓▓▓▓▒▓▓▓▓▒▓▓▓▒▓▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓
▒▒▒▓▒▒▒▒▒▒▓█░ ░░░ ▓▓▒▒▒▒▒▒▒▒▒▒▒▓██▓▒
█▓▒▒▒▒▒▒▒▒▓▓ ░░░ ▓▓▒▒▒▒▒▒▒▒▒▓▓▓▒▒▓▒
██▓▓▒▒▒▒▒▒█▒░░░░█▒▒▒▒▒▒▒▒▓█▓▓▒▒▒▒▒
▒██▓▓▒▒▒▒▒█▓▒▒▓▒▒▒▒▒▒▓███▓▒▒▒▒▒▓▓
░▒▓▓▓▓▒▒▓▓▓▓▓▓████▓▓█▒▒▒▒▒▓▓█░
████████████████████████████████████████████████████████████████████████
█▄─▄▄─█▄─██─▄█▄─▄▄▀█▄─▄▄▀█▄─▀█▄─▄█▄─▄▄─█▄─▄███─▄▄─█▄─▄▄─█▄─▄▄─█─▄▄▄▄████
██─▄▄▄██─██─███─▄─▄██─▄─▄██─█▄▀─███─▄█▀██─██▀█─██─██─▄▄▄██─▄█▀█▄▄▄▄─████
█▄▄▄████▄▄▄▄██▄▄█▄▄█▄▄█▄▄█▄▄▄██▄▄█▄▄▄▄▄█▄▄▄▄▄█▄▄▄▄█▄▄▄███▄▄▄▄▄█▄▄▄▄▄████
████████████████████████████████████████████████████████████████████████
█─▄▄▄─█─▄▄─█▄─██─▄█▄─▀█▄─▄█─▄─▄─█▄─▄▄▀█▄─█─▄███─▄▄▄─█▄─▄███▄─██─▄█▄─▄─▀█
█─███▀█─██─██─██─███─█▄▀─████─████─▄─▄██▄─▄████─███▀██─██▀██─██─███─▄─▀█
█▄▄▄▄▄█▄▄▄▄██▄▄▄▄██▄▄▄██▄▄██▄▄▄██▄▄█▄▄██▄▄▄████▄▄▄▄▄█▄▄▄▄▄██▄▄▄▄██▄▄▄▄██*/
//SPDX-License-Identifier: MIT
//Twitter: @hodl_pcc
//
/* This code should work for most ERC-721 contracts. Please feel free to reuse
- Change IERC721Enumerable contract address in constructor
- Update domainLabel value
- Update nftImageBaseUri value to the base path of the images
- Set controller address of the parent domain to this deployed contract (in ENS web app)
*/
pragma solidity ^0.8.2;
import "@ensdomains/ens-contracts/contracts/registry/ENS.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}
contract EnsMapper is Ownable {
using Strings for uint256;
ENS private ens;
IERC721Enumerable public nft;
bytes32 public domainHash;
mapping(bytes32 => mapping(string => string)) public texts;
mapping(address => uint256) public nextRegisterTimestamp;
string public domainLabel = "pcc";
string public nftImageBaseUri = "https://ipfs.io/ipfs/QmUfyS4W6cBRRWtWpbW1as4ziaiEHTBD9WVcn1nrEB6xPu/";
bool public useEIP155 = true;
mapping(bytes32 => uint256) public hashToIdMap;
mapping(uint256 => bytes32) public tokenHashmap;
mapping(bytes32 => string) public hashToDomainMap;
uint256 public reset_period = 7257600; //12 weeks
bool public publicClaimOpen = false;
mapping(address => bool) public address_whitelist;
event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
event RegisterSubdomain(address indexed registrar, uint256 indexed token_id, string indexed label);
constructor(){
ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
nft = IERC721Enumerable(0x9759226B2F8ddEFF81583e244Ef3bd13AAA7e4A1);
domainHash = getDomainHash();
}
//<interface-functions>
function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
return interfaceID == 0x3b3b57de //addr
|| interfaceID == 0x59d1d43c //text
|| interfaceID == 0x691f3431 //name
|| interfaceID == 0x01ffc9a7; //supportsInterface << [inception]
}
function text(bytes32 node, string calldata key) external view returns (string memory) {
uint256 token_id = hashToIdMap[node];
require(token_id > 0 && tokenHashmap[token_id] != 0x0, "Invalid address");
if(keccak256(abi.encodePacked(key)) == keccak256("avatar")){
//eip155 string did not seem to work in any supported dapps during testing despite the returned string being properly
//formatted. So the toggle was added so that we can direct link the image using http:// if this still does not work on
//mainnet
return useEIP155 ? string(abi.encodePacked("eip155:1/erc721:", addressToString(address(nft)), "/", token_id.toString()))
: string(abi.encodePacked(nftImageBaseUri, token_id.toString(),".png"));
}
else{
return texts[node][key];
}
}
function addr(bytes32 nodeID) public view returns (address) {
uint256 token_id = hashToIdMap[nodeID];
require(token_id > 0 && tokenHashmap[token_id] != 0x0, "Invalid address");
return nft.ownerOf(token_id);
}
function name(bytes32 node) view public returns (string memory){
return (hashToIdMap[node] == 0)
? ""
: string(abi.encodePacked(hashToDomainMap[node], ".", domainLabel, ".eth"));
}
//</interface-functions>
//--------------------------------------------------------------------------------------------//
//<read-functions>
function domainMap(string calldata label) public view returns(bytes32){
bytes32 encoded_label = keccak256(abi.encodePacked(label));
bytes32 big_hash = keccak256(abi.encodePacked(domainHash, encoded_label));
return hashToIdMap[big_hash] > 0 ? big_hash : bytes32(0x0);
}
function getClaimableIdsForAddress(address addy) public view returns(uint256[] memory){
if(((address_whitelist[addy] || publicClaimOpen)
&& block.timestamp > nextRegisterTimestamp[addy])
|| owner() == addy){
return getAllIds(addy);
}
else{
return new uint256[](0);
}
}
function getAllIds(address addy) private view returns(uint256[] memory){
uint256 balance = nft.balanceOf(addy);
uint256[] memory ids = new uint256[](balance);
uint256 count;
for(uint256 i; i < balance; i++){
uint256 id = nft.tokenOfOwnerByIndex(addy, i);
if(tokenHashmap[id] == 0x0){
ids[count++] = id;
}
}
uint256[] memory trim_ids = new uint256[](count);
for(uint256 i; i < count; i++){
trim_ids[i] = ids[i];
}
return trim_ids;
}
function getTokenDomain(uint256 token_id) private view returns(string memory uri){
require(tokenHashmap[token_id] != 0x0, "Token does not have an ENS register");
uri = string(abi.encodePacked(hashToDomainMap[tokenHashmap[token_id]] ,"." ,domainLabel, ".eth"));
}
function getTokensDomains(uint256[] memory token_ids) public view returns(string[] memory){
string[] memory uris = new string[](token_ids.length);
for(uint256 i; i < token_ids.length; i++){
uris[i] = getTokenDomain(token_ids[i]);
}
return uris;
}
function getAllCatsWithDomains(address addy) public view returns(uint256[] memory){
uint256 balance = nft.balanceOf(addy);
uint256[] memory ids = new uint256[](balance);
uint256 count;
for(uint256 i; i < balance; i++){
uint256 id = nft.tokenOfOwnerByIndex(addy, i);
if(tokenHashmap[id] != 0x0){
ids[count++] = id;
}
}
uint256[] memory trim_ids = new uint256[](count);
for(uint256 i; i < count; i++){
trim_ids[i] = ids[i];
}
return trim_ids;
}
//</read-functions>
//--------------------------------------------------------------------------------------------//
//<helper-functions>
function addressToString(address _addr) private pure returns(string memory) {
bytes32 value = bytes32(uint256(uint160(_addr)));
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(51);
str[0] = "0";
str[1] = "x";
for (uint i = 0; i < 20; i++) {
str[2+i*2] = alphabet[uint(uint8(value[i + 12] >> 4))];
str[3+i*2] = alphabet[uint(uint8(value[i + 12] & 0x0f))];
}
return string(str);
}
//this is the correct method for creating a 2 level ENS namehash
function getDomainHash() private view returns (bytes32 namehash) {
namehash = 0x0;
namehash = keccak256(abi.encodePacked(namehash, keccak256(abi.encodePacked('eth'))));
namehash = keccak256(abi.encodePacked(namehash, keccak256(abi.encodePacked(domainLabel))));
}
//</helper-functions>
//--------------------------------------------------------------------------------------------//
//<authorised-functions>
function setDomain(string calldata label, uint256 token_id) public isAuthorised(token_id) {
require(tokenHashmap[token_id] == 0x0, "Token has already been set");
require(address_whitelist[msg.sender] || publicClaimOpen || owner() == msg.sender, "Not authorised");
require(block.timestamp > nextRegisterTimestamp[msg.sender], "Wallet must wait more time to register");
bytes32 encoded_label = keccak256(abi.encodePacked(label));
bytes32 big_hash = keccak256(abi.encodePacked(domainHash, encoded_label));
//contract owner can update / overwrite records. << this may be changed in the future with an updated method but as this is still
//an experiment we'd like to retain some level of control over the sub-domains
//
//ens.recordExists seems to not be reliable (tested removing records through ENS control panel and this still returns true)
require(!ens.recordExists(big_hash) || msg.sender == owner(), "sub-domain already exists");
ens.setSubnodeRecord(domainHash, encoded_label, owner(), address(this), 0);
hashToIdMap[big_hash] = token_id;
tokenHashmap[token_id] = big_hash;
hashToDomainMap[big_hash] = label;
if (owner() != msg.sender){
nextRegisterTimestamp[msg.sender] = block.timestamp + reset_period;
//if user is on whitelist then remove
if (address_whitelist[msg.sender]){
address_whitelist[msg.sender] = false;
}
}
emit RegisterSubdomain(nft.ownerOf(token_id), token_id, label);
}
function setText(bytes32 node, string calldata key, string calldata value) external isAuthorised(hashToIdMap[node]) {
uint256 token_id = hashToIdMap[node];
require(token_id > 0 && tokenHashmap[token_id] != 0x0, "Invalid address");
require(keccak256(abi.encodePacked(key)) != keccak256("avatar"), "cannot set avatar");
texts[node][key] = value;
emit TextChanged(node, key, key);
}
function resetHash(uint256 token_id) public isAuthorised(token_id) {
bytes32 domain = tokenHashmap[token_id];
require(ens.recordExists(domain), "Sub-domain does not exist");
//reset domain mappings
hashToDomainMap[domain] = "";
hashToIdMap[domain] = 0;
tokenHashmap[token_id] = 0x0;
//allow sender to reclaim (if public == true)
if(nextRegisterTimestamp[msg.sender] > block.timestamp && msg.sender != owner()){
nextRegisterTimestamp[msg.sender] = block.timestamp + (60 * 30); //30 minute cooldown
}
}
//</authorised-functions>
//--------------------------------------------------------------------------------------------//
// <owner-functions>
function addAddressWhitelist(address[] calldata addresses) public onlyOwner {
for(uint256 i; i < addresses.length; i++){
address_whitelist[addresses[i]] = true;
}
}
function setDomainLabel(string calldata label) public onlyOwner {
domainLabel = label;
domainHash = getDomainHash();
}
function setNftAddress(address addy) public onlyOwner{
nft = IERC721Enumerable(addy);
}
function toggleNftImageLink() public onlyOwner{
useEIP155 = !useEIP155;
}
function setNftImageBaseUri(string memory _uri) public onlyOwner{
nftImageBaseUri = _uri;
}
function setEnsAddress(address addy) public onlyOwner {
ens = ENS(addy);
}
function resetAddressForClaim(address addy) public onlyOwner {
nextRegisterTimestamp[addy] = 0;
}
function togglePublicClaim() public onlyOwner {
publicClaimOpen = !publicClaimOpen;
}
function updateResetPeriod(uint256 time) public onlyOwner {
reset_period = time;
}
function renounceOwnership() public override onlyOwner {
require(false, "ENS is responsibility. You cannot renounce ownership.");
super.renounceOwnership();
}
//just never know.. do you.
function withdraw() public onlyOwner {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
}
function withdrawTokens(IERC20 token) public onlyOwner {
require(address(token) != address(0));
uint256 balance = token.balanceOf(address(this));
token.transfer(msg.sender, balance);
}
//</owner-functions>
modifier isAuthorised(uint256 tokenId) {
require(owner() == msg.sender || nft.ownerOf(tokenId) == msg.sender, "Not authorised");
_;
}
}
ENS.sol
pragma solidity >=0.8.4;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
// Logged when an operator is added or removed.
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual;
function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual;
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32);
function setResolver(bytes32 node, address resolver) external virtual;
function setOwner(bytes32 node, address owner) external virtual;
function setTTL(bytes32 node, uint64 ttl) external virtual;
function setApprovalForAll(address operator, bool approved) external virtual;
function owner(bytes32 node) external virtual view returns (address);
function resolver(bytes32 node) external virtual view returns (address);
function ttl(bytes32 node) external virtual view returns (uint64);
function recordExists(bytes32 node) external virtual view returns (bool);
function isApprovedForAll(address owner, address operator) external virtual view returns (bool);
}
IERC721.sol
// SPDX-License-Identifier: MIT
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`, 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;
}
IERC721Enumerable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}
Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/Context.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 Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_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 {
_setOwner(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");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Strings.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "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] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
IERC165.sol
// 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 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);
}
Context.sol
// SPDX-License-Identifier: MIT
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;
}
}
Analysis
TBD, no plan yet.
You can also contribute to PCC Archive.