Skip to content

built Built. This is a decision record, not documentation.

What is still current: The batch shipped as approved: the USD3 asset profile, the registry rows, the srUSDe promotion and the five rate series. Migration 074 cites this page as its approved scope.

Landed: migration 074 (v0.31.0)

Header updated 2026-09-14. The body below is frozen history. All plans.

USD yield-token support batch, August 2026: plan ​

Approved by Fred 2026-08-03 (chat). Evidence base: docs/plans/usd-yield-token-coverage-survey-2026-08-verified.md (all venue sizes verified first-hand 2026-08-03). Ships as ONE PR against staging.

Approved scope (the "act now" list) ​

New asset profiles (2):

  • AA_FalconXUSDC, the Pareto senior tranche lending USDC to FalconX (0xC26A6Fa2C37b38E549a4a1807543801Db684f99C), CONDITIONAL on the redemption-rights gate below. wFalconX (0x4614F7A56A3Eb83b2Ff9fA4B4b9575B28Fb68644) folds into the same profile as a second address of the same exposure.
  • USD3 by 3Jane (0x056B269Eb1f75477a8666ae8C7fE01b64dD55eCc), ERC-4626 over USDC.

Portfolio token registry rows, book USD, class variable_rate (8 addresses): AA_FalconXUSDC*, wFalconX*, USD3, srUSDe (Strata), sUSDD (staked USDD), stUSDS (0x99CD4Ec3f88A45940936F469E4bB72A2A701EEB9), wsrUSD (0xd3fD63209FA2D55B07A0f6db36C2f43900be3094) and its underlying srUSD (0x09D4214C03D01F49544C0448DBE3A27f768F2b34). (*conditional on the FalconX gate; on failure these two become declared exclusions instead.)

Declared-exclusion rows (071/073 pattern, book NULL):

  • apyUSD (0x38EEb52F0771140d10c4E9A9a72349A329Fe8a6A): yield is STRC preferred dividends; sister token apxUSD depegged 2026-06-04 and 2026-06-25.
  • sUSDat (0xD166337499E176bbC38a1FBd113Ab144e5bd2Df7): same STRC exposure; on-chain rate 0.9195 (below par) on 2026-08-03.

Rate history series (refresher entries + one-time backfills), 7 series: AA_FalconXUSDC*, wFalconX* (derived from the same vault), USD3, srUSDe, sUSDD, stUSDS, wsrUSD/srUSD. Exclusion rows get NO series.

No Dune price-mirror additions (no liquid secondaries; conserves credits).

Deferred (tracked in a GitHub issue, NOT in this PR): mM1-USD, siUSD, strUSD, savUSD, v-wmtUSDC (watchlist); USTB, mGLOBAL, JAAA, USCC, VBILL (parked with the Aave-Horizon venue decision); mF-ONE (registry+series if a tracked wallet ever borrows against it); wJAAA promotion trigger at $5M; Dune mirror follow-up for any token that develops real DEX depth.

