Arena: TWAP-derived initial charmPriceWad (~$1 DOUB/CHARM) and DOUB spend band #303

Closed
opened 2026-06-08 08:16:55 +00:00 by PlasticDigits · 19 comments
PlasticDigits commented 2026-06-08 08:16:55 +00:00 (Migrated from gitlab.com)

Summary

Replace the hardcoded charmPriceWad = 1000e18 (1000 DOUB per 1 CHARM) production default with an initial price derived from Kumbaya V3 TWAP on the same DOUB↔WETH and WETH↔USDm pools Sir Trading uses, anchored so ~$1.00 USD of DOUB buys 1 CHARM at arena launch. The fixed onchain CHARM envelope (0.99–10 CHARM) stays unchanged; initial DOUB min/max spend for buys becomes a derived consequence of that TWAP-based charmPriceWad.

Relates to existing INV-TIME-ARENA-DOUB-PRICE (#246) and Kumbaya integration (#251, #264).


Current codebase

Onchain CHARM price (flat admin rate, not a bonding curve)

DOUB min/max spend (derived, not separate onchain params)

There is no doubMin / doubMax storage slot. Spend band is computed:

minDoubSpend = CHARM_MIN_WAD × charmPriceWad / 1e18
maxDoubSpend = CHARM_MAX_WAD × charmPriceWad / 1e18

Used in:

With charmPriceWad = 1000e18, the band is ~990–10,000 DOUB per buy.

Kumbaya pricing today (spot QuoterV2, not TWAP)

  • Swap / quote paths for ETH and USDM entry use Kumbaya QuoterV2 spot quoteExactOutputSingle hops — kumbayaRoutes.ts, kumbayaQuoter.ts, useKumbayaExactOutputQuote.ts.
  • MegaETH mainnet (4326) fee tiers (already in repo): DOUB/WETH = 100 (0.01%), USDm/WETH = 3000 (0.3%) — kumbayaRoutes.ts, docs/integrations/kumbaya.md.
  • CL8Y/USD display on /arena/protocol uses a two-hop spot quote (CL8Y←WETH←USDM) — cl8ySpotUsdPrice.ts, useProtocolCl8yUsdSpotPrice.ts. UI copy refers to “DOUB USD” but the hook still quotes the reserve CL8Y token path (technical debt; out of scope unless needed for parity).
  • No TWAP / observe integration exists in contracts or frontend today. Sir Trading deploys against the Kumbaya V3 factory (0x68b34591… on MegaETH); Sir’s live marks for DOUB/ETH and ETH/USD are expected to use V3 pool TWAP over those same pools — implementers must read Sir’s production TWAP window, observation cardinality, and pool addresses and match them (do not invent a new oracle recipe).

Deploy / ops

  • Production charm price env: ARENA_CHARM_PRICE_WAD — docs/operations/deployment-guide.md.
  • Anvil / E2E keep 1000e18 for deterministic tests unless explicitly fork-testing TWAP init.

Why this change is needed

  1. Market alignment at launch — A flat 1000 DOUB/CHARM ignores live DOUB liquidity on Kumbaya. If DOUB trades away from that implicit dollar peg, entry cost in USD is arbitrary and misaligned with ecosystem liquidity (Sir, Kumbaya pools, AdminSellVault liquidation path).
  2. Explicit $1/CHARM anchor — Product wants ~$1.00 USD notional of DOUB per 1 CHARM at initial launch, computed from manipulation-resistant TWAP, not deployer guesswork or spot quotes.
  3. Consistent DOUB spend envelope — Min/max DOUB per buy should move with the TWAP-derived price while CHARM gameplay bounds (0.99–10) stay fixed for XP, routing, and WarBow economics (#246, #250).
  4. Cross-venue parity — Using the same Kumbaya pools and TWAP methodology as Sir avoids internal contradictions where Arena quotes, Sir marks, and ops runbooks disagree on “what is DOUB worth?”

Constraints and guardrails

Guardrail Requirement
Onchain authority charmPriceWad remains TimeArena storage set at deploy and via setCharmPriceWad; indexer/frontend are derived only (yieldomega-guardrails SKILL).
CHARM envelope immutable Do not change CHARM_MIN_WAD / CHARM_MAX_WAD or XP mapping without a separate spec issue (#246).
Not a bonding curve Arena v2 stays a flat admin rate; TWAP informs initial (and optionally documented governance refresh), not per-block AMM pricing.
TWAP, not spot Initial charmPriceWad must use V3 TWAP (pool observe / OracleLibrary pattern), not QuoterV2 spot — spot is manipulable within a block (security-and-threat-model.md).
Sir parity Use Sir’s TWAP window length, pool fee tiers, and token ordering for DOUB/WETH and WETH/USDm on MegaETH mainnet; document sources (pool addresses, observationCardinality, block time assumptions).
Fail closed If TWAP unavailable, stale, or out of sanity bounds → abort production deploy or fall back to an explicit human-signed ARENA_CHARM_PRICE_WAD override — never silent default to 1000e18 on mainnet without logging.
Anvil determinism DeployDev / E2E may keep 1000e18 or fixture TWAP; do not require live MegaETH RPC for default bash scripts/e2e-anvil.sh.
Buy router parity TimeArenaBuyRouter gross DOUB still charmWad × charmPriceWad / 1e18 — INV-TIME-ARENA-BUY-ROUTER unchanged (#251).
AGPL New libraries/scripts stay AGPL-3.0.

Target formula (initial launch):

Let doubUsdTwap = USD value of 1 DOUB from TWAP (USDM treated ≈ $1):

charmPriceWad = round_to_wei( 1e18 / doubUsdTwap )   // DOUB wei per 1e18 CHARM s.t. ~$1 DOUB buys 1 CHARM
minDoubSpend  = CHARM_MIN_WAD × charmPriceWad / 1e18
maxDoubSpend  = CHARM_MAX_WAD × charmPriceWad / 1e18

Human must confirm rounding (floor vs nearest) and max deviation from spot at deploy block.


Relevant files

Layer Paths
Contracts TimeArena.sol, DeployProduction.s.sol, DeployDev.s.sol, UUPSDeployLib.sol, PARAMETERS.md
New (expected) TWAP helper lib or script (e.g. contracts/script/lib/ArenaCharmPriceTwap.sol or scripts/compute-arena-charm-price-twap.sh), fork test against MegaETH pools
Frontend arenaConstants.ts, timeArenaBuySubmitSizing.ts, arenaV2SaleSessionBridge.ts, useArenaSaleSession.ts, kumbayaRoutes.ts
Docs docs/product/arena-v2.md, docs/operations/deployment-guide.md, docs/integrations/kumbaya.md, docs/testing/invariants-and-business-logic.md
Verify scripts/verify-time-arena-buy-router-anvil.sh, contracts/test/TimeArena.t.sol, optional TimeArenaFork.t.sol

  1. Discovery (blocking) — On MegaETH mainnet, record Sir’s TWAP parameters for:
    • DOUB/WETH pool (fee 100 per repo defaults)
    • USDm/WETH pool (fee 3000)
    • TWAP horizon (seconds), observationCardinality, and whether cardinality was increased beyond default 1.
  2. TWAP library — Add a small Forge-tested helper (Solidity OracleLibrary + pool observe, or scripted cast + eth_call for deploy-only) that returns doubUsdTwap and derived charmPriceWad.
  3. Production deploy — Extend DeployProduction (or pre-deploy script invoked by ops) to:
    • Fork MegaETH or call live RPC at deploy time
    • Compute charmPriceWad from TWAP + $1 anchor
    • Log doubUsdTwap, charmPriceWad, minDoubSpend, maxDoubSpend, block number, pools used
    • Allow ARENA_CHARM_PRICE_WAD override for rehearse / emergency
  4. Sanity bounds — Reject TWAP if outside ops-configured band (e.g. charm price implies < $0.50 or > $2.00 per CHARM) or TWAP older than N seconds.
  5. Docs + invariants — Update PARAMETERS.md, deployment guide, and add INV-TIME-ARENA-CHARM-TWAP-INIT with the formula and Sir parity note.
  6. Frontend — No contract change required if charmPriceWad is set correctly onchain; verify buy slider min/max and Kumbaya exactOutput quotes track the new price. Optional: show “launch TWAP” provenance on /arena/protocol (non-blocking).
  7. Keep DeployDev at 1000e18 unless a fork test flag is set — preserves E2E determinism.

Acceptance criteria

  • Documented Sir/Kumbaya TWAP recipe (pools, fees, window) with mainnet addresses.
  • Production path sets charmPriceWad so 1 CHARM ≈ $1.00 DOUB notional at deploy per TWAP (within documented rounding tolerance).
  • Implied DOUB spend band at launch: 0.99 × charmPriceWad to 10 × charmPriceWad (wei), matching onchain math.
  • ARENA_CHARM_PRICE_WAD manual override still works and is documented.
  • TWAP failure → deploy abort or explicit override; no silent 1000e18 on mainnet.
  • DeployDev / default Anvil unchanged for CI unless opt-in fork test.
  • INV-TIME-ARENA-DOUB-PRICE tests updated: default constant may remain 1000e18 for dev; fork test asserts TWAP init when enabled.
  • Product docs no longer claim 1000 DOUB/CHARM as the only production default.

Test plan (functional paths)

# Path Command / evidence
1 Unit: TWAP math forge test — helper returns expected charmPriceWad for mocked observe ticks
2 Fork: MegaETH TWAP forge test --fork-url $MEGAETH_RPC (or TimeArenaFork.t.sol extension) — live pools, log doubUsdTwap
3 DeployProduction dry-run Script with fork prints charmPriceWad, min/max DOUB; compare to manual spreadsheet
4 Override env ARENA_CHARM_PRICE_WAD=… deploy uses override, skips TWAP
5 Onchain buy band cast call charmPriceWad + assert buy(99e16) succeeds and buy(10e18+1) reverts
6 DOUB direct buy TimeArena.t.sol — doubOwed matches new price
7 Kumbaya buy router bash scripts/verify-time-arena-buy-router-anvil.sh — gross DOUB = charmWad × charmPriceWad / 1e18
8 Frontend sizing cd frontend && npm test — timeArenaBuySubmitSizing.test.ts, arenaV2SaleSessionBridge.test.ts with non-1000 price fixture
9 Frontend typecheck/lint cd frontend && npm run typecheck && npm run lint
10 Indexer No schema change expected; optional smoke bash scripts/verify-podium-live-anvil.sh after deploy with new price
11 E2E regression bash scripts/e2e-anvil.sh — still PASS with dev 1000e18

Test plan (attack, hack, and abuse vectors)

Vector Risk Test / mitigation
Single-block spot manipulation Attacker moves pool spot before deploy to skew price TWAP window ≥ Sir’s; reject if spot vs TWAP deviation > X bps; fork test with artificial spot skew
Low observation cardinality TWAP ≈ spot when cardinality = 1 Assert pools have observationCardinality ≥ minimum; document increaseObservationCardinalityNext ops if needed
Stale / zero liquidity TWAP reverts or returns garbage Fail-closed deploy; sanity min/max charmPriceWad
Governance front-run Owner sets malicious setCharmPriceWad after fair TWAP init Out of scope for init issue; note timelock follow-up; test that init price is emitted in deploy logs
Kumbaya path mismatch Wrong fee tier → wrong USD mark Assert fee 100 / 3000 match kumbayaRoutes.ts and integrator-kit
Rounding exploit User buys at floor CHARM to pay less DOUB Existing testFuzz_buy_charmInBand_doubPullParity with TWAP-derived price
ETH/USDM entry arbitrage Router exactOutput vs stale charmPriceWad Re-run TimeArenaBuyRouter.t.sol + submit-time headroom (#82)
USDm depeg $1 anchor wrong if USDm ≠ $1 Document assumption; optional secondary bound vs ETH-only leg
Deploy replay Same TWAP block reused across chains Bind computation to chain id + block number in logs

Verification criteria (merge gate)

Item Command PASS
Contracts cd contracts && forge test All non-fork tests green; fork TWAP test PASS when MEGAETH_RPC set (CI may SKIP)
TWAP script bash scripts/compute-arena-charm-price-twap.sh (or equivalent added by MR) Prints charmPriceWad, min/max DOUB, exits 0 on mainnet fork
Buy router bash scripts/verify-time-arena-buy-router-anvil.sh PASS
Frontend cd frontend && npm run typecheck && npm run lint && npm test PASS
Docs PARAMETERS.md + deployment guide updated Reviewer sign-off
Invariant New INV-TIME-ARENA-CHARM-TWAP-INIT row in invariants-and-business-logic.md Linked to tests

MR checklist row: TWAP charm init → forge test + fork script → PASS/FAIL.


Out of scope

  • Continuous onchain oracle updating charmPriceWad each block (governance refresh is separate).
  • Changing CHARM min/max band or XP curve.
  • Fixing protocol page CL8Y vs DOUB USD label (unless required for launch TWAP display).
  • Sir frontend integration (outbound /sir link only today).

Open questions (resolve before implementation)

  1. Exact Sir TWAP seconds and pool addresses on MegaETH mainnet at implementation time.
  2. Rounding rule for charmPriceWad (floor recommended for protocol-favorable).
  3. Whether post-launch setCharmPriceWad must also be TWAP-gated or remains free-form owner.
  4. Minimum observation cardinality acceptable for production TWAP.
## Summary Replace the hardcoded **`charmPriceWad = 1000e18`** (1000 DOUB per 1 CHARM) production default with an **initial price derived from Kumbaya V3 TWAP** on the same **DOUB↔WETH** and **WETH↔USDm** pools Sir Trading uses, anchored so **~$1.00 USD of DOUB buys 1 CHARM** at arena launch. The fixed onchain CHARM envelope (**0.99–10 CHARM**) stays unchanged; **initial DOUB min/max spend** for buys becomes a derived consequence of that TWAP-based `charmPriceWad`. Relates to existing **`INV-TIME-ARENA-DOUB-PRICE`** ([#246](https://gitlab.com/PlasticDigits/yieldomega/-/issues/246)) and Kumbaya integration ([#251](https://gitlab.com/PlasticDigits/yieldomega/-/issues/251), [#264](https://gitlab.com/PlasticDigits/yieldomega/-/issues/264)). --- ## Current codebase ### Onchain CHARM price (flat admin rate, not a bonding curve) - **`TimeArena.charmPriceWad`** — DOUB wei owed per 1e18 CHARM: `doubOwed = charmWad × charmPriceWad / 1e18` ([`TimeArena.sol`](../../contracts/src/arena/TimeArena.sol)). - **Default `1000e18`** everywhere production-like deploy runs: - [`DeployProduction.s.sol`](../../contracts/script/DeployProduction.s.sol) — `DEFAULT_CHARM_PRICE_WAD = 1000e18`, overridable via `ARENA_CHARM_PRICE_WAD`. - [`DeployDev.s.sol`](../../contracts/script/DeployDev.s.sol) — hardcoded `1000e18` passed to `UUPSDeployLib.deployTimeArenaProductionDefaults`. - [`PARAMETERS.md`](../../contracts/PARAMETERS.md), [`docs/product/arena-v2.md`](../../docs/product/arena-v2.md), [`docs/product/time-arena.md`](../../docs/product/time-arena.md). - Governance may update anytime via **`setCharmPriceWad`** (`onlyOwner`); no timelock today. - **CHARM buy envelope** is fixed onchain: **`CHARM_MIN_WAD = 99e16`** (0.99), **`CHARM_MAX_WAD = 10e18`** (10) — [`TimeArena.sol`](../../contracts/src/arena/TimeArena.sol), [`ArenaXp.sol`](../../contracts/src/arena/libraries/ArenaXp.sol), frontend [`arenaConstants.ts`](../../frontend/src/lib/arenaConstants.ts). ### DOUB min/max spend (derived, not separate onchain params) There is **no** `doubMin` / `doubMax` storage slot. Spend band is computed: ``` minDoubSpend = CHARM_MIN_WAD × charmPriceWad / 1e18 maxDoubSpend = CHARM_MAX_WAD × charmPriceWad / 1e18 ``` Used in: - [`timeArenaBuySubmitSizing.ts`](../../frontend/src/lib/timeArenaBuySubmitSizing.ts) — submit-time sizing + headroom ([#82](https://gitlab.com/PlasticDigits/yieldomega/-/issues/82)). - [`arenaV2SaleSessionBridge.ts`](../../frontend/src/pages/arena/arenaV2SaleSessionBridge.ts) — protocol accordion / envelope display. - [`ArenaLiveBuysActivitySection.tsx`](../../frontend/src/pages/arena/ArenaLiveBuysActivitySection.tsx) — buy envelope fill ratio in activity feed. - [`useArenaSaleSession.ts`](../../frontend/src/pages/arena/useArenaSaleSession.ts) — buy CTA, Kumbaya quotes sized to gross DOUB. With **`charmPriceWad = 1000e18`**, the band is **~990–10,000 DOUB** per buy. ### Kumbaya pricing today (spot QuoterV2, not TWAP) - **Swap / quote paths** for ETH and USDM entry use **Kumbaya QuoterV2 spot `quoteExactOutputSingle`** hops — [`kumbayaRoutes.ts`](../../frontend/src/lib/kumbayaRoutes.ts), [`kumbayaQuoter.ts`](../../frontend/src/lib/kumbayaQuoter.ts), [`useKumbayaExactOutputQuote.ts`](../../frontend/src/hooks/useKumbayaExactOutputQuote.ts). - **MegaETH mainnet (4326) fee tiers** (already in repo): **DOUB/WETH = 100 (0.01%)**, **USDm/WETH = 3000 (0.3%)** — [`kumbayaRoutes.ts`](../../frontend/src/lib/kumbayaRoutes.ts), [`docs/integrations/kumbaya.md`](../../docs/integrations/kumbaya.md). - **CL8Y/USD display** on `/arena/protocol` uses a **two-hop spot quote** (CL8Y←WETH←USDM) — [`cl8ySpotUsdPrice.ts`](../../frontend/src/lib/cl8ySpotUsdPrice.ts), [`useProtocolCl8yUsdSpotPrice.ts`](../../frontend/src/hooks/useProtocolCl8yUsdSpotPrice.ts). UI copy refers to “DOUB USD” but the hook still quotes the **reserve CL8Y** token path (technical debt; out of scope unless needed for parity). - **No TWAP / `observe` integration** exists in contracts or frontend today. Sir Trading deploys against the **Kumbaya V3 factory** (`0x68b34591…` on MegaETH); Sir’s live marks for DOUB/ETH and ETH/USD are expected to use **V3 pool TWAP** over those same pools — implementers must **read Sir’s production TWAP window, observation cardinality, and pool addresses** and match them (do not invent a new oracle recipe). ### Deploy / ops - Production charm price env: `ARENA_CHARM_PRICE_WAD` — [`docs/operations/deployment-guide.md`](../../docs/operations/deployment-guide.md). - Anvil / E2E keep **`1000e18`** for deterministic tests unless explicitly fork-testing TWAP init. --- ## Why this change is needed 1. **Market alignment at launch** — A flat **1000 DOUB/CHARM** ignores live DOUB liquidity on Kumbaya. If DOUB trades away from that implicit dollar peg, entry cost in USD is arbitrary and misaligned with ecosystem liquidity (Sir, Kumbaya pools, AdminSellVault liquidation path). 2. **Explicit $1/CHARM anchor** — Product wants **~$1.00 USD notional of DOUB per 1 CHARM** at initial launch, computed from **manipulation-resistant TWAP**, not deployer guesswork or spot quotes. 3. **Consistent DOUB spend envelope** — Min/max DOUB per buy should move with the TWAP-derived price while **CHARM gameplay bounds (0.99–10)** stay fixed for XP, routing, and WarBow economics ([#246](https://gitlab.com/PlasticDigits/yieldomega/-/issues/246), [#250](https://gitlab.com/PlasticDigits/yieldomega/-/issues/250)). 4. **Cross-venue parity** — Using the **same Kumbaya pools and TWAP methodology as Sir** avoids internal contradictions where Arena quotes, Sir marks, and ops runbooks disagree on “what is DOUB worth?” --- ## Constraints and guardrails | Guardrail | Requirement | |-----------|-------------| | **Onchain authority** | `charmPriceWad` remains **`TimeArena` storage** set at deploy and via **`setCharmPriceWad`**; indexer/frontend are derived only ([`yieldomega-guardrails` SKILL](../../.cursor/skills/yieldomega-guardrails/SKILL.md)). | | **CHARM envelope immutable** | Do **not** change **`CHARM_MIN_WAD` / `CHARM_MAX_WAD`** or XP mapping without a separate spec issue ([#246](https://gitlab.com/PlasticDigits/yieldomega/-/issues/246)). | | **Not a bonding curve** | Arena v2 stays a **flat admin rate**; TWAP informs **initial** (and optionally documented governance refresh), not per-block AMM pricing. | | **TWAP, not spot** | Initial `charmPriceWad` must use **V3 TWAP** (pool `observe` / `OracleLibrary` pattern), **not** QuoterV2 spot — spot is manipulable within a block ([security-and-threat-model.md](../../docs/onchain/security-and-threat-model.md)). | | **Sir parity** | Use Sir’s **TWAP window length**, **pool fee tiers**, and **token ordering** for DOUB/WETH and WETH/USDm on MegaETH mainnet; document sources (pool addresses, `observationCardinality`, block time assumptions). | | **Fail closed** | If TWAP unavailable, stale, or out of sanity bounds → **abort production deploy** or fall back to an explicit human-signed `ARENA_CHARM_PRICE_WAD` override — never silent default to `1000e18` on mainnet without logging. | | **Anvil determinism** | **`DeployDev` / E2E** may keep `1000e18` or fixture TWAP; do not require live MegaETH RPC for default `bash scripts/e2e-anvil.sh`. | | **Buy router parity** | `TimeArenaBuyRouter` gross DOUB still `charmWad × charmPriceWad / 1e18` — **`INV-TIME-ARENA-BUY-ROUTER`** unchanged ([#251](https://gitlab.com/PlasticDigits/yieldomega/-/issues/251)). | | **AGPL** | New libraries/scripts stay AGPL-3.0. | **Target formula (initial launch):** Let `doubUsdTwap` = USD value of **1 DOUB** from TWAP (USDM treated ≈ $1): ``` charmPriceWad = round_to_wei( 1e18 / doubUsdTwap ) // DOUB wei per 1e18 CHARM s.t. ~$1 DOUB buys 1 CHARM minDoubSpend = CHARM_MIN_WAD × charmPriceWad / 1e18 maxDoubSpend = CHARM_MAX_WAD × charmPriceWad / 1e18 ``` Human must confirm rounding (floor vs nearest) and max deviation from spot at deploy block. --- ## Relevant files | Layer | Paths | |-------|-------| | **Contracts** | [`TimeArena.sol`](../../contracts/src/arena/TimeArena.sol), [`DeployProduction.s.sol`](../../contracts/script/DeployProduction.s.sol), [`DeployDev.s.sol`](../../contracts/script/DeployDev.s.sol), [`UUPSDeployLib.sol`](../../contracts/script/UUPSDeployLib.sol), [`PARAMETERS.md`](../../contracts/PARAMETERS.md) | | **New (expected)** | TWAP helper lib or script (e.g. `contracts/script/lib/ArenaCharmPriceTwap.sol` or `scripts/compute-arena-charm-price-twap.sh`), fork test against MegaETH pools | | **Frontend** | [`arenaConstants.ts`](../../frontend/src/lib/arenaConstants.ts), [`timeArenaBuySubmitSizing.ts`](../../frontend/src/lib/timeArenaBuySubmitSizing.ts), [`arenaV2SaleSessionBridge.ts`](../../frontend/src/pages/arena/arenaV2SaleSessionBridge.ts), [`useArenaSaleSession.ts`](../../frontend/src/pages/arena/useArenaSaleSession.ts), [`kumbayaRoutes.ts`](../../frontend/src/lib/kumbayaRoutes.ts) | | **Docs** | [`docs/product/arena-v2.md`](../../docs/product/arena-v2.md), [`docs/operations/deployment-guide.md`](../../docs/operations/deployment-guide.md), [`docs/integrations/kumbaya.md`](../../docs/integrations/kumbaya.md), [`docs/testing/invariants-and-business-logic.md`](../../docs/testing/invariants-and-business-logic.md) | | **Verify** | [`scripts/verify-time-arena-buy-router-anvil.sh`](../../scripts/verify-time-arena-buy-router-anvil.sh), [`contracts/test/TimeArena.t.sol`](../../contracts/test/TimeArena.t.sol), optional [`TimeArenaFork.t.sol`](../../contracts/test/TimeArenaFork.t.sol) | --- ## Recommended direction 1. **Discovery (blocking)** — On MegaETH mainnet, record Sir’s TWAP parameters for: - DOUB/WETH pool (fee **100** per repo defaults) - USDm/WETH pool (fee **3000**) - TWAP horizon (seconds), `observationCardinality`, and whether cardinality was increased beyond default `1`. 2. **TWAP library** — Add a small **Forge-tested** helper (Solidity `OracleLibrary` + pool `observe`, or scripted `cast` + `eth_call` for deploy-only) that returns **`doubUsdTwap`** and derived **`charmPriceWad`**. 3. **Production deploy** — Extend **`DeployProduction`** (or pre-deploy script invoked by ops) to: - Fork MegaETH or call live RPC at deploy time - Compute `charmPriceWad` from TWAP + $1 anchor - Log `doubUsdTwap`, `charmPriceWad`, `minDoubSpend`, `maxDoubSpend`, block number, pools used - Allow **`ARENA_CHARM_PRICE_WAD`** override for rehearse / emergency 4. **Sanity bounds** — Reject TWAP if outside ops-configured band (e.g. charm price implies &lt; $0.50 or &gt; $2.00 per CHARM) or TWAP older than N seconds. 5. **Docs + invariants** — Update **`PARAMETERS.md`**, deployment guide, and add **`INV-TIME-ARENA-CHARM-TWAP-INIT`** with the formula and Sir parity note. 6. **Frontend** — No contract change required if `charmPriceWad` is set correctly onchain; verify buy slider min/max and Kumbaya `exactOutput` quotes track the new price. Optional: show “launch TWAP” provenance on `/arena/protocol` (non-blocking). 7. **Keep `DeployDev` at `1000e18`** unless a fork test flag is set — preserves E2E determinism. --- ## Acceptance criteria - [ ] Documented Sir/Kumbaya TWAP recipe (pools, fees, window) with mainnet addresses. - [ ] Production path sets **`charmPriceWad`** so **1 CHARM ≈ $1.00 DOUB notional** at deploy per TWAP (within documented rounding tolerance). - [ ] **Implied DOUB spend band** at launch: `0.99 × charmPriceWad` to `10 × charmPriceWad` (wei), matching onchain math. - [ ] **`ARENA_CHARM_PRICE_WAD`** manual override still works and is documented. - [ ] TWAP failure → deploy abort or explicit override; no silent `1000e18` on mainnet. - [ ] **`DeployDev` / default Anvil** unchanged for CI unless opt-in fork test. - [ ] **`INV-TIME-ARENA-DOUB-PRICE`** tests updated: default constant may remain `1000e18` for dev; fork test asserts TWAP init when enabled. - [ ] Product docs no longer claim **1000 DOUB/CHARM** as the only production default. --- ## Test plan (functional paths) | # | Path | Command / evidence | |---|------|-------------------| | 1 | **Unit: TWAP math** | `forge test` — helper returns expected `charmPriceWad` for mocked `observe` ticks | | 2 | **Fork: MegaETH TWAP** | `forge test --fork-url $MEGAETH_RPC` (or `TimeArenaFork.t.sol` extension) — live pools, log `doubUsdTwap` | | 3 | **DeployProduction dry-run** | Script with fork prints `charmPriceWad`, min/max DOUB; compare to manual spreadsheet | | 4 | **Override env** | `ARENA_CHARM_PRICE_WAD=…` deploy uses override, skips TWAP | | 5 | **Onchain buy band** | `cast call` `charmPriceWad` + assert `buy(99e16)` succeeds and `buy(10e18+1)` reverts | | 6 | **DOUB direct buy** | `TimeArena.t.sol` — `doubOwed` matches new price | | 7 | **Kumbaya buy router** | `bash scripts/verify-time-arena-buy-router-anvil.sh` — gross DOUB = `charmWad × charmPriceWad / 1e18` | | 8 | **Frontend sizing** | `cd frontend && npm test` — `timeArenaBuySubmitSizing.test.ts`, `arenaV2SaleSessionBridge.test.ts` with non-1000 price fixture | | 9 | **Frontend typecheck/lint** | `cd frontend && npm run typecheck && npm run lint` | | 10 | **Indexer** | No schema change expected; optional smoke `bash scripts/verify-podium-live-anvil.sh` after deploy with new price | | 11 | **E2E regression** | `bash scripts/e2e-anvil.sh` — still PASS with dev `1000e18` | --- ## Test plan (attack, hack, and abuse vectors) | Vector | Risk | Test / mitigation | |--------|------|-------------------| | **Single-block spot manipulation** | Attacker moves pool spot before deploy to skew price | TWAP window ≥ Sir’s; reject if spot vs TWAP deviation &gt; X bps; fork test with artificial spot skew | | **Low observation cardinality** | TWAP ≈ spot when cardinality = 1 | Assert pools have `observationCardinality` ≥ minimum; document `increaseObservationCardinalityNext` ops if needed | | **Stale / zero liquidity** | TWAP reverts or returns garbage | Fail-closed deploy; sanity min/max `charmPriceWad` | | **Governance front-run** | Owner sets malicious `setCharmPriceWad` after fair TWAP init | Out of scope for init issue; note timelock follow-up; test that init price is emitted in deploy logs | | **Kumbaya path mismatch** | Wrong fee tier → wrong USD mark | Assert fee **100** / **3000** match [`kumbayaRoutes.ts`](../../frontend/src/lib/kumbayaRoutes.ts) and integrator-kit | | **Rounding exploit** | User buys at floor CHARM to pay less DOUB | Existing `testFuzz_buy_charmInBand_doubPullParity` with TWAP-derived price | | **ETH/USDM entry arbitrage** | Router `exactOutput` vs stale `charmPriceWad` | Re-run `TimeArenaBuyRouter.t.sol` + submit-time headroom ([#82](https://gitlab.com/PlasticDigits/yieldomega/-/issues/82)) | | **USDm depeg** | $1 anchor wrong if USDm ≠ $1 | Document assumption; optional secondary bound vs ETH-only leg | | **Deploy replay** | Same TWAP block reused across chains | Bind computation to chain id + block number in logs | --- ## Verification criteria (merge gate) | Item | Command | PASS | |------|---------|------| | Contracts | `cd contracts && forge test` | All non-fork tests green; fork TWAP test PASS when `MEGAETH_RPC` set (CI may SKIP) | | TWAP script | `bash scripts/compute-arena-charm-price-twap.sh` (or equivalent added by MR) | Prints `charmPriceWad`, min/max DOUB, exits 0 on mainnet fork | | Buy router | `bash scripts/verify-time-arena-buy-router-anvil.sh` | PASS | | Frontend | `cd frontend && npm run typecheck && npm run lint && npm test` | PASS | | Docs | `PARAMETERS.md` + deployment guide updated | Reviewer sign-off | | Invariant | New **`INV-TIME-ARENA-CHARM-TWAP-INIT`** row in [`invariants-and-business-logic.md`](../../docs/testing/invariants-and-business-logic.md) | Linked to tests | **MR checklist row:** TWAP charm init → `forge test` + fork script → PASS/FAIL. --- ## Out of scope - Continuous onchain oracle updating `charmPriceWad` each block (governance refresh is separate). - Changing CHARM min/max band or XP curve. - Fixing protocol page CL8Y vs DOUB USD label (unless required for launch TWAP display). - Sir frontend integration (outbound `/sir` link only today). --- ## Open questions (resolve before implementation) 1. Exact **Sir TWAP seconds** and **pool addresses** on MegaETH mainnet at implementation time. 2. **Rounding** rule for `charmPriceWad` (floor recommended for protocol-favorable). 3. Whether **post-launch** `setCharmPriceWad` must also be TWAP-gated or remains free-form owner. 4. Minimum **observation cardinality** acceptable for production TWAP.
project_80561744_bot_5118c68a88bff95eacb6ab1c47c19cae commented 2026-06-08 08:38:27 +00:00 (Migrated from gitlab.com)

mentioned in commit 69e05c9ac0

mentioned in commit 69e05c9ac07628cea8f0e700d67dd529c59cfb28
PlasticDigits commented 2026-06-08 08:38:45 +00:00 (Migrated from gitlab.com)

mentioned in merge request !447

mentioned in merge request !447
PlasticDigits commented 2026-06-08 08:38:51 +00:00 (Migrated from gitlab.com)

Implementation MR: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/77

Summary: Production charmPriceWad now comes from Kumbaya V3 TWAP (Sir 15m) on DOUB/WETH + WETH/USDm unless ARENA_CHARM_PRICE_WAD is set. DeployDev/Anvil unchanged at 1000e18.

Blocker for live TWAP: DOUB/WETH pool (fee 100) is not deployed on MegaETH mainnet yet — bash scripts/compute-arena-charm-price-twap.sh fails closed with missing DOUB/WETH pool. Production deploy needs pool liquidity or explicit override until then.

See MR checklist for PASS/FAIL/SKIP per acceptance item.

Implementation MR: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/77 **Summary:** Production `charmPriceWad` now comes from Kumbaya V3 TWAP (Sir 15m) on DOUB/WETH + WETH/USDm unless `ARENA_CHARM_PRICE_WAD` is set. DeployDev/Anvil unchanged at `1000e18`. **Blocker for live TWAP:** DOUB/WETH pool (fee 100) is not deployed on MegaETH mainnet yet — `bash scripts/compute-arena-charm-price-twap.sh` fails closed with `missing DOUB/WETH pool`. Production deploy needs pool liquidity or explicit override until then. See MR checklist for PASS/FAIL/SKIP per acceptance item.
PlasticDigits commented 2026-06-08 08:45:13 +00:00 (Migrated from gitlab.com)

mentioned in commit c67aef1866

mentioned in commit c67aef1866b387cf629bab26ade965b89aa146cd
PlasticDigits commented 2026-06-09 07:13:32 +00:00 (Migrated from gitlab.com)

mentioned in commit 9390010dea

mentioned in commit 9390010deac4984b1e139944d4c3e4c12945b608
PlasticDigits commented 2026-06-09 07:13:59 +00:00 (Migrated from gitlab.com)

mentioned in merge request !450

mentioned in merge request !450
PlasticDigits commented 2026-06-09 07:14:12 +00:00 (Migrated from gitlab.com)

Follow-up MR: !450

Core TWAP implementation landed in !447 (merged). This follow-up closes remaining acceptance gaps from the issue checklist.

What changed (!450)

Verification checklist

  • Review MR !450 diff (docs + tests only; no contract changes)
  • cd contracts && forge test --match-contract ArenaCharmPriceTwap — expect PASS (5/5)
  • bash scripts/verify-time-arena-buy-router-anvil.sh — expect PASS (#251 router parity)
  • cd frontend && npm run typecheck && npm test -- --run src/lib/timeArenaBuySubmitSizing.test.ts src/pages/arena/arenaV2SaleSessionBridge.test.ts — expect PASS
  • bash scripts/compute-arena-charm-price-twap.sh — expect FAIL until DOUB/WETH pool (fee 100) is live on MegaETH; use ARENA_CHARM_PRICE_WAD for rehearsal until then

Follow-ups (when pool exists)

  • Re-run bash scripts/compute-arena-charm-price-twap.sh and FORK_URL=… forge test --match-contract ArenaCharmPriceTwapFork — should move from SKIP/FAIL to PASS
  • Optional (non-blocking): show launch TWAP provenance on /arena/protocol

Issue stays open until DOUB/WETH pool is deployed and live TWAP dry-run passes, or ops explicitly accepts override-only production path.

## Follow-up MR: !450 Core TWAP implementation landed in !447 (merged). This follow-up closes remaining acceptance gaps from the issue checklist. ### What changed (!450) - Dedicated **§303** section in [`invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md#arena-charm-twap-init-gitlab-303) with live-mainnet pool note - **`INV-TIME-ARENA-CHARM-TWAP-INIT`** cross-linked in [`skills/play-time-arena-doub/SKILL.md`](skills/play-time-arena-doub/SKILL.md), [`skills/README.md`](skills/README.md), and [yieldomega-guardrails](.cursor/skills/yieldomega-guardrails/SKILL.md) - **`INV-TIME-ARENA-DOUB-PRICE`** ([#246](https://gitlab.com/PlasticDigits/yieldomega/-/issues/246)) now references production TWAP init - Fixed **AGENTS.md** commit-msg hook reference (was incorrectly linked to #303) - Frontend unit tests for **non-dev `charmPriceWad`** (2500 DOUB/CHARM TWAP-like fixture) in `timeArenaBuySubmitSizing.test.ts` and `arenaV2SaleSessionBridge.test.ts` ### Verification checklist - [ ] Review MR !450 diff (docs + tests only; no contract changes) - [ ] `cd contracts && forge test --match-contract ArenaCharmPriceTwap` — expect **PASS** (5/5) - [ ] `bash scripts/verify-time-arena-buy-router-anvil.sh` — expect **PASS** ([#251](https://gitlab.com/PlasticDigits/yieldomega/-/issues/251) router parity) - [ ] `cd frontend && npm run typecheck && npm test -- --run src/lib/timeArenaBuySubmitSizing.test.ts src/pages/arena/arenaV2SaleSessionBridge.test.ts` — expect **PASS** - [ ] `bash scripts/compute-arena-charm-price-twap.sh` — expect **FAIL** until DOUB/WETH pool (fee 100) is live on MegaETH; use `ARENA_CHARM_PRICE_WAD` for rehearsal until then ### Follow-ups (when pool exists) - Re-run `bash scripts/compute-arena-charm-price-twap.sh` and `FORK_URL=… forge test --match-contract ArenaCharmPriceTwapFork` — should move from SKIP/FAIL to PASS - Optional (non-blocking): show launch TWAP provenance on `/arena/protocol` Issue stays **open** until DOUB/WETH pool is deployed and live TWAP dry-run passes, or ops explicitly accepts override-only production path.
PlasticDigits commented 2026-06-09 07:38:38 +00:00 (Migrated from gitlab.com)

mentioned in commit eec8af160d

mentioned in commit eec8af160d7da6c8c78f5e4b6bcdd77d42b46813
PlasticDigits commented 2026-06-09 07:50:45 +00:00 (Migrated from gitlab.com)

mentioned in commit e9072f0114

mentioned in commit e9072f011479857f8044b4abdd0287bbbe0adfde
PlasticDigits commented 2026-06-09 07:50:47 +00:00 (Migrated from gitlab.com)

TWAP pool path update (!450)

Per product direction: production charm TWAP now uses DOUB/CL8Y (100) → CL8Y/WETH (100) → WETH/USDm (3000) instead of direct DOUB/WETH. Matches the live Kumbaya DOUB/CL8Y spot pool and reserve-asset routing.

Verification checklist

  • cd contracts && forge test --match-contract ArenaCharmPriceTwap — PASS (5/5)
  • FORK_URL=https://mainnet.megaeth.com/rpc forge test --match-test test_fork_megaeth_twap_charm_price — PASS (logs doubUsdWad, charmPriceWad)
  • bash scripts/compute-arena-charm-price-twap.sh — PASS on current MegaETH head
  • Docs: INV-TIME-ARENA-CHARM-TWAP-INIT, kumbaya.md, deployment guide, PARAMETERS.md, play skill — pool recipe updated

Live head (example)

  • doubUsdWad ≈ 2.45e13 (~$0.0000245/DOUB)
  • charmPriceWad ≈ 4.08e22 DOUB/CHARM (~$1/CHARM anchor)

Issue remains open for merge review of !450.

## TWAP pool path update (!450) Per product direction: production charm TWAP now uses **DOUB/CL8Y (100)** → **CL8Y/WETH (100)** → **WETH/USDm (3000)** instead of direct **DOUB/WETH**. Matches the live Kumbaya **DOUB/CL8Y** spot pool and reserve-asset routing. ### Verification checklist - [ ] `cd contracts && forge test --match-contract ArenaCharmPriceTwap` — **PASS** (5/5) - [ ] `FORK_URL=https://mainnet.megaeth.com/rpc forge test --match-test test_fork_megaeth_twap_charm_price` — **PASS** (logs `doubUsdWad`, `charmPriceWad`) - [ ] `bash scripts/compute-arena-charm-price-twap.sh` — **PASS** on current MegaETH head - [ ] Docs: `INV-TIME-ARENA-CHARM-TWAP-INIT`, `kumbaya.md`, deployment guide, `PARAMETERS.md`, play skill — pool recipe updated ### Live head (example) - `doubUsdWad` ≈ 2.45e13 (~\$0.0000245/DOUB) - `charmPriceWad` ≈ 4.08e22 DOUB/CHARM (~\$1/CHARM anchor) Issue remains **open** for merge review of !450.
PlasticDigits commented 2026-06-09 08:05:26 +00:00 (Migrated from gitlab.com)

mentioned in commit 5dffb56c2b

mentioned in commit 5dffb56c2baa7143e34381291ee1cb5a7314ee74
PlasticDigits commented 2026-06-09 09:57:25 +00:00 (Migrated from gitlab.com)

mentioned in commit d8775620e8

mentioned in commit d8775620e8286006bef4184ef2acb8254974ce7f
PlasticDigits commented 2026-06-09 09:57:50 +00:00 (Migrated from gitlab.com)

mentioned in merge request !451

mentioned in merge request !451
PlasticDigits commented 2026-06-09 09:58:29 +00:00 (Migrated from gitlab.com)

Verification complete — MR !451

Branch local1/303-verify-charm-twap · MegaETH head block 18201912 (2026-06-09).

What was verified

Implementation from !447 + !450 is correct: DOUB/CL8Y (100) → CL8Y/WETH (100) → WETH/USDm (3000) Sir 15m TWAP, charmPriceWad = floor(1e36 / doubUsdWad), fail-closed on 4326 without override, DeployDev still 1000e18.

Fixes in verification MR

  • bash scripts/verify-arena-charm-twap.sh — orchestrates unit + fork + dry-run + fail-closed tests
  • DeployProductionCharmPrice.t.sol — non-4326 revert without ARENA_CHARM_PRICE_WAD
  • Re-enable core RPC multicall when VITE_INDEXER_URL= (Anvil E2E) while keeping indexer-first when indexer is configured (#301)
  • Playwright helpers updated for rate-board pay-token picker portal

Checklist

  • forge test --match-contract ArenaCharmPriceTwap — PASS (5/5)
  • FORK_URL=… forge test --match-test test_fork_megaeth_twap_charm_price — PASS
  • bash scripts/compute-arena-charm-price-twap.sh — PASS
  • bash scripts/verify-arena-charm-twap.sh — PASS
  • ARENA_CHARM_PRICE_WAD override on MegaETH fork — PASS
  • bash scripts/verify-time-arena-buy-router-anvil.sh — PASS
  • cd frontend && npm run typecheck && npm test — PASS
  • bash scripts/e2e-anvil.sh — PASS (6/6)
  • bash scripts/check-doc-anchors.sh — PASS
  • INV-TIME-ARENA-CHARM-TWAP-INIT cross-linked in invariants §303 + skills

MR: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/81

Closing — all acceptance criteria and verification gates pass.

## Verification complete — MR !451 Branch `local1/303-verify-charm-twap` · MegaETH head block **18201912** (2026-06-09). ### What was verified Implementation from !447 + !450 is correct: **DOUB/CL8Y (100) → CL8Y/WETH (100) → WETH/USDm (3000)** Sir **15m** TWAP, `charmPriceWad = floor(1e36 / doubUsdWad)`, fail-closed on 4326 without override, **DeployDev** still **`1000e18`**. ### Fixes in verification MR - `bash scripts/verify-arena-charm-twap.sh` — orchestrates unit + fork + dry-run + fail-closed tests - `DeployProductionCharmPrice.t.sol` — non-4326 revert without `ARENA_CHARM_PRICE_WAD` - Re-enable core RPC multicall when `VITE_INDEXER_URL=` (Anvil E2E) while keeping indexer-first when indexer is configured (#301) - Playwright helpers updated for rate-board pay-token picker portal ### Checklist - [x] `forge test --match-contract ArenaCharmPriceTwap` — PASS (5/5) - [x] `FORK_URL=… forge test --match-test test_fork_megaeth_twap_charm_price` — PASS - [x] `bash scripts/compute-arena-charm-price-twap.sh` — PASS - [x] `bash scripts/verify-arena-charm-twap.sh` — PASS - [x] `ARENA_CHARM_PRICE_WAD` override on MegaETH fork — PASS - [x] `bash scripts/verify-time-arena-buy-router-anvil.sh` — PASS - [x] `cd frontend && npm run typecheck && npm test` — PASS - [x] `bash scripts/e2e-anvil.sh` — PASS (6/6) - [x] `bash scripts/check-doc-anchors.sh` — PASS - [x] `INV-TIME-ARENA-CHARM-TWAP-INIT` cross-linked in invariants §303 + skills MR: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/81 Closing — all acceptance criteria and verification gates pass.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-09 09:58:29 +00:00
PlasticDigits commented 2026-06-10 09:09:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #305

mentioned in issue #305
PlasticDigits commented 2026-06-10 09:09:15 +00:00 (Migrated from gitlab.com)

marked as related to #305

marked as related to #305
PlasticDigits commented 2026-06-11 05:44:54 +00:00 (Migrated from gitlab.com)

mentioned in commit 3db1539c92

mentioned in commit 3db1539c92025a37bf43705f5bc0f989c1d91e44
PlasticDigits commented 2026-06-21 03:43:53 +00:00 (Migrated from gitlab.com)

mentioned in issue #341

mentioned in issue #341
PlasticDigits commented 2026-07-07 12:54:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #367

mentioned in issue #367
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#303
No description provided.