Skip to content

refactor: aggregate Tokens namespace from per-chain modules#14

Merged
chrisli30 merged 1 commit into
mainfrom
refactor/tokens-aggregator-from-per-chain
Jun 9, 2026
Merged

refactor: aggregate Tokens namespace from per-chain modules#14
chrisli30 merged 1 commit into
mainfrom
refactor/tokens-aggregator-from-per-chain

Conversation

@chrisli30

Copy link
Copy Markdown
Member

Summary

Refactor — no behavior change, no public surface change.

The root Tokens namespace was importing each chain's JSON directly and re-running the per-row → symbol-keyed transform that the per-chain modules (added in v0.6.0) already perform. Two code paths reading the same source files, with a cross-validation test keeping them honest.

After this PR, src/tokens/index.ts consumes the per-chain modules (./ethereum, ./sepolia, ./base, ./base-sepolia, ./bnb-mainnet) as its source of truth. Per-chain modules stay the canonical typed access for chain-bound consumers (via subpath exports); the root Tokens becomes a pure flatten over their pre-validated symbol → entry maps.

What's preserved

  • Public surface identical: Tokens.SYMBOL[chainId], lookupToken(), @avaprotocol/protocols/tokens/<chain> all keep working unchanged. No consumer disruption.
  • tests/per-chain.test.ts (the cross-validation test) becomes a tautology — both paths now read from the same source — but stays valid as a regression guard if anyone changes the aggregator.
  • Null-prototype + own-property-check hardening from feat: v0.6.0 — per-chain token subpath exports + ERC-20/Chainlink events #12 carried through.

What's dropped

  • The (symbol, chainId) duplicate check in buildTokensFromData(). Per-chain modules already guarantee single-symbol-per-chain at load via buildChainTokenMap, so the aggregator's slot is always empty for any (symbol, chainId) pair. Dropping the redundant check is a small simplification.
  • Direct JSON imports from ./data/*.json — those now live exclusively in the per-chain modules.
  • The TokenDataRow interface — moved to ./per-chain.ts and exported from there (no surface change since this PR's tests still reference it through the helper).

Diff size

src/tokens/index.ts+50 / -57. One file.

Verification

  • yarn build — emits the same six entries (root + 5 chain modules) as before
  • yarn typecheck — clean
  • yarn test:run — 57/57 pass (same count, same suite)
  • Sidecar JSONs unchanged: dist/tokens/{ethereum,sepolia,base,base-sepolia,bnb-mainnet}.json rebuild with identical entry counts (104/6/23/4/2 = 139 total)

Test plan

  • Reviewer: spot-check that Tokens.USDC[Chains.Sepolia] still returns the same address/decimals after the refactor.
  • CI: standard test pipeline.

The root `Tokens` namespace used to import each chain's JSON
directly and re-run the per-row → symbol-keyed transform. That
duplicated the transform the per-chain modules already perform at
their own module load: two code paths reading the same source files.

After this change, `src/tokens/index.ts` consumes the per-chain
modules (`./ethereum`, `./sepolia`, `./base`, `./base-sepolia`,
`./bnb-mainnet`) as its source of truth. Per-chain modules stay the
canonical typed access for chain-bound consumers (via the existing
subpath exports added in 0.6.0); the root `Tokens` becomes a pure
flatten over their pre-validated symbol → entry maps.

Behavior preserved:
- Public surface unchanged: `Tokens.SYMBOL[chainId]`, `lookupToken()`,
  and the per-chain subpath exports all keep working identically.
- The cross-validation test in `tests/per-chain.test.ts` becomes a
  tautology (same source flowing through both paths), but stays
  valid as a regression guard if anyone changes the aggregator.
- Null-prototype + own-property-check hardening from #12 carried
  through; the (symbol, chainId) duplicate check is dropped because
  per-chain modules already guarantee single-symbol-per-chain at
  their own load — the aggregator's slot is always empty.

All 57 tests still pass. Build emits the same six entries and the
same five JSON sidecars.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the root Tokens aggregator to use the per-chain token modules as the single source of truth, avoiding duplicate JSON imports and duplicate per-row transformation while preserving the existing public API shape (Tokens.SYMBOL[chainId], lookupToken, and subpath exports).

Changes:

  • Switches src/tokens/index.ts to import ./ethereum, ./sepolia, ./base, ./base-sepolia, ./bnb-mainnet instead of importing ./data/*.json directly.
  • Rebuilds the root Tokens namespace by flattening the per-chain tokens maps (symbol → entry) into the cross-chain shape (symbol → chainId → entry).
  • Removes the redundant (symbol, chainId) duplicate check previously performed during aggregation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/tokens/index.ts
Comment on lines +82 to 86
for (const [chainId, chainTokens] of PER_CHAIN) {
for (const [symbol, entry] of Object.entries(chainTokens)) {
if (!Object.prototype.hasOwnProperty.call(merged, symbol)) {
merged[symbol] = Object.create(null);
}
@chrisli30 chrisli30 merged commit 9c1cb1d into main Jun 9, 2026
7 checks passed
chrisli30 added a commit that referenced this pull request Jun 9, 2026
…#17)

Addresses Copilot review on #14: the tokens-aggregator refactor dropped
duplicate protection, so a chain listed twice in PER_CHAIN would silently
overwrite the first chain's entries (wrong Tokens.SYMBOL[chainId], no
error at module load). Now throws fast pointing at the duplicate. Patch
changeset; no API change.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants