Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/getname-enforce-normalization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ensdomains/ensjs": patch
---

`getName` now enforces normalization: it only returns a primary name if the value returned by reverse resolution is already in its normalised form, returning `null` otherwise (instead of silently coercing it). This mirrors viem's `getEnsName` behaviour (wevm/viem#4756) and brings v4 in line with v5 (WEB-533). The normalization check is applied to both the matching and `allowMismatch` paths.
67 changes: 66 additions & 1 deletion packages/ensjs/src/functions/public/getName.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import {
RawContractError,
bytesToHex,
encodeErrorResult,
encodeFunctionResult,
zeroAddress,
} from 'viem'
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'
import type { ClientWithEns } from '../../contracts/consts.js'
import { universalResolverErrors } from '../../contracts/universalResolver.js'
import {
universalResolverErrors,
universalResolverReverseSnippet,
} from '../../contracts/universalResolver.js'
import {
deploymentAddresses,
publicClient,
Expand Down Expand Up @@ -150,6 +155,66 @@ describe('getName', () => {
Version: viem@2.37.12]
`)
})
it('should return null for an unnormalised name in the match path', async () => {
const result = await getName.decode(
{} as ClientWithEns,
encodeFunctionResult({
abi: universalResolverReverseSnippet,
functionName: 'reverse',
result: ['Nick.eth', zeroAddress, zeroAddress],
}),
{
address: '0x1234567890abcdef',
args: ['0x', 60n],
},
{
address: accounts[0],
strict: false,
},
)
expect(result).toBeNull()
})
it('should return a normalised match unchanged', async () => {
const result = await getName.decode(
{} as ClientWithEns,
encodeFunctionResult({
abi: universalResolverReverseSnippet,
functionName: 'reverse',
result: ['nick.eth', zeroAddress, zeroAddress],
}),
{
address: '0x1234567890abcdef',
args: ['0x', 60n],
},
{
address: accounts[0],
strict: false,
},
)
expect(result).toMatchObject({ name: 'nick.eth', match: true })
})
it('should return null for an unnormalised name in the mismatch path', async () => {
const result = await getName.decode(
{} as ClientWithEns,
new RawContractError({
data: encodeErrorResult({
abi: universalResolverErrors,
errorName: 'ReverseAddressMismatch',
args: ['Nick.eth', accounts[0]],
}),
}),
{
address: '0x1234567890abcdef',
args: ['0x', 60n],
},
{
address: accounts[0],
allowMismatch: true,
strict: false,
},
)
expect(result).toBeNull()
})
it('should not return unnormalised name', async () => {
const tx1 = await createSubname(walletClient, {
name: 'suB.with-profile.eth',
Expand Down
25 changes: 21 additions & 4 deletions packages/ensjs/src/functions/public/getName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
encodeFunctionData,
zeroAddress,
} from 'viem'
import { normalize } from 'viem/ens'
import type { ClientWithEns } from '../../contracts/consts.js'
import { getChainContractAddress } from '../../contracts/getChainContractAddress.js'
import {
Expand All @@ -24,7 +25,21 @@
generateFunction,
} from '../../utils/generateFunction.js'
import { getRevertErrorData } from '../../utils/getRevertErrorData.js'
import { normalise } from '../../utils/normalise.js'

/**
* Checks whether a name is already normalised, without coercing it.
*
* Mirrors viem's `getEnsName` normalization enforcement (wevm/viem#4756):
* a name is only returned if it is already in its normalised form. Returns
* `false` if the name cannot be normalised (i.e. `normalize` throws).
*/
const isNormalised = (name: string): boolean => {
try {
return name === normalize(name)
} catch {
return false
}
}

type GetNameCoinTypeParameters = {
coinType: number
Expand Down Expand Up @@ -114,7 +129,7 @@
data: Hex | BaseError,
passthrough: GenericPassthrough,
{ allowMismatch, strict, gatewayUrls }: GetNameParameters,
): Promise<GetNameReturnType | null> => {

Check failure on line 132 in packages/ensjs/src/functions/public/getName.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=ensdomains_ensjs-v3&issues=AZ76U-SD1YE0NPfYny6o&open=AZ76U-SD1YE0NPfYny6o&pullRequest=345
const isSafe = checkSafeUniversalResolverData(data, {
strict,
abi: gatewayUrls
Expand All @@ -134,8 +149,10 @@
data: errorData,
})
if (decodedError.errorName !== 'ReverseAddressMismatch') return null
const [name] = decodedError.args
if (!isNormalised(name)) return null
return {
name: decodedError.args[0],
name,
match: false,
reverseResolverAddress: zeroAddress,
resolverAddress: zeroAddress,
Expand All @@ -155,9 +172,9 @@

if (!unnormalisedName) return null

const normalisedName = normalise(unnormalisedName)
if (!isNormalised(unnormalisedName)) return null
return {
name: normalisedName,
name: unnormalisedName,
match: true,
reverseResolverAddress,
resolverAddress,
Expand Down
Loading