WarBow steal/guard/revenge prices derived from DOUB/CHARM TWAP anchor (guard = anchor/2, steal = revenge = anchor/5) #367
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#367
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
Reprice WarBow guard, revenge, and steal from fixed DOUB constants to values derived from the DOUB/CHARM TWAP anchor, recomputed every time the anchor is updated:
20_000e18DOUB per CHARM → guard costs 10,000 DOUB).20_000e18→ revenge costs 4,000 DOUB).20_000e18→ steal costs 4,000 DOUB).Only the steal-limit bypass (
WARBOW_STEAL_LIMIT_BYPASS_DOUB = 50_000e18) is out of scope and stays fixed.Current codebase
contracts/src/arena/TimeArena.soldeclares the prices as compile-time constants (lines ~56–58):WARBOW_STEAL_DOUB = 1000e18,WARBOW_REVENGE_DOUB = 1000e18,WARBOW_GUARD_DOUB = 10_000e18.warbowSteal()(~line 541),warbowRevenge()(~line 578), andwarbowActivateGuard()(~line 596) pull these exact amounts via_pullDoubExactand route 100% to podium vaults via_routeWarbowDoubSpend(#310).charmPriceWad/epochCharmAnchorWad(+epochAnchorTimestamp). It is (re)set by:initialize(...)/setCharmPriceWad(pre-start only),setEpochCharmAnchorWad(owner break-glass, #305),_startNextLastBuyEpoch()→_sampleCharmAnchor()→_setEpochCharmAnchor()on every Last Buy podium roll — Anvil Kumbaya spot whencharmAnchorKumbayaRouteris configured, Kumbaya V3 TWAP viaArenaCharmPriceTwap.compute(megaethMainnetConfig())on chain 4326 (#303, #352), otherwise carry-forward.effectiveCharmPriceWad()grows the anchor ~10%/day (#305); the anchor itself only changes at re-anchor events.WARBOW_STEAL_DOUB()/WARBOW_GUARD_DOUB()/WARBOW_REVENGE_DOUB()via RPC when the indexer is off, and hardcodesWARBOW_STEAL_DOUB_WAD = 1000e18/WARBOW_GUARD_DOUB_WAD = 10_000e18/WARBOW_REVENGE_DOUB_WAD = 1000e18infrontend/src/lib/arenaWarbowConstants.tswhenVITE_INDEXER_URLis set (indexerOn), perINV-FRONTEND-301-INDEXER-FIRST-DISPLAY(useArenaWarbowHero.tslines ~130–137). These hardcoded values become wrong once prices are dynamic.charm_price_wad,epoch_charm_anchor_wad, andepoch_anchor_timestamp_seconGET /v1/arena/timers(indexer/src/chain_timer.rs,indexer/src/api_arena.rs) and persistsLastBuyEpochCharmAnchored(decoder.rs/persist.rs).contracts/PARAMETERS.md(WarBow table),docs/product/time-arena.md(§ guard 10000e18 / revenge 1000e18),docs/product/arena-v2.md,docs/frontend/design.md,skills/play-time-arena-warbow/SKILL.md, and invariantINV-TIME-ARENA-WARBOW-DOUBindocs/testing/invariants-and-business-logic.md.Why this is needed
Steal, guard, and revenge costs are economic levers relative to the price of a CHARM buy. With fixed 1,000 / 10,000 / 1,000 DOUB costs and a market-driven DOUB/CHARM anchor (production initializes from live Kumbaya TWAP, #303), the relative cost of aggression, protection, and retaliation drifts arbitrarily: if DOUB/CHARM is 20k, guard at 10k DOUB is only half a buy and steal at 1k is 5% of a buy; if DOUB/CHARM falls to 2k, guard costs five buys. Tying guard to 1/2 and steal/revenge to 1/5 of the anchor keeps WarBow PvP economics proportional to buy pricing across market conditions and across re-anchor events, without manual owner retuning.
Constraints / guardrails
TimeArena; indexer and frontend only display derived values.TimeArenais upgradeable — append-only storage; do not reorder existing slots. Prefer deriving prices in view functions from the existingepochCharmAnchorWadrather than adding new storage.WARBOW_STEAL_DOUB()/WARBOW_GUARD_DOUB()/WARBOW_REVENGE_DOUB()selectors are consumed byfrontend/src/lib/abis.ts,frontend/public/abis/TimeArena.json,bots/timearena/src/timearena_bot/abis/timearena.json, and cost pills (skill doc). Keep the same function names/selectors (apublicview function has the same ABI as apublic constantgetter) or provide a migration for every consumer in the same MR.epochCharmAnchorWad, the "initial TWAP" of the epoch), not from the growingeffectiveCharmPriceWad(). Steal/guard/revenge costs change only at re-anchor events (epoch roll, owner break-glass, pre-start set) and are stable within an epoch.INV-TIME-ARENA-WARBOW-DOUB): 100% of steal/guard/revenge DOUB (including the fixed bypass) still routes to podium vaults via_routeWarbowDoubSpend,totalDoubRaisedstill increments by gross, no DOUB stranded onTimeArena. Update the invariant text, not the behavior.WARBOW_STEAL_LIMIT_BYPASS_DOUB = 50_000e18remains a constant; only the base steal spend becomes dynamic. The bypass is pulled in addition to the dynamic steal price when the daily cap is exceeded, exactly as today._setEpochCharmAnchorrequireswad > 0, but integer division can still produce 0 (anchor < 2 wei / < 5 wei) — steal/guard/revenge spends must never become 0 silently; enforce a minimum or revert.warbowSteal/warbowActivateGuard/warbowRevengecall_requireLiveAndAutoroll()which can roll the Last Buy epoch and re-anchor in the same transaction. Cost must be computed after autoroll so the paid amount matches the post-roll anchor; document this so frontends quote with the caveat.1000e18(#303) → dev guard becomes 500 DOUB, dev steal and revenge 200 DOUB each. Bots, E2E, and play skills that budget for 1k/10k/1k must be updated.Relevant files
Contracts
contracts/src/arena/TimeArena.sol— constants,warbowSteal,warbowActivateGuard,warbowRevenge,_setEpochCharmAnchor,_startNextLastBuyEpoch,_sampleCharmAnchor,setEpochCharmAnchorWad,_requireLiveAndAutorollcontracts/src/oracle/ArenaCharmPriceTwap.sol(anchor source on 4326; read-only here)contracts/test/TimeArena.t.sol(test_warbow_*),contracts/test/TimeArenaTwapHardReset4326.t.solcontracts/PARAMETERS.mdFrontend
frontend/src/lib/arenaWarbowConstants.ts(hardcoded WADs),frontend/src/pages/arena/useArenaWarbowHero.ts(stealDoubWei/guardDoubWei/revengeDoubWei, approval sizing, steal preflight copy),frontend/src/pages/arena/arenaV2AdvancedSessionBridge.ts,frontend/src/pages/arena/ArenaWarbowHeroPanel.tsx(+ tests),frontend/src/lib/abis.ts,frontend/public/abis/TimeArena.json,frontend/src/lib/warbowRevengeRowPreview.tsIndexer
indexer/src/chain_timer.rs,indexer/src/api_arena.rs(/v1/arena/timersalready carriesepoch_charm_anchor_wad; optionally add explicitwarbow_steal_doub_wad/warbow_guard_doub_wad/warbow_revenge_doub_wadhead fields so the frontend never recomputes ratios)Bots / sims / docs / skills
bots/timearena/src/timearena_bot/actions.py,strategies/pvp.py(approval/budget amounts)simulations/ecostrategy/constants.py,warbow_pvp.pydocs/product/time-arena.md,docs/product/arena-v2.md,docs/frontend/design.md,docs/testing/invariants-and-business-logic.md(INV-TIME-ARENA-WARBOW-DOUB),skills/play-time-arena-warbow/SKILL.mdRecommended direction
TimeArena.sol, replace the three constants with view functions keeping the existing names/selectors:WARBOW_GUARD_DOUB()→anchor / 2WARBOW_REVENGE_DOUB()→anchor / 5WARBOW_STEAL_DOUB()→anchor / 5where
anchor = epochCharmAnchorWad != 0 ? epochCharmAnchorWad : charmPriceWad(same fallback aseffectiveCharmPriceWad). Add ratio constantsWARBOW_GUARD_ANCHOR_DIV = 2,WARBOW_STEAL_ANCHOR_DIV = 5,WARBOW_REVENGE_ANCHOR_DIV = 5for documentation/tests.WARBOW_STEAL_LIMIT_BYPASS_DOUBstays a constant.warbowSteal/warbowActivateGuard/warbowRevenge, read the price after_requireLiveAndAutoroll()andrequire(price > 0, "TimeArena: warbow price"). Steal-limit bypass keeps pulling the fixed50_000e18on top of the dynamic steal price.WarBowSteal/WarBowGuard/WarBowRevengealready carryspent); no event changes required. Consider emitting nothing new — indexer derives prices from the persistedLastBuyEpochCharmAnchoredanchor.arenaWarbowConstants.ts; whenindexerOn, derivestealDoubWei = anchor / 5n,guardDoubWei = anchor / 2n, andrevengeDoubWei = anchor / 5nfromepoch_charm_anchor_wadonGET /v1/arena/timers(or from new explicit indexer fields); when indexer is off, keep the existing RPC reads (they still work since selectors are unchanged). Approval prompts must size to the live quote (steal + optional bypass), with headroom or a re-quote path for the same-transaction autoroll re-anchor case.INV-TIME-ARENA-WARBOW-DOUB(new invariant text: guard = anchor/2, steal = revenge = anchor/5, bypass fixed at 50_000e18, updated at every anchor event).DeployTimeArenaImpl.s.solupgrade path applies for live deployments.Acceptance criteria
20_000e18:WARBOW_GUARD_DOUB()returns10_000e18,WARBOW_STEAL_DOUB()andWARBOW_REVENGE_DOUB()return4_000e18; steal, guard, and revenge pull exactly those amounts.setEpochCharmAnchorWad(owner break-glass) immediately changes all three prices.effectiveCharmPriceWadgrowth does not move steal/guard/revenge).50_000e18) and still pulled on top of the dynamic steal price when the daily cap is exceeded.totalDoubRaised(updatedINV-TIME-ARENA-WARBOW-DOUBholds).grepforWARBOW_STEAL_DOUB_WAD/WARBOW_GUARD_DOUB_WADhardcodes).WARBOW_STEAL_DOUB()/WARBOW_GUARD_DOUB()/WARBOW_REVENGE_DOUB()unchanged;frontend/public/abis/TimeArena.jsonand bot ABI regenerated if metadata changed.PARAMETERS.md, play skill, and sims updated to the ratio-based pricing.Test plan — all paths
Forge (
contracts/test/TimeArena.t.sol+ new cases)1000e18dev → 200/500/200), at20_000e18(→ 4k/10k/4k), and at odd anchors (rounding:999e18/2,999e18/5— floor division asserted).test_warbow_steal_pulls_doub/test_warbow_guard_pulls_doub/test_warbow_revenge_pulls_doubupdated: exact pull equals current getter; ERC20 parity check (_pullDoubExact) still enforced.dynamic steal price + 50_000e18(test_warbow_steal_limit_override_pulls_doubupdated for a non-default anchor).setEpochCharmAnchorWadmid-epoch changes prices;effectiveCharmPriceWadgrowth over 3 simulated days does not.TimeArenaTwapHardReset4326.t.sol— mocked Kumbaya TWAP re-anchor updates steal/guard/revenge.totalDoubRaisedincrements (regression on #310 tests).Frontend (
npm run typecheck && npm run lint && npm test)useArenaWarbowHerounit tests: indexer-on derives steal/guard/revenge fromepoch_charm_anchor_wad; indexer-off falls back to RPC reads; approvalneedWeifollows the dynamic quote (steal + bypass when over cap).ArenaWarbowHeroPanelcost-pill rendering for a non-default anchor.Indexer (
cargo clippy --all-targets -- -D warnings && cargo test)/v1/arena/timersserveswarbow_steal_doub_wad/warbow_guard_doub_wad/warbow_revenge_doub_wadconsistent withepoch_charm_anchor_wad.E2E / Anvil
bash scripts/e2e-anvil.shgreen; add a WarBow scenario asserting guard costanchor/2after a forced epoch roll (or extendscripts/verify-podium-live-anvil.shflow).Test plan — attack / hack / abuse vectors
ArenaCharmBounds-style clamps exist for anchors, assert they bound the derived prices too.docs/onchain/security-and-threat-model.md).WARBOW_GUARD_DURATION_SECand 1% drain — i.e. quantify the maximum economic benefit and record it in the threat model._pullDoubExact+ ERC20 approval is the cap); frontend test that the re-quote path recovers. Applies to steal, guard, and revenge._setEpochCharmAnchorstill rejects 0._pullDoubExactparity check still rejects any non-exact pull at the new dynamic amounts (regression of #123-style checks).setEpochCharmAnchorWadisonlyOwner; confirm no new unauthenticated path can move the anchor (fuzz non-owner callers revert).nonReentrant; add a malicious-token-style test only if DOUB were swappable (it is not — document why not applicable).Verification criteria
cd contracts && forge testtest_warbow_steal_price_*,test_warbow_guard_price_*,test_warbow_revenge_price_*, 4326 TWAP re-anchor tests)cd indexer && cargo clippy --all-targets -- -D warnings && cargo testcd frontend && npm run typecheck && npm run lint && npm testbash scripts/e2e-anvil.shanchor/5,anchor/2,anchor/5onWarBowSteal/WarBowGuard/WarBowRevengeeventsPARAMETERS.md+ product docs + play skill updatedMR checklist row per touched layer (item → command → PASS/FAIL) per
AGENTS.md.Related
changed title from
WarBow guard/revenge prices derived from DOUB/CHARM TWAP anchor (guard = anchor/2, revenge = anchor/5)toWarBow steal/guard/revenge prices derived from DOUB/CHARM TWAP anchor (guard = anchor/2, steal = revenge = anchor/5)changed the description
mentioned in commit
a55964fa25mentioned in merge request !542
mentioned in commit
8cc66b98d3