WarBow steal: tighten BP eligibility band (2×–10× victim vs attacker) #211
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#211
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?
Problem
TimeCurve.warbowStealcurrently allows any victim withvictimBP ≥ 2 × attackerBP(andattackerBP > 0). That makes multi-wallet “shark” swarms economically attractive against a distant BP leader: many low-BP accounts can each satisfy the 2× rule against a whale and repeatedly drain 10% slices (subject to UTC-day caps / bypass / guard / revenge), which undermines the intended peer rivalry framing for the WarBow ladder and reserve podium category.Proposed solution
Add an upper bracket symmetric to the existing lower bound: require the victim’s BP to sit in a finite band relative to the attacker:
victimBP ≥ 2 × attackerBP(existing underdog-vs-uphill framing).victimBP ≤ 10 × attackerBP(prevents micro-wallets from treating a far-ahead leader as an oversized farm).Onchain (minimal bytecode change): extend the single
requireinwarbowStealso both bounds are enforced in one guard (same tx ordering as today: CL8Y pulls already happened before the check — unchanged).Revert string / UX: Today the contract uses
TimeCurve: steal 2x rulefor the lower-bound failure only. Options (pick in MR):requirewith a new consolidated message (e.g.TimeCurve: steal bp band) and map it infriendlyRevertMessage/ docs; orrequires (still tiny) with distinct reverts for lower vs upper bound to preserve precise wallet error copy without ambiguous strings.Constants 2 and 10 may stay literals in v1 (matching existing
2 * abpstyle) or move to nameduint8/uint16immutable-style public constants if governance wants visibility (slightly larger diff).Related files (thorough checklist)
Solidity (authoritative)
contracts/src/TimeCurve.sol—warbowSteal: NatSpec +requirefor BP bracket.contracts/PARAMETERS.md— TimeCurve / WarBow table if steal eligibility is summarized.Foundry tests
contracts/test/TimeCurve.t.sol—test_warbow_steal_revert_2x_rule, fuzz sequences that assertvbp >= 2 * abpmid-sequence, multi-stealer / daily-cap / drain tests that assume unbounded upper ratio; addtest_warbow_steal_revert_victim_too_far_above_attacker(name TBD) forvbp > 10 * abp.contracts/test/TimeCurveWarBowCl8yBurns.t.sol— setups that grant victim≥ 2×stealer; ensure victims are not> 10×unless the test explicitly targets the new revert.Frontend — preflight & copy
frontend/src/lib/timeCurveUx.ts—describeStealPreflight: add upper band branch (titles/detail distinct from lower band); success copy must say 2×–10× (not “passes the 2x rule” alone).frontend/src/lib/timeCurveUx.test.ts— extenddescribeStealPreflightexpectations for too-low vs too-high victim BP.Frontend — Arena hero / steal discovery
frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx—canSuggest(currentlybattlePoints >= viewerBp * 2nonly);warbowStealProspectCandidates(prospects are “below 2× threshold”; redefine or split for above 10× “out of band” rows vs true prospects); comments referencing “2×” only.frontend/src/pages/timeCurveArena/WarbowHeroActions.tsx— hero copy (“Steal 10%… 2x your BP”), empty-state “No indexed 2x BP steal target yet” → band-aware wording.frontend/src/pages/timeCurveArena/WarbowHeroActions.test.tsx— candidate counts / strings /bpBelowStealThresholdsemantics after bracket.Frontend — Simple / misc
frontend/src/pages/timecurve/TimeCurveSections.tsx— WarBow steal explainer +meta="Must be at least 2x your BP".frontend/src/pages/timecurve/TimeCurveSimpleAgentCard.tsx— Python pseudo-card steal linehbp >= 2 * abp(align with band for agent examples).Frontend — revert friendliness
frontend/src/lib/revertMessage.ts+frontend/src/lib/revertMessage.test.ts— map new or existing revert substring to human copy.Docs (product + contributor)
docs/product/primitives.md— § Steal (warbowSteal): document 2×–10× bracket; WarBow ladder intro if it says “≥ 2×” only.docs/testing/invariants-and-business-logic.md—INV-WARBOW-134-RANK(and any row citing only 2×); § GitLab #134 WarBow steal rules; Arena #101 / #182 / live podium rows that mention “2× BP rule”; EcoStrategy / Forge test map bullets that nametest_warbow_steal_revert_2x_rule.docs/frontend/timecurve-views.md— Arena WarBow hero actions / steal candidate discovery text.docs/testing/manual-qa-checklists.md— any WarBow / Arena steal rows (grep2x/WarBow/steal).Player / contributor skills & indices
skills/play-timecurve-warbow/SKILL.md— Steal “ranking” bullet: 2×–10× bracket, UTC caps unchanged.skills/README.md— index row for play-timecurve-warbow if it paraphrases steal rule..cursor/skills/yieldomega-guardrails/SKILL.md— issue #134 / Arena WarBow line: “2×” → “BP bracket (2×–10×)” if spelled out.Bots & local PvP harness
bots/timecurve/src/timecurve_bot/strategies/pvp.py— preflightvbp < 2 * max(abp, 1)→ also warn / block whenvbp > 10 * max(abp, 1).bots/timecurve/README.md—pvpstrategy doc if it mentions 2× only.bots/timecurve/src/timecurve_bot/abis/timecurve.json— only if ABI/regen workflow touches NatSpec selectors (usually unchanged forwarbowStealsignature).Simulations (EcoStrategy mirror)
simulations/ecostrategy/warbow_pvp.py—can_steal/try_stealeligibility: addvbp <= 10 * abp(or equivalent).simulations/ecostrategy/constants.py— manifest string “TimeCurve — 2× steal eligibility” → describe 2×–10× band.simulations/tests/test_ecostrategy.py—test_two_x_rule_blocks_steal→ extend / rename to cover upper bound (victim_bp > 10 * attacker_bpblocks).simulations/README.md— WarBowWorld bullet if it says “2× eligibility” only.Audits (historical narrative)
audits/audit_ecostrategy_1777969776.md— paragraph describing WarBow 2× rule (mark as superseded by bracket or add errata footnote).Indexer / API
2x/stealinindexer/for docstrings or fixture text only.Launch / ops docs (optional grep)
launchplan-timecurve.md,YO-DOUB-Launch-UX-Flows.md,contracts/README.md— if they mention steal BP rule in prose.Suggested implementation notes
abp > 0 && vbp >= 2 * abp && vbp <= 10 * abp(same revert policy as agreed above).
isWarbowStealBpBandOk(viewerBp, victimBp): booleanordescribeWarbowStealBpBand(...)intimeCurveUx.ts(or smallwarbowStealBpBand.ts) so Arena model + preflight + tests share one definition.victimBP < 2 × viewerBP(current prospect list intent).victimBP > 10 × viewerBP— optional separate label/tooltip so players understand why a visible ladder whale is not stealable.WARBOW_STEAL_VICTIM_MAX_MULT = 10from a single frontend module mirroring contract intent (contracts may keep literals for minimal bytecode unless you add public getters).Testing guide
forge test --match-contract TimeCurve(or targeted--match-test warbow_steal) — add explicit revert when victim is 21× attacker with attacker> 0; keep existing lower-bound revert test; fuzz sequences that previously relied onvbp >> abpmust be adjusted.frontend/src/lib/timeCurveUx.test.ts,WarbowHeroActions.test.tsx,revertMessage.test.ts.cd simulations && PYTHONPATH=. python3 -m unittest discover -s tests -v— updateWarBowWorldtests.pvpdry-run with mis-sized BP should print the new guard hint.Acceptance criteria
warbowStealreverts unlessattackerBP > 0and2 × attackerBP ≤ victimBP ≤ 10 × attackerBP(exact inequality semantics match Solidity*uint256 math, no overflow for realistic BP values).docs/product/primitives.mddescribe the full bracket;docs/testing/invariants-and-business-logic.mdupdated (INV-WARBOW-134-RANKor successor ID) so contributor map matches behavior.describeStealPreflightand Arena steal candidate discovery match onchain rules (no UI that suggests a steal that will revert on the BP bracket alone).friendlyRevertMessage(or equivalent) gives participants clear copy for whichever revert string ships.skills/play-timecurve-warbow/SKILL.md(and any grep-hit launch docs that teach steals) aligned so agents/participants are not misled.Labels / follow-ups
Consider labels:
component::contracts,component::frontend,component::docs,area::timecurve,type::feature(behavior change),security::hardening(if used).Upgrade / deployment: UUPS
TimeCurveproxy — confirm maintainer process for bytecode upgrades, storage layout unchanged, and comms to live participants before mainnet cutover.mentioned in commit
3cac3335fdImplemented (merged to
mainin3cac333)TimeCurve.warbowStealnow enforces2 × attackerBP ≤ victimBP ≤ 10 × attackerBPafter CL8Y pulls; new revertTimeCurve: steal 10x cap(lower bound stillTimeCurve: steal 2x rule). Forge:test_warbow_steal_revert_10x_cap, fuzz revenge test usesvm.assumefor in-band draws.frontend/src/lib/warbowStealBpBand.ts;describeStealPreflight+ Arena hero (in-band targets, Too low BP / Too high BP prospect rows) +friendlyRevertMessagecopy; Simple WarBow copy + agent card pseudo-Python.WarBowWorld.can_steal+ constants manifest;pvp.pydry-run guards; UTC rollover unittest now uses a fresh victim on the new UTC day (same victim cannot stay in-band for a 4th steal after three 10% drains without new BP — matches onchain economics).docs/product/primitives.md, invariantsINV-WARBOW-134-RANK+ §134 forge row,timecurve-views,PARAMETERS.md,skills/play-timecurve-warbow,skills/README.md, contributor guardrails skill, audit M-02 narrative footnote.Verification checklist (for @brouie)
cd contracts && forge test --match-test warbow_stealand--match-test warbow_revenge_distinct_stealer(or fullTimeCurvesuite in CI).frontend—warbowStealBpBand.test.ts,timeCurveUx.test.ts,revertMessage.test.ts,WarbowHeroActions.test.tsx.cd simulations && PYTHONPATH=. python3 -m unittest discover -s tests -v.warbowStealrevertssteal 10x cap; in-band rival still steals normally.docs/testing/invariants-and-business-logic.md§134 /docs/product/primitives.md/skills/play-timecurve-warbow/SKILL.md.Please confirm behavior on your target deployment (UUPS bytecode upgrade path is out of scope for this MR but called out in the issue).
marked the checklist item Onchain warbowSteal reverts unless attackerBP > 0 and 2 × attackerBP ≤ victimBP ≤ 10 × attackerBP (exact inequality semantics match Solidity * uint256 math, no overflow for realistic BP values). as completed
marked the checklist item NatSpec + docs/product/primitives.md describe the full bracket; docs/testing/invariants-and-business-logic.md updated
INV\-WARBOW\-134\-RANK or successor IDso contributor map matches behavior. as completedmarked the checklist item describeStealPreflight and Arena steal candidate discovery match onchain rules (no UI that suggests a steal that will revert on the BP bracket alone). as completed
marked the checklist item friendlyRevertMessage
or equivalentgives participants clear copy for whichever revert string ships. as completedmarked the checklist item Foundry + Vitest + EcoStrategy unit tests green in CI for touched areas. as completed
Walked the verification checklist against the patched local stack. All 5 items pass, including a live revert against the deployed bytecode on a fresh redeploy of main HEAD
4f32e63(which includes3cac333).Forge —
warbow_steal+warbow_revenge_distinct_stealerTimeCurve.t.sol: 9/9 PASS including the newtest_warbow_steal_revert_10x_cap.TimeCurveWarBowCl8yBurns.t.sol: 2/2 PASS including the 256-runtestFuzz_warbow_steal_burn_exact_to_sink.testFuzz_warbow_revenge_distinct_stealer_slots_overlap: 256/256 PASS — the newvm.assumein-band draw constraint does not break the per-(victim, stealer) revenge slot semantics from GitLab #135.Vitest —
warbowStealBpBand,timeCurveUx,revertMessage,WarbowHeroActions44/44 PASS across the 4 files. New
warbowStealBpBand.tshelper centralizes the 2x-10x predicate;describeStealPreflightcovers Too low BP / Too high BP / In band branches;friendlyRevertMessagemaps bothsteal 2x ruleandsteal 10x capto player-friendly copy; Arena herobpBelowStealThresholdandbpAboveStealThresholdrow partitioning intact.EcoStrategy —
unittest discover42/42 PASS.
WarBowWorld.can_stealand the band-awaretest_two_x_rule_blocks_stealextension all green. UTC rollover fixture using a fresh victim on the new UTC day verified.Manual / Anvil — live patched bytecode
Brought the local stack up fresh on main HEAD
4f32e63(full redeploy, fresh indexer DB, schema 1.22.0, swarm running). Used anvil accounts #28 (0x40fc963a...96624) as attacker and #29 (0x9dcce783...07c710) as victim. Both pre-funded with CL8Y mock + approved to TimeCurve.Out-of-band steal — must revert with
steal 10x capSet up: attacker 1 buy = 250 BP, victim 11 buys = 2750 BP. Ratio 11.0x, exceeds the 10x cap.
Reverted with exact string:
Hex-decoded revert data:
54696d6543757276653a20737465616c20313078206361702f->TimeCurve: steal 10x cap. The attack vector (multi-wallet swarm against a distant whale) is locked on the deployed bytecode.In-band steal — must succeed with normal economics
Set up: attacker 3 buys = 750 BP, victim 2750 BP. Ratio 3.66x, in the 2x-10x band.
Status 1. Onchain state delta:
The 275 BP delta equals exactly 10% of victim BP at call time, matching
_warbowStealDrainBpdefault (10% unguarded).Cross-links —
docs/testing/invariants-and-business-logic.md+docs/product/primitives.md+skills/play-timecurve-warbow/SKILL.mdAll 3 target docs reference the 2x-10x bracket + both revert strings + #211 link:
INV-WARBOW-134-RANKatinvariants-and-business-logic.md:545— both bounds + revert strings + #211 link.invariants-and-business-logic.md:171— 2x-10x BP band + near-miss row partitioning + #211 link.docs/product/primitives.md:69+:94— finite band 2x-10x + bracket bounds + both revert strings + #211 link.skills/play-timecurve-warbow/SKILL.md:38— Ranking line includes #211 + 2x-10x bracket.Doc shorthand callouts (awareness, no action requested)
Grepped wider per the AC item "any grep-hit launch docs that teach steals". Four remaining references use the lower-bound shorthand
2× rule/2x rulerather than the full bracket phrasing:docs/testing/invariants-and-business-logic.md:1350— EcoStrategy table description: "WarBowWorld mirrors warbowSteal / warbowRevenge / warbowActivateGuard (2× rule, UTC-day steal caps, drain bps, revenge window)"audits/audit_ecostrategy_1777969776.md:135— sim recommendation: "Model WarBow with the real 2x rule, per-attacker/victim daily caps, ..."audits/audit_ecostrategy_1777969776.md:283— audit narrative: "the PvP steal model is explicitly a toy random 10% drain and does not include the 2x rule, CL8Y burn costs, ..."audits/audit_ecostrategy_1777969776.md:355— scenario list: "Multiple low-BP sharks attack under 2x rule, including UTC rollover, ..."Flagging only — your commit already edited
invariants-and-business-logic.md(updatingINV-WARBOW-134-RANKat:545and the Arena hero row at:171) and the audit file (errata footnote at:122pointing to #211). So choosing not to sweep these other lines reads as a deliberate "preserve original audit narrative / table shorthand" call. Surfacing in case you want a follow-up sweep.Summary
All 5 checklist items pass against the patched bytecode + tests + docs. The leaderboard-destroy attack vector (multi-wallet zero-BP swarm vs ladder whale) is provably locked:
abp > 0guard blocks zero-BP attackers entirely, and the new 10x cap prevents legit-but-distant attackers from farming whales. In-band peer rivalry preserved end-to-end.UUPS
upgradeTo()to mainnet is out of scope per the issue body, but flagging that the deployed mainnet proxy still serves the OLD bytecode until that cutover lands.@PlasticDigits
mentioned in issue #217
Balance observation worth surfacing while #211's BP-band tuning is open.
Current state (per TimeCurve.sol):
The 10× upper cap means top-rank players become unreachable for smaller participants. At rank ~53k BP, only attackers with 5.3k–26.5k BP can threaten that position. The cushion grows as the leader's BP grows, eventually freezing the leaderboard for the rest of the sale window.
Adjacent suggestion if open for tuning alongside #211:
Two changes that work together:
Tiered bypass cost (kicks in only on steal four-plus per attacker or per victim per UTC day):
What this changes:
What stays exactly the same:
Strategic effect — what this opens up:
Top-rank players have two viable defensive strategies:
Either strategy burns CL8Y somewhere — guard burn or steal burn. The leaderboard becomes contested because being top rank carries a daily cost, not just a one-time grind.
For attackers:
Honest trade-off:
Aware this is balance opinion, not a bug. Drop if it's out of scope or you've already tuned this elsewhere.
Will consider raising cap from 10x to 20x
mentioned in issue #366