├── .gitignore ├── .npmignore ├── .nvmrc ├── .solhint.json ├── .travis.yml ├── LICENSE.txt ├── README.md ├── contracts ├── dnsregistrar │ ├── DNSClaimChecker.sol │ ├── DNSRegistrar.sol │ ├── PublicSuffixList.sol │ ├── SimplePublicSuffixList.sol │ ├── TLDPublicSuffixList.sol │ └── mocks │ │ └── DummyDnsRegistrarDNSSEC.sol ├── dnssec-oracle │ ├── BytesUtils.sol │ ├── DNSSEC.sol │ ├── DNSSECImpl.sol │ ├── Migrations.sol │ ├── Owned.sol │ ├── RRUtils.sol │ ├── SHA1.sol │ ├── algorithms │ │ ├── Algorithm.sol │ │ ├── DummyAlgorithm.sol │ │ ├── EllipticCurve.sol │ │ ├── ModexpPrecompile.sol │ │ ├── P256SHA256Algorithm.sol │ │ ├── RSASHA1Algorithm.sol │ │ ├── RSASHA256Algorithm.sol │ │ └── RSAVerify.sol │ ├── digests │ │ ├── Digest.sol │ │ ├── DummyDigest.sol │ │ ├── SHA1Digest.sol │ │ └── SHA256Digest.sol │ └── nsec3digests │ │ ├── NSEC3Digest.sol │ │ └── SHA1NSEC3Digest.sol ├── ethregistrar │ ├── BaseRegistrar.sol │ ├── BaseRegistrarImplementation.sol │ ├── DummyOracle.sol │ ├── ETHRegistrarController.sol │ ├── ForeverRegistrarController.sol │ ├── PriceOracle.sol │ ├── SafeMath.sol │ ├── StablePriceOracle.sol │ ├── StringUtils.sol │ ├── TestResolver.sol │ └── mocks │ │ ├── DummyDNSSEC.sol │ │ └── DummyProxyRegistry.sol ├── registry │ ├── ENS.sol │ ├── ENSRegistry.sol │ ├── ENSRegistryWithFallback.sol │ ├── FIFSRegistrar.sol │ ├── ReverseRegistrar.sol │ └── TestRegistrar.sol ├── resolvers │ ├── DefaultReverseResolver.sol │ ├── OwnedResolver.sol │ ├── PublicResolver.sol │ ├── Resolver.sol │ ├── ResolverBase.sol │ └── profiles │ │ ├── ABIResolver.sol │ │ ├── AddrResolver.sol │ │ ├── ContentHashResolver.sol │ │ ├── DNSResolver.sol │ │ ├── InterfaceResolver.sol │ │ ├── NameResolver.sol │ │ ├── PubkeyResolver.sol │ │ └── TextResolver.sol └── root │ ├── Controllable.sol │ ├── Ownable.sol │ └── Root.sol ├── deploy ├── 00_deploy_registry.js ├── 01_deploy_public_resolver.js ├── 02_deploy_reverse_registrar.js ├── 03_deploy_base_registrar.js ├── 04_deploy_price_oracle.js ├── 05_deploy_controller.js ├── 06_deploy_forever_controller.js ├── finalize_forever_controller.js └── finalize_initial.js ├── deployments ├── goerli │ ├── .chainId │ ├── BaseRegistrarImplementation.json │ ├── ENSRegistry.json │ ├── ETHRegistrarController.json │ ├── ForeverRegistrarController.json │ ├── PublicResolver.json │ ├── ReverseRegistrar.json │ ├── StablePriceOracle.json │ └── solcInputs │ │ └── 4f213114e8b4d533011999912bd2fcbf.json └── mainnet │ ├── .chainId │ ├── BaseRegistrarImplementation.json │ ├── ENSRegistry.json │ ├── ETHRegistrarController.json │ ├── ForeverRegistrarController.json │ ├── PublicResolver.json │ ├── ReverseRegistrar.json │ ├── StablePriceOracle.json │ └── solcInputs │ ├── af6dadacf06c5b8f555c7ff398a0eb62.json │ └── cd8af9d3240bfdcb5f41fedbfd7c477d.json ├── hardhat.config.js ├── index.js ├── package.json ├── scripts ├── deploy_local.js ├── finalize.js └── try_local.js ├── test ├── dnsregistrar │ ├── Helpers │ │ └── Utils.js │ ├── TestDNSRegistrar.js │ └── TestTLDPublicSuffixList.js ├── dnssec-oracle │ ├── TestAlgorithms.js │ ├── TestBytesUtils.sol │ ├── TestDNSSEC.js │ ├── TestDigests.js │ ├── TestRRUtils.sol │ ├── TestSHA1NSEC3Digest.js │ ├── TestSolidityTests.js │ └── data │ │ ├── algorithms.js │ │ └── digests.js ├── ethregistrar │ ├── TestBaseRegistrar.js │ ├── TestEthRegistrarController.js │ ├── TestForeverRegistrarController.js │ └── TestStablePriceOracle.js ├── registry │ ├── TestENS.js │ ├── TestENSRegistryWithFallback.js │ ├── TestFIFSRegistrar.js │ ├── TestReverseRegistrar.js │ ├── TestTestRegistrar.js │ └── mocks │ │ └── DummyResolver.sol ├── resolvers │ └── TestPublicResolver.js ├── root │ └── TestRoot.js ├── test-utils │ ├── anchors.js │ ├── dns.js │ ├── evm.js │ ├── exceptions.js │ └── index.js └── truffle-fixture.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | node_modules 3 | build 4 | .idea 5 | 6 | #Hardhat files 7 | cache 8 | artifacts 9 | deployments/localhost 10 | addresses.json 11 | .env 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | node_modules/ 3 | 4 | #Hardhat files 5 | cache/ 6 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.15.0 2 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:default", 3 | "rules": { 4 | "indent": 2, 5 | "max-line-length": false, 6 | "quotes": "double" 7 | } 8 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | dist: trusty 4 | 5 | language: node_js 6 | 7 | node_js: 8 | - "14" 9 | env: 10 | - TASK=test 11 | - TASK=lint 12 | matrix: 13 | fast_finish: true 14 | allow_failures: 15 | - env: TASK=lint 16 | script: 17 | - npm run $TASK 18 | 19 | notifications: 20 | email: false 21 | 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, True Names Limited 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .forever contracts 2 | 3 | This is a fork of [ens contracts](https://github.com/ensdomains/ens-contracts) that allows you to register your name once and own it forever without the need for renewals. 4 | 5 | For documentation of the ENS system, see [docs.ens.domains](https://docs.ens.domains/). 6 | 7 | ## npm package 8 | 9 | This repo doubles as an npm package with the compiled JSON contracts 10 | 11 | ```js 12 | import { 13 | BaseRegistrar, 14 | BaseRegistrarImplementation, 15 | ENS, 16 | ENSRegistry, 17 | ENSRegistryWithFallback, 18 | ETHRegistrarController, 19 | FIFSRegistrar, 20 | PriceOracle, 21 | PublicResolver, 22 | Resolver, 23 | ReverseRegistrar, 24 | StablePriceOracle, 25 | TestRegistrar 26 | } from '@imperviousinc/forever-contracts' 27 | ``` 28 | 29 | ## Importing from solidity 30 | 31 | ``` 32 | // Registry 33 | import 'imperviousinc-forever-contracts/contracts/registry/ENS.sol'; 34 | import 'imperviousinc-forever-contracts/contracts/registry/ENSRegistry.sol'; 35 | import 'imperviousinc-forever-contracts/contracts/registry/ENSRegistryWithFallback.sol'; 36 | import 'imperviousinc-forever-contracts/contracts/registry/ReverseRegistrar.sol'; 37 | import 'imperviousinc-forever-contracts/contracts/registry/TestRegistrar.sol'; 38 | // EthRegistrar 39 | import 'imperviousinc-forever-contracts/contracts/ethregistrar/BaseRegistrar.sol'; 40 | import 'imperviousinc-forever-contracts/contracts/ethregistrar/BaseRegistrarImplementation.sol'; 41 | import 'imperviousinc-forever-contracts/contracts/ethregistrar/BaseRegistrar.sol'; 42 | import 'imperviousinc-forever-contracts/contracts/ethregistrar/ETHRegistrarController.sol'; 43 | import 'imperviousinc-forever-contracts/contracts/ethregistrar/PriceOracle.sol'; 44 | import 'imperviousinc-forever-contracts/contracts/ethregistrar/StablePriceOracle.sol'; 45 | // Resolvers 46 | import 'imperviousinc-forever-contracts/contracts/resolvers/PublicResolver.sol'; 47 | import 'imperviousinc-forever-contracts/contracts/resolvers/Resolver.sol'; 48 | ``` 49 | 50 | ## Accessing to binary file. 51 | 52 | If your environment does not have compiler, you can access to the raw hardhat artifacts files at `node_modules/@imperviousinc/forever-contracts/artifacts/contracts/${modName}/${contractName}.sol/${contractName}.json` 53 | 54 | 55 | ## Contracts 56 | 57 | ## Registry 58 | 59 | The ENS registry is the core contract that lies at the heart of ENS resolution. All ENS lookups start by querying the registry. The registry maintains a list of domains, recording the owner, resolver, and TTL for each, and allows the owner of a domain to make changes to that data. It also includes some generic registrars. 60 | 61 | ### ENS.sol 62 | 63 | Interface of the ENS Registry. 64 | 65 | ### ENSRegistry 66 | 67 | Implementation of the ENS Registry, the central contract used to look up resolvers and owners for domains. 68 | 69 | ### ENSRegistryWithFallback 70 | 71 | The new impelmentation of the ENS Registry after [the 2020 ENS Registry Migration](https://docs.ens.domains/ens-migration-february-2020/technical-description#new-ens-deployment). 72 | 73 | ### FIFSRegistrar 74 | 75 | Implementation of a simple first-in-first-served registrar, which issues (sub-)domains to the first account to request them. 76 | 77 | ### ReverseRegistrar 78 | 79 | Implementation of the reverse registrar responsible for managing reverse resolution via the .addr.reverse special-purpose TLD. 80 | 81 | 82 | ### TestRegistrar 83 | 84 | Implementation of the `.test` registrar facilitates easy testing of ENS on the Ethereum test networks. Currently deployed on Ropsten network, it provides functionality to instantly claim a domain for test purposes, which expires 28 days after it was claimed. 85 | 86 | 87 | ## EthRegistrar 88 | 89 | Implements an [ENS](https://ens.domains/) registrar intended for the .eth TLD. 90 | 91 | These contracts were audited by ConsenSys dilligence; the audit report is available [here](https://github.com/ConsenSys/ens-audit-report-2019-02). 92 | 93 | ### BaseRegistrar 94 | 95 | BaseRegistrar is the contract that owns the TLD in the ENS registry. This contract implements a minimal set of functionality: 96 | 97 | - The owner of the registrar may add and remove controllers. 98 | - Controllers may register new domains. They can not change the ownership of existing domains. 99 | - Name owners may transfer ownership to another address. 100 | - Name owners may reclaim ownership in the ENS registry if they have lost it. 101 | - Owners of names in the interim registrar may transfer them to the new registrar, during the 1 year transition period. When they do so, their deposit is returned to them in its entirety. 102 | 103 | This separation of concerns provides name owners strong guarantees over continued ownership of their existing names, while still permitting innovation and change in the way names are registered via the controller mechanism. 104 | 105 | ### EthRegistrarController 106 | 107 | EthRegistrarController is the first implementation of a registration controller for the new registrar. This contract implements the following functionality: 108 | 109 | - The owner of the registrar may set a price oracle contract, which determines the cost of registrations based on the name. 110 | - The owner of the registrar may withdraw any collected funds to their account. 111 | - Users can register new names using a commit/reveal process and by paying the appropriate registration fee. 112 | 113 | The commit/reveal process is used to avoid frontrunning, and operates as follows: 114 | 115 | 1. A user commits to a hash, the preimage of which contains the name to be registered and a secret value. 116 | 2. After a minimum delay period and before the commitment expires, the user calls the register function with the name to register and the secret value from the commitment. If a valid commitment is found and the other preconditions are met, the name is registered. 117 | 118 | The minimum delay and expiry for commitments exist to prevent miners or other users from effectively frontrunnig registrations. 119 | 120 | ### SimplePriceOracle 121 | 122 | SimplePriceOracle is a trivial implementation of the pricing oracle for the EthRegistrarController that always returns a fixed price per domain per year, determined by the contract owner. 123 | 124 | ### StablePriceOracle 125 | 126 | StablePriceOracle is a price oracle implementation that allows the contract owner to specify pricing based on the length of a name, and uses a fiat currency oracle to set a fixed price in fiat per name. 127 | 128 | ## Resolvers 129 | 130 | Resolver implements a general-purpose ENS resolver that is suitable for most standard ENS use-cases. The public resolver permits updates to ENS records by the owner of the corresponding name. 131 | 132 | PublicResolver includes the following profiles that implements different EIPs. 133 | 134 | ─ ABIResolver = EIP 205 - ABI support (`ABI()`). 135 | ─ AddrResolver = EIP 137 - Contract address interface. EIP 2304 - Multicoin support (`addr()`). 136 | ─ ContentHashResolver = EIP 1577 - Content hash support (`contenthash()`). 137 | ─ InterfaceResolver = EIP 165 - Interface Detection (`supportsInterface()`). 138 | ─ NameResolver = EIP 181 - Reverse resolution (`name()`). 139 | ─ PubkeyResolver = EIP 619 - SECP256k1 public keys (`pubkey()`). 140 | ─ TextResolver = EIP 634 - Text records (`text()`). 141 | ─ DNSResolver = Experimental support is available for hosting DNS domains on the Ethereum blockchain via ENS. [The more detail](https://veox-ens.readthedocs.io/en/latest/dns.html) is on the old ENS doc. 142 | 143 | ## Developer guide 144 | 145 | ### Setup 146 | 147 | ``` 148 | git clone https://github.com/imperviousinc/forever-contracts.git 149 | cd forever-contracts 150 | yarn 151 | ``` 152 | ### Deployment 153 | 154 | Create a .env file and add the following variables: 155 | 156 | ``` 157 | DEPLOYER_KEY= 158 | INFURA_ID= 159 | ETHERSCAN_API_KEY= 160 | ``` 161 | 162 | Deploy the contracts: 163 | 164 | ``` 165 | npx hardhat --network goerli deploy 166 | ``` 167 | 168 | Finalize deployment: updates ens registry and connects the controller and public resolver to the registrar. 169 | 170 | ``` 171 | npx hardhat --network goerli deploy --tags FinalizeInitial 172 | ``` 173 | 174 | add the new controller 175 | 176 | ``` 177 | npx hardhat --network goerli deploy --tags FinalizeForeverController 178 | ``` 179 | 180 | Submit contract code to verify with etherscan (optional) 181 | 182 | ``` 183 | npx hardhat --network goerli etherscan-verify --license MIT --sleep 184 | ``` 185 | 186 | ### How to run tests 187 | 188 | ``` 189 | yarn test 190 | ``` 191 | 192 | 193 | 194 | ### How to publish 195 | 196 | ``` 197 | yarn pub 198 | ``` 199 | -------------------------------------------------------------------------------- /contracts/dnsregistrar/DNSClaimChecker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "../dnssec-oracle/DNSSEC.sol"; 4 | import "../dnssec-oracle/BytesUtils.sol"; 5 | import "../dnssec-oracle/RRUtils.sol"; 6 | import "@ensdomains/buffer/contracts/Buffer.sol"; 7 | 8 | library DNSClaimChecker { 9 | 10 | using BytesUtils for bytes; 11 | using RRUtils for *; 12 | using Buffer for Buffer.buffer; 13 | 14 | uint16 constant CLASS_INET = 1; 15 | uint16 constant TYPE_TXT = 16; 16 | 17 | function getOwnerAddress(DNSSEC oracle, bytes memory name, bytes memory proof) 18 | internal 19 | view 20 | returns (address, bool) 21 | { 22 | // Add "_ens." to the front of the name. 23 | Buffer.buffer memory buf; 24 | buf.init(name.length + 5); 25 | buf.append("\x04_ens"); 26 | buf.append(name); 27 | bytes20 hash; 28 | uint64 inserted; 29 | // Check the provided TXT record has been validated by the oracle 30 | (, inserted, hash) = oracle.rrdata(TYPE_TXT, buf.buf); 31 | if (hash == bytes20(0) && proof.length == 0) return (address(0x0), false); 32 | 33 | require(hash == bytes20(keccak256(proof))); 34 | 35 | for (RRUtils.RRIterator memory iter = proof.iterateRRs(0); !iter.done(); iter.next()) { 36 | require(inserted + iter.ttl >= block.timestamp, "DNS record is stale; refresh or delete it before proceeding."); 37 | 38 | bool found; 39 | address addr; 40 | (addr, found) = parseRR(proof, iter.rdataOffset); 41 | if (found) { 42 | return (addr, true); 43 | } 44 | } 45 | 46 | return (address(0x0), false); 47 | } 48 | 49 | function parseRR(bytes memory rdata, uint idx) internal pure returns (address, bool) { 50 | while (idx < rdata.length) { 51 | uint len = rdata.readUint8(idx); idx += 1; 52 | 53 | bool found; 54 | address addr; 55 | (addr, found) = parseString(rdata, idx, len); 56 | 57 | if (found) return (addr, true); 58 | idx += len; 59 | } 60 | 61 | return (address(0x0), false); 62 | } 63 | 64 | function parseString(bytes memory str, uint idx, uint len) internal pure returns (address, bool) { 65 | // TODO: More robust parsing that handles whitespace and multiple key/value pairs 66 | if (str.readUint32(idx) != 0x613d3078) return (address(0x0), false); // 0x613d3078 == 'a=0x' 67 | if (len < 44) return (address(0x0), false); 68 | return hexToAddress(str, idx + 4); 69 | } 70 | 71 | function hexToAddress(bytes memory str, uint idx) internal pure returns (address, bool) { 72 | if (str.length - idx < 40) return (address(0x0), false); 73 | uint ret = 0; 74 | for (uint i = idx; i < idx + 40; i++) { 75 | ret <<= 4; 76 | uint x = str.readUint8(i); 77 | if (x >= 48 && x < 58) { 78 | ret |= x - 48; 79 | } else if (x >= 65 && x < 71) { 80 | ret |= x - 55; 81 | } else if (x >= 97 && x < 103) { 82 | ret |= x - 87; 83 | } else { 84 | return (address(0x0), false); 85 | } 86 | } 87 | return (address(uint160(ret)), true); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /contracts/dnsregistrar/DNSRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "../dnssec-oracle/BytesUtils.sol"; 5 | import "../dnssec-oracle/DNSSEC.sol"; 6 | import "../registry/ENSRegistry.sol"; 7 | import "../root/Root.sol"; 8 | import "./DNSClaimChecker.sol"; 9 | import "./PublicSuffixList.sol"; 10 | 11 | /** 12 | * @dev An ENS registrar that allows the owner of a DNS name to claim the 13 | * corresponding name in ENS. 14 | */ 15 | contract DNSRegistrar { 16 | using BytesUtils for bytes; 17 | 18 | DNSSEC public oracle; 19 | ENS public ens; 20 | PublicSuffixList public suffixes; 21 | 22 | bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)")); 23 | bytes4 constant private DNSSEC_CLAIM_ID = bytes4( 24 | keccak256("claim(bytes,bytes)") ^ 25 | keccak256("proveAndClaim(bytes,bytes,bytes)") ^ 26 | keccak256("oracle()") 27 | ); 28 | 29 | event Claim(bytes32 indexed node, address indexed owner, bytes dnsname); 30 | event NewOracle(address oracle); 31 | event NewPublicSuffixList(address suffixes); 32 | 33 | constructor(DNSSEC _dnssec, PublicSuffixList _suffixes, ENS _ens) public { 34 | oracle = _dnssec; 35 | emit NewOracle(address(oracle)); 36 | suffixes = _suffixes; 37 | emit NewPublicSuffixList(address(suffixes)); 38 | ens = _ens; 39 | } 40 | 41 | /** 42 | * @dev This contract's owner-only functions can be invoked by the owner of the ENS root. 43 | */ 44 | modifier onlyOwner { 45 | Root root = Root(ens.owner(bytes32(0))); 46 | address owner = root.owner(); 47 | require(msg.sender == owner); 48 | _; 49 | } 50 | 51 | function setOracle(DNSSEC _dnssec) public onlyOwner { 52 | oracle = _dnssec; 53 | emit NewOracle(address(oracle)); 54 | } 55 | 56 | function setPublicSuffixList(PublicSuffixList _suffixes) public onlyOwner { 57 | suffixes = _suffixes; 58 | emit NewPublicSuffixList(address(suffixes)); 59 | } 60 | 61 | /** 62 | * @dev Claims a name by proving ownership of its DNS equivalent. 63 | * @param name The name to claim, in DNS wire format. 64 | * @param proof A DNS RRSet proving ownership of the name. Must be verified 65 | * in the DNSSEC oracle before calling. This RRSET must contain a TXT 66 | * record for '_ens.' + name, with the value 'a=0x...'. Ownership of 67 | * the name will be transferred to the address specified in the TXT 68 | * record. 69 | */ 70 | function claim(bytes memory name, bytes memory proof) public { 71 | // Get the first label 72 | uint labelLen = name.readUint8(0); 73 | bytes32 labelHash = name.keccak(1, labelLen); 74 | 75 | // Parent name must be in the public suffix list. 76 | bytes memory parentName = name.substring(labelLen + 1, name.length - labelLen - 1); 77 | require(suffixes.isPublicSuffix(parentName), "Parent name must be a public suffix"); 78 | 79 | // Make sure the parent name is enabled 80 | bytes32 rootNode = enableNode(parentName, 0); 81 | 82 | address addr; 83 | (addr,) = DNSClaimChecker.getOwnerAddress(oracle, name, proof); 84 | 85 | ens.setSubnodeOwner(rootNode, labelHash, addr); 86 | emit Claim(keccak256(abi.encodePacked(rootNode, labelHash)), addr, name); 87 | } 88 | 89 | /** 90 | * @dev Submits proofs to the DNSSEC oracle, then claims a name using those proofs. 91 | * @param name The name to claim, in DNS wire format. 92 | * @param input The data to be passed to the Oracle's `submitProofs` function. The last 93 | * proof must be the TXT record required by the registrar. 94 | * @param proof The proof record for the first element in input. 95 | */ 96 | function proveAndClaim(bytes memory name, DNSSEC.RRSetWithSignature[] memory input, bytes memory proof) public { 97 | proof = oracle.submitRRSets(input, proof); 98 | claim(name, proof); 99 | } 100 | 101 | function supportsInterface(bytes4 interfaceID) external pure returns (bool) { 102 | return interfaceID == INTERFACE_META_ID || 103 | interfaceID == DNSSEC_CLAIM_ID; 104 | } 105 | 106 | function enableNode(bytes memory domain, uint offset) internal returns(bytes32 node) { 107 | uint len = domain.readUint8(offset); 108 | if(len == 0) { 109 | return bytes32(0); 110 | } 111 | 112 | bytes32 parentNode = enableNode(domain, offset + len + 1); 113 | bytes32 label = domain.keccak(offset + 1, len); 114 | node = keccak256(abi.encodePacked(parentNode, label)); 115 | address owner = ens.owner(node); 116 | require(owner == address(0) || owner == address(this), "Cannot enable a name owned by someone else"); 117 | if(owner != address(this)) { 118 | if(parentNode == bytes32(0)) { 119 | Root root = Root(ens.owner(bytes32(0))); 120 | root.setSubnodeOwner(label, address(this)); 121 | } else { 122 | ens.setSubnodeOwner(parentNode, label, address(this)); 123 | } 124 | } 125 | return node; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /contracts/dnsregistrar/PublicSuffixList.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | interface PublicSuffixList { 4 | function isPublicSuffix(bytes calldata name) external view returns(bool); 5 | } 6 | -------------------------------------------------------------------------------- /contracts/dnsregistrar/SimplePublicSuffixList.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "../root/Ownable.sol"; 5 | import "./PublicSuffixList.sol"; 6 | 7 | contract SimplePublicSuffixList is PublicSuffixList, Ownable { 8 | mapping(bytes=>bool) suffixes; 9 | 10 | function addPublicSuffixes(bytes[] memory names) public onlyOwner { 11 | for(uint i = 0; i < names.length; i++) { 12 | suffixes[names[i]] = true; 13 | } 14 | } 15 | 16 | function isPublicSuffix(bytes calldata name) external override view returns(bool) { 17 | return suffixes[name]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/dnsregistrar/TLDPublicSuffixList.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "../dnssec-oracle/BytesUtils.sol"; 4 | import "./PublicSuffixList.sol"; 5 | 6 | /** 7 | * @dev A public suffix list that treats all TLDs as public suffixes. 8 | */ 9 | contract TLDPublicSuffixList is PublicSuffixList { 10 | using BytesUtils for bytes; 11 | 12 | function isPublicSuffix(bytes calldata name) external override view returns(bool) { 13 | uint labellen = name.readUint8(0); 14 | return labellen > 0 && name.readUint8(labellen + 1) == 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/dnsregistrar/mocks/DummyDnsRegistrarDNSSEC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | contract DummyDNSSEC { 4 | uint16 expectedType; 5 | bytes expectedName; 6 | uint32 inception; 7 | uint64 inserted; 8 | bytes20 hash; 9 | 10 | function setData(uint16 _expectedType, bytes memory _expectedName, uint32 _inception, uint64 _inserted, bytes memory _proof) public { 11 | expectedType = _expectedType; 12 | expectedName = _expectedName; 13 | inception = _inception; 14 | inserted = _inserted; 15 | if(_proof.length != 0) { 16 | hash = bytes20(keccak256(_proof)); 17 | } 18 | } 19 | 20 | function rrdata(uint16 dnstype, bytes memory name) public view returns (uint32, uint64, bytes20) { 21 | require(dnstype == expectedType); 22 | require(keccak256(name) == keccak256(expectedName)); 23 | return (inception, inserted, hash); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/DNSSEC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | pragma experimental ABIEncoderV2; 3 | 4 | abstract contract DNSSEC { 5 | 6 | bytes public anchors; 7 | 8 | struct RRSetWithSignature { 9 | bytes rrset; 10 | bytes sig; 11 | } 12 | 13 | event AlgorithmUpdated(uint8 id, address addr); 14 | event DigestUpdated(uint8 id, address addr); 15 | event NSEC3DigestUpdated(uint8 id, address addr); 16 | event RRSetUpdated(bytes name, bytes rrset); 17 | 18 | function submitRRSets(RRSetWithSignature[] memory input, bytes calldata proof) public virtual returns (bytes memory); 19 | function submitRRSet(RRSetWithSignature calldata input, bytes calldata proof) public virtual returns (bytes memory); 20 | function deleteRRSet(uint16 deleteType, bytes calldata deleteName, RRSetWithSignature calldata nsec, bytes calldata proof) public virtual; 21 | function deleteRRSetNSEC3(uint16 deleteType, bytes memory deleteName, RRSetWithSignature memory closestEncloser, RRSetWithSignature memory nextClosest, bytes memory dnskey) public virtual; 22 | function rrdata(uint16 dnstype, bytes calldata name) external virtual view returns (uint32, uint32, bytes20); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) 9 | _; 10 | } 11 | 12 | constructor() public { 13 | owner = msg.sender; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | 20 | function upgrade(address newAddress) public restricted { 21 | Migrations upgraded = Migrations(newAddress); 22 | upgraded.setCompleted(last_completed_migration); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/Owned.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | /** 4 | * @dev Contract mixin for 'owned' contracts. 5 | */ 6 | contract Owned { 7 | address public owner; 8 | 9 | modifier owner_only() { 10 | require(msg.sender == owner); 11 | _; 12 | } 13 | 14 | constructor() public { 15 | owner = msg.sender; 16 | } 17 | 18 | function setOwner(address newOwner) public owner_only { 19 | owner = newOwner; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/SHA1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | library SHA1 { 4 | event Debug(bytes32 x); 5 | 6 | function sha1(bytes memory data) internal pure returns(bytes20 ret) { 7 | assembly { 8 | // Get a safe scratch location 9 | let scratch := mload(0x40) 10 | 11 | // Get the data length, and point data at the first byte 12 | let len := mload(data) 13 | data := add(data, 32) 14 | 15 | // Find the length after padding 16 | let totallen := add(and(add(len, 1), 0xFFFFFFFFFFFFFFC0), 64) 17 | switch lt(sub(totallen, len), 9) 18 | case 1 { totallen := add(totallen, 64) } 19 | 20 | let h := 0x6745230100EFCDAB890098BADCFE001032547600C3D2E1F0 21 | 22 | function readword(ptr, off, count) -> result { 23 | result := 0 24 | if lt(off, count) { 25 | result := mload(add(ptr, off)) 26 | count := sub(count, off) 27 | if lt(count, 32) { 28 | let mask := not(sub(exp(256, sub(32, count)), 1)) 29 | result := and(result, mask) 30 | } 31 | } 32 | } 33 | 34 | for { let i := 0 } lt(i, totallen) { i := add(i, 64) } { 35 | mstore(scratch, readword(data, i, len)) 36 | mstore(add(scratch, 32), readword(data, add(i, 32), len)) 37 | 38 | // If we loaded the last byte, store the terminator byte 39 | switch lt(sub(len, i), 64) 40 | case 1 { mstore8(add(scratch, sub(len, i)), 0x80) } 41 | 42 | // If this is the last block, store the length 43 | switch eq(i, sub(totallen, 64)) 44 | case 1 { mstore(add(scratch, 32), or(mload(add(scratch, 32)), mul(len, 8))) } 45 | 46 | // Expand the 16 32-bit words into 80 47 | for { let j := 64 } lt(j, 128) { j := add(j, 12) } { 48 | let temp := xor(xor(mload(add(scratch, sub(j, 12))), mload(add(scratch, sub(j, 32)))), xor(mload(add(scratch, sub(j, 56))), mload(add(scratch, sub(j, 64))))) 49 | temp := or(and(mul(temp, 2), 0xFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE), and(div(temp, 0x80000000), 0x0000000100000001000000010000000100000001000000010000000100000001)) 50 | mstore(add(scratch, j), temp) 51 | } 52 | for { let j := 128 } lt(j, 320) { j := add(j, 24) } { 53 | let temp := xor(xor(mload(add(scratch, sub(j, 24))), mload(add(scratch, sub(j, 64)))), xor(mload(add(scratch, sub(j, 112))), mload(add(scratch, sub(j, 128))))) 54 | temp := or(and(mul(temp, 4), 0xFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFCFFFFFFFC), and(div(temp, 0x40000000), 0x0000000300000003000000030000000300000003000000030000000300000003)) 55 | mstore(add(scratch, j), temp) 56 | } 57 | 58 | let x := h 59 | let f := 0 60 | let k := 0 61 | for { let j := 0 } lt(j, 80) { j := add(j, 1) } { 62 | switch div(j, 20) 63 | case 0 { 64 | // f = d xor (b and (c xor d)) 65 | f := xor(div(x, 0x100000000000000000000), div(x, 0x10000000000)) 66 | f := and(div(x, 0x1000000000000000000000000000000), f) 67 | f := xor(div(x, 0x10000000000), f) 68 | k := 0x5A827999 69 | } 70 | case 1{ 71 | // f = b xor c xor d 72 | f := xor(div(x, 0x1000000000000000000000000000000), div(x, 0x100000000000000000000)) 73 | f := xor(div(x, 0x10000000000), f) 74 | k := 0x6ED9EBA1 75 | } 76 | case 2 { 77 | // f = (b and c) or (d and (b or c)) 78 | f := or(div(x, 0x1000000000000000000000000000000), div(x, 0x100000000000000000000)) 79 | f := and(div(x, 0x10000000000), f) 80 | f := or(and(div(x, 0x1000000000000000000000000000000), div(x, 0x100000000000000000000)), f) 81 | k := 0x8F1BBCDC 82 | } 83 | case 3 { 84 | // f = b xor c xor d 85 | f := xor(div(x, 0x1000000000000000000000000000000), div(x, 0x100000000000000000000)) 86 | f := xor(div(x, 0x10000000000), f) 87 | k := 0xCA62C1D6 88 | } 89 | // temp = (a leftrotate 5) + f + e + k + w[i] 90 | let temp := and(div(x, 0x80000000000000000000000000000000000000000000000), 0x1F) 91 | temp := or(and(div(x, 0x800000000000000000000000000000000000000), 0xFFFFFFE0), temp) 92 | temp := add(f, temp) 93 | temp := add(and(x, 0xFFFFFFFF), temp) 94 | temp := add(k, temp) 95 | temp := add(div(mload(add(scratch, mul(j, 4))), 0x100000000000000000000000000000000000000000000000000000000), temp) 96 | x := or(div(x, 0x10000000000), mul(temp, 0x10000000000000000000000000000000000000000)) 97 | x := or(and(x, 0xFFFFFFFF00FFFFFFFF000000000000FFFFFFFF00FFFFFFFF), mul(or(and(div(x, 0x4000000000000), 0xC0000000), and(div(x, 0x400000000000000000000), 0x3FFFFFFF)), 0x100000000000000000000)) 98 | } 99 | 100 | h := and(add(h, x), 0xFFFFFFFF00FFFFFFFF00FFFFFFFF00FFFFFFFF00FFFFFFFF) 101 | } 102 | ret := mul(or(or(or(or(and(div(h, 0x100000000), 0xFFFFFFFF00000000000000000000000000000000), and(div(h, 0x1000000), 0xFFFFFFFF000000000000000000000000)), and(div(h, 0x10000), 0xFFFFFFFF0000000000000000)), and(div(h, 0x100), 0xFFFFFFFF00000000)), and(h, 0xFFFFFFFF)), 0x1000000000000000000000000) 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/Algorithm.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | /** 4 | * @dev An interface for contracts implementing a DNSSEC (signing) algorithm. 5 | */ 6 | interface Algorithm { 7 | /** 8 | * @dev Verifies a signature. 9 | * @param key The public key to verify with. 10 | * @param data The signed data to verify. 11 | * @param signature The signature to verify. 12 | * @return True iff the signature is valid. 13 | */ 14 | function verify(bytes calldata key, bytes calldata data, bytes calldata signature) external virtual view returns (bool); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/DummyAlgorithm.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Algorithm.sol"; 4 | 5 | /** 6 | * @dev Implements a dummy DNSSEC (signing) algorithm that approves all 7 | * signatures, for testing. 8 | */ 9 | contract DummyAlgorithm is Algorithm { 10 | function verify(bytes calldata, bytes calldata, bytes calldata) external override view returns (bool) { return true; } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/ModexpPrecompile.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "@ensdomains/buffer/contracts/Buffer.sol"; 4 | 5 | 6 | library ModexpPrecompile { 7 | using Buffer for *; 8 | 9 | /** 10 | * @dev Computes (base ^ exponent) % modulus over big numbers. 11 | */ 12 | function modexp(bytes memory base, bytes memory exponent, bytes memory modulus) internal view returns (bool success, bytes memory output) { 13 | uint size = (32 * 3) + base.length + exponent.length + modulus.length; 14 | 15 | Buffer.buffer memory input; 16 | input.init(size); 17 | 18 | input.appendBytes32(bytes32(base.length)); 19 | input.appendBytes32(bytes32(exponent.length)); 20 | input.appendBytes32(bytes32(modulus.length)); 21 | input.append(base); 22 | input.append(exponent); 23 | input.append(modulus); 24 | 25 | output = new bytes(modulus.length); 26 | 27 | assembly { 28 | success := staticcall(gas(), 5, add(mload(input), 32), size, add(output, 32), mload(modulus)) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/P256SHA256Algorithm.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Algorithm.sol"; 4 | import "./EllipticCurve.sol"; 5 | import "../BytesUtils.sol"; 6 | 7 | contract P256SHA256Algorithm is Algorithm, EllipticCurve { 8 | 9 | using BytesUtils for *; 10 | 11 | /** 12 | * @dev Verifies a signature. 13 | * @param key The public key to verify with. 14 | * @param data The signed data to verify. 15 | * @param signature The signature to verify. 16 | * @return True iff the signature is valid. 17 | */ 18 | function verify(bytes calldata key, bytes calldata data, bytes calldata signature) external override view returns (bool) { 19 | return validateSignature(sha256(data), parseSignature(signature), parseKey(key)); 20 | } 21 | 22 | function parseSignature(bytes memory data) internal pure returns (uint256[2] memory) { 23 | return [uint256(data.readBytes32(0)), uint256(data.readBytes32(32))]; 24 | } 25 | 26 | function parseKey(bytes memory data) internal pure returns (uint256[2] memory) { 27 | return [uint256(data.readBytes32(4)), uint256(data.readBytes32(36))]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/RSASHA1Algorithm.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Algorithm.sol"; 4 | import "../BytesUtils.sol"; 5 | import "./RSAVerify.sol"; 6 | import "@ensdomains/solsha1/contracts/SHA1.sol"; 7 | 8 | /** 9 | * @dev Implements the DNSSEC RSASHA1 algorithm. 10 | */ 11 | contract RSASHA1Algorithm is Algorithm { 12 | using BytesUtils for *; 13 | 14 | function verify(bytes calldata key, bytes calldata data, bytes calldata sig) external override view returns (bool) { 15 | bytes memory exponent; 16 | bytes memory modulus; 17 | 18 | uint16 exponentLen = uint16(key.readUint8(4)); 19 | if (exponentLen != 0) { 20 | exponent = key.substring(5, exponentLen); 21 | modulus = key.substring(exponentLen + 5, key.length - exponentLen - 5); 22 | } else { 23 | exponentLen = key.readUint16(5); 24 | exponent = key.substring(7, exponentLen); 25 | modulus = key.substring(exponentLen + 7, key.length - exponentLen - 7); 26 | } 27 | 28 | // Recover the message from the signature 29 | bool ok; 30 | bytes memory result; 31 | (ok, result) = RSAVerify.rsarecover(modulus, exponent, sig); 32 | 33 | // Verify it ends with the hash of our data 34 | return ok && SHA1.sha1(data) == result.readBytes20(result.length - 20); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/RSASHA256Algorithm.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Algorithm.sol"; 4 | import "../BytesUtils.sol"; 5 | import "./RSAVerify.sol"; 6 | 7 | /** 8 | * @dev Implements the DNSSEC RSASHA256 algorithm. 9 | */ 10 | contract RSASHA256Algorithm is Algorithm { 11 | using BytesUtils for *; 12 | 13 | function verify(bytes calldata key, bytes calldata data, bytes calldata sig) external override view returns (bool) { 14 | bytes memory exponent; 15 | bytes memory modulus; 16 | 17 | uint16 exponentLen = uint16(key.readUint8(4)); 18 | if (exponentLen != 0) { 19 | exponent = key.substring(5, exponentLen); 20 | modulus = key.substring(exponentLen + 5, key.length - exponentLen - 5); 21 | } else { 22 | exponentLen = key.readUint16(5); 23 | exponent = key.substring(7, exponentLen); 24 | modulus = key.substring(exponentLen + 7, key.length - exponentLen - 7); 25 | } 26 | 27 | // Recover the message from the signature 28 | bool ok; 29 | bytes memory result; 30 | (ok, result) = RSAVerify.rsarecover(modulus, exponent, sig); 31 | 32 | // Verify it ends with the hash of our data 33 | return ok && sha256(data) == result.readBytes32(result.length - 32); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/algorithms/RSAVerify.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "../BytesUtils.sol"; 4 | import "./ModexpPrecompile.sol"; 5 | 6 | library RSAVerify { 7 | /** 8 | * @dev Recovers the input data from an RSA signature, returning the result in S. 9 | * @param N The RSA public modulus. 10 | * @param E The RSA public exponent. 11 | * @param S The signature to recover. 12 | * @return True if the recovery succeeded. 13 | */ 14 | function rsarecover(bytes memory N, bytes memory E, bytes memory S) internal view returns (bool, bytes memory) { 15 | return ModexpPrecompile.modexp(S, E, N); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/digests/Digest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | /** 4 | * @dev An interface for contracts implementing a DNSSEC digest. 5 | */ 6 | interface Digest { 7 | /** 8 | * @dev Verifies a cryptographic hash. 9 | * @param data The data to hash. 10 | * @param hash The hash to compare to. 11 | * @return True iff the hashed data matches the provided hash value. 12 | */ 13 | function verify(bytes calldata data, bytes calldata hash) external virtual pure returns (bool); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/digests/DummyDigest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Digest.sol"; 4 | 5 | /** 6 | * @dev Implements a dummy DNSSEC digest that approves all hashes, for testing. 7 | */ 8 | contract DummyDigest is Digest { 9 | function verify(bytes calldata, bytes calldata) external override pure returns (bool) { return true; } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/digests/SHA1Digest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Digest.sol"; 4 | import "../BytesUtils.sol"; 5 | import "@ensdomains/solsha1/contracts/SHA1.sol"; 6 | 7 | /** 8 | * @dev Implements the DNSSEC SHA1 digest. 9 | */ 10 | contract SHA1Digest is Digest { 11 | using BytesUtils for *; 12 | 13 | function verify(bytes calldata data, bytes calldata hash) external override pure returns (bool) { 14 | bytes32 expected = hash.readBytes20(0); 15 | bytes20 computed = SHA1.sha1(data); 16 | return expected == computed; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/digests/SHA256Digest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Digest.sol"; 4 | import "../BytesUtils.sol"; 5 | 6 | /** 7 | * @dev Implements the DNSSEC SHA256 digest. 8 | */ 9 | contract SHA256Digest is Digest { 10 | using BytesUtils for *; 11 | 12 | function verify(bytes calldata data, bytes calldata hash) external override pure returns (bool) { 13 | return sha256(data) == hash.readBytes32(0); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/nsec3digests/NSEC3Digest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | /** 4 | * @dev Interface for contracts that implement NSEC3 digest algorithms. 5 | */ 6 | interface NSEC3Digest { 7 | /** 8 | * @dev Performs an NSEC3 iterated hash. 9 | * @param salt The salt value to use on each iteration. 10 | * @param data The data to hash. 11 | * @param iterations The number of iterations to perform. 12 | * @return The result of the iterated hash operation. 13 | */ 14 | function hash(bytes calldata salt, bytes calldata data, uint iterations) external virtual pure returns (bytes32); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/dnssec-oracle/nsec3digests/SHA1NSEC3Digest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./NSEC3Digest.sol"; 4 | import "../SHA1.sol"; 5 | import "@ensdomains/buffer/contracts/Buffer.sol"; 6 | /** 7 | * @dev Implements the DNSSEC iterated SHA1 digest used for NSEC3 records. 8 | */ 9 | contract SHA1NSEC3Digest is NSEC3Digest { 10 | using Buffer for Buffer.buffer; 11 | 12 | function hash(bytes calldata salt, bytes calldata data, uint iterations) external override pure returns (bytes32) { 13 | Buffer.buffer memory buf; 14 | buf.init(salt.length + data.length + 16); 15 | 16 | buf.append(data); 17 | buf.append(salt); 18 | bytes20 h = SHA1.sha1(buf.buf); 19 | if (iterations > 0) { 20 | buf.truncate(); 21 | buf.appendBytes20(bytes20(0)); 22 | buf.append(salt); 23 | 24 | for (uint i = 0; i < iterations; i++) { 25 | buf.writeBytes20(0, h); 26 | h = SHA1.sha1(buf.buf); 27 | } 28 | } 29 | 30 | return bytes32(h); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/ethregistrar/BaseRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "../registry/ENS.sol"; 4 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | abstract contract BaseRegistrar is Ownable, IERC721 { 8 | event ControllerAdded(address indexed controller); 9 | event ControllerRemoved(address indexed controller); 10 | event NameRegistered(uint256 indexed id, address indexed owner); 11 | 12 | // The ENS registry 13 | ENS public ens; 14 | 15 | // The namehash of the TLD this registrar owns (eg, .eth) 16 | bytes32 public baseNode; 17 | 18 | // A map of addresses that are authorised to register names. 19 | mapping(address=>bool) public controllers; 20 | 21 | // Authorises a controller, who can register domains. 22 | function addController(address controller) virtual external; 23 | 24 | // Revoke controller permission for an address. 25 | function removeController(address controller) virtual external; 26 | 27 | // Set the resolver for the TLD this registrar manages. 28 | function setResolver(address resolver) virtual external; 29 | 30 | // Returns true iff the specified name is available for registration. 31 | function available(uint256 id) virtual public view returns(bool); 32 | 33 | /** 34 | * @dev Register a name. 35 | */ 36 | function register(uint256 id, address owner) virtual external; 37 | 38 | /** 39 | * @dev Reclaim ownership of a name in ENS, if you own it in the registrar. 40 | */ 41 | function reclaim(uint256 id, address owner) virtual external; 42 | } 43 | -------------------------------------------------------------------------------- /contracts/ethregistrar/BaseRegistrarImplementation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "../registry/ENS.sol"; 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | 6 | import "./BaseRegistrar.sol"; 7 | contract BaseRegistrarImplementation is ERC721, BaseRegistrar { 8 | bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)")); 9 | bytes4 constant private ERC721_ID = bytes4( 10 | keccak256("balanceOf(address)") ^ 11 | keccak256("ownerOf(uint256)") ^ 12 | keccak256("approve(address,uint256)") ^ 13 | keccak256("getApproved(uint256)") ^ 14 | keccak256("setApprovalForAll(address,bool)") ^ 15 | keccak256("isApprovedForAll(address,address)") ^ 16 | keccak256("transferFrom(address,address,uint256)") ^ 17 | keccak256("safeTransferFrom(address,address,uint256)") ^ 18 | keccak256("safeTransferFrom(address,address,uint256,bytes)") 19 | ); 20 | bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)")); 21 | 22 | constructor(ENS _ens, bytes32 _baseNode) ERC721("","") { 23 | ens = _ens; 24 | baseNode = _baseNode; 25 | } 26 | 27 | modifier live { 28 | require(ens.owner(baseNode) == address(this)); 29 | _; 30 | } 31 | 32 | modifier onlyController { 33 | require(controllers[msg.sender]); 34 | _; 35 | } 36 | 37 | // Authorises a controller, who can register domains. 38 | function addController(address controller) external override onlyOwner { 39 | controllers[controller] = true; 40 | emit ControllerAdded(controller); 41 | } 42 | 43 | // Revoke controller permission for an address. 44 | function removeController(address controller) external override onlyOwner { 45 | controllers[controller] = false; 46 | emit ControllerRemoved(controller); 47 | } 48 | 49 | // Set the resolver for the TLD this registrar manages. 50 | function setResolver(address resolver) external override onlyOwner { 51 | ens.setResolver(baseNode, resolver); 52 | } 53 | 54 | // Returns true iff the specified name is available for registration. 55 | function available(uint256 id) public view override returns(bool) { 56 | // Not available if it's registered here 57 | return !_exists(id); 58 | } 59 | 60 | /** 61 | * @dev Register a name. 62 | * @param id The token ID (keccak256 of the label). 63 | * @param owner The address that should own the registration. 64 | */ 65 | function register(uint256 id, address owner) external override { 66 | _register(id, owner, true); 67 | } 68 | 69 | /** 70 | * @dev Register a name, without modifying the registry. 71 | * @param id The token ID (keccak256 of the label). 72 | * @param owner The address that should own the registration. 73 | */ 74 | function registerOnly(uint256 id, address owner) external { 75 | _register(id, owner, false); 76 | } 77 | 78 | function _register(uint256 id, address owner, bool updateRegistry) internal live onlyController { 79 | require(available(id)); 80 | 81 | _mint(owner, id); 82 | if(updateRegistry) { 83 | ens.setSubnodeOwner(baseNode, bytes32(id), owner); 84 | } 85 | 86 | emit NameRegistered(id, owner); 87 | } 88 | 89 | /** 90 | * @dev Reclaim ownership of a name in ENS, if you own it in the registrar. 91 | */ 92 | function reclaim(uint256 id, address owner) external override live { 93 | require(_isApprovedOrOwner(msg.sender, id)); 94 | ens.setSubnodeOwner(baseNode, bytes32(id), owner); 95 | } 96 | 97 | function supportsInterface(bytes4 interfaceID) public override(ERC721, IERC165) view returns (bool) { 98 | return interfaceID == INTERFACE_META_ID || 99 | interfaceID == ERC721_ID || 100 | interfaceID == RECLAIM_ID; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /contracts/ethregistrar/DummyOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | contract DummyOracle { 4 | int value; 5 | 6 | constructor(int _value) public { 7 | set(_value); 8 | } 9 | 10 | function set(int _value) public { 11 | value = _value; 12 | } 13 | 14 | function latestAnswer() public view returns(int256) { 15 | return value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/ethregistrar/ETHRegistrarController.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./PriceOracle.sol"; 4 | import "./BaseRegistrarImplementation.sol"; 5 | import "./StringUtils.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "../resolvers/Resolver.sol"; 8 | 9 | /** 10 | * @dev A registrar controller for registering names at fixed cost. 11 | */ 12 | contract ETHRegistrarController is Ownable { 13 | using StringUtils for *; 14 | 15 | bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)")); 16 | bytes4 constant private COMMITMENT_CONTROLLER_ID = bytes4( 17 | keccak256("price(string)") ^ 18 | keccak256("available(string)") ^ 19 | keccak256("makeCommitment(string,address,bytes32)") ^ 20 | keccak256("commit(bytes32)") ^ 21 | keccak256("register(string,address,bytes32)") 22 | ); 23 | 24 | bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4( 25 | keccak256("registerWithConfig(string,address,bytes32,address,address)") ^ 26 | keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)") 27 | ); 28 | 29 | BaseRegistrarImplementation base; 30 | PriceOracle prices; 31 | uint public minCommitmentAge; 32 | uint public maxCommitmentAge; 33 | 34 | mapping(bytes32=>uint) public commitments; 35 | 36 | event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost); 37 | event NewPriceOracle(address indexed oracle); 38 | 39 | constructor(BaseRegistrarImplementation _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public { 40 | require(_maxCommitmentAge > _minCommitmentAge); 41 | 42 | base = _base; 43 | prices = _prices; 44 | minCommitmentAge = _minCommitmentAge; 45 | maxCommitmentAge = _maxCommitmentAge; 46 | } 47 | 48 | function price(string memory name) view public returns(uint) { 49 | return prices.price(name); 50 | } 51 | 52 | function valid(string memory name) public pure returns(bool) { 53 | return name.strlen() >= 1; 54 | } 55 | 56 | function available(string memory name) public view returns(bool) { 57 | bytes32 label = keccak256(bytes(name)); 58 | return valid(name) && base.available(uint256(label)); 59 | } 60 | 61 | function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) { 62 | return makeCommitmentWithConfig(name, owner, secret, address(0), address(0)); 63 | } 64 | 65 | function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) { 66 | bytes32 label = keccak256(bytes(name)); 67 | if (resolver == address(0) && addr == address(0)) { 68 | return keccak256(abi.encodePacked(label, owner, secret)); 69 | } 70 | require(resolver != address(0)); 71 | return keccak256(abi.encodePacked(label, owner, resolver, addr, secret)); 72 | } 73 | 74 | function commit(bytes32 commitment) public { 75 | require(commitments[commitment] + maxCommitmentAge < block.timestamp); 76 | commitments[commitment] = block.timestamp; 77 | } 78 | 79 | function register(string calldata name, address owner, bytes32 secret) external payable { 80 | registerWithConfig(name, owner, secret, address(0), address(0)); 81 | } 82 | 83 | function registerWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) public payable { 84 | bytes32 commitment = makeCommitmentWithConfig(name, owner, secret, resolver, addr); 85 | uint cost = _consumeCommitment(name, commitment); 86 | 87 | bytes32 label = keccak256(bytes(name)); 88 | uint256 tokenId = uint256(label); 89 | 90 | if(resolver != address(0)) { 91 | // Set this contract as the (temporary) owner, giving it 92 | // permission to set up the resolver. 93 | base.register(tokenId, address(this)); 94 | 95 | // The nodehash of this label 96 | bytes32 nodehash = keccak256(abi.encodePacked(base.baseNode(), label)); 97 | 98 | // Set the resolver 99 | base.ens().setResolver(nodehash, resolver); 100 | 101 | // Configure the resolver 102 | if (addr != address(0)) { 103 | Resolver(resolver).setAddr(nodehash, addr); 104 | } 105 | 106 | // Now transfer full ownership to the expected owner 107 | base.reclaim(tokenId, owner); 108 | base.transferFrom(address(this), owner, tokenId); 109 | } else { 110 | require(addr == address(0)); 111 | base.register(tokenId, owner); 112 | } 113 | 114 | emit NameRegistered(name, label, owner, cost); 115 | 116 | // Refund any extra payment 117 | if(msg.value > cost) { 118 | payable(msg.sender).transfer(msg.value - cost); 119 | } 120 | } 121 | 122 | function setPriceOracle(PriceOracle _prices) public onlyOwner { 123 | prices = _prices; 124 | emit NewPriceOracle(address(prices)); 125 | } 126 | 127 | function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner { 128 | minCommitmentAge = _minCommitmentAge; 129 | maxCommitmentAge = _maxCommitmentAge; 130 | } 131 | 132 | function withdraw() public onlyOwner { 133 | payable(msg.sender).transfer(address(this).balance); 134 | } 135 | 136 | function supportsInterface(bytes4 interfaceID) external pure returns (bool) { 137 | return interfaceID == INTERFACE_META_ID || 138 | interfaceID == COMMITMENT_CONTROLLER_ID || 139 | interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID; 140 | } 141 | 142 | function _consumeCommitment(string memory name, bytes32 commitment) internal returns (uint256) { 143 | // Require a valid commitment 144 | require(commitments[commitment] + minCommitmentAge <= block.timestamp); 145 | 146 | // If the commitment is too old, or the name is registered, stop 147 | require(commitments[commitment] + maxCommitmentAge > block.timestamp); 148 | require(available(name)); 149 | 150 | delete(commitments[commitment]); 151 | 152 | uint cost = price(name); 153 | require(msg.value >= cost); 154 | 155 | return cost; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /contracts/ethregistrar/ForeverRegistrarController.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./PriceOracle.sol"; 4 | import "./BaseRegistrarImplementation.sol"; 5 | import "./StringUtils.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "../resolvers/Resolver.sol"; 8 | 9 | /** 10 | * @dev A registrar controller for registering names at fixed cost. 11 | * An updated version of the previously deployed ETHRegistrarController. 12 | */ 13 | contract ForeverRegistrarController is Ownable { 14 | using StringUtils for *; 15 | 16 | bool public requireCommitReveal = false; 17 | 18 | bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)")); 19 | bytes4 constant private COMMITMENT_CONTROLLER_ID = bytes4( 20 | keccak256("price(string)") ^ 21 | keccak256("available(string)") ^ 22 | keccak256("makeCommitment(string,address,bytes32)") ^ 23 | keccak256("commit(bytes32)") ^ 24 | keccak256("register(string,address,bytes32)") 25 | ); 26 | 27 | bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4( 28 | keccak256("registerWithConfig(string,address,bytes32,address,address)") ^ 29 | keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)") 30 | ); 31 | 32 | BaseRegistrarImplementation base; 33 | PriceOracle prices; 34 | uint public minCommitmentAge; 35 | uint public maxCommitmentAge; 36 | 37 | mapping(bytes32=>uint) public commitments; 38 | 39 | event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost); 40 | event NewPriceOracle(address indexed oracle); 41 | 42 | constructor(BaseRegistrarImplementation _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public { 43 | require(_maxCommitmentAge > _minCommitmentAge); 44 | 45 | base = _base; 46 | prices = _prices; 47 | minCommitmentAge = _minCommitmentAge; 48 | maxCommitmentAge = _maxCommitmentAge; 49 | } 50 | 51 | function setRequireCommitReveal(bool _requireCommitReveal) external onlyOwner { 52 | requireCommitReveal = _requireCommitReveal; 53 | } 54 | 55 | function price(string memory name) view public returns(uint) { 56 | return prices.price(name); 57 | } 58 | 59 | function valid(string memory name) public pure returns(bool) { 60 | return name.strlen() >= 1; 61 | } 62 | 63 | function available(string memory name) public view returns(bool) { 64 | bytes32 label = keccak256(bytes(name)); 65 | return valid(name) && base.available(uint256(label)); 66 | } 67 | 68 | function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) { 69 | return makeCommitmentWithConfig(name, owner, secret, address(0), address(0)); 70 | } 71 | 72 | function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) { 73 | bytes32 label = keccak256(bytes(name)); 74 | if (resolver == address(0) && addr == address(0)) { 75 | return keccak256(abi.encodePacked(label, owner, secret)); 76 | } 77 | require(resolver != address(0)); 78 | return keccak256(abi.encodePacked(label, owner, resolver, addr, secret)); 79 | } 80 | 81 | function commit(bytes32 commitment) public { 82 | require(commitments[commitment] + maxCommitmentAge < block.timestamp); 83 | commitments[commitment] = block.timestamp; 84 | } 85 | 86 | function register(string calldata name, address owner, bytes32 secret) external payable { 87 | registerWithConfig(name, owner, secret, address(0), address(0)); 88 | } 89 | 90 | function registerWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) public payable { 91 | bytes32 commitment = requireCommitReveal ? makeCommitmentWithConfig(name, owner, secret, resolver, addr) : bytes32(0); 92 | uint cost = _consumeCommitment(name, commitment); 93 | 94 | bytes32 label = keccak256(bytes(name)); 95 | uint256 tokenId = uint256(label); 96 | 97 | if(resolver != address(0)) { 98 | // Set this contract as the (temporary) owner, giving it 99 | // permission to set up the resolver. 100 | base.register(tokenId, address(this)); 101 | 102 | // The nodehash of this label 103 | bytes32 nodehash = keccak256(abi.encodePacked(base.baseNode(), label)); 104 | 105 | // Set the resolver 106 | base.ens().setResolver(nodehash, resolver); 107 | 108 | // Configure the resolver 109 | if (addr != address(0)) { 110 | Resolver(resolver).setAddr(nodehash, addr); 111 | } 112 | 113 | // Now transfer full ownership to the expected owner 114 | base.reclaim(tokenId, owner); 115 | base.transferFrom(address(this), owner, tokenId); 116 | } else { 117 | require(addr == address(0)); 118 | base.register(tokenId, owner); 119 | } 120 | 121 | emit NameRegistered(name, label, owner, cost); 122 | 123 | // Refund any extra payment 124 | if(msg.value > cost) { 125 | payable(msg.sender).transfer(msg.value - cost); 126 | } 127 | } 128 | 129 | function setPriceOracle(PriceOracle _prices) public onlyOwner { 130 | prices = _prices; 131 | emit NewPriceOracle(address(prices)); 132 | } 133 | 134 | function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner { 135 | minCommitmentAge = _minCommitmentAge; 136 | maxCommitmentAge = _maxCommitmentAge; 137 | } 138 | 139 | function withdraw() public onlyOwner { 140 | payable(msg.sender).transfer(address(this).balance); 141 | } 142 | 143 | function supportsInterface(bytes4 interfaceID) external pure returns (bool) { 144 | return interfaceID == INTERFACE_META_ID || 145 | interfaceID == COMMITMENT_CONTROLLER_ID || 146 | interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID; 147 | } 148 | 149 | function _consumeCommitment(string memory name, bytes32 commitment) internal returns (uint256) { 150 | // If the name is registered, stop 151 | require(available(name)); 152 | 153 | if (requireCommitReveal) { 154 | // Require a valid commitment 155 | require(commitments[commitment] + minCommitmentAge <= block.timestamp); 156 | 157 | // If the commitment is too old, stop 158 | require(commitments[commitment] + maxCommitmentAge > block.timestamp); 159 | 160 | delete(commitments[commitment]); 161 | } 162 | 163 | uint cost = price(name); 164 | require(msg.value >= cost); 165 | 166 | return cost; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /contracts/ethregistrar/PriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | interface PriceOracle { 4 | /** 5 | * @dev Returns the price to register a name. 6 | * @param name The name being registered. 7 | * @return The price of this registration, in wei. 8 | */ 9 | function price(string calldata name) external view returns(uint); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/ethregistrar/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | /** 4 | * @title SafeMath 5 | * @dev Unsigned math operations with safety checks that revert on error 6 | */ 7 | library SafeMath { 8 | /** 9 | * @dev Multiplies two unsigned integers, reverts on overflow. 10 | */ 11 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 12 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 13 | // benefit is lost if 'b' is also tested. 14 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 15 | if (a == 0) { 16 | return 0; 17 | } 18 | 19 | uint256 c = a * b; 20 | require(c / a == b); 21 | 22 | return c; 23 | } 24 | 25 | /** 26 | * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. 27 | */ 28 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 29 | // Solidity only automatically asserts when dividing by 0 30 | require(b > 0); 31 | uint256 c = a / b; 32 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 33 | 34 | return c; 35 | } 36 | 37 | /** 38 | * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). 39 | */ 40 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 41 | require(b <= a); 42 | uint256 c = a - b; 43 | 44 | return c; 45 | } 46 | 47 | /** 48 | * @dev Adds two unsigned integers, reverts on overflow. 49 | */ 50 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 51 | uint256 c = a + b; 52 | require(c >= a); 53 | 54 | return c; 55 | } 56 | 57 | /** 58 | * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), 59 | * reverts when dividing by zero. 60 | */ 61 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 62 | require(b != 0); 63 | return a % b; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/ethregistrar/StablePriceOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./PriceOracle.sol"; 4 | import "./SafeMath.sol"; 5 | import "./StringUtils.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | interface AggregatorInterface { 9 | function latestAnswer() external view returns (int256); 10 | } 11 | 12 | 13 | // StablePriceOracle sets a price in USD, based on an oracle. 14 | contract StablePriceOracle is Ownable, PriceOracle { 15 | using SafeMath for *; 16 | using StringUtils for *; 17 | 18 | // Price by length. Element 0 is for 1-length names, and so on. 19 | uint[] public prices; 20 | 21 | // Oracle address 22 | AggregatorInterface public usdOracle; 23 | 24 | event OracleChanged(address oracle); 25 | event PriceChanged(uint[] prices); 26 | 27 | bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)")); 28 | bytes4 constant private ORACLE_ID = bytes4(keccak256("price(string)") ^ keccak256("premium(string)")); 29 | 30 | constructor(AggregatorInterface _usdOracle, uint[] memory _prices) public { 31 | usdOracle = _usdOracle; 32 | setPrices(_prices); 33 | } 34 | 35 | function price(string calldata name) external view override returns(uint) { 36 | uint len = name.strlen(); 37 | if(len > prices.length) { 38 | len = prices.length; 39 | } 40 | require(len > 0); 41 | 42 | uint basePrice = prices[len - 1]; 43 | basePrice = basePrice.add(_premium(name)); 44 | 45 | return attoUSDToWei(basePrice); 46 | } 47 | 48 | /** 49 | * @dev Sets prices. 50 | * @param _prices The price array. Each element corresponds to a specific 51 | * name length; names longer than the length of the array 52 | * default to the price of the last element. Values are 53 | * in base price units, equal to one attodollar (1e-18 54 | * dollar) each. 55 | */ 56 | function setPrices(uint[] memory _prices) public onlyOwner { 57 | prices = _prices; 58 | emit PriceChanged(_prices); 59 | } 60 | 61 | /** 62 | * @dev Sets the price oracle address 63 | * @param _usdOracle The address of the price oracle to use. 64 | */ 65 | function setOracle(AggregatorInterface _usdOracle) public onlyOwner { 66 | usdOracle = _usdOracle; 67 | emit OracleChanged(address(_usdOracle)); 68 | } 69 | 70 | /** 71 | * @dev Returns the pricing premium in wei. 72 | */ 73 | function premium(string calldata name) external view returns(uint) { 74 | return attoUSDToWei(_premium(name)); 75 | } 76 | 77 | /** 78 | * @dev Returns the pricing premium in internal base units. 79 | */ 80 | function _premium(string memory name) virtual internal view returns(uint) { 81 | return 0; 82 | } 83 | 84 | function attoUSDToWei(uint amount) internal view returns(uint) { 85 | uint ethPrice = uint(usdOracle.latestAnswer()); 86 | return amount.mul(1e8).div(ethPrice); 87 | } 88 | 89 | function weiToAttoUSD(uint amount) internal view returns(uint) { 90 | uint ethPrice = uint(usdOracle.latestAnswer()); 91 | return amount.mul(ethPrice).div(1e8); 92 | } 93 | 94 | function supportsInterface(bytes4 interfaceID) public view virtual returns (bool) { 95 | return interfaceID == INTERFACE_META_ID || interfaceID == ORACLE_ID; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /contracts/ethregistrar/StringUtils.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | library StringUtils { 4 | /** 5 | * @dev Returns the length of a given string 6 | * 7 | * @param s The string to measure the length of 8 | * @return The length of the input string 9 | */ 10 | function strlen(string memory s) internal pure returns (uint) { 11 | uint len; 12 | uint i = 0; 13 | uint bytelength = bytes(s).length; 14 | for(len = 0; i < bytelength; len++) { 15 | bytes1 b = bytes(s)[i]; 16 | if(b < 0x80) { 17 | i += 1; 18 | } else if (b < 0xE0) { 19 | i += 2; 20 | } else if (b < 0xF0) { 21 | i += 3; 22 | } else if (b < 0xF8) { 23 | i += 4; 24 | } else if (b < 0xFC) { 25 | i += 5; 26 | } else { 27 | i += 6; 28 | } 29 | } 30 | return len; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/ethregistrar/TestResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | /** 4 | * @dev A test resolver implementation 5 | */ 6 | contract TestResolver { 7 | mapping (bytes32 => address) addresses; 8 | 9 | constructor() public { 10 | } 11 | 12 | function supportsInterface(bytes4 interfaceID) public pure returns (bool) { 13 | return interfaceID == 0x01ffc9a7 || interfaceID == 0x3b3b57de; 14 | } 15 | 16 | function addr(bytes32 node) public view returns (address) { 17 | return addresses[node]; 18 | } 19 | 20 | function setAddr(bytes32 node, address addr) public { 21 | addresses[node] = addr; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/ethregistrar/mocks/DummyDNSSEC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "../../registry/ENSRegistry.sol"; 4 | 5 | contract DummyDnsRegistrarDNSSEC { 6 | 7 | struct Data { 8 | uint32 inception; 9 | uint64 inserted; 10 | bytes20 hash; 11 | } 12 | 13 | mapping (bytes32 => Data) private datas; 14 | 15 | function setData(uint16 _expectedType, bytes memory _expectedName, uint32 _inception, uint64 _inserted, bytes memory _proof) public { 16 | Data storage rr = datas[keccak256(abi.encodePacked(_expectedType, _expectedName))]; 17 | rr.inception = _inception; 18 | rr.inserted = _inserted; 19 | 20 | if (_proof.length != 0) { 21 | rr.hash = bytes20(keccak256(_proof)); 22 | } else { 23 | rr.hash = bytes20(0); 24 | } 25 | } 26 | 27 | function rrdata(uint16 dnstype, bytes memory name) public view returns (uint32, uint64, bytes20) { 28 | Data storage rr = datas[keccak256(abi.encodePacked(dnstype, name))]; 29 | return (rr.inception, rr.inserted, rr.hash); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/ethregistrar/mocks/DummyProxyRegistry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | contract DummyProxyRegistry { 4 | address target; 5 | 6 | constructor(address _target) public { 7 | target = _target; 8 | } 9 | 10 | function proxies(address a) external view returns(address) { 11 | return target; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/registry/ENS.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | interface ENS { 4 | 5 | // Logged when the owner of a node assigns a new owner to a subnode. 6 | event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); 7 | 8 | // Logged when the owner of a node transfers ownership to a new account. 9 | event Transfer(bytes32 indexed node, address owner); 10 | 11 | // Logged when the resolver for a node changes. 12 | event NewResolver(bytes32 indexed node, address resolver); 13 | 14 | // Logged when the TTL of a node changes 15 | event NewTTL(bytes32 indexed node, uint64 ttl); 16 | 17 | // Logged when an operator is added or removed. 18 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 19 | 20 | function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual; 21 | function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual; 22 | function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32); 23 | function setResolver(bytes32 node, address resolver) external virtual; 24 | function setOwner(bytes32 node, address owner) external virtual; 25 | function setTTL(bytes32 node, uint64 ttl) external virtual; 26 | function setApprovalForAll(address operator, bool approved) external virtual; 27 | function owner(bytes32 node) external virtual view returns (address); 28 | function resolver(bytes32 node) external virtual view returns (address); 29 | function ttl(bytes32 node) external virtual view returns (uint64); 30 | function recordExists(bytes32 node) external virtual view returns (bool); 31 | function isApprovedForAll(address owner, address operator) external virtual view returns (bool); 32 | } 33 | -------------------------------------------------------------------------------- /contracts/registry/ENSRegistry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./ENS.sol"; 4 | 5 | /** 6 | * The ENS registry contract. 7 | */ 8 | contract ENSRegistry is ENS { 9 | 10 | struct Record { 11 | address owner; 12 | address resolver; 13 | uint64 ttl; 14 | } 15 | 16 | mapping (bytes32 => Record) records; 17 | mapping (address => mapping(address => bool)) operators; 18 | 19 | // Permits modifications only by the owner of the specified node. 20 | modifier authorised(bytes32 node) { 21 | address owner = records[node].owner; 22 | require(owner == msg.sender || operators[owner][msg.sender]); 23 | _; 24 | } 25 | 26 | /** 27 | * @dev Constructs a new ENS registrar. 28 | */ 29 | constructor() public { 30 | records[0x0].owner = msg.sender; 31 | } 32 | 33 | /** 34 | * @dev Sets the record for a node. 35 | * @param node The node to update. 36 | * @param owner The address of the new owner. 37 | * @param resolver The address of the resolver. 38 | * @param ttl The TTL in seconds. 39 | */ 40 | function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual override { 41 | setOwner(node, owner); 42 | _setResolverAndTTL(node, resolver, ttl); 43 | } 44 | 45 | /** 46 | * @dev Sets the record for a subnode. 47 | * @param node The parent node. 48 | * @param label The hash of the label specifying the subnode. 49 | * @param owner The address of the new owner. 50 | * @param resolver The address of the resolver. 51 | * @param ttl The TTL in seconds. 52 | */ 53 | function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual override { 54 | bytes32 subnode = setSubnodeOwner(node, label, owner); 55 | _setResolverAndTTL(subnode, resolver, ttl); 56 | } 57 | 58 | /** 59 | * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. 60 | * @param node The node to transfer ownership of. 61 | * @param owner The address of the new owner. 62 | */ 63 | function setOwner(bytes32 node, address owner) public virtual override authorised(node) { 64 | _setOwner(node, owner); 65 | emit Transfer(node, owner); 66 | } 67 | 68 | /** 69 | * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. 70 | * @param node The parent node. 71 | * @param label The hash of the label specifying the subnode. 72 | * @param owner The address of the new owner. 73 | */ 74 | function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) { 75 | bytes32 subnode = keccak256(abi.encodePacked(node, label)); 76 | _setOwner(subnode, owner); 77 | emit NewOwner(node, label, owner); 78 | return subnode; 79 | } 80 | 81 | /** 82 | * @dev Sets the resolver address for the specified node. 83 | * @param node The node to update. 84 | * @param resolver The address of the resolver. 85 | */ 86 | function setResolver(bytes32 node, address resolver) public virtual override authorised(node) { 87 | emit NewResolver(node, resolver); 88 | records[node].resolver = resolver; 89 | } 90 | 91 | /** 92 | * @dev Sets the TTL for the specified node. 93 | * @param node The node to update. 94 | * @param ttl The TTL in seconds. 95 | */ 96 | function setTTL(bytes32 node, uint64 ttl) public virtual override authorised(node) { 97 | emit NewTTL(node, ttl); 98 | records[node].ttl = ttl; 99 | } 100 | 101 | /** 102 | * @dev Enable or disable approval for a third party ("operator") to manage 103 | * all of `msg.sender`'s ENS records. Emits the ApprovalForAll event. 104 | * @param operator Address to add to the set of authorized operators. 105 | * @param approved True if the operator is approved, false to revoke approval. 106 | */ 107 | function setApprovalForAll(address operator, bool approved) external virtual override { 108 | operators[msg.sender][operator] = approved; 109 | emit ApprovalForAll(msg.sender, operator, approved); 110 | } 111 | 112 | /** 113 | * @dev Returns the address that owns the specified node. 114 | * @param node The specified node. 115 | * @return address of the owner. 116 | */ 117 | function owner(bytes32 node) public virtual override view returns (address) { 118 | address addr = records[node].owner; 119 | if (addr == address(this)) { 120 | return address(0x0); 121 | } 122 | 123 | return addr; 124 | } 125 | 126 | /** 127 | * @dev Returns the address of the resolver for the specified node. 128 | * @param node The specified node. 129 | * @return address of the resolver. 130 | */ 131 | function resolver(bytes32 node) public virtual override view returns (address) { 132 | return records[node].resolver; 133 | } 134 | 135 | /** 136 | * @dev Returns the TTL of a node, and any records associated with it. 137 | * @param node The specified node. 138 | * @return ttl of the node. 139 | */ 140 | function ttl(bytes32 node) public virtual override view returns (uint64) { 141 | return records[node].ttl; 142 | } 143 | 144 | /** 145 | * @dev Returns whether a record has been imported to the registry. 146 | * @param node The specified node. 147 | * @return Bool if record exists 148 | */ 149 | function recordExists(bytes32 node) public virtual override view returns (bool) { 150 | return records[node].owner != address(0x0); 151 | } 152 | 153 | /** 154 | * @dev Query if an address is an authorized operator for another address. 155 | * @param owner The address that owns the records. 156 | * @param operator The address that acts on behalf of the owner. 157 | * @return True if `operator` is an approved operator for `owner`, false otherwise. 158 | */ 159 | function isApprovedForAll(address owner, address operator) external virtual override view returns (bool) { 160 | return operators[owner][operator]; 161 | } 162 | 163 | function _setOwner(bytes32 node, address owner) internal virtual { 164 | records[node].owner = owner; 165 | } 166 | 167 | function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal { 168 | if(resolver != records[node].resolver) { 169 | records[node].resolver = resolver; 170 | emit NewResolver(node, resolver); 171 | } 172 | 173 | if(ttl != records[node].ttl) { 174 | records[node].ttl = ttl; 175 | emit NewTTL(node, ttl); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /contracts/registry/ENSRegistryWithFallback.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./ENS.sol"; 4 | import "./ENSRegistry.sol"; 5 | 6 | /** 7 | * The ENS registry contract. 8 | */ 9 | contract ENSRegistryWithFallback is ENSRegistry { 10 | 11 | ENS public old; 12 | 13 | /** 14 | * @dev Constructs a new ENS registrar. 15 | */ 16 | constructor(ENS _old) public ENSRegistry() { 17 | old = _old; 18 | } 19 | 20 | /** 21 | * @dev Returns the address of the resolver for the specified node. 22 | * @param node The specified node. 23 | * @return address of the resolver. 24 | */ 25 | function resolver(bytes32 node) public override view returns (address) { 26 | if (!recordExists(node)) { 27 | return old.resolver(node); 28 | } 29 | 30 | return super.resolver(node); 31 | } 32 | 33 | /** 34 | * @dev Returns the address that owns the specified node. 35 | * @param node The specified node. 36 | * @return address of the owner. 37 | */ 38 | function owner(bytes32 node) public override view returns (address) { 39 | if (!recordExists(node)) { 40 | return old.owner(node); 41 | } 42 | 43 | return super.owner(node); 44 | } 45 | 46 | /** 47 | * @dev Returns the TTL of a node, and any records associated with it. 48 | * @param node The specified node. 49 | * @return ttl of the node. 50 | */ 51 | function ttl(bytes32 node) public override view returns (uint64) { 52 | if (!recordExists(node)) { 53 | return old.ttl(node); 54 | } 55 | 56 | return super.ttl(node); 57 | } 58 | 59 | function _setOwner(bytes32 node, address owner) internal override { 60 | address addr = owner; 61 | if (addr == address(0x0)) { 62 | addr = address(this); 63 | } 64 | 65 | super._setOwner(node, addr); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /contracts/registry/FIFSRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./ENS.sol"; 4 | 5 | /** 6 | * A registrar that allocates subdomains to the first person to claim them. 7 | */ 8 | contract FIFSRegistrar { 9 | ENS ens; 10 | bytes32 rootNode; 11 | 12 | modifier only_owner(bytes32 label) { 13 | address currentOwner = ens.owner(keccak256(abi.encodePacked(rootNode, label))); 14 | require(currentOwner == address(0x0) || currentOwner == msg.sender); 15 | _; 16 | } 17 | 18 | /** 19 | * Constructor. 20 | * @param ensAddr The address of the ENS registry. 21 | * @param node The node that this registrar administers. 22 | */ 23 | constructor(ENS ensAddr, bytes32 node) public { 24 | ens = ensAddr; 25 | rootNode = node; 26 | } 27 | 28 | /** 29 | * Register a name, or change the owner of an existing registration. 30 | * @param label The hash of the label to register. 31 | * @param owner The address of the new owner. 32 | */ 33 | function register(bytes32 label, address owner) public only_owner(label) { 34 | ens.setSubnodeOwner(rootNode, label, owner); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/registry/ReverseRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./ENS.sol"; 4 | 5 | abstract contract NameResolver { 6 | function setName(bytes32 node, string memory name) public virtual; 7 | } 8 | 9 | contract ReverseRegistrar { 10 | // namehash('addr.reverse') 11 | bytes32 public constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; 12 | 13 | ENS public ens; 14 | NameResolver public defaultResolver; 15 | 16 | /** 17 | * @dev Constructor 18 | * @param ensAddr The address of the ENS registry. 19 | * @param resolverAddr The address of the default reverse resolver. 20 | */ 21 | constructor(ENS ensAddr, NameResolver resolverAddr) public { 22 | ens = ensAddr; 23 | defaultResolver = resolverAddr; 24 | 25 | // Assign ownership of the reverse record to our deployer 26 | ReverseRegistrar oldRegistrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)); 27 | if (address(oldRegistrar) != address(0x0)) { 28 | oldRegistrar.claim(msg.sender); 29 | } 30 | } 31 | 32 | /** 33 | * @dev Transfers ownership of the reverse ENS record associated with the 34 | * calling account. 35 | * @param owner The address to set as the owner of the reverse record in ENS. 36 | * @return The ENS node hash of the reverse record. 37 | */ 38 | function claim(address owner) public returns (bytes32) { 39 | return claimWithResolver(owner, address(0x0)); 40 | } 41 | 42 | /** 43 | * @dev Transfers ownership of the reverse ENS record associated with the 44 | * calling account. 45 | * @param owner The address to set as the owner of the reverse record in ENS. 46 | * @param resolver The address of the resolver to set; 0 to leave unchanged. 47 | * @return The ENS node hash of the reverse record. 48 | */ 49 | function claimWithResolver(address owner, address resolver) public returns (bytes32) { 50 | bytes32 label = sha3HexAddress(msg.sender); 51 | bytes32 node = keccak256(abi.encodePacked(ADDR_REVERSE_NODE, label)); 52 | address currentOwner = ens.owner(node); 53 | 54 | // Update the resolver if required 55 | if (resolver != address(0x0) && resolver != ens.resolver(node)) { 56 | // Transfer the name to us first if it's not already 57 | if (currentOwner != address(this)) { 58 | ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, address(this)); 59 | currentOwner = address(this); 60 | } 61 | ens.setResolver(node, resolver); 62 | } 63 | 64 | // Update the owner if required 65 | if (currentOwner != owner) { 66 | ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner); 67 | } 68 | 69 | return node; 70 | } 71 | 72 | /** 73 | * @dev Sets the `name()` record for the reverse ENS record associated with 74 | * the calling account. First updates the resolver to the default reverse 75 | * resolver if necessary. 76 | * @param name The name to set for this address. 77 | * @return The ENS node hash of the reverse record. 78 | */ 79 | function setName(string memory name) public returns (bytes32) { 80 | bytes32 node = claimWithResolver(address(this), address(defaultResolver)); 81 | defaultResolver.setName(node, name); 82 | return node; 83 | } 84 | 85 | /** 86 | * @dev Returns the node hash for a given account's reverse records. 87 | * @param addr The address to hash 88 | * @return The ENS node hash. 89 | */ 90 | function node(address addr) public pure returns (bytes32) { 91 | return keccak256(abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr))); 92 | } 93 | 94 | /** 95 | * @dev An optimised function to compute the sha3 of the lower-case 96 | * hexadecimal representation of an Ethereum address. 97 | * @param addr The address to hash 98 | * @return ret The SHA3 hash of the lower-case hexadecimal encoding of the 99 | * input address. 100 | */ 101 | function sha3HexAddress(address addr) private pure returns (bytes32 ret) { 102 | addr; 103 | ret; // Stop warning us about unused variables 104 | assembly { 105 | let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000 106 | 107 | for { let i := 40 } gt(i, 0) { } { 108 | i := sub(i, 1) 109 | mstore8(i, byte(and(addr, 0xf), lookup)) 110 | addr := div(addr, 0x10) 111 | i := sub(i, 1) 112 | mstore8(i, byte(and(addr, 0xf), lookup)) 113 | addr := div(addr, 0x10) 114 | } 115 | 116 | ret := keccak256(0, 40) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /contracts/registry/TestRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "./ENS.sol"; 4 | 5 | /** 6 | * A registrar that allocates subdomains to the first person to claim them, but 7 | * expires registrations a fixed period after they're initially claimed. 8 | */ 9 | contract TestRegistrar { 10 | uint constant registrationPeriod = 4 weeks; 11 | 12 | ENS public ens; 13 | bytes32 public rootNode; 14 | mapping (bytes32 => uint) public expiryTimes; 15 | 16 | /** 17 | * Constructor. 18 | * @param ensAddr The address of the ENS registry. 19 | * @param node The node that this registrar administers. 20 | */ 21 | constructor(ENS ensAddr, bytes32 node) public { 22 | ens = ensAddr; 23 | rootNode = node; 24 | } 25 | 26 | /** 27 | * Register a name that's not currently registered 28 | * @param label The hash of the label to register. 29 | * @param owner The address of the new owner. 30 | */ 31 | function register(bytes32 label, address owner) public { 32 | require(expiryTimes[label] < block.timestamp); 33 | 34 | expiryTimes[label] = block.timestamp + registrationPeriod; 35 | ens.setSubnodeOwner(rootNode, label, owner); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/resolvers/DefaultReverseResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "../registry/ENS.sol"; 4 | import "../registry/ReverseRegistrar.sol"; 5 | 6 | /** 7 | * @dev Provides a default implementation of a resolver for reverse records, 8 | * which permits only the owner to update it. 9 | */ 10 | contract DefaultReverseResolver { 11 | // namehash('addr.reverse') 12 | bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; 13 | 14 | ENS public ens; 15 | mapping (bytes32 => string) public name; 16 | 17 | /** 18 | * @dev Only permits calls by the reverse registrar. 19 | * @param node The node permission is required for. 20 | */ 21 | modifier onlyOwner(bytes32 node) { 22 | require(msg.sender == ens.owner(node)); 23 | _; 24 | } 25 | 26 | /** 27 | * @dev Constructor 28 | * @param ensAddr The address of the ENS registry. 29 | */ 30 | constructor(ENS ensAddr) { 31 | ens = ensAddr; 32 | 33 | // Assign ownership of the reverse record to our deployer 34 | ReverseRegistrar registrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)); 35 | if (address(registrar) != address(0x0)) { 36 | registrar.claim(msg.sender); 37 | } 38 | } 39 | 40 | /** 41 | * @dev Sets the name for a node. 42 | * @param node The node to update. 43 | * @param _name The name to set. 44 | */ 45 | function setName(bytes32 node, string memory _name) public onlyOwner(node) { 46 | name[node] = _name; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/resolvers/OwnedResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "@openzeppelin/contracts/access/Ownable.sol"; 3 | import "./profiles/ABIResolver.sol"; 4 | import "./profiles/AddrResolver.sol"; 5 | import "./profiles/ContentHashResolver.sol"; 6 | import "./profiles/DNSResolver.sol"; 7 | import "./profiles/InterfaceResolver.sol"; 8 | import "./profiles/NameResolver.sol"; 9 | import "./profiles/PubkeyResolver.sol"; 10 | import "./profiles/TextResolver.sol"; 11 | 12 | /** 13 | * A simple resolver anyone can use; only allows the owner of a node to set its 14 | * address. 15 | */ 16 | contract OwnedResolver is Ownable, ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver { 17 | function isAuthorised(bytes32 node) internal override view returns(bool) { 18 | return msg.sender == owner(); 19 | } 20 | 21 | function supportsInterface(bytes4 interfaceID) virtual override(ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver) public pure returns(bool) { 22 | return super.supportsInterface(interfaceID); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/resolvers/PublicResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | 3 | import "../registry/ENS.sol"; 4 | import "./profiles/ABIResolver.sol"; 5 | import "./profiles/AddrResolver.sol"; 6 | import "./profiles/ContentHashResolver.sol"; 7 | import "./profiles/DNSResolver.sol"; 8 | import "./profiles/InterfaceResolver.sol"; 9 | import "./profiles/NameResolver.sol"; 10 | import "./profiles/PubkeyResolver.sol"; 11 | import "./profiles/TextResolver.sol"; 12 | 13 | /** 14 | * A simple resolver anyone can use; only allows the owner of a node to set its 15 | * address. 16 | */ 17 | contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver { 18 | ENS ens; 19 | 20 | /** 21 | * A mapping of authorisations. An address that is authorised for a name 22 | * may make any changes to the name that the owner could, but may not update 23 | * the set of authorisations. 24 | * (node, owner, caller) => isAuthorised 25 | */ 26 | mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations; 27 | 28 | event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised); 29 | 30 | constructor(ENS _ens) { 31 | ens = _ens; 32 | } 33 | 34 | /** 35 | * @dev Sets or clears an authorisation. 36 | * Authorisations are specific to the caller. Any account can set an authorisation 37 | * for any name, but the authorisation that is checked will be that of the 38 | * current owner of a name. Thus, transferring a name effectively clears any 39 | * existing authorisations, and new authorisations can be set in advance of 40 | * an ownership transfer if desired. 41 | * 42 | * @param node The name to change the authorisation on. 43 | * @param target The address that is to be authorised or deauthorised. 44 | * @param isAuthorised True if the address should be authorised, or false if it should be deauthorised. 45 | */ 46 | function setAuthorisation(bytes32 node, address target, bool isAuthorised) external { 47 | authorisations[node][msg.sender][target] = isAuthorised; 48 | emit AuthorisationChanged(node, msg.sender, target, isAuthorised); 49 | } 50 | 51 | function isAuthorised(bytes32 node) internal override view returns(bool) { 52 | address owner = ens.owner(node); 53 | return owner == msg.sender || authorisations[node][owner][msg.sender]; 54 | } 55 | 56 | function multicall(bytes[] calldata data) external returns(bytes[] memory results) { 57 | results = new bytes[](data.length); 58 | for(uint i = 0; i < data.length; i++) { 59 | (bool success, bytes memory result) = address(this).delegatecall(data[i]); 60 | require(success); 61 | results[i] = result; 62 | } 63 | return results; 64 | } 65 | 66 | function supportsInterface(bytes4 interfaceID) virtual override(ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver) public pure returns(bool) { 67 | return super.supportsInterface(interfaceID); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/resolvers/Resolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | pragma experimental ABIEncoderV2; 3 | 4 | /** 5 | * A generic resolver interface which includes all the functions including the ones deprecated 6 | */ 7 | interface Resolver{ 8 | event AddrChanged(bytes32 indexed node, address a); 9 | event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress); 10 | event NameChanged(bytes32 indexed node, string name); 11 | event ABIChanged(bytes32 indexed node, uint256 indexed contentType); 12 | event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); 13 | event TextChanged(bytes32 indexed node, string indexed indexedKey, string key); 14 | event ContenthashChanged(bytes32 indexed node, bytes hash); 15 | /* Deprecated events */ 16 | event ContentChanged(bytes32 indexed node, bytes32 hash); 17 | 18 | function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory); 19 | function addr(bytes32 node) external view returns (address); 20 | function addr(bytes32 node, uint coinType) external view returns(bytes memory); 21 | function contenthash(bytes32 node) external view returns (bytes memory); 22 | function dnsrr(bytes32 node) external view returns (bytes memory); 23 | function name(bytes32 node) external view returns (string memory); 24 | function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y); 25 | function text(bytes32 node, string calldata key) external view returns (string memory); 26 | function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address); 27 | function setABI(bytes32 node, uint256 contentType, bytes calldata data) external; 28 | function setAddr(bytes32 node, address addr) external; 29 | function setAddr(bytes32 node, uint coinType, bytes calldata a) external; 30 | function setContenthash(bytes32 node, bytes calldata hash) external; 31 | function setDnsrr(bytes32 node, bytes calldata data) external; 32 | function setName(bytes32 node, string calldata _name) external; 33 | function setPubkey(bytes32 node, bytes32 x, bytes32 y) external; 34 | function setText(bytes32 node, string calldata key, string calldata value) external; 35 | function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external; 36 | function supportsInterface(bytes4 interfaceID) external pure returns (bool); 37 | function multicall(bytes[] calldata data) external returns(bytes[] memory results); 38 | 39 | /* Deprecated functions */ 40 | function content(bytes32 node) external view returns (bytes32); 41 | function multihash(bytes32 node) external view returns (bytes memory); 42 | function setContent(bytes32 node, bytes32 hash) external; 43 | function setMultihash(bytes32 node, bytes calldata hash) external; 44 | } 45 | -------------------------------------------------------------------------------- /contracts/resolvers/ResolverBase.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | abstract contract ResolverBase { 3 | bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; 4 | 5 | function supportsInterface(bytes4 interfaceID) virtual public pure returns(bool) { 6 | return interfaceID == INTERFACE_META_ID; 7 | } 8 | 9 | function isAuthorised(bytes32 node) internal virtual view returns(bool); 10 | 11 | modifier authorised(bytes32 node) { 12 | require(isAuthorised(node)); 13 | _; 14 | } 15 | 16 | function bytesToAddress(bytes memory b) internal pure returns(address payable a) { 17 | require(b.length == 20); 18 | assembly { 19 | a := div(mload(add(b, 32)), exp(256, 12)) 20 | } 21 | } 22 | 23 | function addressToBytes(address a) internal pure returns(bytes memory b) { 24 | b = new bytes(20); 25 | assembly { 26 | mstore(add(b, 32), mul(a, exp(256, 12))) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/ABIResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | 4 | abstract contract ABIResolver is ResolverBase { 5 | bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56; 6 | 7 | event ABIChanged(bytes32 indexed node, uint256 indexed contentType); 8 | 9 | mapping(bytes32=>mapping(uint256=>bytes)) abis; 10 | 11 | /** 12 | * Sets the ABI associated with an ENS node. 13 | * Nodes may have one ABI of each content type. To remove an ABI, set it to 14 | * the empty string. 15 | * @param node The node to update. 16 | * @param contentType The content type of the ABI 17 | * @param data The ABI data. 18 | */ 19 | function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) { 20 | // Content types must be powers of 2 21 | require(((contentType - 1) & contentType) == 0); 22 | 23 | abis[node][contentType] = data; 24 | emit ABIChanged(node, contentType); 25 | } 26 | 27 | /** 28 | * Returns the ABI associated with an ENS node. 29 | * Defined in EIP205. 30 | * @param node The ENS node to query 31 | * @param contentTypes A bitwise OR of the ABI formats accepted by the caller. 32 | * @return contentType The content type of the return value 33 | * @return data The ABI data 34 | */ 35 | function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) { 36 | mapping(uint256=>bytes) storage abiset = abis[node]; 37 | 38 | for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) { 39 | if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) { 40 | return (contentType, abiset[contentType]); 41 | } 42 | } 43 | 44 | return (0, bytes("")); 45 | } 46 | 47 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 48 | return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/AddrResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | 4 | abstract contract AddrResolver is ResolverBase { 5 | bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de; 6 | bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06; 7 | uint constant private COIN_TYPE_ETH = 60; 8 | 9 | event AddrChanged(bytes32 indexed node, address a); 10 | event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress); 11 | 12 | mapping(bytes32=>mapping(uint=>bytes)) _addresses; 13 | 14 | /** 15 | * Sets the address associated with an ENS node. 16 | * May only be called by the owner of that node in the ENS registry. 17 | * @param node The node to update. 18 | * @param a The address to set. 19 | */ 20 | function setAddr(bytes32 node, address a) external authorised(node) { 21 | setAddr(node, COIN_TYPE_ETH, addressToBytes(a)); 22 | } 23 | 24 | /** 25 | * Returns the address associated with an ENS node. 26 | * @param node The ENS node to query. 27 | * @return The associated address. 28 | */ 29 | function addr(bytes32 node) public view returns (address payable) { 30 | bytes memory a = addr(node, COIN_TYPE_ETH); 31 | if(a.length == 0) { 32 | return payable(0); 33 | } 34 | return bytesToAddress(a); 35 | } 36 | 37 | function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) { 38 | emit AddressChanged(node, coinType, a); 39 | if(coinType == COIN_TYPE_ETH) { 40 | emit AddrChanged(node, bytesToAddress(a)); 41 | } 42 | _addresses[node][coinType] = a; 43 | } 44 | 45 | function addr(bytes32 node, uint coinType) public view returns(bytes memory) { 46 | return _addresses[node][coinType]; 47 | } 48 | 49 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 50 | return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/ContentHashResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | 4 | abstract contract ContentHashResolver is ResolverBase { 5 | bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1; 6 | 7 | event ContenthashChanged(bytes32 indexed node, bytes hash); 8 | 9 | mapping(bytes32=>bytes) hashes; 10 | 11 | /** 12 | * Sets the contenthash associated with an ENS node. 13 | * May only be called by the owner of that node in the ENS registry. 14 | * @param node The node to update. 15 | * @param hash The contenthash to set 16 | */ 17 | function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) { 18 | hashes[node] = hash; 19 | emit ContenthashChanged(node, hash); 20 | } 21 | 22 | /** 23 | * Returns the contenthash associated with an ENS node. 24 | * @param node The ENS node to query. 25 | * @return The associated contenthash. 26 | */ 27 | function contenthash(bytes32 node) external view returns (bytes memory) { 28 | return hashes[node]; 29 | } 30 | 31 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 32 | return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/DNSResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | import "../../dnssec-oracle/RRUtils.sol"; 4 | 5 | abstract contract DNSResolver is ResolverBase { 6 | using RRUtils for *; 7 | using BytesUtils for bytes; 8 | 9 | bytes4 constant private DNS_RECORD_INTERFACE_ID = 0xa8fa5682; 10 | bytes4 constant private DNS_ZONE_INTERFACE_ID = 0x5c47637c; 11 | 12 | // DNSRecordChanged is emitted whenever a given node/name/resource's RRSET is updated. 13 | event DNSRecordChanged(bytes32 indexed node, bytes name, uint16 resource, bytes record); 14 | // DNSRecordDeleted is emitted whenever a given node/name/resource's RRSET is deleted. 15 | event DNSRecordDeleted(bytes32 indexed node, bytes name, uint16 resource); 16 | // DNSZoneCleared is emitted whenever a given node's zone information is cleared. 17 | event DNSZoneCleared(bytes32 indexed node); 18 | 19 | // DNSZonehashChanged is emitted whenever a given node's zone hash is updated. 20 | event DNSZonehashChanged(bytes32 indexed node, bytes lastzonehash, bytes zonehash); 21 | 22 | // Zone hashes for the domains. 23 | // A zone hash is an EIP-1577 content hash in binary format that should point to a 24 | // resource containing a single zonefile. 25 | // node => contenthash 26 | mapping(bytes32=>bytes) private zonehashes; 27 | 28 | // Version the mapping for each zone. This allows users who have lost 29 | // track of their entries to effectively delete an entire zone by bumping 30 | // the version number. 31 | // node => version 32 | mapping(bytes32=>uint256) private versions; 33 | 34 | // The records themselves. Stored as binary RRSETs 35 | // node => version => name => resource => data 36 | mapping(bytes32=>mapping(uint256=>mapping(bytes32=>mapping(uint16=>bytes)))) private records; 37 | 38 | // Count of number of entries for a given name. Required for DNS resolvers 39 | // when resolving wildcards. 40 | // node => version => name => number of records 41 | mapping(bytes32=>mapping(uint256=>mapping(bytes32=>uint16))) private nameEntriesCount; 42 | 43 | /** 44 | * Set one or more DNS records. Records are supplied in wire-format. 45 | * Records with the same node/name/resource must be supplied one after the 46 | * other to ensure the data is updated correctly. For example, if the data 47 | * was supplied: 48 | * a.example.com IN A 1.2.3.4 49 | * a.example.com IN A 5.6.7.8 50 | * www.example.com IN CNAME a.example.com. 51 | * then this would store the two A records for a.example.com correctly as a 52 | * single RRSET, however if the data was supplied: 53 | * a.example.com IN A 1.2.3.4 54 | * www.example.com IN CNAME a.example.com. 55 | * a.example.com IN A 5.6.7.8 56 | * then this would store the first A record, the CNAME, then the second A 57 | * record which would overwrite the first. 58 | * 59 | * @param node the namehash of the node for which to set the records 60 | * @param data the DNS wire format records to set 61 | */ 62 | function setDNSRecords(bytes32 node, bytes calldata data) external authorised(node) { 63 | uint16 resource = 0; 64 | uint256 offset = 0; 65 | bytes memory name; 66 | bytes memory value; 67 | bytes32 nameHash; 68 | // Iterate over the data to add the resource records 69 | for (RRUtils.RRIterator memory iter = data.iterateRRs(0); !iter.done(); iter.next()) { 70 | if (resource == 0) { 71 | resource = iter.dnstype; 72 | name = iter.name(); 73 | nameHash = keccak256(abi.encodePacked(name)); 74 | value = bytes(iter.rdata()); 75 | } else { 76 | bytes memory newName = iter.name(); 77 | if (resource != iter.dnstype || !name.equals(newName)) { 78 | setDNSRRSet(node, name, resource, data, offset, iter.offset - offset, value.length == 0); 79 | resource = iter.dnstype; 80 | offset = iter.offset; 81 | name = newName; 82 | nameHash = keccak256(name); 83 | value = bytes(iter.rdata()); 84 | } 85 | } 86 | } 87 | if (name.length > 0) { 88 | setDNSRRSet(node, name, resource, data, offset, data.length - offset, value.length == 0); 89 | } 90 | } 91 | 92 | /** 93 | * Obtain a DNS record. 94 | * @param node the namehash of the node for which to fetch the record 95 | * @param name the keccak-256 hash of the fully-qualified name for which to fetch the record 96 | * @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types 97 | * @return the DNS record in wire format if present, otherwise empty 98 | */ 99 | function dnsRecord(bytes32 node, bytes32 name, uint16 resource) public view returns (bytes memory) { 100 | return records[node][versions[node]][name][resource]; 101 | } 102 | 103 | /** 104 | * Check if a given node has records. 105 | * @param node the namehash of the node for which to check the records 106 | * @param name the namehash of the node for which to check the records 107 | */ 108 | function hasDNSRecords(bytes32 node, bytes32 name) public view returns (bool) { 109 | return (nameEntriesCount[node][versions[node]][name] != 0); 110 | } 111 | 112 | /** 113 | * Clear all information for a DNS zone. 114 | * @param node the namehash of the node for which to clear the zone 115 | */ 116 | function clearDNSZone(bytes32 node) public authorised(node) { 117 | versions[node]++; 118 | emit DNSZoneCleared(node); 119 | } 120 | 121 | /** 122 | * setZonehash sets the hash for the zone. 123 | * May only be called by the owner of that node in the ENS registry. 124 | * @param node The node to update. 125 | * @param hash The zonehash to set 126 | */ 127 | function setZonehash(bytes32 node, bytes calldata hash) external authorised(node) { 128 | bytes memory oldhash = zonehashes[node]; 129 | zonehashes[node] = hash; 130 | emit DNSZonehashChanged(node, oldhash, hash); 131 | } 132 | 133 | /** 134 | * zonehash obtains the hash for the zone. 135 | * @param node The ENS node to query. 136 | * @return The associated contenthash. 137 | */ 138 | function zonehash(bytes32 node) external view returns (bytes memory) { 139 | return zonehashes[node]; 140 | } 141 | 142 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 143 | return interfaceID == DNS_RECORD_INTERFACE_ID || 144 | interfaceID == DNS_ZONE_INTERFACE_ID || 145 | super.supportsInterface(interfaceID); 146 | } 147 | 148 | function setDNSRRSet( 149 | bytes32 node, 150 | bytes memory name, 151 | uint16 resource, 152 | bytes memory data, 153 | uint256 offset, 154 | uint256 size, 155 | bool deleteRecord) private 156 | { 157 | uint256 version = versions[node]; 158 | bytes32 nameHash = keccak256(name); 159 | bytes memory rrData = data.substring(offset, size); 160 | if (deleteRecord) { 161 | if (records[node][version][nameHash][resource].length != 0) { 162 | nameEntriesCount[node][version][nameHash]--; 163 | } 164 | delete(records[node][version][nameHash][resource]); 165 | emit DNSRecordDeleted(node, name, resource); 166 | } else { 167 | if (records[node][version][nameHash][resource].length == 0) { 168 | nameEntriesCount[node][version][nameHash]++; 169 | } 170 | records[node][version][nameHash][resource] = rrData; 171 | emit DNSRecordChanged(node, name, resource, rrData); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/InterfaceResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | import "./AddrResolver.sol"; 4 | 5 | abstract contract InterfaceResolver is ResolverBase, AddrResolver { 6 | bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)")); 7 | bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; 8 | 9 | event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer); 10 | 11 | mapping(bytes32=>mapping(bytes4=>address)) interfaces; 12 | 13 | /** 14 | * Sets an interface associated with a name. 15 | * Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support. 16 | * @param node The node to update. 17 | * @param interfaceID The EIP 165 interface ID. 18 | * @param implementer The address of a contract that implements this interface for this node. 19 | */ 20 | function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) { 21 | interfaces[node][interfaceID] = implementer; 22 | emit InterfaceChanged(node, interfaceID, implementer); 23 | } 24 | 25 | /** 26 | * Returns the address of a contract that implements the specified interface for this name. 27 | * If an implementer has not been set for this interfaceID and name, the resolver will query 28 | * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that 29 | * contract implements EIP165 and returns `true` for the specified interfaceID, its address 30 | * will be returned. 31 | * @param node The ENS node to query. 32 | * @param interfaceID The EIP 165 interface ID to check for. 33 | * @return The address that implements this interface, or 0 if the interface is unsupported. 34 | */ 35 | function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) { 36 | address implementer = interfaces[node][interfaceID]; 37 | if(implementer != address(0)) { 38 | return implementer; 39 | } 40 | 41 | address a = addr(node); 42 | if(a == address(0)) { 43 | return address(0); 44 | } 45 | 46 | (bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID)); 47 | if(!success || returnData.length < 32 || returnData[31] == 0) { 48 | // EIP 165 not supported by target 49 | return address(0); 50 | } 51 | 52 | (success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID)); 53 | if(!success || returnData.length < 32 || returnData[31] == 0) { 54 | // Specified interface not supported by target 55 | return address(0); 56 | } 57 | 58 | return a; 59 | } 60 | 61 | function supportsInterface(bytes4 interfaceID) virtual override(AddrResolver, ResolverBase) public pure returns(bool) { 62 | return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/NameResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | 4 | abstract contract NameResolver is ResolverBase { 5 | bytes4 constant private NAME_INTERFACE_ID = 0x691f3431; 6 | 7 | event NameChanged(bytes32 indexed node, string name); 8 | 9 | mapping(bytes32=>string) names; 10 | 11 | /** 12 | * Sets the name associated with an ENS node, for reverse records. 13 | * May only be called by the owner of that node in the ENS registry. 14 | * @param node The node to update. 15 | * @param name The name to set. 16 | */ 17 | function setName(bytes32 node, string calldata name) external authorised(node) { 18 | names[node] = name; 19 | emit NameChanged(node, name); 20 | } 21 | 22 | /** 23 | * Returns the name associated with an ENS node, for reverse records. 24 | * Defined in EIP181. 25 | * @param node The ENS node to query. 26 | * @return The associated name. 27 | */ 28 | function name(bytes32 node) external view returns (string memory) { 29 | return names[node]; 30 | } 31 | 32 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 33 | return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/PubkeyResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | 4 | abstract contract PubkeyResolver is ResolverBase { 5 | bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233; 6 | 7 | event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); 8 | 9 | struct PublicKey { 10 | bytes32 x; 11 | bytes32 y; 12 | } 13 | 14 | mapping(bytes32=>PublicKey) pubkeys; 15 | 16 | /** 17 | * Sets the SECP256k1 public key associated with an ENS node. 18 | * @param node The ENS node to query 19 | * @param x the X coordinate of the curve point for the public key. 20 | * @param y the Y coordinate of the curve point for the public key. 21 | */ 22 | function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) { 23 | pubkeys[node] = PublicKey(x, y); 24 | emit PubkeyChanged(node, x, y); 25 | } 26 | 27 | /** 28 | * Returns the SECP256k1 public key associated with an ENS node. 29 | * Defined in EIP 619. 30 | * @param node The ENS node to query 31 | * @return x The X coordinate of the curve point for the public key. 32 | * @return y The Y coordinate of the curve point for the public key. 33 | */ 34 | function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) { 35 | return (pubkeys[node].x, pubkeys[node].y); 36 | } 37 | 38 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 39 | return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/resolvers/profiles/TextResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.4; 2 | import "../ResolverBase.sol"; 3 | 4 | abstract contract TextResolver is ResolverBase { 5 | bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c; 6 | 7 | event TextChanged(bytes32 indexed node, string indexed indexedKey, string key); 8 | 9 | mapping(bytes32=>mapping(string=>string)) texts; 10 | 11 | /** 12 | * Sets the text data associated with an ENS node and key. 13 | * May only be called by the owner of that node in the ENS registry. 14 | * @param node The node to update. 15 | * @param key The key to set. 16 | * @param value The text data value to set. 17 | */ 18 | function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) { 19 | texts[node][key] = value; 20 | emit TextChanged(node, key, key); 21 | } 22 | 23 | /** 24 | * Returns the text data associated with an ENS node and key. 25 | * @param node The ENS node to query. 26 | * @param key The text data key to query. 27 | * @return The associated text data. 28 | */ 29 | function text(bytes32 node, string calldata key) external view returns (string memory) { 30 | return texts[node][key]; 31 | } 32 | 33 | function supportsInterface(bytes4 interfaceID) virtual override public pure returns(bool) { 34 | return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/root/Controllable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "./Ownable.sol"; 4 | 5 | contract Controllable is Ownable { 6 | mapping(address=>bool) public controllers; 7 | 8 | event ControllerChanged(address indexed controller, bool enabled); 9 | 10 | modifier onlyController { 11 | require(controllers[msg.sender]); 12 | _; 13 | } 14 | 15 | function setController(address controller, bool enabled) public onlyOwner { 16 | controllers[controller] = enabled; 17 | emit ControllerChanged(controller, enabled); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/root/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | contract Ownable { 4 | 5 | address public owner; 6 | 7 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 8 | 9 | modifier onlyOwner { 10 | require(isOwner(msg.sender)); 11 | _; 12 | } 13 | 14 | constructor() public { 15 | owner = msg.sender; 16 | } 17 | 18 | function transferOwnership(address newOwner) public onlyOwner { 19 | emit OwnershipTransferred(owner, newOwner); 20 | owner = newOwner; 21 | } 22 | 23 | function isOwner(address addr) public view returns (bool) { 24 | return owner == addr; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/root/Root.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.4; 2 | 3 | import "../registry/ENS.sol"; 4 | import "./Ownable.sol"; 5 | import "./Controllable.sol"; 6 | 7 | contract Root is Ownable, Controllable { 8 | bytes32 constant private ROOT_NODE = bytes32(0); 9 | 10 | bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)")); 11 | 12 | event TLDLocked(bytes32 indexed label); 13 | 14 | ENS public ens; 15 | mapping(bytes32=>bool) public locked; 16 | 17 | constructor(ENS _ens) public { 18 | ens = _ens; 19 | } 20 | 21 | function setSubnodeOwner(bytes32 label, address owner) external onlyController { 22 | require(!locked[label]); 23 | ens.setSubnodeOwner(ROOT_NODE, label, owner); 24 | } 25 | 26 | function setResolver(address resolver) external onlyOwner { 27 | ens.setResolver(ROOT_NODE, resolver); 28 | } 29 | 30 | function lock(bytes32 label) external onlyOwner { 31 | emit TLDLocked(label); 32 | locked[label] = true; 33 | } 34 | 35 | function supportsInterface(bytes4 interfaceID) external pure returns (bool) { 36 | return interfaceID == INTERFACE_META_ID; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /deploy/00_deploy_registry.js: -------------------------------------------------------------------------------- 1 | // deploy/00_deploy_my_contract.js 2 | module.exports = async ({getNamedAccounts, deployments}) => { 3 | const {deploy} = deployments; 4 | const {deployer} = await getNamedAccounts(); 5 | await deploy('ENSRegistry', { 6 | from: deployer, 7 | log: true, 8 | skipIfAlreadyDeployed: true, 9 | }); 10 | }; 11 | 12 | module.exports.tags = ['ENSRegistry']; -------------------------------------------------------------------------------- /deploy/01_deploy_public_resolver.js: -------------------------------------------------------------------------------- 1 | // deploy/01_deploy_public_resolver.js 2 | module.exports = async ({getNamedAccounts, deployments}) => { 3 | const {deploy} = deployments; 4 | const {deployer} = await getNamedAccounts(); 5 | const ENSRegistry = await deployments.get('ENSRegistry'); 6 | await deploy('PublicResolver', { 7 | from: deployer, 8 | args: [ENSRegistry.address], 9 | log: true, 10 | skipIfAlreadyDeployed: true, 11 | }); 12 | }; 13 | 14 | module.exports.tags = ['PublicResolver']; 15 | module.exports.dependencies = ['ENSRegistry']; -------------------------------------------------------------------------------- /deploy/02_deploy_reverse_registrar.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, deployments}) => { 2 | const {deploy} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | 5 | const ENSRegistry = await deployments.get('ENSRegistry'); 6 | const PublicResolver = await deployments.get('PublicResolver'); 7 | 8 | await deploy('ReverseRegistrar', { 9 | from: deployer, 10 | args: [ENSRegistry.address, PublicResolver.address], 11 | log: true, 12 | skipIfAlreadyDeployed: true, 13 | }); 14 | }; 15 | 16 | module.exports.tags = ['ReverseRegistrar']; 17 | module.exports.dependencies = ['ENSRegistry', 'PublicResolver']; -------------------------------------------------------------------------------- /deploy/03_deploy_base_registrar.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, deployments, ethers, config}) => { 2 | const {deploy} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | const ENSRegistry = await deployments.get('ENSRegistry'); 5 | 6 | await deploy('BaseRegistrarImplementation', { 7 | from: deployer, 8 | args: [ENSRegistry.address, ethers.utils.namehash(config.tld)], 9 | log: true, 10 | skipIfAlreadyDeployed: true, 11 | }); 12 | }; 13 | 14 | module.exports.tags = ['BaseRegistrarImplementation']; 15 | module.exports.dependencies = ['ENSRegistry']; -------------------------------------------------------------------------------- /deploy/04_deploy_price_oracle.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, hardhatArguments, deployments, ethers, config}) => { 2 | const {deploy} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | 5 | const net = hardhatArguments && hardhatArguments.network || config.defaultNetwork; 6 | const netConfig = config.networks[net]; 7 | 8 | let oracleAddress; 9 | // check if there is a USD oracle in the config 10 | if (netConfig && netConfig.usdOracle) { 11 | oracleAddress = netConfig.usdOracle; 12 | } else { 13 | // No USD oracle ... deploy DummyOracle with 1 ETH == 2000 USD 14 | const dummyOracle = await deploy('DummyOracle', { 15 | from: deployer, 16 | args: [ethers.BigNumber.from('200000000000')], 17 | log: true, 18 | }) 19 | oracleAddress = dummyOracle.address; 20 | console.log('Using DummyOracle with address: ', oracleAddress); 21 | } 22 | 23 | // 1 character names = $1000 (or 1000 * 1e18 attousd) 24 | // 2 character names = $500 25 | // 3 or more = $100 26 | const prices = [ 27 | ethers.BigNumber.from('1000000000000000000000'), 28 | ethers.BigNumber.from('500000000000000000000'), 29 | ethers.BigNumber.from('100000000000000000000') 30 | ]; 31 | 32 | await deploy('StablePriceOracle', { 33 | from: deployer, 34 | args: [oracleAddress, prices], 35 | log: true, 36 | skipIfAlreadyDeployed: true, 37 | }); 38 | }; 39 | 40 | module.exports.tags = ['StablePriceOracle']; -------------------------------------------------------------------------------- /deploy/05_deploy_controller.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, hardhatArguments, deployments, ethers, config}) => { 2 | const {deploy} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | 5 | const registrar = await deployments.get('BaseRegistrarImplementation'); 6 | const priceOracle = await deployments.get('StablePriceOracle'); 7 | const minCommitmentAge = 60; 8 | const maxCommitmentAge = 86400; 9 | 10 | await deploy('ETHRegistrarController', { 11 | from: deployer, 12 | args: [ 13 | registrar.address, 14 | priceOracle.address, 15 | minCommitmentAge, 16 | maxCommitmentAge 17 | ], 18 | log: true, 19 | skipIfAlreadyDeployed: true, 20 | }); 21 | }; 22 | 23 | module.exports.tags = ['ETHRegistrarController']; 24 | module.exports.dependencies = ['BaseRegistrarImplementation', 'StablePriceOracle']; -------------------------------------------------------------------------------- /deploy/06_deploy_forever_controller.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, hardhatArguments, deployments, ethers, config}) => { 2 | const {deploy} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | 5 | const registrar = await deployments.get('BaseRegistrarImplementation'); 6 | const priceOracle = await deployments.get('StablePriceOracle'); 7 | const minCommitmentAge = 60; 8 | const maxCommitmentAge = 604800; 9 | 10 | await deploy('ForeverRegistrarController', { 11 | from: deployer, 12 | args: [ 13 | registrar.address, 14 | priceOracle.address, 15 | minCommitmentAge, 16 | maxCommitmentAge 17 | ], 18 | log: true, 19 | }); 20 | }; 21 | 22 | module.exports.tags = ['ForeverRegistrarController']; 23 | module.exports.dependencies = ['BaseRegistrarImplementation', 'StablePriceOracle']; 24 | -------------------------------------------------------------------------------- /deploy/finalize_forever_controller.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, hardhatArguments, deployments, ethers, config}) => { 2 | const {execute} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | 5 | const ForeverRegistrarController = await deployments.get('ForeverRegistrarController'); 6 | await execute( 7 | 'BaseRegistrarImplementation', 8 | {from: deployer, log: true}, 9 | 'addController', 10 | ForeverRegistrarController.address 11 | ); 12 | 13 | }; 14 | 15 | module.exports.tags = ['FinalizeForeverController']; 16 | module.exports.skip = function (hre) { 17 | return hre.network.name !== 'hardhat' && !process.argv.includes('FinalizeForeverController'); 18 | } 19 | module.exports.runAtTheEnd = true; 20 | -------------------------------------------------------------------------------- /deploy/finalize_initial.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({getNamedAccounts, hardhatArguments, deployments, ethers, config}) => { 2 | const {execute} = deployments; 3 | const {deployer} = await getNamedAccounts(); 4 | 5 | const ROOT_NODE = '0x0000000000000000000000000000000000000000000000000000000000000000'; 6 | const BURN_ADDRESS = '0x0000000000000000000000000000000000000000'; 7 | 8 | const BaseRegistrarImplementation = await deployments.get('BaseRegistrarImplementation'); 9 | const ReverseRegistrar = await deployments.get('ReverseRegistrar'); 10 | const ETHRegistrarController = await deployments.get('ETHRegistrarController'); 11 | 12 | // Assign BaseRegistrarImplementation as the owner of the TLD 13 | await execute( 14 | 'ENSRegistry', 15 | {from: deployer, log: true}, 16 | 'setSubnodeOwner', 17 | ROOT_NODE, 18 | ethers.utils.id(config.tld), 19 | BaseRegistrarImplementation.address 20 | ); 21 | 22 | // Assign deployer as the owner of .reverse TLD 23 | await execute( 24 | 'ENSRegistry', 25 | {from: deployer, log: true}, 26 | 'setSubnodeOwner', 27 | ROOT_NODE, 28 | ethers.utils.id('reverse'), 29 | deployer 30 | ); 31 | 32 | // Assign ReverseRegistrar as the owner of addr.reverse 33 | await execute( 34 | 'ENSRegistry', 35 | {from: deployer, log: true}, 36 | 'setSubnodeOwner', 37 | ethers.utils.namehash('reverse'), 38 | ethers.utils.id('addr'), 39 | ReverseRegistrar.address 40 | ); 41 | 42 | // Enable ETHRegistrarController 43 | await execute( 44 | 'BaseRegistrarImplementation', 45 | {from: deployer, log: true}, 46 | 'addController', 47 | ETHRegistrarController.address 48 | ); 49 | 50 | // Assign PublicResolver for the TLD node 51 | await execute( 52 | 'BaseRegistrarImplementation', 53 | {from: deployer, log: true}, 54 | 'setResolver', 55 | (await deployments.get('PublicResolver')).address 56 | ); 57 | 58 | // Make .reverse owner-less 59 | await execute( 60 | 'ENSRegistry', 61 | {from: deployer, log: true}, 62 | 'setOwner', 63 | ethers.utils.namehash('reverse'), 64 | BURN_ADDRESS 65 | ); 66 | 67 | // Make ENSRegistry owner-less 68 | await execute( 69 | 'ENSRegistry', 70 | {from: deployer, log: true}, 71 | 'setOwner', 72 | ROOT_NODE, 73 | BURN_ADDRESS 74 | ); 75 | }; 76 | 77 | module.exports.tags = ['FinalizeInitial']; 78 | module.exports.skip = function (hre) { 79 | return hre.network.name !== 'hardhat' && !process.argv.includes('FinalizeInitial'); 80 | } 81 | module.exports.runAtTheEnd = true; 82 | -------------------------------------------------------------------------------- /deployments/goerli/.chainId: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /deployments/mainnet/.chainId: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-truffle5"); 2 | require("@nomiclabs/hardhat-waffle"); 3 | require("hardhat-abi-exporter"); 4 | require("@nomiclabs/hardhat-solhint"); 5 | require("hardhat-gas-reporter"); 6 | require('hardhat-deploy'); 7 | require("@nomiclabs/hardhat-ethers"); 8 | 9 | // Load environment variables from .env file. Suppress warnings using silent 10 | // if this file is missing. dotenv will never modify any environment variables 11 | // that have already been set. 12 | // https://github.com/motdotla/dotenv 13 | require('dotenv').config({ silent: true }); 14 | 15 | 16 | // You need to export an object to set up your config 17 | // Go to https://hardhat.org/config/ to learn more 18 | 19 | // TLD to use in deployment 20 | const TLD = 'forever'; 21 | 22 | // Replace this private key with your account private key 23 | // To export your private key from Metamask, open Metamask and 24 | // go to Account Details > Export Private Key 25 | real_accounts = undefined 26 | if (process.env.DEPLOYER_KEY) { 27 | real_accounts = [process.env.DEPLOYER_KEY] 28 | } 29 | 30 | // using ChainLink USD Oracle on Mainnet or set null to use DummyOracle 31 | // https://data.chain.link/ethereum/mainnet/crypto-usd/eth-usd 32 | const MAINNET_USD_ORACLE = '0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419'; 33 | 34 | 35 | /** 36 | * @type import('hardhat/config').HardhatUserConfig 37 | */ 38 | module.exports = { 39 | tld: TLD, 40 | networks: { 41 | mainnet: { 42 | url: `https://mainnet.infura.io/v3/${process.env.INFURA_ID}`, 43 | usdOracle: MAINNET_USD_ORACLE, 44 | accounts: real_accounts, 45 | tags: ["production"] 46 | }, 47 | goerli: { 48 | url: `https://goerli.infura.io/v3/${process.env.INFURA_ID}`, 49 | accounts: real_accounts, 50 | usdOracle: '0xa92F3BE2dFf40c82b902Ffa82e50B1db414bC7E1', 51 | tags: ["staging"] 52 | }, 53 | hardhat: { 54 | // Required for real DNS record tests 55 | initialDate: "2019-03-15T14:06:45.000+13:00", 56 | tags: ["test", "local"] 57 | }, 58 | localhost: { 59 | url: "http://127.0.0.1:8545", 60 | tags: ["local"] 61 | }, 62 | }, 63 | namedAccounts: { 64 | deployer: { 65 | default: 0 66 | } 67 | }, 68 | mocha: { 69 | }, 70 | abiExporter: { 71 | path: './build/contracts', 72 | clear: true, 73 | flat: true, 74 | spacing: 2 75 | }, 76 | etherscan: { 77 | apiKey: process.env.ETHERSCAN_API_KEY, 78 | }, 79 | solidity: { 80 | compilers: [ 81 | { 82 | version: "0.8.4", 83 | settings: { 84 | optimizer: { 85 | enabled: true, 86 | runs: 10000, 87 | }, 88 | }, 89 | }, 90 | ], 91 | }, 92 | }; 93 | 94 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const BaseRegistrar = require('./build/contracts/BaseRegistrar') 2 | const BaseRegistrarImplementation = require('./build/contracts/BaseRegistrarImplementation') 3 | const ENS = require('./build/contracts/ENS') 4 | const ENSRegistry = require('./build/contracts/ENSRegistry') 5 | const ENSRegistryWithFallback = require('./build/contracts/ENSRegistryWithFallback') 6 | const ETHRegistrarController = require('./build/contracts/ETHRegistrarController') 7 | const FIFSRegistrar = require('./build/contracts/FIFSRegistrar') 8 | const PriceOracle = require('./build/contracts/PriceOracle') 9 | const PublicResolver = require('./build/contracts/PublicResolver') 10 | const Resolver = require('./build/contracts/Resolver') 11 | const ReverseRegistrar = require('./build/contracts/ReverseRegistrar') 12 | const TestRegistrar = require('./build/contracts/TestRegistrar') 13 | const StablePriceOracle = require('./build/contracts/StablePriceOracle') 14 | const DNSRegistrar = require('./build/contracts/DNSRegistrar') 15 | const PublicSuffixList = require('./build/contracts/PublicSuffixList') 16 | const SimplePublicSuffixList = require('./build/contracts/SimplePublicSuffixList') 17 | const TLDPublicSuffixList = require('./build/contracts/TLDPublicSuffixList') 18 | 19 | const Root = require('./build/contracts/Root') 20 | const DNSSEC = require('./build/contracts/DNSSEC') 21 | const RSASHA256Algorithm = require('./build/contracts/RSASHA256Algorithm') 22 | const RSASHA1Algorithm = require('./build/contracts/RSASHA1Algorithm') 23 | const SHA256Digest = require('./build/contracts/SHA256Digest') 24 | const SHA1Digest = require('./build/contracts/SHA1Digest') 25 | const SHA1NSEC3Digest = require('./build/contracts/SHA1NSEC3Digest') 26 | 27 | module.exports = { 28 | BaseRegistrar, 29 | BaseRegistrarImplementation, 30 | ENS, 31 | ENSRegistry, 32 | ENSRegistryWithFallback, 33 | ETHRegistrarController, 34 | FIFSRegistrar, 35 | PriceOracle, 36 | PublicResolver, 37 | Resolver, 38 | ReverseRegistrar, 39 | StablePriceOracle, 40 | TestRegistrar, 41 | DNSRegistrar, 42 | PublicSuffixList, 43 | SimplePublicSuffixList, 44 | TLDPublicSuffixList, 45 | Root, 46 | DNSSEC, 47 | RSASHA256Algorithm, 48 | RSASHA1Algorithm, 49 | SHA256Digest, 50 | SHA1Digest, 51 | SHA1NSEC3Digest 52 | } 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@imperviousinc/forever-contracts", 3 | "version": "0.0.5", 4 | "description": "Forever contracts", 5 | "scripts": { 6 | "test": "npx hardhat test", 7 | "lint": "npx hardhat check", 8 | "build": "npx hardhat compile", 9 | "prepublishOnly": "yarn build", 10 | "pub": "yarn publish --access public" 11 | }, 12 | "files": [ 13 | "build/contracts/*json", 14 | "contracts/*", 15 | "artifacts/contracts/*/*/*.json" 16 | ], 17 | "main": "index.js", 18 | "devDependencies": { 19 | "@ensdomains/dnsprovejs": "^0.3.7", 20 | "@ensdomains/test-utils": "^1.3.0", 21 | "@nomiclabs/hardhat-ethers": "^2.0.2", 22 | "@nomiclabs/hardhat-solhint": "^2.0.0", 23 | "@nomiclabs/hardhat-truffle5": "^2.0.0", 24 | "@nomiclabs/hardhat-waffle": "^2.0.1", 25 | "@nomiclabs/hardhat-web3": "^2.0.0", 26 | "@openzeppelin/test-helpers": "^0.5.11", 27 | "chai": "^4.3.4", 28 | "elliptic-solidity": "^1.0.0", 29 | "ethereum-waffle": "^3.3.0", 30 | "ethers": "^5.4.0", 31 | "hardhat": "^2.2.1", 32 | "hardhat-abi-exporter": "^2.2.1", 33 | "hardhat-deploy": "^0.8.9", 34 | "hardhat-gas-reporter": "^1.0.4", 35 | "rfc4648": "^1.5.0" 36 | }, 37 | "resolutions": { 38 | "ethereum-ens": "0.8.0", 39 | "js-sha3": "0.8.0" 40 | }, 41 | "dependencies": { 42 | "@ensdomains/buffer": "^0.0.13", 43 | "@ensdomains/solsha1": "0.0.3", 44 | "@openzeppelin/contracts": "^4.1.0", 45 | "dns-packet": "^5.2.4", 46 | "dotenv": "^16.0.3" 47 | }, 48 | "directories": { 49 | "test": "test" 50 | }, 51 | "repository": { 52 | "type": "git", 53 | "url": "git+https://github.com/imperviousinc/forever-contracts.git" 54 | }, 55 | "author": "", 56 | "license": "MIT", 57 | "bugs": { 58 | "url": "https://github.com/imperviousinc/forever-contracts/issues" 59 | }, 60 | "homepage": "https://github.com/imperviousinc/forever-contracts#readme" 61 | } 62 | -------------------------------------------------------------------------------- /scripts/deploy_local.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node