AUDIT page: DOUB projection card (supply, market cap, CHARM rate)
Context
The TimeCurve AUDIT surface (/timecurve/protocol, subnav label AUDIT) is the operator-facing protocol view (TimeCurveProtocolPage). It already exposes semilive sale reads, total raise, immutable parameters, and a collapsed Raw contract and operator context accordion with liquidity anchors and a charm redemption curve — but those projection economics are buried and not summarized for quick scanning.
Product ask: add a prominent “DOUB projection” card on this page showing projected full supply, implied market cap, price per DOUB, CHARM→DOUB conversion, and related launch economics.
Note on supply figure: Request cites 251M projected supply. Canonical genesis policy in launchplan-timecurve.md §4 is 250M DOUB (200M TimeCurve sale + 21.5M presale + 28.5M V3 LP). Confirm with product whether the UI constant should be 250M (doc-aligned) or 251M (updated policy) before hard-coding.
Current codebase (research)
| Area | Location | Relevance |
|---|---|---|
| AUDIT page shell | frontend/src/pages/TimeCurveProtocolPage.tsx |
Mount new section after Sale state or before Immutable parameters |
| Subnav | frontend/src/pages/timecurve/TimeCurveSubnav.tsx |
Route /timecurve/protocol, label AUDIT |
| Protocol reads | frontend/src/pages/timecurve/TimeCurveProtocolDataContext.tsx |
Multicall already returns totalRaised, totalTokensForSale, totalCharmWeight, currentPricePerCharmWad |
| Projection math (shared) | frontend/src/lib/timeCurvePodiumMath.ts |
doubPerCharmAtLaunchWad, projectedReservePerDoubWad, launchLiquidityAnchorWad, kumbayaBandLowerWad |
| Accordion mirrors | frontend/src/pages/timecurve/useTimecurveProtocolRawAccordion.ts |
Already builds liquidityAnchors from onchain totals |
| Simple rate board | frontend/src/pages/TimeCurveSimplePage.tsx |
Shows 1 CHARM = N DOUB = M CL8Y at launch — keep formulas aligned |
| Stat card pattern | frontend/src/pages/timecurve/timecurveUi.tsx StatCard + .stats-grid |
Match Arena / WarBow stat grids |
| Empty states (#200 (closed)) | EmptyDataPlaceholder, statFromContractRead |
No bare em dashes for loading/gated values |
| Redemption economics | docs/product/primitives.md |
DOUB/CHARM dilutes; implied CL8Y/DOUB rises with totalRaised |
Authoritative rule: Displayed economics must be derivable from onchain views + documented constants — do not invent offchain “market cap” from external oracles on this surface. USD lines may follow existing illustrative pattern (1 CL8Y = $1, same as TOTAL USD on this page — GitLab #192 (closed)).
Recommended solution
1. New UI block on TimeCurveProtocolPage
Add a PageSection (or data-panel data-panel--stack) titled “DOUB projection” with a short lede: Live redemption and launch-liquidity economics from onchain sale totals; full-supply figure is policy constant.
Use a stats-grid of StatCard rows (4 sigfig compact formatting via existing formatBuyHubDerivedCompact / formatCompactFromRaw where appropriate — GitLab #191 (closed)).
2. Proposed stat rows
| Label | Source | Formula / notes |
|---|---|---|
| Projected total supply | Product constant (250M or 251M per sign-off) | Whole-token display with checksum footnote linking to launchplan-timecurve.md §4 |
| Sale bucket (onchain) | TimeCurve.totalTokensForSale() |
Should read 200M when deploy follows launch plan |
| CHARM → DOUB at launch | doubPerCharmAtLaunchWad({ totalTokensForSale, totalCharmWeight }) |
Label as redemption rate (decreases as sale progresses) |
| Implied CL8Y / DOUB (clearing) | projectedReservePerDoubWad(totalRaised, totalTokensForSale) |
Same as accordion “Projected reserve / DOUB” |
| Launch anchor CL8Y / DOUB | launchLiquidityAnchorWad(clearing) |
1.275× clearing — GitLab #158 (closed) |
| Kumbaya band floor | kumbayaBandLowerWad(launchAnchor) |
0.8× launch anchor |
| Implied market cap (CL8Y) | projectedSupplyWei × clearingWad / 1e18 |
Footnote: uses clearing price, not launch anchor |
| Implied market cap (USD illustrative) | marketCapCl8y × static $1/CL8Y |
Same staleness/disclosure pattern as hero TOTAL USD (#192 (closed)) |
| Per-CHARM price (live) | currentPricePerCharmWad() |
Context for launch CL8Y projection chain |
| Sale progress | totalCharmWeight / totalTokensForSale or % of sale bucket “allocated” via weight |
Optional; helps operators |
Reuse protocol page’s existing totalRaiseSerialized / freshness affordance where market cap should show “seen X ago”.
3. Implementation sketch
- Extract a small pure helper module, e.g.
frontend/src/lib/doubProjectionStats.ts, that takes bigint inputs +projectedSupplyWholeTokensand returns formatted stat payloads (unit-tested). - Optional presentational component:
TimeCurveProtocolDoubProjectionSection.tsxto keepTimeCurveProtocolPagereadable. - Wire reads from existing
useTimeCurveProtocolData()/get(3..5,8)indices — no new RPC surface unless product requires total ERC20Doubloon.totalSupply()(not needed for launch projection card). - Do not duplicate accordion-only content: link or repeat the three anchor lines consistently with
liquidityAnchorsin the raw accordion.
4. Out of scope (unless product expands)
- Wallet-level projected DOUB holdings (Simple stake panel policy — dilution UX).
- External price feeds / MegaETH indexers for “real” USD market cap.
- Changing genesis mint policy in contracts (docs-only constant for full supply).
Verification criteria (definition of done)
Automated
- Unit tests in
doubProjectionStats.test.ts(or extendtimeCurvePodiumMath.test.ts): fixturetotalRaised+totalTokensForSale+totalCharmWeightproduce expected CHARM→DOUB, clearing CL8Y/DOUB, launch anchor, market cap at fixed projected supply. - Regression: existing
timeCurvePodiumMathtests remain green. - Vitest for any formatter wiring if extracted.
Manual / visual QA
- Open
/timecurve/protocol(AUDIT tab) withVITE_TIMECURVE_ADDRESSset and sale live on Anvil or devnet. - DOUB projection card is visible without expanding “Raw contract and operator context”.
- As bots/users buy CHARM: CHARM → DOUB decreases; implied CL8Y / DOUB stays flat or increases; cards refresh on ~1s multicall cadence without flicker to em dash (sticky latch behavior preserved).
- Before first buy (
totalCharmWeight == 0): CHARM→DOUB shows intentional empty placeholder per INV-FRONTEND-200, not0orInfinity. - Projected total supply matches signed-off constant (250M or 251M) and footnote matches
launchplan-timecurve.md. - Market cap USD line includes illustrative/static-rate disclosure consistent with TOTAL USD block on same page (#192 (closed)).
- Values in card match expanded accordion Reserve routing and launch anchors rows (clearing / launch / Kumbaya lower) when accordion is opened.
- Mobile:
stats-gridwraps; no horizontal overflow on ≤479px.
E2E (optional but recommended)
- Extend
frontend/e2e/timecurve.spec.tsAUDIT navigation test: assertdata-testid="timecurve-protocol-doub-projection"(or section heading) is present after clicking AUDIT.
Docs / invariants (light touch)
- One-line cross-link in
docs/frontend/timecurve-views.mdunder protocol/AUDIT section. - Optional
INV-FRONTEND-*row indocs/testing/invariants-and-business-logic.mdif maintainers want guardrail coverage.
Dependencies / decisions needed
- Confirm 250M vs 251M projected supply constant.
- Confirm market cap uses clearing CL8Y/DOUB vs launch anchor (recommend clearing for “spot implied”, anchor as secondary row — already listed above).
- Whether card should appear pre-sale (
saleStartPending) with static policy rows only.