From 5f88387eb29984989c24b16acf523e335cb3d822 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 25 Jan 2023 13:28:22 +1100 Subject: [PATCH 1/5] added getDecodedName + added decoded name fetching in getProfile --- .../src/functions/getDecodedName.test.ts | 22 +++++++++++++ .../ensjs/src/functions/getDecodedName.ts | 32 +++++++++++++++++++ .../ensjs/src/functions/getProfile.test.ts | 7 ++++ packages/ensjs/src/functions/getProfile.ts | 15 +++++++-- packages/ensjs/src/functions/types.ts | 2 ++ packages/ensjs/src/index.ts | 4 +++ packages/ensjs/src/tests/func-imports.ts | 2 ++ packages/ensjs/src/utils/hexEncodedName.ts | 3 ++ 8 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 packages/ensjs/src/functions/getDecodedName.test.ts create mode 100644 packages/ensjs/src/functions/getDecodedName.ts diff --git a/packages/ensjs/src/functions/getDecodedName.test.ts b/packages/ensjs/src/functions/getDecodedName.test.ts new file mode 100644 index 00000000..32c1eca4 --- /dev/null +++ b/packages/ensjs/src/functions/getDecodedName.test.ts @@ -0,0 +1,22 @@ +import { ENS } from '..' +import setup from '../tests/setup' + +let ensInstance: ENS + +beforeAll(async () => { + ;({ ensInstance } = await setup()) +}) + +describe('getDecodedName', () => { + it('should return undefined for a name that is not wrapped', async () => { + const result = await ensInstance.getDecodedName('test123.eth') + expect(result).toBeUndefined() + }) + it('should return decoded name for a wrapped name with encoded labels', async () => { + const result = await ensInstance.getDecodedName( + '[9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658].wrapped-with-subnames.eth', + ) + expect(result).toBeDefined() + expect(result).toBe('test.wrapped-with-subnames.eth') + }) +}) diff --git a/packages/ensjs/src/functions/getDecodedName.ts b/packages/ensjs/src/functions/getDecodedName.ts new file mode 100644 index 00000000..11cbec35 --- /dev/null +++ b/packages/ensjs/src/functions/getDecodedName.ts @@ -0,0 +1,32 @@ +import { hexStripZeros } from '@ethersproject/bytes' +import { ENSArgs } from '..' +import { hexDecodeName } from '../utils/hexEncodedName' +import { namehash } from '../utils/normalise' + +const raw = async ({ contracts }: ENSArgs<'contracts'>, name: string) => { + const nameWrapper = await contracts?.getNameWrapper()! + + return { + to: nameWrapper.address, + data: nameWrapper.interface.encodeFunctionData('names', [namehash(name)]), + } +} + +const decode = async ({ contracts }: ENSArgs<'contracts'>, data: string) => { + if (data === null) return + const nameWrapper = await contracts?.getNameWrapper()! + try { + const result = nameWrapper.interface.decodeFunctionResult('names', data) + if (hexStripZeros(result['0']) === '0x') return + const decoded = hexDecodeName(result['0']) + return decoded + } catch (e: any) { + console.error('Error decoding name: ', e) + return + } +} + +export default { + raw, + decode, +} diff --git a/packages/ensjs/src/functions/getProfile.test.ts b/packages/ensjs/src/functions/getProfile.test.ts index bb60fe89..610061ea 100644 --- a/packages/ensjs/src/functions/getProfile.test.ts +++ b/packages/ensjs/src/functions/getProfile.test.ts @@ -119,6 +119,13 @@ describe('getProfile', () => { deploymentAddresses.LegacyPublicResolver, ) }) + it('should return the decoded name for a name with encoded labels', async () => { + const result = await ensInstance.getProfile( + '[9dd2c369a187b4e6b9c402f030e50743e619301ea62aa4c0737d4ef7e10a3d49].with-subnames.eth', + ) + expect(result).toBeDefined() + expect(result?.decodedName).toBe('xyz.with-subnames.eth') + }) it('should return undefined for an unregistered name', async () => { const result = await ensInstance.getProfile('test123123123cool.eth') expect(result).toBeUndefined() diff --git a/packages/ensjs/src/functions/getProfile.ts b/packages/ensjs/src/functions/getProfile.ts index 05c3d69d..054c7bdc 100644 --- a/packages/ensjs/src/functions/getProfile.ts +++ b/packages/ensjs/src/functions/getProfile.ts @@ -37,6 +37,7 @@ type ResolvedProfile = { createdAt: string | null address?: string name?: string | null + decodedName?: string | null match?: boolean message?: string records?: { @@ -360,6 +361,7 @@ const graphFetch = async ( const query = gqlInstance.gql` query getRecords($id: String!) { domain(id: $id) { + name isMigrated createdAt resolver { @@ -377,6 +379,7 @@ const graphFetch = async ( const customResolverQuery = gqlInstance.gql` query getRecordsWithCustomResolver($id: String!, $resolverId: String!) { domain(id: $id) { + name isMigrated createdAt } @@ -414,11 +417,12 @@ const graphFetch = async ( if (!domain) return - const { isMigrated, createdAt } = domain + const { isMigrated, createdAt, name: decodedName } = domain const returnedRecords: ProfileResponse = {} - if (!resolverResponse || !wantedRecords) return { isMigrated, createdAt } + if (!resolverResponse || !wantedRecords) + return { isMigrated, createdAt, decodedName } Object.keys(wantedRecords).forEach((key: string) => { const data = wantedRecords[key as keyof ProfileOptions] @@ -433,6 +437,7 @@ const graphFetch = async ( return { ...returnedRecords, + decodedName, isMigrated, createdAt, } @@ -486,6 +491,7 @@ const getProfileFromName = async ( ) let isMigrated: boolean | null = null let createdAt: string | null = null + let decodedName: string | null = null let result: Awaited> | null = null if (!graphResult) { if (!fallback) return @@ -506,13 +512,16 @@ const getProfileFromName = async ( const { isMigrated: _isMigrated, createdAt: _createdAt, + decodedName: _decodedName, ...wantedRecords }: { isMigrated: boolean createdAt: string + decodedName: string } & InternalProfileOptions = graphResult isMigrated = _isMigrated createdAt = _createdAt + decodedName = _decodedName let recordsWithFallback = usingOptions ? wantedRecords : (_options as InternalProfileOptions) @@ -547,7 +556,7 @@ const getProfileFromName = async ( ? "Records fetch didn't complete" : "Name doesn't have a resolver", } - return { ...result, isMigrated, createdAt, message: undefined } + return { ...result, isMigrated, createdAt, decodedName, message: undefined } } const getProfileFromAddress = async ( diff --git a/packages/ensjs/src/functions/types.ts b/packages/ensjs/src/functions/types.ts index ce308755..9836994e 100644 --- a/packages/ensjs/src/functions/types.ts +++ b/packages/ensjs/src/functions/types.ts @@ -8,6 +8,7 @@ import type commitName from './commitName' import type createSubname from './createSubname' import type deleteSubname from './deleteSubname' import type getAvailable from './getAvailable' +import type getDecodedName from './getDecodedName' import type getDNSOwner from './getDNSOwner' import type getExpiry from './getExpiry' import type { getHistory } from './getHistory' @@ -61,6 +62,7 @@ type Function = { createSubname: typeof createSubname deleteSubname: typeof deleteSubname getAvailable: typeof getAvailable + getDecodedName: typeof getDecodedName getDNSOwner: typeof getDNSOwner getExpiry: typeof getExpiry getHistory: typeof getHistory diff --git a/packages/ensjs/src/index.ts b/packages/ensjs/src/index.ts index 44cc4357..68b3f8dd 100644 --- a/packages/ensjs/src/index.ts +++ b/packages/ensjs/src/index.ts @@ -548,6 +548,10 @@ export class ENS { ['contracts'], ) + public getDecodedName = this.generateRawFunction< + FunctionTypes['getDecodedName'] + >('getDecodedName', ['contracts']) + public universalWrapper = this.generateRawFunction< FunctionTypes['universalWrapper'] >('initialGetters', ['contracts'], 'universalWrapper') diff --git a/packages/ensjs/src/tests/func-imports.ts b/packages/ensjs/src/tests/func-imports.ts index 9a869a1a..fc487d32 100644 --- a/packages/ensjs/src/tests/func-imports.ts +++ b/packages/ensjs/src/tests/func-imports.ts @@ -8,6 +8,7 @@ import commitName from '../functions/commitName' import createSubname from '../functions/createSubname' import deleteSubname from '../functions/deleteSubname' import getAvailable from '../functions/getAvailable' +import getDecodedName from '../functions/getDecodedName' import getDNSOwner from '../functions/getDNSOwner' import getExpiry from '../functions/getExpiry' import { getHistory } from '../functions/getHistory' @@ -60,6 +61,7 @@ export default { createSubname, deleteSubname, getAvailable, + getDecodedName, getDNSOwner, getExpiry, getHistory, diff --git a/packages/ensjs/src/utils/hexEncodedName.ts b/packages/ensjs/src/utils/hexEncodedName.ts index 668003c2..c82e9a2a 100644 --- a/packages/ensjs/src/utils/hexEncodedName.ts +++ b/packages/ensjs/src/utils/hexEncodedName.ts @@ -2,3 +2,6 @@ import packet from 'dns-packet' export const hexEncodeName = (name: string) => `0x${packet.name.encode(name).toString('hex')}` + +export const hexDecodeName = (hex: string): string => + packet.name.decode(Buffer.from(hex.slice(2), 'hex')).toString() From 21f3d7dfdec26a7c7a46438e792c5b74e75bf440 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 25 Jan 2023 15:20:34 +1100 Subject: [PATCH 2/5] change getDecoded to getDecrypted + add graph fallback --- packages/ensjs/deploy/00_register_legacy.ts | 14 ++ .../src/functions/getDecodedName.test.ts | 22 ---- .../ensjs/src/functions/getDecodedName.ts | 32 ----- .../src/functions/getDecryptedName.test.ts | 49 +++++++ .../ensjs/src/functions/getDecryptedName.ts | 122 ++++++++++++++++++ packages/ensjs/src/functions/getNames.test.ts | 2 +- packages/ensjs/src/functions/types.ts | 4 +- packages/ensjs/src/index.ts | 6 +- packages/ensjs/src/tests/func-imports.ts | 4 +- 9 files changed, 193 insertions(+), 62 deletions(-) delete mode 100644 packages/ensjs/src/functions/getDecodedName.test.ts delete mode 100644 packages/ensjs/src/functions/getDecodedName.ts create mode 100644 packages/ensjs/src/functions/getDecryptedName.test.ts create mode 100644 packages/ensjs/src/functions/getDecryptedName.ts diff --git a/packages/ensjs/deploy/00_register_legacy.ts b/packages/ensjs/deploy/00_register_legacy.ts index d0ae9caf..f6fecd3b 100644 --- a/packages/ensjs/deploy/00_register_legacy.ts +++ b/packages/ensjs/deploy/00_register_legacy.ts @@ -233,6 +233,20 @@ const names: { { label: 'addr', namedOwner: 'owner2' }, ], }, + { + label: 'with-unknown-subnames', + namedOwner: 'owner', + namedAddr: 'owner', + subnames: [ + { label: 'aaa123', namedOwner: 'owner2' }, + { label: 'not-known', namedOwner: 'owner2' }, + ], + }, + { + label: 'aaa123', + namedOwner: 'owner', + namedAddr: 'owner', + }, { label: 'with-type-1-abi', namedOwner: 'owner', diff --git a/packages/ensjs/src/functions/getDecodedName.test.ts b/packages/ensjs/src/functions/getDecodedName.test.ts deleted file mode 100644 index 32c1eca4..00000000 --- a/packages/ensjs/src/functions/getDecodedName.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ENS } from '..' -import setup from '../tests/setup' - -let ensInstance: ENS - -beforeAll(async () => { - ;({ ensInstance } = await setup()) -}) - -describe('getDecodedName', () => { - it('should return undefined for a name that is not wrapped', async () => { - const result = await ensInstance.getDecodedName('test123.eth') - expect(result).toBeUndefined() - }) - it('should return decoded name for a wrapped name with encoded labels', async () => { - const result = await ensInstance.getDecodedName( - '[9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658].wrapped-with-subnames.eth', - ) - expect(result).toBeDefined() - expect(result).toBe('test.wrapped-with-subnames.eth') - }) -}) diff --git a/packages/ensjs/src/functions/getDecodedName.ts b/packages/ensjs/src/functions/getDecodedName.ts deleted file mode 100644 index 11cbec35..00000000 --- a/packages/ensjs/src/functions/getDecodedName.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { hexStripZeros } from '@ethersproject/bytes' -import { ENSArgs } from '..' -import { hexDecodeName } from '../utils/hexEncodedName' -import { namehash } from '../utils/normalise' - -const raw = async ({ contracts }: ENSArgs<'contracts'>, name: string) => { - const nameWrapper = await contracts?.getNameWrapper()! - - return { - to: nameWrapper.address, - data: nameWrapper.interface.encodeFunctionData('names', [namehash(name)]), - } -} - -const decode = async ({ contracts }: ENSArgs<'contracts'>, data: string) => { - if (data === null) return - const nameWrapper = await contracts?.getNameWrapper()! - try { - const result = nameWrapper.interface.decodeFunctionResult('names', data) - if (hexStripZeros(result['0']) === '0x') return - const decoded = hexDecodeName(result['0']) - return decoded - } catch (e: any) { - console.error('Error decoding name: ', e) - return - } -} - -export default { - raw, - decode, -} diff --git a/packages/ensjs/src/functions/getDecryptedName.test.ts b/packages/ensjs/src/functions/getDecryptedName.test.ts new file mode 100644 index 00000000..3bd080eb --- /dev/null +++ b/packages/ensjs/src/functions/getDecryptedName.test.ts @@ -0,0 +1,49 @@ +import { ENS } from '../index' +import setup from '../tests/setup' + +let ensInstance: ENS + +beforeAll(async () => { + ;({ ensInstance } = await setup()) +}) + +describe('getDecryptedName', () => { + it('should decrypt a wrapped name with on-chain data', async () => { + const result = await ensInstance.getDecryptedName( + '[9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658].wrapped-with-subnames.eth', + ) + expect(result).toBeDefined() + expect(result).toBe('test.wrapped-with-subnames.eth') + }) + it('should decrypt a name via namehash lookup', async () => { + const result = await ensInstance.getDecryptedName( + '[f81b517a242b218999ec8eec0ea6e2ddbef2a367a14e93f4a32a39e260f686ad].eth', + ) + expect(result).toBeDefined() + expect(result).toBe('test123.eth') + }) + it('should decrypt a name via labelhash lookup', async () => { + const result = await ensInstance.getDecryptedName( + '[4d2920c35d976f8478bee89292ba85074d1bbea73f1571363b41a1629e1bac68].with-unknown-subnames.eth', + ) + expect(result).toBeDefined() + expect(result).toBe('aaa123.with-unknown-subnames.eth') + }) + it('should partially decrypt a name when allowIncomplete is true', async () => { + const result = await ensInstance.getDecryptedName( + '[7bffb6e3ebf801bbc438fea5c11d957ba49978bdc8d52b71cba974139d22edea].[6c14e1739568670447af1d5af8a571008f7a582068af18bcd7ac2dbc13bb37c1].eth', + true, + ) + expect(result).toBeDefined() + expect(result).toBe( + '[7bffb6e3ebf801bbc438fea5c11d957ba49978bdc8d52b71cba974139d22edea].with-unknown-subnames.eth', + ) + }) + it('should not partially decrypt a name when allowIncomplete is false', async () => { + const result = await ensInstance.getDecryptedName( + '[7bffb6e3ebf801bbc438fea5c11d957ba49978bdc8d52b71cba974139d22edea].[6c14e1739568670447af1d5af8a571008f7a582068af18bcd7ac2dbc13bb37c1].eth', + false, + ) + expect(result).toBeUndefined() + }) +}) diff --git a/packages/ensjs/src/functions/getDecryptedName.ts b/packages/ensjs/src/functions/getDecryptedName.ts new file mode 100644 index 00000000..646f3eae --- /dev/null +++ b/packages/ensjs/src/functions/getDecryptedName.ts @@ -0,0 +1,122 @@ +import { hexStripZeros } from '@ethersproject/bytes' +import { ENSArgs } from '../index' +import { hexDecodeName } from '../utils/hexEncodedName' +import { + checkIsDecrypted, + decodeLabelhash, + isEncodedLabelhash, +} from '../utils/labels' +import { namehash } from '../utils/normalise' + +const raw = async ( + { contracts }: ENSArgs<'contracts'>, + name: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + allowIncomplete?: boolean, +) => { + const nameWrapper = await contracts?.getNameWrapper()! + + return { + to: nameWrapper.address, + data: nameWrapper.interface.encodeFunctionData('names', [namehash(name)]), + } +} + +const generateNameQuery = (labels: string[]) => { + let query = '' + + for (let i = 0; i < labels.length; i += 1) { + const label = labels[i] + if (isEncodedLabelhash(label)) { + query += ` + label${i}: domains(where: { labelhash: "${decodeLabelhash( + label, + ).toLowerCase()}", labelName_not: null }) { + labelName + } + ` + } + } + + return query +} + +const encodedLabelRegex = /\[[a-fA-F0-9]{64}\]/g +const getEncodedLabelLength = (name: string) => + name.match(encodedLabelRegex)?.length || 0 + +const decode = async ( + { contracts, gqlInstance }: ENSArgs<'contracts' | 'gqlInstance'>, + data: string, + name: string, + allowIncomplete: boolean = false, +) => { + if (data !== null) { + const nameWrapper = await contracts?.getNameWrapper()! + try { + const result = nameWrapper.interface.decodeFunctionResult('names', data) + if (hexStripZeros(result['0']) !== '0x') { + const decoded = hexDecodeName(result['0']) + if (decoded && decoded !== '.') return decoded + } + } catch (e: any) { + console.error('Error decoding name: ', e) + return + } + } + + if (checkIsDecrypted(name)) return name + // if the name isn't wrapped, try to fetch the name from an existing Domain entity + // also try to fetch the label names from any Domain entities that have a corresponding labelhash + const labels = name.split('.') + const decryptedNameQuery = gqlInstance.gql` + query decodedName($id: String!) { + namehashLookup: domains(where: { id: $id }) { + name + } + ${generateNameQuery(labels)} + } + ` + + const decryptedNameResult = await gqlInstance.client.request( + decryptedNameQuery, + { + id: namehash(name), + }, + ) + + if (!decryptedNameResult) return + + const namehashLookupResult = decryptedNameResult?.namehashLookup[0]?.name + let bestResult: string | undefined = namehashLookupResult + if (namehashLookupResult && checkIsDecrypted(namehashLookupResult)) { + return namehashLookupResult + } + + const { namehashLookup: _, ...labelNames } = decryptedNameResult + if (Object.keys(labelNames).length !== 0) { + for (const [key, value] of Object.entries<[{ labelName?: string }]>( + labelNames, + )) { + if (value.length && value[0].labelName) { + labels[parseInt(key.replace('label', ''))] = value[0].labelName + } + } + const labelLookupResult = labels.join('.') + if ( + !namehashLookupResult || + getEncodedLabelLength(namehashLookupResult) > + getEncodedLabelLength(labelLookupResult) + ) + bestResult = labelLookupResult + } + + if (!bestResult || (!allowIncomplete && !checkIsDecrypted(bestResult))) return + + return bestResult +} + +export default { + raw, + decode, +} diff --git a/packages/ensjs/src/functions/getNames.test.ts b/packages/ensjs/src/functions/getNames.test.ts index e4469684..2e9696ce 100644 --- a/packages/ensjs/src/functions/getNames.test.ts +++ b/packages/ensjs/src/functions/getNames.test.ts @@ -29,7 +29,7 @@ const letterItems = [ ] const domainLetterItems = [ - '[', + ...Array(3).fill('['), 'x', 'w', 't', diff --git a/packages/ensjs/src/functions/types.ts b/packages/ensjs/src/functions/types.ts index 9836994e..c100b0dd 100644 --- a/packages/ensjs/src/functions/types.ts +++ b/packages/ensjs/src/functions/types.ts @@ -8,7 +8,7 @@ import type commitName from './commitName' import type createSubname from './createSubname' import type deleteSubname from './deleteSubname' import type getAvailable from './getAvailable' -import type getDecodedName from './getDecodedName' +import type getDecryptedName from './getDecryptedName' import type getDNSOwner from './getDNSOwner' import type getExpiry from './getExpiry' import type { getHistory } from './getHistory' @@ -62,7 +62,7 @@ type Function = { createSubname: typeof createSubname deleteSubname: typeof deleteSubname getAvailable: typeof getAvailable - getDecodedName: typeof getDecodedName + getDecryptedName: typeof getDecryptedName getDNSOwner: typeof getDNSOwner getExpiry: typeof getExpiry getHistory: typeof getHistory diff --git a/packages/ensjs/src/index.ts b/packages/ensjs/src/index.ts index 68b3f8dd..352ef699 100644 --- a/packages/ensjs/src/index.ts +++ b/packages/ensjs/src/index.ts @@ -548,9 +548,9 @@ export class ENS { ['contracts'], ) - public getDecodedName = this.generateRawFunction< - FunctionTypes['getDecodedName'] - >('getDecodedName', ['contracts']) + public getDecryptedName = this.generateRawFunction< + FunctionTypes['getDecryptedName'] + >('getDecryptedName', ['contracts', 'gqlInstance']) public universalWrapper = this.generateRawFunction< FunctionTypes['universalWrapper'] diff --git a/packages/ensjs/src/tests/func-imports.ts b/packages/ensjs/src/tests/func-imports.ts index fc487d32..d72b9cbe 100644 --- a/packages/ensjs/src/tests/func-imports.ts +++ b/packages/ensjs/src/tests/func-imports.ts @@ -8,7 +8,7 @@ import commitName from '../functions/commitName' import createSubname from '../functions/createSubname' import deleteSubname from '../functions/deleteSubname' import getAvailable from '../functions/getAvailable' -import getDecodedName from '../functions/getDecodedName' +import getDecryptedName from '../functions/getDecryptedName' import getDNSOwner from '../functions/getDNSOwner' import getExpiry from '../functions/getExpiry' import { getHistory } from '../functions/getHistory' @@ -61,7 +61,7 @@ export default { createSubname, deleteSubname, getAvailable, - getDecodedName, + getDecryptedName, getDNSOwner, getExpiry, getHistory, From 392403781b6e0b8b5c90ec59b12e0e21202fc885 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 25 Jan 2023 15:21:48 +1100 Subject: [PATCH 3/5] change getProfile ref to decryptedName --- .../ensjs/src/functions/getProfile.test.ts | 2 +- packages/ensjs/src/functions/getProfile.ts | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/ensjs/src/functions/getProfile.test.ts b/packages/ensjs/src/functions/getProfile.test.ts index 610061ea..61d7a234 100644 --- a/packages/ensjs/src/functions/getProfile.test.ts +++ b/packages/ensjs/src/functions/getProfile.test.ts @@ -124,7 +124,7 @@ describe('getProfile', () => { '[9dd2c369a187b4e6b9c402f030e50743e619301ea62aa4c0737d4ef7e10a3d49].with-subnames.eth', ) expect(result).toBeDefined() - expect(result?.decodedName).toBe('xyz.with-subnames.eth') + expect(result?.decryptedName).toBe('xyz.with-subnames.eth') }) it('should return undefined for an unregistered name', async () => { const result = await ensInstance.getProfile('test123123123cool.eth') diff --git a/packages/ensjs/src/functions/getProfile.ts b/packages/ensjs/src/functions/getProfile.ts index 054c7bdc..c6d6fdd0 100644 --- a/packages/ensjs/src/functions/getProfile.ts +++ b/packages/ensjs/src/functions/getProfile.ts @@ -37,7 +37,7 @@ type ResolvedProfile = { createdAt: string | null address?: string name?: string | null - decodedName?: string | null + decryptedName?: string | null match?: boolean message?: string records?: { @@ -417,12 +417,12 @@ const graphFetch = async ( if (!domain) return - const { isMigrated, createdAt, name: decodedName } = domain + const { isMigrated, createdAt, name: decryptedName } = domain const returnedRecords: ProfileResponse = {} if (!resolverResponse || !wantedRecords) - return { isMigrated, createdAt, decodedName } + return { isMigrated, createdAt, decryptedName } Object.keys(wantedRecords).forEach((key: string) => { const data = wantedRecords[key as keyof ProfileOptions] @@ -437,7 +437,7 @@ const graphFetch = async ( return { ...returnedRecords, - decodedName, + decryptedName, isMigrated, createdAt, } @@ -491,7 +491,7 @@ const getProfileFromName = async ( ) let isMigrated: boolean | null = null let createdAt: string | null = null - let decodedName: string | null = null + let decryptedName: string | null = null let result: Awaited> | null = null if (!graphResult) { if (!fallback) return @@ -512,16 +512,16 @@ const getProfileFromName = async ( const { isMigrated: _isMigrated, createdAt: _createdAt, - decodedName: _decodedName, + decryptedName: _decryptedName, ...wantedRecords }: { isMigrated: boolean createdAt: string - decodedName: string + decryptedName: string } & InternalProfileOptions = graphResult isMigrated = _isMigrated createdAt = _createdAt - decodedName = _decodedName + decryptedName = _decryptedName let recordsWithFallback = usingOptions ? wantedRecords : (_options as InternalProfileOptions) @@ -556,7 +556,7 @@ const getProfileFromName = async ( ? "Records fetch didn't complete" : "Name doesn't have a resolver", } - return { ...result, isMigrated, createdAt, decodedName, message: undefined } + return { ...result, isMigrated, createdAt, decryptedName, message: undefined } } const getProfileFromAddress = async ( From 2a2b55dda38cf63dc6e24312ce78731b3a1586f2 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 25 Jan 2023 15:49:02 +1100 Subject: [PATCH 4/5] label save tweak --- packages/ensjs/src/utils/labels.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/ensjs/src/utils/labels.ts b/packages/ensjs/src/utils/labels.ts index 5b220fb6..bf02ea39 100644 --- a/packages/ensjs/src/utils/labels.ts +++ b/packages/ensjs/src/utils/labels.ts @@ -64,21 +64,19 @@ export function saveLabel(label: string) { export function saveName(name: string) { const nameArray = name.split('.') - nameArray.forEach((label: any) => { - saveLabel(label) - }) + for (const label of nameArray) { + if (!isEncodedLabelhash(label)) { + saveLabel(label) + } + } } -// eslint-disable-next-line consistent-return -export function checkLabel(hash: string): string | undefined { +export function checkLabel(hash: string): string { const labels = getLabels() if (isEncodedLabelhash(hash)) { - return labels[decodeLabelhash(hash)] - } - - if (hash.startsWith('0x')) { - return labels[`${hash.slice(2)}`] + return labels[decodeLabelhash(hash)] || hash } + return hash } export function encodeLabel(label: any) { @@ -101,7 +99,7 @@ export function checkIsDecrypted(string: string | string[]) { export function decryptName(name: string) { return name .split('.') - .map((label: any) => checkLabel(label) || label) + .map((label: any) => checkLabel(label)) .join('.') } From 41f807ae26953214452dffbbba6cfca927d08546 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 25 Jan 2023 16:25:48 +1100 Subject: [PATCH 5/5] export getEncryptedLabelAmount --- packages/ensjs/src/functions/getDecryptedName.ts | 9 +++------ packages/ensjs/src/utils/labels.ts | 4 ++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/ensjs/src/functions/getDecryptedName.ts b/packages/ensjs/src/functions/getDecryptedName.ts index 646f3eae..f52c244f 100644 --- a/packages/ensjs/src/functions/getDecryptedName.ts +++ b/packages/ensjs/src/functions/getDecryptedName.ts @@ -4,6 +4,7 @@ import { hexDecodeName } from '../utils/hexEncodedName' import { checkIsDecrypted, decodeLabelhash, + getEncryptedLabelAmount, isEncodedLabelhash, } from '../utils/labels' import { namehash } from '../utils/normalise' @@ -41,10 +42,6 @@ const generateNameQuery = (labels: string[]) => { return query } -const encodedLabelRegex = /\[[a-fA-F0-9]{64}\]/g -const getEncodedLabelLength = (name: string) => - name.match(encodedLabelRegex)?.length || 0 - const decode = async ( { contracts, gqlInstance }: ENSArgs<'contracts' | 'gqlInstance'>, data: string, @@ -105,8 +102,8 @@ const decode = async ( const labelLookupResult = labels.join('.') if ( !namehashLookupResult || - getEncodedLabelLength(namehashLookupResult) > - getEncodedLabelLength(labelLookupResult) + getEncryptedLabelAmount(namehashLookupResult) > + getEncryptedLabelAmount(labelLookupResult) ) bestResult = labelLookupResult } diff --git a/packages/ensjs/src/utils/labels.ts b/packages/ensjs/src/utils/labels.ts index bf02ea39..a7c864f7 100644 --- a/packages/ensjs/src/utils/labels.ts +++ b/packages/ensjs/src/utils/labels.ts @@ -117,3 +117,7 @@ export function checkLocalStorageSize() { ? `${3 + (allStrings.length * 16) / (8 * 1024)} KB` : 'Empty (0 KB)' } + +const encodedLabelRegex = /\[[a-fA-F0-9]{64}\]/g +export const getEncryptedLabelAmount = (name: string) => + name.match(encodedLabelRegex)?.length || 0