Skip to content

Commit 3e74f20

Browse files
authored
fix: ens refactor (#2907)
1 parent b3e6c15 commit 3e74f20

File tree

7 files changed

+142
-131
lines changed

7 files changed

+142
-131
lines changed

src/hooks/governance/useGovernanceProposals.ts

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ChainId } from '@aave/contract-helpers';
22
import { normalizeBN } from '@aave/math-utils';
33
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
4-
import { constants, Contract } from 'ethers';
4+
import { constants } from 'ethers';
55
import { gql } from 'graphql-request';
66
import {
77
adaptCacheProposalToDetail,
@@ -26,7 +26,7 @@ import {
2626
import { useRootStore } from 'src/store/root';
2727
import { governanceV3Config } from 'src/ui-config/governanceConfig';
2828
import { useSharedDependencies } from 'src/ui-config/SharedDependenciesProvider';
29-
import { getProvider } from 'src/utils/marketsAndNetworksConfig';
29+
import { getEnsProfilesMap } from 'src/utils/ens';
3030
import { subgraphRequest } from 'src/utils/subgraphRequest';
3131

3232
import { getProposal } from './useProposal';
@@ -42,7 +42,6 @@ const USE_GOVERNANCE_CACHE = process.env.NEXT_PUBLIC_USE_GOVERNANCE_CACHE === 't
4242
const PAGE_SIZE = 10;
4343
const VOTES_PAGE_SIZE = 50;
4444
const SEARCH_RESULTS_LIMIT = 10;
45-
export const ENS_REVERSE_REGISTRAR = '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C';
4645

4746
// ============================================
4847
// Subgraph search query
@@ -78,16 +77,6 @@ const getProposalVotesQuery = gql`
7877
}
7978
`;
8079

81-
const ensAbi = [
82-
{
83-
inputs: [{ internalType: 'address[]', name: 'addresses', type: 'address[]' }],
84-
name: 'getNames',
85-
outputs: [{ internalType: 'string[]', name: 'r', type: 'string[]' }],
86-
stateMutability: 'view',
87-
type: 'function',
88-
},
89-
];
90-
9180
type SubgraphVote = {
9281
proposalId: string;
9382
support: boolean;
@@ -334,18 +323,13 @@ export const useGovernanceVotersSplit = (
334323
const { data: graphVotes, isFetching: graphFetching } = useQuery({
335324
queryFn: async () => {
336325
const votes = await fetchSubgraphVotes(proposalId, votingChainId as ChainId);
337-
try {
338-
const provider = getProvider(governanceV3Config.coreChainId);
339-
const contract = new Contract(ENS_REVERSE_REGISTRAR, ensAbi);
340-
const connectedContract = contract.connect(provider);
341-
const ensNames: string[] = await connectedContract.getNames(votes.map((v) => v.voter));
342-
return votes.map((vote, i) => ({
343-
...vote,
344-
ensName: ensNames[i] || undefined,
345-
}));
346-
} catch {
347-
return votes;
348-
}
326+
const ensProfiles = await getEnsProfilesMap(votes.map((vote) => vote.voter));
327+
328+
return votes.map((vote) => ({
329+
...vote,
330+
ensName: ensProfiles[vote.voter.toLowerCase()]?.name,
331+
ensAvatar: ensProfiles[vote.voter.toLowerCase()]?.avatar,
332+
}));
349333
},
350334
queryKey: ['governance-voters-graph', proposalId],
351335
enabled: !USE_GOVERNANCE_CACHE && votingChainId !== undefined && !isNaN(proposalId),
@@ -362,18 +346,8 @@ export const useGovernanceVotersSplit = (
362346
]
363347
: [];
364348

365-
const { data: cacheEnsNames } = useQuery({
366-
queryFn: async () => {
367-
const provider = getProvider(governanceV3Config.coreChainId);
368-
const contract = new Contract(ENS_REVERSE_REGISTRAR, ensAbi);
369-
const connectedContract = contract.connect(provider);
370-
const names: string[] = await connectedContract.getNames(cacheVoterAddresses);
371-
const map: Record<string, string> = {};
372-
cacheVoterAddresses.forEach((addr, i) => {
373-
if (names[i]) map[addr.toLowerCase()] = names[i];
374-
});
375-
return map;
376-
},
349+
const { data: cacheEnsProfiles } = useQuery({
350+
queryFn: () => getEnsProfilesMap(cacheVoterAddresses),
377351
queryKey: ['governance-voters-ens', proposalId, cacheVoterAddresses],
378352
enabled: USE_GOVERNANCE_CACHE && cacheVoterAddresses.length > 0,
379353
refetchOnMount: false,
@@ -384,7 +358,8 @@ export const useGovernanceVotersSplit = (
384358
if (USE_GOVERNANCE_CACHE) {
385359
const withEns = (vote: VoteDisplay): VoteDisplay => ({
386360
...vote,
387-
ensName: cacheEnsNames?.[vote.voter.toLowerCase()],
361+
ensName: cacheEnsProfiles?.[vote.voter.toLowerCase()]?.name,
362+
ensAvatar: cacheEnsProfiles?.[vote.voter.toLowerCase()]?.avatar,
388363
});
389364
const yaeVotes = (cacheForData?.pages.flatMap((p) => p.votes.map(adaptCacheVote)) || []).map(
390365
withEns
@@ -411,11 +386,13 @@ export const useGovernanceVotersSplit = (
411386
support: boolean;
412387
votingPower: string;
413388
ensName?: string;
389+
ensAvatar?: string;
414390
}): VoteDisplay => ({
415391
voter: v.voter,
416392
support: v.support,
417393
votingPower: v.votingPower,
418394
ensName: v.ensName,
395+
ensAvatar: v.ensAvatar,
419396
});
420397

421398
const yaeVotes =

src/hooks/governance/useProposalVotes.ts

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { ChainId } from '@aave/contract-helpers';
22
import { normalizeBN } from '@aave/math-utils';
33
import { useQuery, UseQueryResult } from '@tanstack/react-query';
4-
import { Contract } from 'ethers';
54
import { gql } from 'graphql-request';
65
import { governanceV3Config } from 'src/ui-config/governanceConfig';
7-
import { getProvider } from 'src/utils/marketsAndNetworksConfig';
6+
import { getEnsProfilesMap } from 'src/utils/ens';
87
import { subgraphRequest } from 'src/utils/subgraphRequest';
98

10-
import { ENS_REVERSE_REGISTRAR } from './useGovernanceProposals';
11-
129
export type ProposalVote = {
1310
proposalId: string;
1411
support: boolean;
@@ -18,6 +15,7 @@ export type ProposalVote = {
1815

1916
export type EnhancedProposalVote = ProposalVote & {
2017
ensName?: string;
18+
ensAvatar?: string;
2119
};
2220

2321
export interface ProposalVotes {
@@ -27,21 +25,6 @@ export interface ProposalVotes {
2725
isFetching: boolean;
2826
}
2927

30-
const abi = [
31-
{
32-
inputs: [{ internalType: 'contract ENS', name: '_ens', type: 'address' }],
33-
stateMutability: 'nonpayable',
34-
type: 'constructor',
35-
},
36-
{
37-
inputs: [{ internalType: 'address[]', name: 'addresses', type: 'address[]' }],
38-
name: 'getNames',
39-
outputs: [{ internalType: 'string[]', name: 'r', type: 'string[]' }],
40-
stateMutability: 'view',
41-
type: 'function',
42-
},
43-
];
44-
4528
const getProposalVotes = gql`
4629
query getProposalVotes($proposalId: Int!) {
4730
voteEmitteds(where: { proposalId: $proposalId }) {
@@ -71,13 +54,6 @@ const fetchProposalVotes = async (
7154
}));
7255
};
7356

74-
const fetchProposalVotesEnsNames = async (addresses: string[]) => {
75-
const provider = getProvider(governanceV3Config.coreChainId);
76-
const contract = new Contract(ENS_REVERSE_REGISTRAR, abi);
77-
const connectedContract = contract.connect(provider);
78-
return connectedContract.getNames(addresses) as Promise<string[]>;
79-
};
80-
8157
export const useProposalVotesQuery = ({
8258
proposalId,
8359
votingChainId,
@@ -88,8 +64,12 @@ export const useProposalVotesQuery = ({
8864
return useQuery({
8965
queryFn: async () => {
9066
const votes = await fetchProposalVotes(proposalId, votingChainId as ChainId);
91-
const votesEnsNames = await fetchProposalVotesEnsNames(votes.map((vote) => vote.voter));
92-
return votes.map((vote, index) => ({ ...vote, ensName: votesEnsNames[index] }));
67+
const ensProfiles = await getEnsProfilesMap(votes.map((vote) => vote.voter));
68+
return votes.map((vote) => ({
69+
...vote,
70+
ensName: ensProfiles[vote.voter.toLowerCase()]?.name,
71+
ensAvatar: ensProfiles[vote.voter.toLowerCase()]?.avatar,
72+
}));
9373
},
9474
queryKey: ['proposalVotes', proposalId],
9575
enabled: votingChainId !== undefined && !isNaN(proposalId),

src/libs/hooks/use-get-ens.tsx

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { blo } from 'blo';
2-
import { utils } from 'ethers';
32
import { useEffect, useState } from 'react';
4-
import { getENSProvider } from 'src/utils/marketsAndNetworksConfig';
5-
6-
const mainnetProvider = getENSProvider();
3+
import { getEnsAvatar, getEnsName } from 'src/utils/ens';
74

85
interface EnsResponse {
96
name?: string;
@@ -13,45 +10,41 @@ interface EnsResponse {
1310
const useGetEns = (address: string): EnsResponse => {
1411
const [ensName, setEnsName] = useState<string | undefined>(undefined);
1512
const [ensAvatar, setEnsAvatar] = useState<string | undefined>(undefined);
16-
const getName = async (address: string) => {
17-
try {
18-
const name = await mainnetProvider.lookupAddress(address);
19-
setEnsName(name ? name : undefined);
20-
} catch (error) {
21-
console.error('ENS name lookup error', error);
22-
}
23-
};
24-
25-
const getAvatar = async (name: string) => {
26-
try {
27-
const labelHash = utils.keccak256(utils.toUtf8Bytes(name?.replace('.eth', '')));
28-
const result: { background_image: string } = await (
29-
await fetch(
30-
`https://metadata.ens.domains/mainnet/0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85/${labelHash}/`
31-
)
32-
).json();
33-
setEnsAvatar(
34-
result && result.background_image ? result.background_image : blo(address as `0x${string}`)
35-
);
36-
} catch (error) {
37-
console.error('ENS avatar lookup error', error);
38-
}
39-
};
4013

4114
useEffect(() => {
42-
if (address) {
43-
setEnsAvatar(blo(address as `0x${string}`));
44-
getName(address);
45-
} else {
15+
let cancelled = false;
16+
17+
const loadEns = async () => {
18+
if (!address) {
19+
setEnsName(undefined);
20+
setEnsAvatar(undefined);
21+
return;
22+
}
23+
24+
const fallbackAvatar = blo(address as `0x${string}`);
25+
4626
setEnsName(undefined);
47-
}
48-
}, [address]);
27+
setEnsAvatar(fallbackAvatar);
4928

50-
useEffect(() => {
51-
if (ensName) {
52-
getAvatar(ensName);
53-
}
54-
}, [ensName]);
29+
const name = await getEnsName(address);
30+
if (cancelled) return;
31+
32+
setEnsName(name ?? undefined);
33+
34+
if (!name) return;
35+
36+
const avatar = await getEnsAvatar(name);
37+
if (cancelled) return;
38+
39+
setEnsAvatar(avatar ?? fallbackAvatar);
40+
};
41+
42+
loadEns();
43+
44+
return () => {
45+
cancelled = true;
46+
};
47+
}, [address]);
5548

5649
return { name: ensName, avatar: ensAvatar };
5750
};

src/modules/governance/proposal/VotersListItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type VotersListItemProps = {
1919
};
2020

2121
export const VotersListItem = ({ compact, voter }: VotersListItemProps): JSX.Element | null => {
22-
const { voter: address, ensName } = voter;
22+
const { voter: address, ensAvatar, ensName } = voter;
2323
const blockieAvatar = blo(address !== '' ? (address as `0x${string}`) : '0x');
2424
const trackEvent = useRootStore((store) => store.trackEvent);
2525

@@ -55,7 +55,7 @@ export const VotersListItem = ({ compact, voter }: VotersListItemProps): JSX.Ele
5555
<Box sx={{ my: 6, '&:first-of-type': { mt: 0 }, '&:last-of-type': { mb: 0 } }}>
5656
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
5757
<Box sx={{ display: 'flex', justifyContent: 'flex-start', alignItems: 'center' }}>
58-
<Avatar src={blockieAvatar} sx={{ width: 24, height: 24, mr: 2 }} />
58+
<Avatar src={ensAvatar || blockieAvatar} sx={{ width: 24, height: 24, mr: 2 }} />
5959
<Link
6060
href={`https://etherscan.io/address/${address}`}
6161
onClick={() =>

src/modules/governance/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export type VoteDisplay = {
8282
support: boolean;
8383
votingPower: string;
8484
ensName?: string;
85+
ensAvatar?: string;
8586
};
8687

8788
/**

src/store/utils/domain-fetchers/ens.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,5 @@
11
import { DomainType, WalletDomain } from 'src/store/walletDomains';
2-
import { getENSProvider } from 'src/utils/marketsAndNetworksConfig';
3-
import { tFetch } from 'src/utils/tFetch';
4-
5-
const mainnetProvider = getENSProvider();
6-
7-
const getEnsName = async (address: string): Promise<string | null> => {
8-
try {
9-
const name = await mainnetProvider.lookupAddress(address);
10-
return name;
11-
} catch (error) {
12-
console.error('ENS name lookup error', error);
13-
}
14-
return null;
15-
};
16-
17-
const getEnsAvatar = async (name: string): Promise<string | undefined> => {
18-
try {
19-
const image = `https://metadata.ens.domains/mainnet/avatar/${name}/`;
20-
await tFetch<never>(image, { method: 'HEAD' });
21-
return image;
22-
} catch (error) {
23-
console.error('ENS avatar lookup error', error);
24-
}
25-
};
2+
import { getEnsAvatar, getEnsName } from 'src/utils/ens';
263

274
export const getEnsDomain = async (address: string): Promise<WalletDomain | null> => {
285
const name = await getEnsName(address);

0 commit comments

Comments
 (0)