diff --git a/.changeset/every-donuts-stop.md b/.changeset/every-donuts-stop.md new file mode 100644 index 00000000000..8f624eb26d6 --- /dev/null +++ b/.changeset/every-donuts-stop.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +ENS: add coinType param to reverse resolver diff --git a/packages/thirdweb/src/extensions/ens/__generated__/UniversalResolver/read/reverse.ts b/packages/thirdweb/src/extensions/ens/__generated__/UniversalResolver/read/reverse.ts index f9e4de7d1a1..a33b67961aa 100644 --- a/packages/thirdweb/src/extensions/ens/__generated__/UniversalResolver/read/reverse.ts +++ b/packages/thirdweb/src/extensions/ens/__generated__/UniversalResolver/read/reverse.ts @@ -14,14 +14,22 @@ export type ReverseParams = { type: "bytes"; name: "reverseName"; }>; + coinType: AbiParameterToPrimitiveType<{ + type: "uint256"; + name: "coinType"; + }>; }; -export const FN_SELECTOR = "0xec11c823" as const; +export const FN_SELECTOR = "0x5d78a217" as const; const FN_INPUTS = [ { name: "reverseName", type: "bytes", }, + { + name: "coinType", + type: "uint256", + }, ] as const; const FN_OUTPUTS = [ { @@ -33,9 +41,6 @@ const FN_OUTPUTS = [ { type: "address", }, - { - type: "address", - }, ] as const; /** @@ -70,7 +75,10 @@ export function isReverseSupported(availableSelectors: string[]) { * ``` */ export function encodeReverseParams(options: ReverseParams) { - return encodeAbiParameters(FN_INPUTS, [options.reverseName]); + return encodeAbiParameters(FN_INPUTS, [ + options.reverseName, + options.coinType, + ]); } /** @@ -128,6 +136,6 @@ export async function reverse(options: BaseTransactionOptions) { return readContract({ contract: options.contract, method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, - params: [options.reverseName], + params: [options.reverseName, options.coinType], }); } diff --git a/packages/thirdweb/src/extensions/ens/resolve-name.ts b/packages/thirdweb/src/extensions/ens/resolve-name.ts index 0d9c8b70303..12ea520214a 100644 --- a/packages/thirdweb/src/extensions/ens/resolve-name.ts +++ b/packages/thirdweb/src/extensions/ens/resolve-name.ts @@ -3,8 +3,6 @@ import { ethereum } from "../../chains/chain-definitions/ethereum.js"; import type { Chain } from "../../chains/types.js"; import type { ThirdwebClient } from "../../client/client.js"; import { getContract } from "../../contract/contract.js"; -import { toHex } from "../../utils/encoding/hex.js"; -import { packetToBytes } from "../../utils/ens/packetToBytes.js"; import { withCache } from "../../utils/promise/withCache.js"; import { reverse } from "./__generated__/UniversalResolver/read/reverse.js"; import { UNIVERSAL_RESOLVER_ADDRESS } from "./constants.js"; @@ -33,7 +31,9 @@ export type ResolveNameOptions = { * @extension ENS * @returns A promise that resolves to the Ethereum address. */ -export async function resolveName(options: ResolveNameOptions) { +export async function resolveName( + options: ResolveNameOptions, +): Promise { const { client, address, resolverAddress, resolverChain } = options; return withCache( @@ -44,25 +44,28 @@ export async function resolveName(options: ResolveNameOptions) { client, }); - const reverseName = toHex( - packetToBytes(`${address.toLowerCase().substring(2)}.addr.reverse`), - ); - - const [name, resolvedAddress] = await reverse({ + const [name] = await reverse({ contract, - reverseName, + reverseName: address as `0x${string}`, + coinType: 60n, }).catch((e) => { - if ("data" in e && e.data === "0x7199966d") { - return [null, address] as const; + // Re-throw verification errors so callers can detect data integrity issues + if ( + typeof e === "object" && + e !== null && + "data" in e && + typeof e.data === "string" + ) { + // ReverseAddressMismatch(string,bytes) = 0xef9c03ce + if (e.data.startsWith("0xef9c03ce")) { + throw e; + } } - throw e; + // Swallow expected "no resolver" / "no name" errors + return [null] as const; }); - if (address.toLowerCase() !== resolvedAddress.toLowerCase()) { - return null; - } - - return name; + return name || null; }, { cacheKey: `ens:name:${resolverChain?.id || 1}:${address}`,