From 08e2251a9417a96a14142c6524dce9b8f2063350 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 12 Nov 2025 13:12:44 +1100 Subject: [PATCH 1/2] fix: getname behaviour --- packages/ensjs/src/errors/public.ts | 25 ++++++++ .../src/functions/public/getName.test.ts | 60 +++++++++++++------ .../ensjs/src/functions/public/getName.ts | 10 +++- packages/ensjs/src/test/addTestContracts.ts | 40 +++++++------ 4 files changed, 99 insertions(+), 36 deletions(-) diff --git a/packages/ensjs/src/errors/public.ts b/packages/ensjs/src/errors/public.ts index 543592103..1f71b4304 100644 --- a/packages/ensjs/src/errors/public.ts +++ b/packages/ensjs/src/errors/public.ts @@ -1,3 +1,4 @@ +import type { Address } from 'viem' import { BaseError } from './base.js' export class CoinFormatterNotFoundError extends BaseError { @@ -29,3 +30,27 @@ export class NoRecordsSpecifiedError extends BaseError { super('No records specified') } } + +export class NameNotNormalisedError extends BaseError { + override name = 'NameNotNormalisedError' + + address: Address + resolvedName: string + coinType: number + + constructor({ + address, + resolvedName, + coinType, + }: { address: Address; resolvedName: string; coinType: number }) { + super(`Name ${resolvedName} resolved from address is not normalised`, { + metaMessages: [ + `- Resolved from address: ${address}`, + `Resolved for coinType: ${coinType}`, + ], + }) + this.address = address + this.resolvedName = resolvedName + this.coinType = coinType + } +} diff --git a/packages/ensjs/src/functions/public/getName.test.ts b/packages/ensjs/src/functions/public/getName.test.ts index 47739fe12..5c8dc163f 100644 --- a/packages/ensjs/src/functions/public/getName.test.ts +++ b/packages/ensjs/src/functions/public/getName.test.ts @@ -4,9 +4,14 @@ import { RawContractError, bytesToHex, encodeErrorResult, + labelhash, + namehash, } from 'viem' +import { writeContract } from 'viem/actions' import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest' import type { ClientWithEns } from '../../contracts/consts.js' +import { publicResolverSetAddrSnippet } from '../../contracts/publicResolver.js' +import { registrySetSubnodeRecordSnippet } from '../../contracts/registry.js' import { universalResolverErrors } from '../../contracts/universalResolver.js' import { deploymentAddresses, @@ -16,8 +21,6 @@ import { walletClient, } from '../../test/addTestContracts.js' import { packetToBytes } from '../../utils/hexEncodedName.js' -import createSubname from '../wallet/createSubname.js' -import setAddressRecord from '../wallet/setAddressRecord.js' import setPrimaryName from '../wallet/setPrimaryName.js' import getName from './getName.js' @@ -151,20 +154,26 @@ describe('getName', () => { `) }) it('should not return unnormalised name', async () => { - const tx1 = await createSubname(walletClient, { - name: 'suB.with-profile.eth', - contract: 'registry', - owner: accounts[0], - resolverAddress: deploymentAddresses.PublicResolver, - account: accounts[0], + const tx1 = await writeContract(walletClient, { + abi: registrySetSubnodeRecordSnippet, + account: accounts[2], + address: deploymentAddresses.ENSRegistry, + functionName: 'setSubnodeRecord', + args: [ + namehash('with-profile.eth'), + labelhash('suB'), + accounts[0], + deploymentAddresses.PublicResolver, + 0n, + ], }) await waitForTransaction(tx1) - const tx2 = await setAddressRecord(walletClient, { - name: 'suB.with-profile.eth', - coin: 'eth', - resolverAddress: deploymentAddresses.PublicResolver, - value: accounts[0], + const tx2 = await writeContract(walletClient, { + abi: publicResolverSetAddrSnippet, account: accounts[0], + address: deploymentAddresses.PublicResolver, + functionName: 'setAddr', + args: [namehash('suB.with-profile.eth'), 60n, accounts[0]], }) await waitForTransaction(tx2) const tx3 = await setPrimaryName(walletClient, { @@ -173,10 +182,27 @@ describe('getName', () => { }) await waitForTransaction(tx3) - const result = await getName(publicClient, { - address: accounts[0], - }) + // Should throw NameNotNormalisedError + await expect( + getName(publicClient, { + address: accounts[0], + strict: true, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [NameNotNormalisedError: Name suB.with-profile.eth resolved from address is not normalised - expect(result).toBeNull() + - Resolved from address: 0x82e01223d51Eb87e16A03E24687EDF0F294da6f1 + Resolved for coinType: 60 + + Version: @ensdomains/ensjs@1.0.0-mock.0] + `) + + // should return null when strict is false + await expect( + getName(publicClient, { + address: accounts[0], + strict: false, + }), + ).resolves.toBeNull() }) }) diff --git a/packages/ensjs/src/functions/public/getName.ts b/packages/ensjs/src/functions/public/getName.ts index 85dc434f6..ffef660bd 100644 --- a/packages/ensjs/src/functions/public/getName.ts +++ b/packages/ensjs/src/functions/public/getName.ts @@ -14,6 +14,7 @@ import { universalResolverReverseSnippet, universalResolverReverseWithGatewaysSnippet, } from '../../contracts/universalResolver.js' +import { NameNotNormalisedError } from '../../errors/public.js' import type { GenericPassthrough, TransactionRequestWithPassthrough, @@ -154,9 +155,14 @@ const decode = async ( if (!unnormalisedName) return null - const normalisedName = normalise(unnormalisedName) + if (unnormalisedName !== normalise(unnormalisedName)) + throw new NameNotNormalisedError({ + address: passthrough.address, + resolvedName: unnormalisedName, + coinType: passthrough.args[1] as number, + }) return { - name: normalisedName, + name: unnormalisedName, match: true, reverseResolverAddress, resolverAddress, diff --git a/packages/ensjs/src/test/addTestContracts.ts b/packages/ensjs/src/test/addTestContracts.ts index 17e67b4d0..73bb76c8a 100644 --- a/packages/ensjs/src/test/addTestContracts.ts +++ b/packages/ensjs/src/test/addTestContracts.ts @@ -1,7 +1,6 @@ -import { resolve } from 'node:path' import { config } from 'dotenv' +import { resolve } from 'node:path' import { - http, type Account, type Address, type Hash, @@ -13,6 +12,7 @@ import { createPublicClient, createTestClient, createWalletClient, + http, } from 'viem' import { localhost as _localhost } from 'viem/chains' @@ -104,6 +104,7 @@ export const publicClient: PublicClient = createPublicClient({ chain: localhost, transport, + cacheTime: 0, }) export const testClient: TestClient< @@ -125,18 +126,23 @@ export const walletClient: WalletClient< transport, }) -export const waitForTransaction = async (hash: Hash) => - new Promise((resolveFn, reject) => { - publicClient - .getTransactionReceipt({ hash }) - .then(resolveFn) - .catch((e) => { - if (e instanceof TransactionReceiptNotFoundError) { - setTimeout(() => { - waitForTransaction(hash).then(resolveFn) - }, 100) - } else { - reject(e) - } - }) - }) +export const waitForTransaction = async ( + hash: Hash, +): Promise => { + const receipt = await publicClient + .getTransactionReceipt({ hash }) + .catch((e) => { + if (e instanceof TransactionReceiptNotFoundError) return null + throw e + }) + if (receipt === null) { + return new Promise((resolve, reject) => { + setTimeout(() => { + waitForTransaction(hash).then(resolve).catch(reject) + }, 100) + }) + } + + if (receipt.status !== 'success') throw new Error('Transaction failed') + return receipt +} From ff5bd91695fdb93593db5efa956b768dfbcc21f8 Mon Sep 17 00:00:00 2001 From: tate Date: Wed, 12 Nov 2025 13:20:29 +1100 Subject: [PATCH 2/2] lint --- packages/ensjs/src/test/addTestContracts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ensjs/src/test/addTestContracts.ts b/packages/ensjs/src/test/addTestContracts.ts index 73bb76c8a..1c5e2e520 100644 --- a/packages/ensjs/src/test/addTestContracts.ts +++ b/packages/ensjs/src/test/addTestContracts.ts @@ -1,6 +1,7 @@ -import { config } from 'dotenv' import { resolve } from 'node:path' +import { config } from 'dotenv' import { + http, type Account, type Address, type Hash, @@ -12,7 +13,6 @@ import { createPublicClient, createTestClient, createWalletClient, - http, } from 'viem' import { localhost as _localhost } from 'viem/chains'