Gates and decisions already made ​

  1. FalconX redemption gate: if redemption at face value is whitelist-only while transfers are free (the apyUSD test), then NO profile and NO USD rows for the pair; declared exclusions instead; finding goes in the PR body. Rest ships.
  2. Icons: approved sources only (brand repo, web3icons). Missing icon = neutral placeholder + a "missing icons" list in the PR body. Never hand-drawn.
  3. USD3 profile approved despite young age (Fred's call, 2026-08-03).

Work breakdown (agent-executed) ​

D1 Verification + wiring spec (blocks everything): FalconX gate verdict with on-chain evidence; for each series token the exact contract + function for the rate read, verified at latest AND at a months-old block (archive RPC); wFalconX wrap target pinned. D2 Migration scripts/sql/074-usd-yield-batch-tokens.sql: portfolio_tokens rows + declared exclusions. Conventions: additive, idempotent, forward-only, lower-cased addresses, decimals read on-chain, GRANTs to the app role (migrations run as owner; the 035/041/052 lesson). Plus runtime updates if the registry loader needs them (src/lib/portfolio/registry.ts, buckets.ts). D3 Rate series: refresher entries following the existing token-yields pattern + per-token backfill scripts modeled on scripts/backfill-sgho.ts / backfill-susds.ts. Backfill depth: token genesis (or earliest readable block). Scripts are ADDED but NOT RUN against the DB in this PR; running them is a server step post-merge. D4 USD3 asset profile: src/data/asset-narratives.ts entry (tone reference: the PST entry; plain product language; NO em-dashes anywhere in user-facing copy; no CSS help cursor), assets-table/refresher row, icon registry entries, docs page updates. D5 FalconX profile: same touchpoints, only if D1 gate passes. Runs after D4 (same files). D6 Docs: this plan + the survey land in docs/plans/; update the relevant docs pages (portfolio.md, data-pipeline.md, database.md as applicable) in the SAME PR; npm run docs:build (vitepress) must pass as the dead-link check. D7 GitHub issue for the deferred set (full table from the survey). D8 Review + fix + verify: profiles reviewed by Fable agents; migration, adapters, backfills, docs reviewed by Opus agents (financial soundness + 1st/2nd/3rd-order effects). Fixes applied, then: typecheck/build, unit tests, vitepress build all green. Review findings get posted to the PR.

Server steps (post-merge, NOT part of this PR's execution) ​

  1. Staging deploy auto-applies 074 (additive; no prerendered page reads the new rows, so no prerender-migrate deadlock).
  2. Verify 074 actually landed in the intended shape, before anything else. ON CONFLICT DO NOTHING cannot promote a row that already exists, and the weekly portfolio-tokens-diff --approve flow writes rows that are not in git, so a pre-existing row would silently keep its old shape: SELECT address, symbol, decimals, book, class, wallet_tracked, rate_source, source, status FROM onchain_credit.portfolio_tokens WHERE chain_id = 1 AND address IN (USD3, sUSDD, stUSDS, wsrUSD, srUSDe, AA_FalconXUSDC, wFalconX, sUSDat); and check all eight against the shapes pinned in src/lib/portfolio/buckets.test.ts.
  3. Run the R6 backfill repair for any wallet already holding one of the four re-booked tokens (USD3, sUSDD, stUSDS, wsrUSD). They move EXCLUDED → USD, classification is read-time off the latest registry row, and stored rows keep the values they were written under, so a pre-migration leg carries book = 'EXCLUDED' and value_redemption NULL and would otherwise appear to spring into existence at the migration date with no matching flow (R6). Find them by accounting_asset in portfolio_position_snapshots, then run the repair per wallet, the same procedure the 071 and 073 headers point at, with the same caveat: it rewrites that wallet's stored history rather than patching it. If the query returns nothing, record that as the check having been made.
  4. Run the new backfill scripts once (run-cron pattern, drained window): backfill-usd3.ts, backfill-srusde.ts, backfill-susdd.ts, backfill-stusds.ts, backfill-wsrusd.ts. backfill-wsrusd.ts is ~1,850 archive windows with no per-window retry, so a transient RPC blip skips a window and exits non-zero; re-running is idempotent and fills only the gaps.
  5. Run the two shared follow-ups, which close the column shape the per-token backfills leave open (they write 6 of the 8 columns the live refresher does): backfill-token-supply.ts (fills total_supply from the block_at_snapshot each row already carries; only touches NULLs) then backfill-apy-30d.ts (pure DB, recomputes apy_30d for every token).
  6. Verify each new series has rows and the refresher appends on the next cycle.
  7. Confirm /portfolio values a wallet holding each new token (live + historical). Note the four new rows carry a REDEMPTION mark only: the batch adds no Dune mirror rows, so value_market is NULL for their legs and the market-mode toggle omits them (074's header records why).

Verification bar (before the PR opens) ​

  • Every rate read re-verified against the survey's on-chain values (e.g. USD3 convertToAssets 1.1679, stUSDS 1.0673, wsrUSD 1.0913 on 2026-08-03).
  • Unit tests use magnitude-unique fixtures (no vacuous regex asserts).
  • No em-dashes in any user-facing string added by this PR.
  • git status reviewed before commit; no blind git add -A.

Private documentation. creddit.xyz