fix: lookupToken walks per-protocol token tables (0.4.1)#8
Merged
Conversation
The Studio-wide seed in v0.4.0 swapped Tokens.LINK[Sepolia] for the canonical Chainlink address (0x779877…4789), leaving the AAVE-V3 Sepolia faucet LINK (0xf8Fb37…0EBE5) — which is the address AAVE templates actually use on that chain — unable to be recovered by lookupToken. The faucet still lives at Protocols.aaveV3.tokens.LINK[Sepolia] but lookupToken only walked the top-level Tokens catalog. Add a third pass: when neither chain-specific nor cross-chain Tokens lookups hit, walk Object.values(Protocols) and inspect each modules tokens property for an address match. Symbol + address come from the per-protocol entry. When the same symbol exists in Tokens on any chain (LINK does, since 0.4.0 added canonical Chainlink LINK across Mainnet/Sepolia), decimals/name get lifted from there so the returned shape stays consistent. Otherwise decimals default to 18 — true for every per-protocol token the catalog currently ships. Verified: downstream context-memory regression tests (tests/aave-write-descriptions.test.ts, tests/token-registry.test.ts) all pass once they consume this patch.
There was a problem hiding this comment.
Pull request overview
This PR fixes a regression in token reverse-lookup by teaching lookupToken to fall back to per-protocol token tables (e.g., Protocols.aaveV3.tokens) after failing to resolve an address in the top-level Tokens catalog, restoring correct symbol/decimals resolution for protocol-shipped “non-canonical” token addresses like the AAVE-V3 Sepolia faucet LINK.
Changes:
- Add a third resolution pass in
lookupTokenthat scansProtocols.*.tokensmaps when the address is not found inTokens. - Add tests covering the AAVE-V3 Sepolia faucet LINK address for both chain-specific and chainless lookups.
- Add a changeset for the patch release.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/tokens/index.ts |
Adds per-protocol token-table fallback logic to lookupToken and documents the resolution strategy. |
tests/tokens.test.ts |
Adds regression tests ensuring faucet LINK resolves via the per-protocol fallback. |
.changeset/lookuptoken-protocol-tokens.md |
Documents the behavior change for the patch release. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+151
to
+155
| * canonical Chainlink LINK lives elsewhere. When the symbol | ||
| * resolved from a per-protocol map also appears in `Tokens` on | ||
| * some chain, the richer metadata (decimals, name, links) is | ||
| * lifted from there; otherwise decimals default to 18 (true for | ||
| * every per-protocol token the catalog currently ships). |
Comment on lines
+204
to
+223
| const resolvedChainId = Number(rawChainId); | ||
| const canonicalSymbolByChain = (Tokens as Record<string, TokenByChain>)[symbol]; | ||
| const canonicalSameChain = canonicalSymbolByChain?.[resolvedChainId as keyof TokenByChain]; | ||
| if (canonicalSameChain && canonicalSameChain.address.toLowerCase() !== target) { | ||
| // The catalog has this symbol on this chain at a DIFFERENT | ||
| // address (e.g. Tokens.LINK[Sepolia] is canonical Chainlink | ||
| // LINK, not the AAVE faucet at addr). Prefer the | ||
| // per-protocol entry's address; lift decimals/name from any | ||
| // other-chain catalog entry under the same symbol so the | ||
| // returned shape stays consistent. | ||
| } | ||
| const richSibling = | ||
| canonicalSymbolByChain && | ||
| (Object.values(canonicalSymbolByChain).find(e => e) as TokenChainEntry | undefined); | ||
| return { | ||
| symbol, | ||
| address: addr as `0x${string}`, | ||
| decimals: richSibling?.decimals ?? PROTOCOL_TOKEN_DEFAULT_DECIMALS, | ||
| ...(richSibling?.name ? { name: richSibling.name } : {}), | ||
| }; |
- Docstring: drop the mention of 'links' from the lifted-metadata list (the implementation only lifts decimals + name; URL-shaped fields stay on the per-protocol entry's own deployment context). Also document that same-chain catalog entries are preferred over cross-chain ones for the metadata source. - Remove the no-op 'if (canonicalSameChain ...)' block. The previous shape computed canonicalSameChain but did nothing with it, and the metadata source (richSibling) was always the first-enumerated cross-chain entry — which could pick the wrong per-chain name when the symbol has entries on multiple chains. New logic prefers the same-chain catalog entry when one exists, falls back to any other catalog entry, then to default 18 decimals.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Downstream regression fix from v0.4.0. The Studio-wide seed swapped
Tokens.LINK[Sepolia]for the canonical Chainlink address (0x779877…4789), but the AAVE-V3 Sepolia faucet LINK (0xf8Fb37…0EBE5) is the address AAVE templates actually use on that chain. After 0.4.0 there was no path to recover the faucet symbol from the catalog.The faucet still ships at
Protocols.aaveV3.tokens.LINK[Sepolia]. This PR teacheslookupTokento walk per-protocol token tables as a third resolution pass after the top-levelTokenscatalog scan. Symbol + address come from the per-protocol entry; richer metadata (decimals, name) lifts fromTokens[symbol]on any other chain when that symbol has an entry, otherwise decimals default to 18 (true for every per-protocol token currently shipped).Concretely fixes
In
AvaProtocol/context-memory:tests/token-registry.test.ts— 4 cases that look up the faucet address and expect{symbol: "LINK", decimals: 18}tests/aave-write-descriptions.test.ts— 2 integration tests that render an AAVE-V3 Sepolia approve flow and expectApproved 0.1 LINK to AAVE V3 Pool for tradinginstead of100,000,000,000,…(raw)In production: anyone using the AVS V3 Sepolia AAVE template will now see correct LINK symbols in notifications instead of UNKNOWN.
Test plan
yarn test:run28/28 pass (including 2 new cases for the per-protocol fallback)yarn typecheckcleanyarn buildregeneratesdist/tokens/*.json(sidecar unchanged — per-protocol fallback is runtime-only, doesn't affect the static JSON output)Version
Patch bump (
0.4.0→0.4.1) — backward-compatible behaviour addition; the only callable change is thatlookupTokennow returns a value for some addresses where it previously returnedundefined.🤖 Generated with Claude Code