Arena: epoch-anchored TWAP charm price +10%/day growth; re-anchor on Last Buy reset #305
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#305
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replace the flat
TimeArena.charmPriceWad(governance/TWAP-at-deploy only) with Last Buy epoch–scoped DOUB pricing:charmPriceWadfrom Kumbaya 15m TWAP at each Last Buy epoch start (hard reset →LastBuyEpochStarted).CRED_PER_CHARM_WAD = 100e18forever — only DOUBbuy/ router path uses the growing epoch price.Supersedes the “flat admin rate” semantics documented in #303 for live pricing (TWAP remains the epoch anchor source, not a continuous updater). CHARM envelope 0.99–10 stays fixed (#246).
Motivation / product examples
buyWithCredalways burns 100 CRED × charmWad / 1e18 — no epoch anchor, no growth.Acceptance criteria
Onchain (
TimeArena)effectiveCharmPriceWad()(or equivalent view) returns the current DOUB wei per 1e18 CHARM for DOUB buys, incorporating epoch anchor + elapsed growth.ArenaCharmPriceTwap.compute(...).charmPriceWad(production) or Anvil spot parity (#303); growth elapsed =block.timestamp - epochAnchorTimestamp.epochAnchorTimestamp = block.timestamp, incrementlastBuyEpoch, emit auditable event (e.g.LastBuyEpochCharmAnchored(epoch, anchorWad, doubUsdWad, twapBlock)). The triggering buy is priced at the post-reset anchor (see Design notes).anchor × exp(ln(1.1) × elapsed / 86400)(WAD math; reuse/extendTimeMath+ prb-math like retired v1currentMinBuy).effectiveCharmPriceWadexcept via elapsed time.buy/buyFor/ router DOUB pulls useeffectiveCharmPriceWad(), not a flat storage slot.buyWithCredstill usesCRED_PER_CHARM_WADonly; no DOUB price reads; behavior unchanged (#268).effectiveCharmPriceWad().setCharmPriceWad: either remove, gate to epoch-0 bootstrap only, or document as emergency break-glass with epoch re-anchor semantics — no silent flat override during an epoch.ArenaCharmPriceTwap(#303).Indexer
doub_usd_wad, block/time, tx) — extendidx_arena_last_buy_epoch_startedor sibling table keyed byepoch.GET /v1/arena/timers(or new/v1/arena/epoch-charm-pricing) exposes effectivecharm_price_wadat head + current epoch anchor +epoch_started_sec.last_buy_epoch(already) and indexer can attribute buys to epoch pricing for audit charts.Frontend (protocol / audit page)
ArenaProtocolPageshows current effective DOUB/CHARM, epoch anchor, epoch age, and projected +10%/day (indexer-first whenVITE_INDEXER_URLset — #301).useArenaSaleSession, submit sizing, envelope displays) uses effective price, not flatcharmPriceWadstorage read.Docs / invariants
docs/product/time-arena.md,arena-v2.md,PARAMETERS.md,invariants-and-business-logic.md— replace flat-price invariant with epoch-anchor + 10%/day growth.INV-TIME-ARENA-EPOCH-CHARM-GROWTH,INV-TIME-ARENA-CRED-FLAT.Verification checklist
cd contracts && forge test --match-contract TimeArenaforge test --match-contract ArenaCharmPriceTwapbash scripts/verify-anvil-kumbaya-charm-price.shcast call effectiveCharmPriceWadcd indexer && cargo clippy --all-targets -- -D warnings && cargo testbash scripts/verify-podium-live-anvil.sh(or wallet-profile)charm_price_wadin timers = effectivecd frontend && npm run typecheck && npm run lint && npm testbash scripts/e2e-anvil.sh(arena buy paths)/arena/protocolaudit epoch tableFiles likely to change
Contracts
contracts/src/arena/TimeArena.sol— storage,_finishBuyhard-reset hook,effectiveCharmPriceWad, buy pathcontracts/src/libraries/TimeMath.sol—GROWTH_RATE_10PCT_WAD,growWad(anchor, elapsed)helpercontracts/src/oracle/ArenaCharmPriceTwap.sol— callable anchor read (may stay library-only)contracts/script/DeployProduction.s.sol— init epoch 0 anchorcontracts/script/DeployKumbayaAnvilFixtures.s.sol— Anvil anchor + drop flatsetCharmPriceWadif obsoletecontracts/test/TimeArena.t.sol, newTimeArenaEpochCharmPrice.t.solcontracts/PARAMETERS.mdIndexer
indexer/migrations/*_arena_epoch_charm_price.up.sql— anchor columns / tableindexer/src/decoder.rs,persist.rs— new eventindexer/src/chain_timer.rs— effective price at head blockindexer/src/api_arena.rs— timers + epoch audit routeindexer/tests/integration_stage2.rsFrontend
frontend/src/pages/arena/ArenaProtocolPage.tsx— epoch pricing audit sectionfrontend/src/pages/arena/useArenaSaleSession.tsfrontend/src/pages/arena/arenaV2SaleSessionBridge.tsfrontend/src/lib/timeArenaBuySubmitSizing.tsfrontend/src/lib/indexerApi.tsfrontend/src/lib/abis.ts/ regeneratedTimeArena.jsonScripts / docs
scripts/verify-anvil-kumbaya-charm-price.shdocs/product/time-arena.md,docs/product/arena-v2.mddocs/testing/invariants-and-business-logic.mdRecommended implementation strategy
Phase 1 — Onchain core (single PR)
Storage model (minimal):
Keep
lastBuyEpochas today (#278).effectiveCharmPriceWad()— pure/view:return TimeMath.growWad(epochCharmAnchorWad, CHARM_GROWTH_RATE_WAD, block.timestamp - epochAnchorTimestamp);Initialize epoch 0 in
initialize/ deploy: TWAP →epochCharmAnchorWad,epochAnchorTimestamp = block.timestamp(orarenaStart).Hard reset + pricing order — refactor buy path so reset-triggering buys use post-reset anchor:
_buyDoubprices then calls_finishBuy(timer). Invert or split: detect imminent hard reset (remaining < threshold), run TWAP re-anchor +lastBuyEpoch += 1beforedoubOwed = charmWad × effectiveCharmPriceWad().twap = ArenaCharmPriceTwap.compute(...)(or Anvil spot adapter) →epochCharmAnchorWad = twap.charmPriceWad→epochAnchorTimestamp = block.timestamp→lastBuyEpoch += 1→ emitLastBuyEpochCharmAnchored→ price pull at ~zero elapsed (new anchor).effectiveCharmPriceWad()from current anchor + elapsed as usual.Replace flat
charmPriceWadreads in DOUB buy paths witheffectiveCharmPriceWad(). LeavecharmPriceWadas deprecated alias or remove and migrate ABI.TWAP on-chain at reset: Production must call pools via
ArenaCharmPriceTwap(gas acceptable on reset-only). Anvil:AnvilKumbayaPools.charmPriceWadFromSpotbehindchainid == 31337or fixture router address check.Tests: unit growth math; integration buy at T0 and T0+1day; reset re-anchors; CRED buy ignores growth; fuzz charm band × effective price.
Phase 2 — Indexer + API
LastBuyEpochCharmAnchored(or extendLastBuyEpochStartedwith indexed anchor fields if gas-sensitive).anchor_charm_price_wad,doub_usd_wad,anchor_block_numberon epoch table.effectiveCharmPriceWad()atread_block_number→charm_price_wadin/v1/arena/timers.GET /v1/arena/last-buy-epoch-pricingfor audit table.Phase 3 — Frontend audit + buy UX
Design notes / edge cases
doubOwedis computed on that tx. All later buys in the epoch continue growing from that anchor.setCharmPriceWadin favor of epoch anchors; if kept, only owner + timelock + event.deployBlockfor epoch anchor history.bots/timearenasale snapshot should readeffectiveCharmPriceWad.Related issues
idx_arena_last_buy_epoch_startedLabels
contracts,frontend,indexer,time-arena,operationsmarked as related to #303
marked as related to #246
marked as related to #278
marked as related to #268
changed the description
mentioned in commit
fc4fc99173mentioned in merge request !453
Implementation summary (MR !453)
Epoch-anchored DOUB/CHARM pricing with +10%/day growth until Last Buy hard reset.
Onchain
effectiveCharmPriceWad(),epochCharmAnchorWad,epochAnchorTimestampLastBuyEpochCharmAnchoredbefore DOUB pullTimeArenaBuyRouteruses effective price;buyWithCredunchangedTimeArenaEpochCharmPrice.t.solIndexer
effectiveCharmPriceWad()+ anchor fields in/v1/arena/timersLastBuyEpochCharmAnchored; migration20260610120000_idx_arena_epoch_charm_price_gl305GET /v1/arena/last-buy-epoch-pricingFrontend
effectiveCharmPriceWadDocs / invariants:
INV-TIME-ARENA-EPOCH-CHARM-GROWTH,INV-TIME-ARENA-CRED-FLAT,time-arena.md,skills/play-time-arena-doub/SKILL.mdMR: https://gitlab.com/PlasticDigits/yieldomega/-/merge_requests/83
Verification checklist
cd contracts && forge test --match-contract TimeArenaEpochCharmPrice -vvcd contracts && forge test --match-contract TimeArenaTestcd indexer && cargo clippy --all-targets -- -D warnings && cargo testcd frontend && npm run typecheck && npm testbash scripts/verify-anvil-kumbaya-charm-price.sh(epoch-0 anchor)cast call TimeArena effectiveCharmPriceWad()≈ anchor × 1.1GET /v1/arena/timers—charm_price_wadmatches onchain effective; anchor fields present/arena/protocol— effective DOUB/CHARM label/copyFollow-up: Full per-epoch audit table UI on
/arena/protocolconsumingGET /v1/arena/last-buy-epoch-pricing(API is in this MR; rich table can land as a small follow-up if desired).mentioned in commit
8118e98ddamentioned in commit
168d8de150Verification — GitLab #305 (epoch-anchored TWAP charm price +10%/day growth)
Verified MR !453 on
main. No repo changes required from this verify pass.Onchain (AC-1–AC-10)
effectiveCharmPriceWad()+ epoch anchor/growthcd contracts && forge test --match-contract TimeArenaEpochCharmPrice -vv— 7/7 (growth, monotonic, buy pull, CRED unchanged, hard-reset re-anchor)forge test --match-contract TimeArenaTest— 76/76forge test --match-contract ArenaCharmPriceTwap— 5/51000e18DeployDev)cast call epochCharmAnchorWadafter DeployDev on Anvil:8548anvil_setNextBlockTimestamp(anchorTs+86400)→effectiveCharmPriceWad≈1100e18(WAD tolerance)TimeArenaEpochCharmPrice.t.sol::test_hard_reset_reanchors_and_prices_at_new_anchorIndexer (AC-11–AC-14)
cd indexer && cargo clippy --all-targets -- -D warnings && cargo test/v1/arena/timerseffective + anchor fieldscharm_price_wad,epoch_charm_anchor_wad,epoch_anchor_timestamp_secmatch onchain after +1d warp/v1/arena/last-buy-epoch-pricingepochsarray; empty until a hard-reset event is ingested — epoch-0 anchor is on/v1/arena/timers(initialize, no event)verify-podium-live-anvil.shintegration_stage2::arena_wallet_stats_two_epochs_and_bonus_fields—warbow_battle_points1000 vs expected 750; not #305-scopedFrontend (AC-15–AC-18)
cd frontend && npm run typecheck && npm run lint && npm test— 632/632effectiveCharmPriceWadarenaV2SaleSessionBridge.ts,timeArenaBuySubmitSizing.ts,kumbayaQuoter.tsArenaProtocolPage.tsx— "DOUB / CHARM (effective)" + #305 growth copy; E2E navigates/arena/protocolanvil-arena-04-cred-buy.spec.ts;walletCharmCredCopy+ protocol page CRED noteDocs / invariants (AC-19–AC-20)
INV-TIME-ARENA-EPOCH-CHARM-GROWTH,INV-TIME-ARENA-CRED-FLATininvariants-and-business-logic.md;time-arena.md,PARAMETERS.md,skills/play-time-arena-doub/SKILL.mdE2E / scripts
bash scripts/e2e-anvil.shbash scripts/verify-anvil-kumbaya-charm-price.shArenaCharmPriceTwapforge testsFollow-ups (non-blocking)
/arena/protocolconsumingGET /v1/arena/last-buy-epoch-pricing(already noted in MR !453).last-buy-epoch-pricingfrom initialize/head poller if full audit history should include pre-reset epoch 0 without a hard reset.integration_stage2::arena_wallet_stats_two_epochs_and_bonus_fields(warbow_battle_pointsexpectation) — blocksverify-podium-live-anvil.shon fresh Postgres.Verifier verdict: #305 acceptance criteria PASS (with AC-16 UI and verify-script caveats above).
mentioned in issue #309
mentioned in issue #320
mentioned in merge request !476
mentioned in merge request !523
mentioned in commit
aca60bbf21mentioned in commit
a460abfb23mentioned in commit
63373db936mentioned in issue #367