Conversation
This reverts commit 8a63d9a.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Reverts prior ENS-resolution changes by switching ENS lookups away from viem and back to ethers providers, and updates governance vote/voter flows accordingly.
Changes:
- Replace
viemENS client usage with anethersmainnet provider for name/address resolution. - Update governance voter ENS enrichment to use a ReverseRecords
getNamescontract call. - Simplify voter list avatar rendering back to blockies.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/marketsAndNetworksConfig.ts | Removes viem client creation and introduces getENSProvider using StaticJsonRpcProvider. |
| src/store/utils/domain-fetchers/ens.ts | Updates ENS name lookup to use lookupAddress on the ethers provider. |
| src/modules/governance/proposal/VotersListItem.tsx | Removes ENS avatar URL handling; renders blockie avatar only. |
| src/libs/hooks/use-get-ens.tsx | Switches ENS name lookup to ethers provider; replaces avatar resolution logic with metadata fetch. |
| src/hooks/governance/useProposalVotes.ts | Replaces per-address ENS lookups with a batched getNames contract call. |
| src/hooks/governance/useGovernanceProposals.ts | Adds ReverseRecords constant/ABI and uses getNames for voter ENS enrichment (graph + cache paths). |
| src/components/transactions/Bridge/BridgeDestinationInput.tsx | Updates ENS destination resolution to provider.resolveName. |
| src/components/WalletConnection/ReadOnlyModal.tsx | Updates ENS resolution to provider.resolveName and adds provider instantiation. |
Comments suppressed due to low confidence (1)
src/components/transactions/Bridge/BridgeDestinationInput.tsx:57
- The ENS lookup effect does not handle errors from
resolveName. If it throws,setValidatingENS(false)is never reached and the loading spinner can get stuck. Wrap the resolution in try/catch/finally (and consider reusing a single cached provider instead of callinggetENSProvider()inside the effect).
useEffect(() => {
const checkENS = async () => {
setValidatingENS(true);
const resolvedAddress = await getENSProvider().resolveName(destinationAccount);
if (resolvedAddress) {
setDestinationAccount(resolvedAddress.toLowerCase());
}
setValidatingENS(false);
};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const resolvedAddress = await mainnetProvider.resolveName(normalizedENS); | ||
| if (resolvedAddress && utils.isAddress(resolvedAddress)) { | ||
| saveAndClose(resolvedAddress); | ||
| } else { |
There was a problem hiding this comment.
resolveName is awaited without try/catch. If the RPC call fails, the handler will throw and the UI won't set validAddressError, leaving the modal in a broken state. Catch resolution errors and treat them as an invalid ENS (set the error state and avoid throwing).
| const resolvedAddress = await mainnetProvider.resolveName(normalizedENS); | |
| if (resolvedAddress && utils.isAddress(resolvedAddress)) { | |
| saveAndClose(resolvedAddress); | |
| } else { | |
| try { | |
| const resolvedAddress = await mainnetProvider.resolveName(normalizedENS); | |
| if (resolvedAddress && utils.isAddress(resolvedAddress)) { | |
| saveAndClose(resolvedAddress); | |
| } else { | |
| setValidAddressError(true); | |
| } | |
| } catch { | |
| // Treat resolution errors as invalid ENS |
| export const getENSProvider = () => { | ||
| const chainId = 1; | ||
| const config = getNetworkConfig(chainId); | ||
| return new StaticJsonRpcProvider(config.publicJsonRPCUrl[0], chainId); | ||
| }; |
There was a problem hiding this comment.
getENSProvider hard-codes chainId = 1 and always uses publicJsonRPCUrl[0], bypassing the existing provider caching/rotation logic in getProvider. This reduces resilience (no RPC failover) and introduces a magic number. Consider using ChainId.mainnet and returning a cached mainnet provider (e.g., reusing getProvider(ChainId.mainnet) or a module-level singleton) so ENS lookups benefit from the same fallback behavior.
| 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}`) | ||
| ); |
There was a problem hiding this comment.
getAvatar is building an ENS NFT metadata URL and reading background_image, which is not the ENS avatar text-record and will likely return unrelated data (or fail) for many names/subdomains/TLDs. Ethers providers support ENS avatars directly (e.g., provider.getAvatar(name)), which matches the previous behavior more closely. Also, name.replace('.eth', '') is not anchored and can transform unexpected inputs.
| 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<string[]>; |
There was a problem hiding this comment.
fetchProposalVotesEnsNames now performs a single getNames call without any error handling. If the RPC call fails/reverts (rate limits, payload too large, etc.), the entire useProposalVotesQuery will reject and votes won't render. Wrap the ENS fetch in try/catch and fall back to empty strings/undefined, and consider chunking/deduping large address lists to avoid oversized eth_call payloads.
| // ENS resolution for cache path voters | ||
| const cacheVoterAddresses = USE_GOVERNANCE_CACHE | ||
| ? [ | ||
| ...(cacheForData?.pages.flatMap((p) => p.votes.map((v) => v.voter)) || []), | ||
| ...(cacheAgainstData?.pages.flatMap((p) => p.votes.map((v) => v.voter)) || []), | ||
| ] | ||
| : []; | ||
|
|
||
| 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); |
There was a problem hiding this comment.
ENS name resolution for cached voters calls getNames(cacheVoterAddresses) with the raw flattened list, which can contain duplicates and can grow with each loaded page. This can lead to large calldata and RPC failures. Consider deduplicating addresses and chunking requests (then remapping results) to keep eth_call payloads bounded.
| const { breakpoints } = useTheme(); | ||
| const sm = useMediaQuery(breakpoints.down('sm')); | ||
| const mainnetProvider = getENSProvider(); | ||
| const trackEvent = useRootStore((store) => store.trackEvent); |
There was a problem hiding this comment.
mainnetProvider is created inside the component body, so a new StaticJsonRpcProvider instance is constructed on every render. This can create unnecessary connections and makes caching harder. Prefer a module-level singleton (like other ENS helpers) or useMemo to construct it once.
|
📦 Next.js Bundle Analysis for aave-uiThis analysis was generated by the Next.js Bundle Analysis action. 🤖 This PR introduced no changes to the JavaScript bundle! 🙌 |
Reverts #2892