Skip to content

Commit fc65ac9

Browse files
fix(token-lists): preserve first-wins semantics in chainsById and drop dead catch
viem/chains exports 702 chain objects spanning 675 unique IDs; 27 export names share a chainId, and 10 of those collisions have divergent nativeCurrency. The previous Map(...map) construction was last-wins, silently changing buildNativeToken results versus the original find()-first-wins behaviour for chainIds 9, 166, 260, 999, 1001, 1337, 8217, 9496, 200810, 200901 -- most consequentially chainId 1337 (Localhost ETH/18 → Tempo USD/6). Replace with an explicit first-wins loop. Hoist chainsById to the top of the module so both the filter in combineTokenLists and buildNativeToken reference the same declaration without a forward-reference. After the chainsById.has(token.chainId) filter, every token entering the reduce accumulator has a known chain, making the try/catch/console.error branch unreachable. Remove it; the throw inside buildNativeToken remains as a defensive invariant for callers that bypass the filter. Add a positive-survival assertion to the unsupported-chainId test to guard against future filter inversions silently dropping valid tokens.
1 parent 72059eb commit fc65ac9

2 files changed

Lines changed: 9 additions & 16 deletions

File tree

src/hooks/useTokenLists.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ describe('useTokenLists', () => {
241241
expect(result.current.tokens.some((t) => t.chainId === 3)).toBe(false)
242242
expect(result.current.tokensByChainId[3]).toBeUndefined()
243243
expect(errorSpy).not.toHaveBeenCalled()
244+
expect(result.current.tokens.some((t) => t.address === mockToken1.address)).toBe(true)
244245

245246
errorSpy.mockRestore()
246247
})

src/hooks/useTokenLists.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import { type Token, type TokenList, tokenSchema } from '@/src/types/token'
1313
import { logger } from '@/src/utils/logger'
1414
import tokenListsCache, { type TokensMap, updateTokenListsCache } from '@/src/utils/tokenListsCache'
1515

16+
const chainsById: Map<number, (typeof chains)[keyof typeof chains]> = new Map()
17+
for (const chain of Object.values(chains)) {
18+
if (!chainsById.has(chain.id)) chainsById.set(chain.id, chain)
19+
}
20+
1621
/**
1722
* Loads and processes token lists from configured sources
1823
*
@@ -122,18 +127,9 @@ function combineTokenLists(results: Array<UseSuspenseQueryResult<TokenList>>): T
122127
const tokensMap = uniqueTokens.reduce<TokensMap>(
123128
(acc, token) => {
124129
if (!acc.tokensByChainId[token.chainId]) {
125-
try {
126-
// if there's a native token for the chain
127-
const nativeToken = buildNativeToken(token.chainId)
128-
129-
// add it to the list
130-
acc.tokensByChainId[token.chainId] = [nativeToken]
131-
acc.tokens.push(nativeToken)
132-
} catch (err) {
133-
console.error(err)
134-
// if there's no native token for the chain, ignore the error
135-
acc.tokensByChainId[token.chainId] = []
136-
}
130+
const nativeToken = buildNativeToken(token.chainId)
131+
acc.tokensByChainId[token.chainId] = [nativeToken]
132+
acc.tokens.push(nativeToken)
137133
}
138134

139135
acc.tokens.push(token)
@@ -197,10 +193,6 @@ export async function fetchTokenList(url: string): Promise<TokenList> {
197193
}
198194
}
199195

200-
const chainsById: Map<number, (typeof chains)[keyof typeof chains]> = new Map(
201-
Object.values(chains).map((c) => [c.id, c]),
202-
)
203-
204196
/**
205197
* Builds a native token object based on the chain ID.
206198
*

0 commit comments

Comments
 (0)