import { BaseError, decodeFunctionResult, encodeFunctionData, getAddress, toHex, type Address, type Hex, } from 'viem' import type { ClientWithPns } from '../../contracts/consts.js' import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' import { universalResolverReverseSnippet, universalResolverReverseWithGatewaysSnippet, } from '../../contracts/universalResolver.js' import type { GenericPassthrough, TransactionRequestWithPassthrough, } from '../../types.js' import { checkSafeUniversalResolverData } from '../../utils/checkSafeUniversalResolverData.js' import { generateFunction, type GeneratedFunction, } from '../../utils/generateFunction.js' import { packetToBytes } from '../../utils/hexEncodedName.js' import { normalise } from '../../utils/normalise.js' export type GetNameParameters = { /** Address to get name for */ address: Address /** Whether or not to allow mismatched forward resolution */ allowMismatch?: boolean /** Whether or not to throw decoding errors */ strict?: boolean /** Batch gateway URLs to use for resolving CCIP-read requests. */ gatewayUrls?: string[] } export type GetNameReturnType = { /** Primary name for address */ name: string /** Indicates if forward resolution for name matches address */ match: boolean /** Resolver address for reverse node */ reverseResolverAddress: Address /** Resolver address for resolved name */ resolverAddress: Address } const encode = ( client: ClientWithPns, { address, gatewayUrls }: Omit, ): TransactionRequestWithPassthrough => { const reverseNode = `${address.toLowerCase().substring(2)}.addr.reverse` const to = getChainContractAddress({ client, contract: 'ensUniversalResolver', }) const args = [toHex(packetToBytes(reverseNode))] as const return { to, ...(gatewayUrls?.length ? { data: encodeFunctionData({ abi: universalResolverReverseWithGatewaysSnippet, functionName: 'reverse', args: [...args, gatewayUrls] as const, }), passthrough: { args: [...args, gatewayUrls], address: to, }, } : { data: encodeFunctionData({ abi: universalResolverReverseSnippet, functionName: 'reverse', args, }), passthrough: { args, address: to, }, }), } } const decode = async ( _client: ClientWithPns, data: Hex | BaseError, passthrough: GenericPassthrough, { address, allowMismatch, strict, gatewayUrls }: GetNameParameters, ): Promise => { const isSafe = checkSafeUniversalResolverData(data, { strict, abi: gatewayUrls ? universalResolverReverseWithGatewaysSnippet : universalResolverReverseSnippet, args: passthrough.args, functionName: 'reverse', address: passthrough.address, }) if (!isSafe) return null try { const [ unnormalisedName, forwardResolvedAddress, reverseResolverAddress, resolverAddress, ] = decodeFunctionResult({ abi: universalResolverReverseSnippet, functionName: 'reverse', data, }) if (!unnormalisedName) return null const match = getAddress(forwardResolvedAddress) === getAddress(address) if (!match && !allowMismatch) return null const normalisedName = normalise(unnormalisedName) return { name: normalisedName, match, reverseResolverAddress, resolverAddress, } } catch (error) { if (strict) throw error return null } } type BatchableFunctionObject = GeneratedFunction /** * Gets the primary name for an address * @param client - {@link ClientWithPns} * @param parameters - {@link GetNameParameters} * @returns Name data object, or `null` if no primary name is set. {@link GetNameReturnType} * * @example * import { createPublicClient, http } from 'viem' * import { mainnet } from 'viem/chains' * import { addPnsContracts } from '@pnsdomains/pnsjs' * import { getName } from '@pnsdomains/pnsjs/public' * * const client = createPublicClient({ * chain: addPnsContracts(mainnet), * transport: http(), * }) * const result = await getName(client, { address: '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' }) * // { name: 'nick.pls', match: true, reverseResolverAddress: '0xa2c122be93b0074270ebee7f6b7292c7deb45047', resolverAddress: '0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41' } */ const getName = generateFunction({ encode, decode }) as (( client: ClientWithPns, { address, allowMismatch, strict }: GetNameParameters, ) => Promise) & BatchableFunctionObject export default getName