From bfacd552bda5109c25f3d7f2509a456b0b80ae21 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Tue, 24 Feb 2026 17:49:53 -0700 Subject: [PATCH 1/3] feat: improve ens caching of treasury votes table Co-authored-by: Rick Staa --- .../Treasury/TreasuryVoteTable/index.tsx | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/components/Treasury/TreasuryVoteTable/index.tsx b/components/Treasury/TreasuryVoteTable/index.tsx index 583ab35c..e59b74b9 100644 --- a/components/Treasury/TreasuryVoteTable/index.tsx +++ b/components/Treasury/TreasuryVoteTable/index.tsx @@ -10,6 +10,58 @@ import TreasuryVotePopover from "./TreasuryVotePopover"; import { DesktopVoteTable, Vote } from "./Views/DesktopVoteTable"; import { MobileVoteCards } from "./Views/MobileVoteTable"; +// Module-level cache to avoid repeated ENS lookups across renders/navigations. +// Bound size keeps memory usage predictable during long sessions. +const ENS_CACHE_MAX_ENTRIES = 2000; +const ensCache = new Map(); +const ensLookupInFlight = new Map>(); + +const getCachedEns = (address: string) => { + const cached = ensCache.get(address); + if (!cached) return undefined; + + // Refresh insertion order so frequently used addresses stay cached. + ensCache.delete(address); + ensCache.set(address, cached); + + return cached; +}; + +const setCachedEns = (address: string, ensName: string) => { + if (ensCache.has(address)) { + ensCache.delete(address); + } + ensCache.set(address, ensName); + + if (ensCache.size > ENS_CACHE_MAX_ENTRIES) { + const oldestKey = ensCache.keys().next().value; + if (oldestKey !== undefined) { + ensCache.delete(oldestKey); + } + } +}; + +const resolveEnsName = (address: string): Promise => { + const cached = getCachedEns(address); + if (cached) return Promise.resolve(cached); + + const inFlightLookup = ensLookupInFlight.get(address); + if (inFlightLookup) return inFlightLookup; + + const lookupPromise = getEnsForVotes(address) + .then((ensAddress) => { + const ensName = ensAddress?.name || formatAddress(address); + setCachedEns(address, ensName); + return ensName; + }) + .finally(() => { + ensLookupInFlight.delete(address); + }); + + ensLookupInFlight.set(address, lookupPromise); + return lookupPromise; +}; + interface TreasuryVoteTableProps { proposalId: string; } @@ -25,6 +77,7 @@ const useVotes = (proposalId: string) => { proposal: proposalId, }, }, + fetchPolicy: "cache-and-network", }); const { @@ -38,6 +91,7 @@ const useVotes = (proposalId: string) => { proposal: proposalId, }, }, + fetchPolicy: "cache-and-network", }); const [votes, setVotes] = useState([]); @@ -62,13 +116,7 @@ const useVotes = (proposalId: string) => { if (localEnsCache[address]) { return; } - const ensAddress = await getEnsForVotes(address); - - if (ensAddress && ensAddress.name) { - localEnsCache[address] = ensAddress.name; - } else { - localEnsCache[address] = formatAddress(address); - } + localEnsCache[address] = await resolveEnsName(address); } catch (e) { console.warn(`Failed to fetch ENS for ${address}`, e); } From 5efa4b7409d5132c3ce02a18121f6a18b6dca13c Mon Sep 17 00:00:00 2001 From: ECWireless Date: Tue, 24 Feb 2026 18:32:49 -0700 Subject: [PATCH 2/3] fix: replace truthiness check with existence check --- components/Treasury/TreasuryVoteTable/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/Treasury/TreasuryVoteTable/index.tsx b/components/Treasury/TreasuryVoteTable/index.tsx index e59b74b9..1d990479 100644 --- a/components/Treasury/TreasuryVoteTable/index.tsx +++ b/components/Treasury/TreasuryVoteTable/index.tsx @@ -17,8 +17,8 @@ const ensCache = new Map(); const ensLookupInFlight = new Map>(); const getCachedEns = (address: string) => { - const cached = ensCache.get(address); - if (!cached) return undefined; + if (!ensCache.has(address)) return undefined; + const cached = ensCache.get(address)!; // Refresh insertion order so frequently used addresses stay cached. ensCache.delete(address); From 036fdecc6f1c57c1a4128fda7c2693338ef8eee1 Mon Sep 17 00:00:00 2001 From: ECWireless Date: Tue, 24 Feb 2026 18:44:00 -0700 Subject: [PATCH 3/3] fix: apply copilot suggestions --- components/Treasury/TreasuryVoteTable/index.tsx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/components/Treasury/TreasuryVoteTable/index.tsx b/components/Treasury/TreasuryVoteTable/index.tsx index 1d990479..a434a124 100644 --- a/components/Treasury/TreasuryVoteTable/index.tsx +++ b/components/Treasury/TreasuryVoteTable/index.tsx @@ -42,23 +42,24 @@ const setCachedEns = (address: string, ensName: string) => { }; const resolveEnsName = (address: string): Promise => { - const cached = getCachedEns(address); + const cacheKey = address.toLowerCase(); + const cached = getCachedEns(cacheKey); if (cached) return Promise.resolve(cached); - const inFlightLookup = ensLookupInFlight.get(address); + const inFlightLookup = ensLookupInFlight.get(cacheKey); if (inFlightLookup) return inFlightLookup; const lookupPromise = getEnsForVotes(address) .then((ensAddress) => { const ensName = ensAddress?.name || formatAddress(address); - setCachedEns(address, ensName); + setCachedEns(cacheKey, ensName); return ensName; }) .finally(() => { - ensLookupInFlight.delete(address); + ensLookupInFlight.delete(cacheKey); }); - ensLookupInFlight.set(address, lookupPromise); + ensLookupInFlight.set(cacheKey, lookupPromise); return lookupPromise; }; @@ -77,7 +78,6 @@ const useVotes = (proposalId: string) => { proposal: proposalId, }, }, - fetchPolicy: "cache-and-network", }); const { @@ -91,7 +91,6 @@ const useVotes = (proposalId: string) => { proposal: proposalId, }, }, - fetchPolicy: "cache-and-network", }); const [votes, setVotes] = useState([]); @@ -113,9 +112,6 @@ const useVotes = (proposalId: string) => { await Promise.all( uniqueVoters.map(async (address) => { try { - if (localEnsCache[address]) { - return; - } localEnsCache[address] = await resolveEnsName(address); } catch (e) { console.warn(`Failed to fetch ENS for ${address}`, e);