UNS Registry smart contracts.
Author: Unstoppable Domains, Inc., 2021. All rights reserved.
-
Implements ERC721
ERC-721 Non-Fungible Token Standard
-
Implements ERC165
ERC-165 Standard Interface Detection
-
Implements IERC721Metadata
IERC721Metadata is an extension of ERC-721. IERC721Metadata allows smart contract to be interrogated for its name and for details about the assets which your NFTs represent.
-
Implements IUNSRegistry
-
Record Storage (aka Resolver)
Record Storage implements IRecordStorage
-
Support meta-transactions
EIP-2771: Secure Protocol for Native Meta Transactions
In order to support
EIP-2771recepient should implementContext.interface Context { function _msgSender() internal view returns (address); function _msgData() internal view returns (bytes calldata); }
The implementation should allow replacement of
_msgSenderand_msgDatain case of forwarding.Implementation ERC2771RegistryContext.sol
struct ForwardRequest { address from; uint256 gas; uint256 tokenId; uint256 nonce; bytes data; } interface Forwarder { /** * @dev Return current token nonce */ function nonceOf(uint256 tokenId) public view returns (uint256); /** * @dev Verify signature against provided request */ function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool); /** * @dev Execute bytecode if signature is correct */ function execute(ForwardRequest calldata req, bytes calldata signature) public returns (bool, bytes memory); }
Implementation UNSRegistryForwarder.sol
-
Upgradability
By design, smart contracts are immutable. On the other hand, software quality heavily depends on the ability to upgrade and patch source code in order to produce iterative releases. Even though blockchain based software profits significantly from the technology’s immutability, still a certain degree of mutability is needed for bug fixing and potential product improvements.
Upgradability comes from two patterns:
- Initializable
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. - Context
Provides information about the current execution context, including the sender of the transaction and its data.
UNS uses Transparent Proxy for upgradability.
Refs:
- Initializable
-
TLD management
UNS TLD management is delegated to MintingManager contract.
contract IMintingManager { /** * @dev Mapping TLD `hashname` to TLD label * * `hashname` = uint256(keccak256(abi.encodePacked(uint256(0x0), keccak256(abi.encodePacked(label))))) */ mapping(uint256 => string) internal _tlds; }
-
Domin minting
Unstoppable Domains, Inc. reserves all rights of domains minting and defines rules of domain minting through MintingManager contract.
-
Roles model
TBD
- Solidity
^0.8.0 - OpenZeppelin contracts-upgradeable
^4.1.0 - Hardhat
^2.4.1 - ethers.js
^5.1.4
RINKEBY_INFURA_KEY- Infura key for connecting to Ethereum NodeRINKEBY_UNS_PRIVATE_KEY- Private key of account for contracts deploymentETHERSCAN_API_KEY- Etherscan API Key for smart contracts verification
NOTE: All private keys should be in HEX format with 0x prefix
Variables [RINKEBY_INFURA_KEY, RINKEBY_UNS_PRIVATE_KEY] are required for operating with Rinkeby network, including deployment and making smart contract calls.
Note: List of changes which makes UNS and CNS backward incompatibile
event Approved ApprovedForAll Transfer NewURI- Unchanged
event Resolve(uint256 indexed tokenId, address indexed to)- Removed
- UNS has a single resolver which is Registry, so one can assume that resolver is always set to registry address
event Sync(address indexed resolver, uint256 indexed updateId, uint256 indexed tokenId)- Removed
- There is no need for this event because there is only one resolver and changes can be tracked by
Setevent instead
event Set(uint256 indexed tokenId, string indexed keyIndex, string indexed valueIndex, string key, string value)- Moved from Resolver to Registry
event NewKey(uint256 indexed tokenId, string indexed keyIndex, string key)- Moved from Resolver to Regisry
event ResetRecords(uint256 indexed tokenId)- Moved from Resolver to Registry
- Registry now fires this event when records are reset on transfer.
- Website Backend
- UNS Mirror
- Resolution Service
- UNS Mirror
- Resolution Libs
- allRecords
Registry.isController- Removed
None
function resolveTo(address to, uint256 tokenId) external {}- Removed - UNS uses a single Resolver which is Registry itself.
function resolverOf(uint256 tokenId)- Now always returns Regsitry address itself
- Website Backend
- Records Management
- Website Frontend
- Records Management
- Mobile App
- Records Management
Changes:
MintingController- Removed - minting permissions are now controlled by single upgradable
MintingManagercontract
- Removed - minting permissions are now controlled by single upgradable
WhitelistedMinter- Removed -
MintingManagernow has methods to do that.
- Removed -
MintingManager- Added - it's minting methods are similar to old
WhitelistedMinterbut they all have additional parameter -tld
- Added - it's minting methods are similar to old
- Website Backend
- Claim a domain
- Including Delegate Claiming fee
- Claim a domain
function controlledResolveTo(address to, uint256 tokenId) external {}- Removed
function sync(uint256 tokenId, uint256 updateId) external {}- Removed
function preconfigure(string[] memory keys, string[] memory values, uint256 tokenId) external {}- Removed due to removing controllers
Scripts for deploying contracts are located inside ./scripts directory. In order to run them, you can use
the following command:
yarn hardhat run --network <network> scripts/filename.js
As scripts make JSON-RPC calls to Infura and sign transactions, you'll need to specify 2 environment variables. Those variables' names depend on the network you want deploy the contracts to. Assuming you want to deploy new contracts to Rinkeby, you'll need the following variables:
export RINKEBY_UNS_PRIVATE_KEY=<HEX_PRIVATE_KEY>
export RINKEBY_INFURA_KEY=<INFURA_PROJECT_ID>
The scripts located in ./scripts directory are wrappers around Deployer tasks. You can see their definitions inside
src/tasks.js.
yarn hardhat run --network <network> scripts/deploy_CNS.js
UNS depends on CNS, as CNS registry address is used as a MintingManager's initializer argument. Before deploying UNS,
make sure that CNS is deployed and actual CNS contract addresses are specified in the uns-config.json file.
yarn hardhat run --network <network> scripts/deploy.js
Warning: In case of contracts' redeployment, make sure there is no deployment output file .deployer/{chain_id}.json
env ETHERSCAN_API_KEY=$POLYGONSCAN_API_KEY yarn hardhat run --network mumbai scripts/deploy_UNS_only.js
yarn hardhat run --network rinkeby scripts/deploy_CNS_Forwarders.jsyarn hardhat run --network rinkeby scripts/upgrade_MintingManager.jsyarn hardhat run --network rinkeby scripts/deploy_MintingManagerForwarder.jsyarn hardhat run --network rinkeby scripts/upgrade_UNSRegistry.js- Undate config
- Upgrade MintingManager
- explicitly bisable blocklist (Rinkeby, Mainnet)
- Deploy ProxyReader
- Update config
- Add minting support .crypto in UNS
mintingManager.addTld('crypto')(if needed)