Skip to content

feat!: audit the token catalog for coverage, provenance and transfer constraints - #40

Merged
chrisli30 merged 2 commits into
mainfrom
feat/38-token-catalog-audit
Oct 29, 2026
Merged

chrisli30 merged 2 commits into
mainfrom
feat/38-token-catalog-audit

Conversation

@chrisli30

Copy link
Copy Markdown
Member

Closes #38.

What changes for consumers

  • More tokens. BNB Chain goes from 2 rows to 17 (WBNB first). Ethereum gains 25 (RLUSD, USDG, GHO, cbETH, sDAI, crvUSD, …) and Base gains 13 (RLUSD, WBTC, GHO, LBTC, tBTC, weETH, …). Every Aave V3 Core reserve on the three mainnets is now in Tokens by address.
  • Fixes
    • Base Sepolia USDT said 6 decimals; the contract returns 18.
    • lookupToken(56, WBNB) returned symbol "WETH" through Uniswap's wrapped-native slot.
  • Breaking: Ethereum Tokens["SolvBTC.BBN"] is renamed Tokens.xSolvBTC, following Solv's rebrand (the contract's name() and symbol() both return xSolvBTC). This is a major changeset, so the next release is 2.0.0.
  • New optional field TokenChainEntry.transferConstraints (TOKEN_TRANSFER_CONSTRAINT):
    • permissionedRecipient on OUSG, BUIDL and USYC: a transfer to a fresh address reverts.
    • rebasing on stETH and eETH: a full-balance transfer can leave 1–2 wei.

Audit deliverables (#38)

Deliverable Where
Per-chain coverage matrix (present / missing / bridged / legacy / unverified / transfer handling, with sources and verification dates) docs/TOKEN_COVERAGE.md, generated by yarn report:tokens; a test fails if it is stale
Prioritized gaps, filled with changesets and integrity tests WBNB and RLUSD first; KNOWN_GAPS is now empty
Inclusion and maintenance policy, plus remaining gaps docs/TOKEN_CATALOG.md (an explicit per-chain target, not "all common tokens")
Consumer guidance: recognizing an asset vs. transferring it docs/TOKEN_CATALOG.md, "Guidance for consumers"

Provenance. Each audited row records its canonical source, issuance (native, bridged or test), bridge, legacy status and verifiedAt in src/tokens/provenance/*.json. These files are not shipped.

  • Tests pin each provenance address to the catalog.
  • The 80 rows listed before the audit are on an explicit pending list, and that list can only shrink.

Tooling

  • yarn verify:tokens: an on-chain check of code, decimals() and symbol(). It needs network access, so it is not part of yarn test.
  • An EIP-55 checksum test, which caught one address I had mistyped.

How it was verified

No transaction was sent and nothing was signed.

  • On-chain: yarn verify:tokens checks all 196 rows with 0 errors and 0 warnings.
  • Addresses: every added address was checked against the issuer's or protocol's own address page.
  • Transfer behaviour: every mainnet row was simulated with a read-only full-balance transfer (eth_call with a state override), except seven bridged rows that had no simulatable holder. They are named in the audit record.
  • Repo checks: yarn typecheck is clean, 127 tests pass, and yarn build works. The sidecar has 196 entries, and the tarball excludes the provenance files and docs.

Follow-ups outside this repo

  • EigenLayer-AVS core/taskengine/tokenwhitelist/ carries the same wrong Base Sepolia USDT decimals (6) and still names Ethereum SolvBTC.BBN. Its drift check against the sidecar should flag both.
  • Studio:
    • drop the EURC compatibility rows in app/lib/erc20/catalog.ts;
    • show transferConstraints on the Sweep selection step;
    • bump to @avaprotocol/protocols@^2.

🤖 Generated with Claude Code

chrisli30 and others added 2 commits October 28, 2026 21:30
…constraints

Closes #38.

Coverage: the catalog now promises an explicit per-chain target instead of
"all common tokens" (scripts/token-catalog/coverage.ts), and a test fails
when a target asset is neither listed nor a recorded known gap. Every target
asset is listed: BNB Chain goes from 2 rows to 17 (WBNB first), Ethereum
gains 25 (RLUSD, USDG, GHO, cbETH, ...) and Base 13 (RLUSD, WBTC, ...),
covering every Aave V3 Core reserve on the three mainnets.

Fixes found by the audit:
- Base Sepolia USDT said 6 decimals; the contract returns 18.
- lookupToken(56, WBNB) returned "WETH" through Uniswap's wrapped-native
  slot; WBNB is now catalogued.
- Ethereum SolvBTC.BBN is renamed xSolvBTC to follow Solv's on-chain rebrand.

Provenance: canonical sources, issuance (native/bridged/test), bridge and
legacy status live in src/tokens/provenance/*.json (not shipped). Tests pin
each provenance address to the catalog. 80 pre-audit rows are listed as
pending, and that list can only shrink.

Transfer constraints: TokenChainEntry.transferConstraints records
permissionedRecipient (OUSG, BUIDL, USYC) and rebasing (stETH, eETH),
established by read-only eth_call simulation of full-balance transfers.

Also: yarn verify:tokens (on-chain code/decimals/symbol check), yarn
report:tokens (generates docs/TOKEN_COVERAGE.md), docs/TOKEN_CATALOG.md
(policy, consumer guidance, audit record), and an EIP-55 checksum test.

BREAKING CHANGE: Tokens["SolvBTC.BBN"] no longer exists; use
Tokens.xSolvBTC. The Ethereum sidecar carries xSolvBTC for that address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`--rows` dropped rows whose chainId was missing or had no catalog file,
so an invalid candidate printed "0 rows checked, 0 with errors" and
exited 0. Candidate rows are now validated before any RPC call: a row
off the catalog chains, off `--chain`, or with a malformed symbol,
address (including a bad EIP-55 checksum) or decimals fails the run,
and every bad row is reported. A run that exits 0 checked every row.

The argument parser is strict too: `--rows` without a file, and unknown
flags such as `--row`, used to fall back to checking the whole catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chrisli30
chrisli30 merged commit 0562978 into main Oct 29, 2026
6 checks passed
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.

Audit shared token catalog coverage and canonical address provenance

1 participant