diff --git a/packages/ensjs/src/actions/wallet/setResolver.test.ts b/packages/ensjs/src/actions/wallet/setResolver.test.ts index 5c9e9e33..08807698 100644 --- a/packages/ensjs/src/actions/wallet/setResolver.test.ts +++ b/packages/ensjs/src/actions/wallet/setResolver.test.ts @@ -1,4 +1,4 @@ -import type { Address, Hex } from 'viem' +import { type Address, type Hex, zeroAddress } from 'viem' import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' import { publicClient, @@ -67,6 +67,18 @@ it('should error if unknown contract', async () => { ).rejects.toThrow('Unknown contract: random') }) +it.skip('should allow specifying a custom registry address', async () => { + const tx = await setResolver(walletClient, { + name: 'wrapped.eth', + registryAddress: zeroAddress, + resolverAddress: '0xAEfF4f4d8e2cB51854BEa2244B3C5Fb36b41C7fC', + account: accounts[1], + }) + expect(tx).toBeTruthy() + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') +}) + // it('should return a transaction for a name and set successfully', async () => { // const tx = await setName(walletClient, { // name: 'test123.eth', diff --git a/packages/ensjs/src/actions/wallet/setResolver.ts b/packages/ensjs/src/actions/wallet/setResolver.ts index 814626b0..fba87511 100644 --- a/packages/ensjs/src/actions/wallet/setResolver.ts +++ b/packages/ensjs/src/actions/wallet/setResolver.ts @@ -7,6 +7,7 @@ import type { WriteContractParameters, WriteContractReturnType, } from 'viem' +import { isAddress, labelhash } from 'viem' import { writeContract } from 'viem/actions' import { getAction } from 'viem/utils' import { @@ -14,6 +15,7 @@ import { getChainContractAddress, type RequireClientContracts, } from '../../clients/chain.js' +import { standardRegistrySetResolverSnippet } from '../../contracts/namechain/standardRegistry.js' import { nameWrapperSetResolverSnippet } from '../../contracts/nameWrapper.js' import { registrySetResolverSnippet } from '../../contracts/registry.js' import type { ErrorType } from '../../errors/utils.js' @@ -25,14 +27,25 @@ import { } from '../../utils/clientWithOverrides.js' import { type NamehashErrorType, namehash } from '../../utils/name/namehash.js' +type SetResolverContractParameters = { + /** Contract to set resolver on - can be 'registry', 'nameWrapper', or an address of a namechain registry */ + contract?: 'registry' | 'nameWrapper' + registryAddress?: never +} + +type SetResolverRegistryAddressParameters = { + /** Contract to set resolver on - can be 'registry', 'nameWrapper', or an address of a namechain registry */ + contract?: never + registryAddress: Address +} + export type SetResolverWriteParametersParameters = { /** Name to set resolver for */ name: string - /** Contract to set resolver on */ - contract: 'registry' | 'nameWrapper' + /** Resolver address to set */ resolverAddress: Address -} +} & (SetResolverContractParameters | SetResolverRegistryAddressParameters) export type SetResolverWriteParametersReturnType = ReturnType< typeof setResolverWriteParameters @@ -56,42 +69,76 @@ export const setResolverWriteParameters = < 'ensNameWrapper' | 'ensRegistry', account >, - { name, contract, resolverAddress }: SetResolverWriteParametersParameters, + { + name, + contract, + resolverAddress, + registryAddress, + }: SetResolverWriteParametersParameters, ) => { ASSERT_NO_TYPE_ERROR(client) - if (contract !== 'registry' && contract !== 'nameWrapper') + if (!isAddress(resolverAddress)) { + throw new Error(`Invalid resolver address: ${resolverAddress}`) + } + + // Handle legacy contracts + if (contract === 'registry' || contract === 'nameWrapper') { + const address = getChainContractAddress({ + chain: client.chain, + contract: contract === 'nameWrapper' ? 'ensNameWrapper' : 'ensRegistry', + }) + + const args = [namehash(name), resolverAddress] as const + const functionName = 'setResolver' + + const baseParams = { + address, + functionName, + args, + chain: client.chain, + account: client.account, + } as const + + if (contract === 'nameWrapper') + return { + ...baseParams, + abi: nameWrapperSetResolverSnippet, + } as const satisfies WriteContractParameters< + typeof nameWrapperSetResolverSnippet + > + + return { + ...baseParams, + abi: registrySetResolverSnippet, + } as const satisfies WriteContractParameters< + typeof registrySetResolverSnippet + > + } + + // Handle namechain contracts + if (registryAddress && !isAddress(registryAddress)) { throw new Error(`Unknown contract: ${contract}`) + } - const address = getChainContractAddress({ - chain: client.chain, - contract: contract === 'nameWrapper' ? 'ensNameWrapper' : 'ensRegistry', - }) + const label = name.split('.')[0] + const tokenId = BigInt(labelhash(label)) - const args = [namehash(name), resolverAddress] as const - const functionName = 'setResolver' + const args = [tokenId, resolverAddress] as const const baseParams = { - address, - functionName, + address: registryAddress!, + functionName: 'setResolver', args, chain: client.chain, account: client.account, } as const - if (contract === 'nameWrapper') - return { - ...baseParams, - abi: nameWrapperSetResolverSnippet, - } as const satisfies WriteContractParameters< - typeof nameWrapperSetResolverSnippet - > - return { ...baseParams, - abi: registrySetResolverSnippet, + abi: standardRegistrySetResolverSnippet, } as const satisfies WriteContractParameters< - typeof registrySetResolverSnippet + typeof standardRegistrySetResolverSnippet > } diff --git a/packages/ensjs/src/contracts/index.ts b/packages/ensjs/src/contracts/index.ts index 9783f207..357b383c 100644 --- a/packages/ensjs/src/contracts/index.ts +++ b/packages/ensjs/src/contracts/index.ts @@ -46,6 +46,7 @@ export { multicallGetCurrentBlockTimestampSnippet, multicallTryAggregateSnippet, } from './multicall.js' +export { standardRegistrySetResolverSnippet } from './namechain/standardRegistry.js' export { nameWrapperErrors, nameWrapperGetDataSnippet, diff --git a/packages/ensjs/src/contracts/namechain/standardRegistry.ts b/packages/ensjs/src/contracts/namechain/standardRegistry.ts new file mode 100644 index 00000000..4a2c45be --- /dev/null +++ b/packages/ensjs/src/contracts/namechain/standardRegistry.ts @@ -0,0 +1,20 @@ +// Namechain contract ABI snippets + +export const standardRegistrySetResolverSnippet = [ + { + inputs: [ + { + name: 'tokenId', + type: 'uint256', + }, + { + name: 'resolver', + type: 'address', + }, + ], + name: 'setResolver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const