diff --git a/src/components/WalletConnection/ReadOnlyModal.tsx b/src/components/WalletConnection/ReadOnlyModal.tsx index 4f0c82208a..b3db05c6a1 100644 --- a/src/components/WalletConnection/ReadOnlyModal.tsx +++ b/src/components/WalletConnection/ReadOnlyModal.tsx @@ -15,15 +15,13 @@ import { ModalType, useModalContext } from 'src/hooks/useModal'; import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; import { useRootStore } from 'src/store/root'; import { AUTH } from 'src/utils/events'; -import { getENSClient } from 'src/utils/marketsAndNetworksConfig'; +import { getENSProvider } from 'src/utils/marketsAndNetworksConfig'; import { normalize } from 'viem/ens'; import { useAccount, useDisconnect } from 'wagmi'; import { BasicModal } from '../primitives/BasicModal'; import { TxModalTitle } from '../transactions/FlowCommons/TxModalTitle'; -const viemClient = getENSClient(); - export const ReadOnlyModal = () => { const { disconnectAsync } = useDisconnect(); const { isConnected } = useAccount(); @@ -33,6 +31,7 @@ export const ReadOnlyModal = () => { const { type, close } = useModalContext(); const { breakpoints } = useTheme(); const sm = useMediaQuery(breakpoints.down('sm')); + const mainnetProvider = getENSProvider(); const trackEvent = useRootStore((store) => store.trackEvent); const handleReadAddress = async (inputMockWalletAddress: string): Promise => { @@ -44,7 +43,7 @@ export const ReadOnlyModal = () => { if (inputMockWalletAddress.slice(-4) === '.eth') { const normalizedENS = normalize(inputMockWalletAddress); // Attempt to resolve ENS name and use resolved address if valid - const resolvedAddress = await viemClient.getEnsAddress({ name: normalizedENS }); + const resolvedAddress = await mainnetProvider.resolveName(normalizedENS); if (resolvedAddress && utils.isAddress(resolvedAddress)) { saveAndClose(resolvedAddress); } else { diff --git a/src/components/transactions/Bridge/BridgeDestinationInput.tsx b/src/components/transactions/Bridge/BridgeDestinationInput.tsx index 0bad1f28f7..5c05568b5e 100644 --- a/src/components/transactions/Bridge/BridgeDestinationInput.tsx +++ b/src/components/transactions/Bridge/BridgeDestinationInput.tsx @@ -10,9 +10,7 @@ import { import { isAddress } from 'ethers/lib/utils'; import { useEffect, useState } from 'react'; import { useIsContractAddress } from 'src/hooks/useIsContractAddress'; -import { getENSClient } from 'src/utils/marketsAndNetworksConfig'; - -const viemClient = getENSClient(); +import { getENSProvider } from 'src/utils/marketsAndNetworksConfig'; export const BridgeDestinationInput = ({ connectedAccount, @@ -51,7 +49,7 @@ export const BridgeDestinationInput = ({ useEffect(() => { const checkENS = async () => { setValidatingENS(true); - const resolvedAddress = await viemClient.getEnsAddress({ name: destinationAccount }); + const resolvedAddress = await getENSProvider().resolveName(destinationAccount); if (resolvedAddress) { setDestinationAccount(resolvedAddress.toLowerCase()); } diff --git a/src/hooks/governance/useGovernanceProposals.ts b/src/hooks/governance/useGovernanceProposals.ts index 17fb6f4ee7..bb3c100f1d 100644 --- a/src/hooks/governance/useGovernanceProposals.ts +++ b/src/hooks/governance/useGovernanceProposals.ts @@ -1,7 +1,7 @@ import { ChainId } from '@aave/contract-helpers'; import { normalizeBN } from '@aave/math-utils'; import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; -import { constants } from 'ethers'; +import { constants, Contract } from 'ethers'; import { gql } from 'graphql-request'; import { adaptCacheProposalToDetail, @@ -26,7 +26,7 @@ import { import { useRootStore } from 'src/store/root'; import { governanceV3Config } from 'src/ui-config/governanceConfig'; import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider'; -import { getENSClient } from 'src/utils/marketsAndNetworksConfig'; +import { getProvider } from 'src/utils/marketsAndNetworksConfig'; import { subgraphRequest } from 'src/utils/subgraphRequest'; import { getProposal } from './useProposal'; @@ -42,7 +42,7 @@ const USE_GOVERNANCE_CACHE = process.env.NEXT_PUBLIC_USE_GOVERNANCE_CACHE === 't const PAGE_SIZE = 10; const VOTES_PAGE_SIZE = 50; const SEARCH_RESULTS_LIMIT = 10; -const viemClient = getENSClient(); +export const ENS_REVERSE_REGISTRAR = '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C'; // ============================================ // Subgraph search query @@ -78,6 +78,16 @@ const getProposalVotesQuery = gql` } `; +const ensAbi = [ + { + inputs: [{ internalType: 'address[]', name: 'addresses', type: 'address[]' }], + name: 'getNames', + outputs: [{ internalType: 'string[]', name: 'r', type: 'string[]' }], + stateMutability: 'view', + type: 'function', + }, +]; + type SubgraphVote = { proposalId: string; support: boolean; @@ -325,14 +335,13 @@ export const useGovernanceVotersSplit = ( queryFn: async () => { const votes = await fetchSubgraphVotes(proposalId, votingChainId as ChainId); try { - const ensNames = await Promise.all( - votes.map((v) => - viemClient.getEnsName({ address: v.voter as `0x${string}` }).catch(() => null) - ) - ); + const provider = getProvider(governanceV3Config.coreChainId); + const contract = new Contract(ENS_REVERSE_REGISTRAR, ensAbi); + const connectedContract = contract.connect(provider); + const ensNames: string[] = await connectedContract.getNames(votes.map((v) => v.voter)); return votes.map((vote, i) => ({ ...vote, - ensName: ensNames[i] ?? undefined, + ensName: ensNames[i] || undefined, })); } catch { return votes; @@ -355,11 +364,10 @@ export const useGovernanceVotersSplit = ( const { data: cacheEnsNames } = useQuery({ queryFn: async () => { - const names = await Promise.all( - cacheVoterAddresses.map((addr) => - viemClient.getEnsName({ address: addr as `0x${string}` }).catch(() => null) - ) - ); + const provider = getProvider(governanceV3Config.coreChainId); + const contract = new Contract(ENS_REVERSE_REGISTRAR, ensAbi); + const connectedContract = contract.connect(provider); + const names: string[] = await connectedContract.getNames(cacheVoterAddresses); const map: Record = {}; cacheVoterAddresses.forEach((addr, i) => { if (names[i]) map[addr.toLowerCase()] = names[i]; diff --git a/src/hooks/governance/useProposalVotes.ts b/src/hooks/governance/useProposalVotes.ts index 6e40455322..4dc0c46fcc 100644 --- a/src/hooks/governance/useProposalVotes.ts +++ b/src/hooks/governance/useProposalVotes.ts @@ -1,11 +1,14 @@ import { ChainId } from '@aave/contract-helpers'; import { normalizeBN } from '@aave/math-utils'; import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import { Contract } from 'ethers'; import { gql } from 'graphql-request'; import { governanceV3Config } from 'src/ui-config/governanceConfig'; -import { getENSClient } from 'src/utils/marketsAndNetworksConfig'; +import { getProvider } from 'src/utils/marketsAndNetworksConfig'; import { subgraphRequest } from 'src/utils/subgraphRequest'; +import { ENS_REVERSE_REGISTRAR } from './useGovernanceProposals'; + export type ProposalVote = { proposalId: string; support: boolean; @@ -24,7 +27,20 @@ export interface ProposalVotes { isFetching: boolean; } -const viemClient = getENSClient(); +const abi = [ + { + inputs: [{ internalType: 'contract ENS', name: '_ens', type: 'address' }], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [{ internalType: 'address[]', name: 'addresses', type: 'address[]' }], + name: 'getNames', + outputs: [{ internalType: 'string[]', name: 'r', type: 'string[]' }], + stateMutability: 'view', + type: 'function', + }, +]; const getProposalVotes = gql` query getProposalVotes($proposalId: Int!) { @@ -55,13 +71,11 @@ const fetchProposalVotes = async ( })); }; -const fetchProposalVotesEnsNames = async (addresses: string[]): Promise => { - const names = await Promise.all( - addresses.map((addr) => - viemClient.getEnsName({ address: addr as `0x${string}` }).catch(() => null) - ) - ); - return names.map((name) => name ?? ''); +const fetchProposalVotesEnsNames = async (addresses: string[]) => { + const provider = getProvider(governanceV3Config.coreChainId); + const contract = new Contract(ENS_REVERSE_REGISTRAR, abi); + const connectedContract = contract.connect(provider); + return connectedContract.getNames(addresses) as Promise; }; export const useProposalVotesQuery = ({ diff --git a/src/libs/hooks/use-get-ens.tsx b/src/libs/hooks/use-get-ens.tsx index a186e847fd..66abfba7ba 100644 --- a/src/libs/hooks/use-get-ens.tsx +++ b/src/libs/hooks/use-get-ens.tsx @@ -1,8 +1,9 @@ import { blo } from 'blo'; +import { utils } from 'ethers'; import { useEffect, useState } from 'react'; -import { getENSClient } from 'src/utils/marketsAndNetworksConfig'; +import { getENSProvider } from 'src/utils/marketsAndNetworksConfig'; -const viemClient = getENSClient(); +const mainnetProvider = getENSProvider(); interface EnsResponse { name?: string; @@ -12,11 +13,10 @@ interface EnsResponse { const useGetEns = (address: string): EnsResponse => { const [ensName, setEnsName] = useState(undefined); const [ensAvatar, setEnsAvatar] = useState(undefined); - const getName = async (address: string) => { try { - const name = await viemClient.getEnsName({ address: address as `0x${string}` }); - setEnsName(name ?? undefined); + const name = await mainnetProvider.lookupAddress(address); + setEnsName(name ? name : undefined); } catch (error) { console.error('ENS name lookup error', error); } @@ -24,8 +24,15 @@ const useGetEns = (address: string): EnsResponse => { const getAvatar = async (name: string) => { try { - const avatar = await viemClient.getEnsAvatar({ name }); - setEnsAvatar(avatar ?? blo(address as `0x${string}`)); + const labelHash = utils.keccak256(utils.toUtf8Bytes(name?.replace('.eth', ''))); + const result: { background_image: string } = await ( + await fetch( + `https://metadata.ens.domains/mainnet/0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85/${labelHash}/` + ) + ).json(); + setEnsAvatar( + result && result.background_image ? result.background_image : blo(address as `0x${string}`) + ); } catch (error) { console.error('ENS avatar lookup error', error); } diff --git a/src/modules/governance/proposal/VotersListItem.tsx b/src/modules/governance/proposal/VotersListItem.tsx index 94a702dc69..8e9aa1c446 100644 --- a/src/modules/governance/proposal/VotersListItem.tsx +++ b/src/modules/governance/proposal/VotersListItem.tsx @@ -1,7 +1,6 @@ import { ExternalLinkIcon } from '@heroicons/react/solid'; import { Avatar, Box, SvgIcon, Typography } from '@mui/material'; import { blo } from 'blo'; -import React, { useEffect, useState } from 'react'; import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; import { Link } from 'src/components/primitives/Link'; import { VoteDisplay } from 'src/modules/governance/types'; @@ -22,13 +21,6 @@ type VotersListItemProps = { export const VotersListItem = ({ compact, voter }: VotersListItemProps): JSX.Element | null => { const { voter: address, ensName } = voter; const blockieAvatar = blo(address !== '' ? (address as `0x${string}`) : '0x'); - const [avatar, setAvatar] = useState(blockieAvatar); - - useEffect(() => { - if (ensName) { - setAvatar(`https://metadata.ens.domains/mainnet/avatar/${ensName}`); - } - }, [ensName]); const trackEvent = useRootStore((store) => store.trackEvent); const displayName = (name?: string) => { @@ -63,11 +55,7 @@ export const VotersListItem = ({ compact, voter }: VotersListItemProps): JSX.Ele - setAvatar(blockieAvatar) } }} - /> + diff --git a/src/store/utils/domain-fetchers/ens.ts b/src/store/utils/domain-fetchers/ens.ts index b3958b6a0f..b51d00e4e9 100644 --- a/src/store/utils/domain-fetchers/ens.ts +++ b/src/store/utils/domain-fetchers/ens.ts @@ -1,13 +1,13 @@ import { DomainType, WalletDomain } from 'src/store/walletDomains'; -import { getENSClient } from 'src/utils/marketsAndNetworksConfig'; +import { getENSProvider } from 'src/utils/marketsAndNetworksConfig'; import { tFetch } from 'src/utils/tFetch'; -const viemClient = getENSClient(); +const mainnetProvider = getENSProvider(); const getEnsName = async (address: string): Promise => { try { - const name = await viemClient.getEnsName({ address: address as `0x${string}` }); - return name ?? null; + const name = await mainnetProvider.lookupAddress(address); + return name; } catch (error) { console.error('ENS name lookup error', error); } diff --git a/src/utils/marketsAndNetworksConfig.ts b/src/utils/marketsAndNetworksConfig.ts index 9925666904..28447d104e 100644 --- a/src/utils/marketsAndNetworksConfig.ts +++ b/src/utils/marketsAndNetworksConfig.ts @@ -1,8 +1,6 @@ import { ChainId, ChainIdToNetwork } from '@aave/contract-helpers'; import { StaticJsonRpcProvider } from '@ethersproject/providers'; import { ProviderWithSend } from 'src/components/transactions/GovVote/temporary/VotingMachineService'; -import { createPublicClient, http, PublicClient } from 'viem'; -import { mainnet } from 'viem/chains'; import { CustomMarket, @@ -190,13 +188,10 @@ export const getProvider = (chainId: ChainId): ProviderWithSend => { return providers[chainId]; }; -export const getENSClient = (): PublicClient => { - const config = getNetworkConfig(ChainId.mainnet); - return createPublicClient({ - chain: mainnet, - transport: http(config.publicJsonRPCUrl[0]), - batch: { multicall: true }, - }); +export const getENSProvider = () => { + const chainId = 1; + const config = getNetworkConfig(chainId); + return new StaticJsonRpcProvider(config.publicJsonRPCUrl[0], chainId); }; const ammDisableProposal = 'https://governance-v2.aave.com/governance/proposal/44';