From d6da0dd901610ce3c6b7e16aab45ad678a1772ba Mon Sep 17 00:00:00 2001 From: Ari Gibson Date: Tue, 31 May 2022 22:19:55 -0600 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8E=89=20Init=20resolveName=20with=20?= =?UTF-8?q?test=20and=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/BaseProvider.ts | 16 +++++++++ .../json-rpc-provider/resolve-name.test.ts | 33 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/providers/test/json-rpc-provider/resolve-name.test.ts diff --git a/src/providers/BaseProvider.ts b/src/providers/BaseProvider.ts index 1f4df93b..20207544 100644 --- a/src/providers/BaseProvider.ts +++ b/src/providers/BaseProvider.ts @@ -506,4 +506,20 @@ export abstract class BaseProvider { const logs = rpcLogs.map((log) => cleanLog(log, false)); return logs; } + + /** + * Resolves an ENS name to a full Ethereum address. + * + * * [Identical](/docs/api#isd) to [`ethers.provider.resolveName`](https://docs.ethers.io/v5/api/providers/provider/#Provider-ResolveName) in ethers.js + * * [Identical](/docs/api#isd) to [`web3.eth.ens.getAddress`](https://web3js.readthedocs.io/en/v1.7.3/web3-eth-ens.html#getaddress) in web3.js + * + * @param name the ENS name to resolve to an Ethereum address + * @returns the full Ethereum address matching the ENS name specified + * @example + * ```javascript + * await provider.resolveName('daws.eth'); + * // '0xc0DEAF6bD3F0c6574a6a625EF2F22f62A5150EAB' + * ``` + */ + public async resolveName(name: string): Promise {} } diff --git a/src/providers/test/json-rpc-provider/resolve-name.test.ts b/src/providers/test/json-rpc-provider/resolve-name.test.ts new file mode 100644 index 00000000..d78e405d --- /dev/null +++ b/src/providers/test/json-rpc-provider/resolve-name.test.ts @@ -0,0 +1,33 @@ +import { ethers } from 'ethers'; +import Web3 from 'web3'; +import { JsonRpcProvider } from '../../..'; +import { rpcUrls } from './../rpc-urls'; + +const rpcUrl = rpcUrls.mainnet; + +const names = ['daws.eth', 'ricmoo.eth', 'vitalik.eth']; + +describe('provider.resolveName', () => { + const essentialEthProvider = new JsonRpcProvider(rpcUrl); + const web3Provider = new Web3(rpcUrl); + const ethersProvider = new ethers.providers.StaticJsonRpcProvider(rpcUrl); + + it('should match ethers.js', async () => { + await names.forEach(async (name) => { + const [eeName, ethersName] = await Promise.all([ + essentialEthProvider.resolveName(name), + ethersProvider.resolveName(name), + ]); + expect(eeName).toBe(ethersName); + }); + }); + it('should match web3.js', async () => { + await names.forEach(async (name) => { + const [eeName, web3Name] = await Promise.all([ + essentialEthProvider.resolveName(name), + web3Provider.eth.ens.getAddress(name), + ]); + expect(eeName).toBe(web3Name); + }); + }); +}); From 7365a0e8a212545fcba48ca3062e921349eda5d3 Mon Sep 17 00:00:00 2001 From: Ari Gibson Date: Tue, 31 May 2022 23:24:26 -0600 Subject: [PATCH 2/2] Added namehash util function, failing tests --- package-lock.json | 5 +++-- package.json | 1 + src/index.ts | 2 ++ src/providers/BaseProvider.ts | 4 +++- src/utils/namehash.ts | 22 ++++++++++++++++++++++ src/utils/tests/namehash.test.ts | 19 +++++++++++++++++++ 6 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/utils/namehash.ts create mode 100644 src/utils/tests/namehash.test.ts diff --git a/package-lock.json b/package-lock.json index 7b1812ef..c12f7d21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "eslint": "^8.16.0", "eslint-plugin-jest": "^26.2.2", "eslint-plugin-jsdoc": "38.0.2", + "eth-ens-namehash": "^2.0.8", "ethers": "^5.6.8", "express": "^4.17.1", "husky": "^7.0.4", @@ -4563,7 +4564,7 @@ "node_modules/eth-ens-namehash": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", - "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", "dev": true, "dependencies": { "idna-uts46-hx": "^2.3.1", @@ -14716,7 +14717,7 @@ "eth-ens-namehash": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", - "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", "dev": true, "requires": { "idna-uts46-hx": "^2.3.1", diff --git a/package.json b/package.json index c012cba8..fc2ed439 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "eslint": "^8.16.0", "eslint-plugin-jest": "^26.2.2", "eslint-plugin-jsdoc": "38.0.2", + "eth-ens-namehash": "^2.0.8", "ethers": "^5.6.8", "express": "^4.17.1", "husky": "^7.0.4", diff --git a/src/index.ts b/src/index.ts index 6f1470a5..4706caed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,7 @@ import { splitSignature } from './utils/split-signature'; import { toChecksumAddress } from './utils/to-checksum-address'; import { toUtf8Bytes } from './utils/to-utf8-bytes'; import { weiToEther } from './utils/wei-to-ether'; +import { namehash } from './utils/namehash'; export * from './providers/types'; export * from './utils/bytes'; @@ -55,6 +56,7 @@ export { toUtf8Bytes, computeAddress, computePublicKey, + namehash, /* classes */ Contract, TinyBig, diff --git a/src/providers/BaseProvider.ts b/src/providers/BaseProvider.ts index 20207544..70eac9c2 100644 --- a/src/providers/BaseProvider.ts +++ b/src/providers/BaseProvider.ts @@ -521,5 +521,7 @@ export abstract class BaseProvider { * // '0xc0DEAF6bD3F0c6574a6a625EF2F22f62A5150EAB' * ``` */ - public async resolveName(name: string): Promise {} + public async resolveName(name: string) // : Promise + { + } } diff --git a/src/utils/namehash.ts b/src/utils/namehash.ts new file mode 100644 index 00000000..a82711a9 --- /dev/null +++ b/src/utils/namehash.ts @@ -0,0 +1,22 @@ +import { keccak256 } from './keccak256'; + +/** + * Hashs a name according to the [EIP 137](https://eips.ethereum.org/EIPS/eip-137) specification. + * Function based off of [danfinlay/eth-ens-namehash](https://github.com/danfinlay/eth-ens-namehash). + * + * @param name the name to hash + * @returns the hashed name + */ +export function namehash(name: string) { + if (!name) + return '0x0000000000000000000000000000000000000000000000000000000000000000'; + + name = name.normalize(); + const labels = name.split('.'); + let node; + for (var i = labels.length - 1; i >= 0; i--) { + const labelSha = keccak256(labels[i]); + node = keccak256(Buffer.from(node + labelSha, 'hex')); + } + return `0x${node}`; +} diff --git a/src/utils/tests/namehash.test.ts b/src/utils/tests/namehash.test.ts new file mode 100644 index 00000000..5a185971 --- /dev/null +++ b/src/utils/tests/namehash.test.ts @@ -0,0 +1,19 @@ +// @ts-ignore +import { hash as ethHash } from 'eth-ens-namehash'; +import { namehash as eeHash } from '../..'; + +const names = [ + 'faceboŠ¾k.eth', + 'facebook.eth', + '\u017F\u0323\u0307.eth', + '\u0073\u0323\u0307.eth', +]; + +describe('utils.namehash', () => { + it('should match eth-ens-namehash', () => { + names.forEach((name) => { + const [eeNamehash, ethNamehash] = [eeHash(name), ethHash(name)]; + expect(eeNamehash).toBe(ethNamehash); + }); + }); +});