From b0d6c8951aaa184abc183ad9f0b019e3307dffd9 Mon Sep 17 00:00:00 2001 From: Paul Cramer Date: Fri, 2 Aug 2024 12:58:31 -0700 Subject: [PATCH] Use chainId instead of chain --- src/identity/hooks/useName.test.tsx | 26 +++++++++------- src/identity/hooks/useName.ts | 6 ++-- src/identity/types.ts | 4 +-- src/identity/utils/getName.test.tsx | 30 +++++++++---------- src/identity/utils/getName.ts | 12 ++++---- src/network/getChainPublicClient.test.ts | 2 +- src/network/getChainPublicClient.ts | 16 ++++++++-- .../WalletDropdownBaseName.test.tsx | 8 ++--- .../components/WalletDropdownBaseName.tsx | 6 ++-- src/wallet/types.ts | 4 +-- 10 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/identity/hooks/useName.test.tsx b/src/identity/hooks/useName.test.tsx index d62dc9dd9e..65af1df276 100644 --- a/src/identity/hooks/useName.test.tsx +++ b/src/identity/hooks/useName.test.tsx @@ -2,7 +2,7 @@ * @vitest-environment jsdom */ import { renderHook, waitFor } from '@testing-library/react'; -import { base, optimism } from 'viem/chains'; +import { base, mainnet, optimism } from 'viem/chains'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { Mock } from 'vitest'; import { publicClient } from '../../network/client'; @@ -31,9 +31,12 @@ describe('useName', () => { mockGetEnsName.mockResolvedValue(testEnsName); // Use the renderHook function to create a test harness for the useName hook - const { result } = renderHook(() => useName({ address: testAddress }), { - wrapper: getNewReactQueryTestProvider(), - }); + const { result } = renderHook( + () => useName({ address: testAddress, chainId: mainnet.id }), + { + wrapper: getNewReactQueryTestProvider(), + }, + ); // Wait for the hook to finish fetching the ENS name await waitFor(() => { @@ -45,9 +48,12 @@ describe('useName', () => { it('returns the loading state true while still fetching from ens action', async () => { // Use the renderHook function to create a test harness for the useName hook - const { result } = renderHook(() => useName({ address: testAddress }), { - wrapper: getNewReactQueryTestProvider(), - }); + const { result } = renderHook( + () => useName({ address: testAddress, chainId: mainnet.id }), + { + wrapper: getNewReactQueryTestProvider(), + }, + ); // Wait for the hook to finish fetching the ENS name await waitFor(() => { @@ -65,7 +71,7 @@ describe('useName', () => { // Use the renderHook function to create a test harness for the useName hook const { result } = renderHook( - () => useName({ address: testAddress, chain: base }), + () => useName({ address: testAddress, chainId: base.id }), { wrapper: getNewReactQueryTestProvider(), }, @@ -89,7 +95,7 @@ describe('useName', () => { // Use the renderHook function to create a test harness for the useName hook const { result } = renderHook( - () => useName({ address: testAddress, chain: base }), + () => useName({ address: testAddress, chainId: base.id }), { wrapper: getNewReactQueryTestProvider(), }, @@ -106,7 +112,7 @@ describe('useName', () => { it('returns error for unsupported chain ', async () => { // Use the renderHook function to create a test harness for the useName hook const { result } = renderHook( - () => useName({ address: testAddress, chain: optimism }), + () => useName({ address: testAddress, chainId: optimism.id }), { wrapper: getNewReactQueryTestProvider(), }, diff --git a/src/identity/hooks/useName.ts b/src/identity/hooks/useName.ts index 6dbf48d217..eb7e6a7010 100644 --- a/src/identity/hooks/useName.ts +++ b/src/identity/hooks/useName.ts @@ -14,15 +14,15 @@ import { getName } from '../utils/getName'; * - `{UseQueryResult}`: The rest of useQuery return values. including isLoading, isError, error, isFetching, refetch, etc. */ export const useName = ( - { address, chain = mainnet }: UseNameOptions, + { address, chainId = mainnet.id }: UseNameOptions, queryOptions?: UseNameQueryOptions, ) => { const { enabled = true, cacheTime } = queryOptions ?? {}; - const ensActionKey = `ens-name-${address}-${chain.id}`; + const ensActionKey = `ens-name-${address}-${chainId}`; return useQuery({ queryKey: ['useName', ensActionKey], queryFn: async () => { - return await getName({ address, chain }); + return await getName({ address, chainId }); }, gcTime: cacheTime, enabled, diff --git a/src/identity/types.ts b/src/identity/types.ts index 7a25f198e0..85fa62626b 100644 --- a/src/identity/types.ts +++ b/src/identity/types.ts @@ -119,7 +119,7 @@ export type GetAvatarReturnType = string | null; */ export type GetName = { address: Address; - chain?: Chain; + chainId?: number; // Optional chainId for domain resolution }; /** @@ -184,7 +184,7 @@ export type UseAttestations = { */ export type UseNameOptions = { address: Address; // The Ethereum address for which the ENS name is to be fetched. - chain?: Chain; // Optional chain for domain resolution + chainId: number; // chainId for domain resolution }; /** diff --git a/src/identity/utils/getName.test.tsx b/src/identity/utils/getName.test.tsx index 2f949bff50..200816502e 100644 --- a/src/identity/utils/getName.test.tsx +++ b/src/identity/utils/getName.test.tsx @@ -58,17 +58,17 @@ describe('getName', () => { it('should return mainnet username', async () => { const expectedEnsName = 'leo.eth'; mockGetEnsName.mockResolvedValue(expectedEnsName); - const name = await getName({ address: walletAddress, chain: mainnet }); + const name = await getName({ address: walletAddress, chainId: mainnet.id }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(mainnet); + expect(getChainPublicClient).toHaveBeenCalledWith(mainnet.id); }); it('should return sepolia username', async () => { const expectedEnsName = 'leo.test.eth'; mockGetEnsName.mockResolvedValue(expectedEnsName); - const name = await getName({ address: walletAddress, chain: sepolia }); + const name = await getName({ address: walletAddress, chainId: sepolia.id }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(sepolia); + expect(getChainPublicClient).toHaveBeenCalledWith(sepolia.id); }); it('should return custom testnet chain username', async () => { @@ -76,27 +76,27 @@ describe('getName', () => { mockReadContract.mockResolvedValue(expectedEnsName); const name = await getName({ address: walletAddress, - chain: baseSepolia, + chainId: baseSepolia.id, }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(baseSepolia); + expect(getChainPublicClient).toHaveBeenCalledWith(baseSepolia.id); }); it('should return custom mainnet username', async () => { const expectedEnsName = 'leo.custommainnet.eth'; mockReadContract.mockResolvedValue(expectedEnsName); - const name = await getName({ address: walletAddress, chain: base }); + const name = await getName({ address: walletAddress, chainId: base.id }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(base); + expect(getChainPublicClient).toHaveBeenCalledWith(base.id); }); it('should return null if user is not registered', async () => { const expectedEnsName = null; mockReadContract.mockResolvedValue(expectedEnsName); mockGetEnsName.mockResolvedValue(expectedEnsName); - const name = await getName({ address: walletAddress, chain: base }); + const name = await getName({ address: walletAddress, chainId: base.id }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(base); + expect(getChainPublicClient).toHaveBeenCalledWith(base.id); }); it('should default to ENS name if custom chain name is not registered', async () => { @@ -104,14 +104,14 @@ describe('getName', () => { const expectedEnsName = 'registered.eth'; mockReadContract.mockResolvedValue(expectedBaseName); mockGetEnsName.mockResolvedValue(expectedEnsName); - const name = await getName({ address: walletAddress, chain: base }); + const name = await getName({ address: walletAddress, chainId: base.id }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(base); + expect(getChainPublicClient).toHaveBeenCalledWith(base.id); }); it('should throw an error on unsupported chain', async () => { await expect( - getName({ address: walletAddress, chain: optimism }), + getName({ address: walletAddress, chainId: optimism.id }), ).rejects.toBe( 'ChainId not supported, name resolution is only supported on Ethereum and Base.', ); @@ -121,8 +121,8 @@ describe('getName', () => { const expectedEnsName = 'zizzamia.eth'; mockReadContract.mockRejectedValue(new Error('This is an error')); mockGetEnsName.mockResolvedValue(expectedEnsName); - const name = await getName({ address: walletAddress, chain: base }); + const name = await getName({ address: walletAddress, chainId: base.id }); expect(name).toBe(expectedEnsName); - expect(getChainPublicClient).toHaveBeenCalledWith(base); + expect(getChainPublicClient).toHaveBeenCalledWith(base.id); }); }); diff --git a/src/identity/utils/getName.ts b/src/identity/utils/getName.ts index 331f994124..aa4e1b18f6 100644 --- a/src/identity/utils/getName.ts +++ b/src/identity/utils/getName.ts @@ -15,10 +15,10 @@ import { convertReverseNodeToBytes } from './convertReverseNodeToBytes'; export const getName = async ({ address, - chain = mainnet, + chainId = mainnet.id, }: GetName): Promise => { - const chainIsBase = isBase({ chainId: chain.id }); - const chainIsEthereum = isEthereum({ chainId: chain.id }); + const chainIsBase = isBase({ chainId }); + const chainIsEthereum = isEthereum({ chainId }); const chainSupportsUniversalResolver = chainIsEthereum || chainIsBase; if (!chainSupportsUniversalResolver) { @@ -27,14 +27,14 @@ export const getName = async ({ ); } - let client = getChainPublicClient(chain); + let client = getChainPublicClient(chainId); if (chainIsBase) { const addressReverseNode = convertReverseNodeToBytes(address, base.id); try { const baseName = await client.readContract({ abi: L2ResolverAbi, - address: RESOLVER_ADDRESSES_BY_CHAIN_ID[chain.id], + address: RESOLVER_ADDRESSES_BY_CHAIN_ID[chainId], functionName: 'name', args: [addressReverseNode], }); @@ -47,7 +47,7 @@ export const getName = async ({ } // Default to mainnet - client = getChainPublicClient(mainnet); + client = getChainPublicClient(mainnet.id); // ENS username const ensName = await client.getEnsName({ address, diff --git a/src/network/getChainPublicClient.test.ts b/src/network/getChainPublicClient.test.ts index ce5469c0d0..38f03bb6c3 100644 --- a/src/network/getChainPublicClient.test.ts +++ b/src/network/getChainPublicClient.test.ts @@ -11,7 +11,7 @@ describe('getChainPublicClient', () => { }); it('should return a public client matching the given chain', async () => { - const publicClient = getChainPublicClient(base); + const publicClient = getChainPublicClient(base.id); expect(publicClient.chain.id).toBe(base.id); }); }); diff --git a/src/network/getChainPublicClient.ts b/src/network/getChainPublicClient.ts index 8f98f415d7..2b471ef528 100644 --- a/src/network/getChainPublicClient.ts +++ b/src/network/getChainPublicClient.ts @@ -1,7 +1,19 @@ import { http, createPublicClient } from 'viem'; -import type { Chain } from 'viem/chains'; +import { type Chain, base, baseSepolia, goerli, mainnet } from 'viem/chains'; + +const supportedChains = [mainnet, goerli, base, baseSepolia]; + +function getChainFromId(chainId: number): Chain | undefined { + return supportedChains.find((chain) => chain.id === chainId); +} + +export function getChainPublicClient(chainId: number) { + const chain = getChainFromId(chainId); + + if (!chain) { + throw new Error(`Unsupported chain ID: ${chainId}`); + } -export function getChainPublicClient(chain: Chain) { return createPublicClient({ chain: chain, transport: http(), diff --git a/src/wallet/components/WalletDropdownBaseName.test.tsx b/src/wallet/components/WalletDropdownBaseName.test.tsx index de1fcd2b22..46ac59e612 100644 --- a/src/wallet/components/WalletDropdownBaseName.test.tsx +++ b/src/wallet/components/WalletDropdownBaseName.test.tsx @@ -27,7 +27,7 @@ describe('WalletDropdownBaseName', () => { isConnected: true, }); (useWalletContext as vi.Mock).mockReturnValue({ - chain: base, + chainId: base.id, }); ( useName as vi.Mock<[], Partial>> @@ -49,7 +49,7 @@ describe('WalletDropdownBaseName', () => { isConnected: true, }); (useWalletContext as vi.Mock).mockReturnValue({ - chain: base, + chainId: base.id, }); ( useName as vi.Mock<[], Partial>> @@ -71,7 +71,7 @@ describe('WalletDropdownBaseName', () => { isConnected: true, }); (useWalletContext as vi.Mock).mockReturnValue({ - chain: base, + chainId: base.id, }); ( useName as vi.Mock<[], Partial>> @@ -95,7 +95,7 @@ describe('WalletDropdownBaseName', () => { isConnected: false, }); (useWalletContext as vi.Mock).mockReturnValue({ - chain: base, + chainId: base.id, }); const { container } = render(); diff --git a/src/wallet/components/WalletDropdownBaseName.tsx b/src/wallet/components/WalletDropdownBaseName.tsx index cbf512803d..1975fd878e 100644 --- a/src/wallet/components/WalletDropdownBaseName.tsx +++ b/src/wallet/components/WalletDropdownBaseName.tsx @@ -10,15 +10,15 @@ export function WalletDropdownBaseName({ className, }: WalletDropdownBaseNameReact) { const { address } = useAccount(); - const { chain } = useWalletContext(); + const { chainId } = useWalletContext(); - if (!address || !chain) { + if (!address || !chainId) { return null; } const { data: baseName, isLoading } = useName({ address, - chain, + chainId, }); const hasBaseUserName = !!baseName; diff --git a/src/wallet/types.ts b/src/wallet/types.ts index 631a2566f9..0c78ae678f 100644 --- a/src/wallet/types.ts +++ b/src/wallet/types.ts @@ -1,6 +1,6 @@ import type { UserOperation } from 'permissionless'; import type { Dispatch, ReactNode, SetStateAction } from 'react'; -import type { Address, Chain, PublicClient } from 'viem'; +import type { Address, PublicClient } from 'viem'; import type { UseBalanceReturnType, UseReadContractReturnType } from 'wagmi'; import type { SwapError } from '../swap'; @@ -61,7 +61,7 @@ export type UseGetTokenBalanceResponse = { */ export type WalletContextType = { address?: Address | null; // The Ethereum address to fetch the avatar and name for. - chain?: Chain; // Optional chain for domain resolution + chainId?: number; // Optional chainId for domain resolution isOpen: boolean; setIsOpen: Dispatch>; };