Make WarBow flag planting an explicit buy option #63

Closed
opened 2026-04-25 06:57:05 +00:00 by PlasticDigits · 11 comments
PlasticDigits commented 2026-04-25 06:57:05 +00:00 (Migrated from gitlab.com)

Problem

Flag planting is currently treated as automatic on successful TimeCurve buys: every buy updates the pending WarBow flag owner/plant timestamp, and the indexed Buy.flagPlanted field is true for every buy.

That is not the intended UX/risk model. Planting a flag should be an explicit option attached to a CHARM buy, because if the flag is not defended/claimed correctly the buyer can lose WarBow points. A user placing a normal CHARM buy should not accidentally opt into that WarBow risk.

Desired behavior

  • The buy flow exposes a clear Plant WarBow flag option when the sale is live.
  • If the option is off, the user can buy CHARM without planting/replacing the pending flag.
  • If the option is on, the UI explains the risk: the flag can be claimed after the silence window, but an undefended/interrupted flag can cost WarBow points.
  • Onchain/event/indexer semantics should distinguish an intentional flag plant from a plain buy.
  • Arena buy panel should keep this as an action option, not bury it in informational strategy content.

Notes

Current docs describe pending holder state from warbowPendingFlagOwner / warbowPendingFlagPlantAt, and warn that Buy.flagPlanted is currently true on every successful buy. This issue asks to change the product/contract/UI behavior so planting is opt-in rather than automatic.

Acceptance criteria

  • Plain CHARM buy path does not plant a WarBow flag.
  • Opt-in buy path can plant a WarBow flag and surfaces the BP-loss risk before confirmation.
  • Frontend, contract events, and indexer rows let the UI tell plain buys and flag-planting buys apart.
## Problem Flag planting is currently treated as automatic on successful TimeCurve buys: every buy updates the pending WarBow flag owner/plant timestamp, and the indexed `Buy.flagPlanted` field is true for every buy. That is not the intended UX/risk model. Planting a flag should be an explicit option attached to a CHARM buy, because if the flag is not defended/claimed correctly the buyer can lose WarBow points. A user placing a normal CHARM buy should not accidentally opt into that WarBow risk. ## Desired behavior - The buy flow exposes a clear `Plant WarBow flag` option when the sale is live. - If the option is off, the user can buy CHARM without planting/replacing the pending flag. - If the option is on, the UI explains the risk: the flag can be claimed after the silence window, but an undefended/interrupted flag can cost WarBow points. - Onchain/event/indexer semantics should distinguish an intentional flag plant from a plain buy. - Arena buy panel should keep this as an action option, not bury it in informational strategy content. ## Notes Current docs describe pending holder state from `warbowPendingFlagOwner` / `warbowPendingFlagPlantAt`, and warn that `Buy.flagPlanted` is currently true on every successful buy. This issue asks to change the product/contract/UI behavior so planting is opt-in rather than automatic. ## Acceptance criteria - Plain CHARM buy path does not plant a WarBow flag. - Opt-in buy path can plant a WarBow flag and surfaces the BP-loss risk before confirmation. - Frontend, contract events, and indexer rows let the UI tell plain buys and flag-planting buys apart.
PlasticDigits commented 2026-04-26 08:42:01 +00:00 (Migrated from gitlab.com)

mentioned in commit 6d52ce6dff

mentioned in commit 6d52ce6dffdb4e7b7281b95bfd6756dd81c12a1d
PlasticDigits commented 2026-04-26 08:44:15 +00:00 (Migrated from gitlab.com)

Implemented (merged to main @ d2f6675)

Summary: WarBow pending-flag planting is now opt-in per buy. Default buy(charmWad) does not touch warbowPendingFlag*. Buy.flagPlanted / indexer flag_planted mirror the opt-in for that tx.

@brouie — please verify on your side (issue stays open).

Checklist for verification

  1. Plain buy: Call buy(1e18) (or min CHARM) → warbowPendingFlagOwner stays address(0) unless a prior plant left a holder; Buy.flagPlanted is false in the receipt/event.
  2. Plant buy: Call buy(1e18, true) → pending owner is buyer; Buy.flagPlanted is true.
  3. Referral path: buy(charmWad, codeHash, false) and (..., true) both succeed when referral valid; flag only sets on true.
  4. Same holder: Plant then plain second buy → warbowPendingFlagPlantAt unchanged (silence not reset).
  5. Interrupt: Alice plants; Bob buys plain before Alice claims → Alice cleared / penalty rules unchanged vs pre-#63 semantics.
  6. Router: TimeCurveBuyRouter.buyViaKumbaya(..., plantWarBowFlag, ...) — third arg after codeHash; false leaves flag slot as expected.
  7. Frontend: Simple + Arena — Plant WarBow flag checkbox default off; CTA builds correct calldata (watch wallet simulation).
  8. Docs: docs/product/primitives.md, docs/frontend/timecurve-views.md, docs/testing/invariants-and-business-logic.md (#63 section), skills/play-timecurve-warbow/SKILL.md, guardrails item 16.

Tests run (agent)

  • forge test TimeCurve + referral + buy router suites on feature branch (all pass). Local out/ permission error on merge machine smoke — CI should run full suite.
## Implemented (merged to `main` @ d2f6675) **Summary:** WarBow pending-flag planting is now **opt-in** per buy. Default `buy(charmWad)` does **not** touch `warbowPendingFlag*`. `Buy.flagPlanted` / indexer `flag_planted` mirror the opt-in for that tx. **@brouie** — please verify on your side (issue stays open). ### Checklist for verification 1. **Plain buy:** Call `buy(1e18)` (or min CHARM) → `warbowPendingFlagOwner` stays `address(0)` unless a prior plant left a holder; `Buy.flagPlanted` is `false` in the receipt/event. 2. **Plant buy:** Call `buy(1e18, true)` → pending owner is buyer; `Buy.flagPlanted` is `true`. 3. **Referral path:** `buy(charmWad, codeHash, false)` and `(..., true)` both succeed when referral valid; flag only sets on `true`. 4. **Same holder:** Plant then plain second buy → `warbowPendingFlagPlantAt` unchanged (silence not reset). 5. **Interrupt:** Alice plants; Bob buys plain before Alice claims → Alice cleared / penalty rules unchanged vs pre-#63 semantics. 6. **Router:** `TimeCurveBuyRouter.buyViaKumbaya(..., plantWarBowFlag, ...)` — third arg after `codeHash`; `false` leaves flag slot as expected. 7. **Frontend:** Simple + Arena — **Plant WarBow flag** checkbox default off; CTA builds correct calldata (watch wallet simulation). 8. **Docs:** `docs/product/primitives.md`, `docs/frontend/timecurve-views.md`, `docs/testing/invariants-and-business-logic.md` (#63 section), `skills/play-timecurve-warbow/SKILL.md`, guardrails item **16**. ### Tests run (agent) - `forge test` TimeCurve + referral + buy router suites on feature branch (all pass). Local `out/` permission error on merge machine smoke — CI should run full suite.
PlasticDigits commented 2026-04-26 09:08:49 +00:00 (Migrated from gitlab.com)

mentioned in commit 54ba8cd736

mentioned in commit 54ba8cd736f81b92faaeafe830302c97f05d540c
PlasticDigits commented 2026-04-26 10:27:18 +00:00 (Migrated from gitlab.com)

mentioned in issue #66

mentioned in issue #66
Brouie commented 2026-04-27 02:58:45 +00:00 (Migrated from gitlab.com)

@PlasticDigits source-level verification of d2f6675.

Contracts (TimeCurve.sol)

Opt-in plumbing:

  • L319-321: plain buy(charmWad) -> _buy(..., false). AC1 met at the function default.
  • L324-325: buy(charmWad, plantWarBowFlag) explicit opt-in
  • L329-330: buy(charmWad, codeHash, plantWarBowFlag) referral + opt-in
  • L334-340: buyFor(buyer, charmWad, plantWarBowFlag) and buyFor(buyer, charmWad, codeHash, plantWarBowFlag) router paths
  • L358: _buy internal takes plantWarBowFlag parameter
  • L467: 'if (plantWarBowFlag)' gate around the pending-flag mutation
  • L489: Buy event flagPlanted field mirrors the parameter

Tests

contracts/test/TimeCurve.t.sol covers checklist items 1, 2, 4:

  • L250 test_buy_plain_does_not_plant_warbow_flag (item 1)
  • L259 test_buy_with_plant_sets_pending_flag (item 2)
  • L267 test_holder_second_plain_buy_preserves_plant_at (item 4)

forge: 191/191 passing on FOUNDRY_PROFILE=ci.

Gap noticed: no direct unit test asserts the opt-in semantics on the referral or buyFor router paths (items 3 and 6). Function entry points exist and forge passes the existing referral + router suites, but those tests dont assert flagPlanted=false on plain buys through those entry points specifically. Worth a follow-up unit test like:

  • test_referral_buy_with_plant_false_does_not_plant
  • test_buy_for_with_plant_false_does_not_plant

Not blocking — the contract logic is shared via _buy, so the L250/L259 tests indirectly cover via the parameter wiring. But explicit assertions would close the gap for future regressions.

Frontend

Simple page (frontend/src/pages/TimeCurveSimplePage.tsx + useTimeCurveSaleSession.ts):

  • useTimeCurveSaleSession.ts L210: useState(false) - default off (item 7)
  • L875 / L968-970 calldata branches: plain (1-arg), opt-in only (2-arg), referral+opt-in (3-arg)
  • L998 exposed via session
  • TimeCurveSimplePage.tsx L793-798: checkbox + 'Plant WarBow flag (opt-in)' label

Arena (frontend/src/pages/timeCurveArena/):

  • useTimeCurveArenaModel.tsx L138: useState(false) - default off
  • L1808-1809: calldata branches mirror Simple
  • TimeCurveArenaView.tsx L780-790: checkbox + 'Plant WarBow flag' label

Both surfaces match dev's 'checkbox default off, CTA builds correct calldata' AC.

Docs (item 8)

#63 referenced in:

  • docs/product/primitives.md
  • docs/frontend/timecurve-views.md
  • docs/testing/invariants-and-business-logic.md
  • skills/play-timecurve-warbow/SKILL.md
  • guardrails item 16

Deferred until #72 lands

Items requiring on-chain tx evidence (live receipts, indexer flag_planted column with real txs, frontend wallet simulation):

  • Items 1, 2 with actual tx receipts (covered by forge but not live-anvil yet)
  • Item 3 referral path live opt-in / opt-out
  • Item 5 interrupt scenarios with multi-wallet plant + plain buy chain
  • Item 6 router buyViaKumbaya plantWarBowFlag third arg

These deferred items are fully blocked by #72 EIP-170 contract size launch-blocker - DeployDev fails before any of this can be reached on a live stack. Will close once #72 unblocks the deploy.

Issue stays open.

@PlasticDigits source-level verification of d2f6675. ## Contracts (TimeCurve.sol) Opt-in plumbing: - L319-321: plain buy(charmWad) -> _buy(..., false). AC1 met at the function default. - L324-325: buy(charmWad, plantWarBowFlag) explicit opt-in - L329-330: buy(charmWad, codeHash, plantWarBowFlag) referral + opt-in - L334-340: buyFor(buyer, charmWad, plantWarBowFlag) and buyFor(buyer, charmWad, codeHash, plantWarBowFlag) router paths - L358: _buy internal takes plantWarBowFlag parameter - L467: 'if (plantWarBowFlag)' gate around the pending-flag mutation - L489: Buy event flagPlanted field mirrors the parameter ## Tests contracts/test/TimeCurve.t.sol covers checklist items 1, 2, 4: - L250 test_buy_plain_does_not_plant_warbow_flag (item 1) - L259 test_buy_with_plant_sets_pending_flag (item 2) - L267 test_holder_second_plain_buy_preserves_plant_at (item 4) forge: 191/191 passing on FOUNDRY_PROFILE=ci. Gap noticed: no direct unit test asserts the opt-in semantics on the **referral** or **buyFor router** paths (items 3 and 6). Function entry points exist and forge passes the existing referral + router suites, but those tests dont assert flagPlanted=false on plain buys through those entry points specifically. Worth a follow-up unit test like: - test_referral_buy_with_plant_false_does_not_plant - test_buy_for_with_plant_false_does_not_plant Not blocking — the contract logic is shared via _buy, so the L250/L259 tests indirectly cover via the parameter wiring. But explicit assertions would close the gap for future regressions. ## Frontend Simple page (frontend/src/pages/TimeCurveSimplePage.tsx + useTimeCurveSaleSession.ts): - useTimeCurveSaleSession.ts L210: useState(false) - default off (item 7) - L875 / L968-970 calldata branches: plain (1-arg), opt-in only (2-arg), referral+opt-in (3-arg) - L998 exposed via session - TimeCurveSimplePage.tsx L793-798: checkbox + 'Plant WarBow flag (opt-in)' label Arena (frontend/src/pages/timeCurveArena/): - useTimeCurveArenaModel.tsx L138: useState(false) - default off - L1808-1809: calldata branches mirror Simple - TimeCurveArenaView.tsx L780-790: checkbox + 'Plant WarBow flag' label Both surfaces match dev's 'checkbox default off, CTA builds correct calldata' AC. ## Docs (item 8) #63 referenced in: - docs/product/primitives.md - docs/frontend/timecurve-views.md - docs/testing/invariants-and-business-logic.md - skills/play-timecurve-warbow/SKILL.md - guardrails item 16 ## Deferred until #72 lands Items requiring on-chain tx evidence (live receipts, indexer flag_planted column with real txs, frontend wallet simulation): - Items 1, 2 with actual tx receipts (covered by forge but not live-anvil yet) - Item 3 referral path live opt-in / opt-out - Item 5 interrupt scenarios with multi-wallet plant + plain buy chain - Item 6 router buyViaKumbaya plantWarBowFlag third arg These deferred items are fully blocked by #72 EIP-170 contract size launch-blocker - DeployDev fails before any of this can be reached on a live stack. Will close once #72 unblocks the deploy. Issue stays open.
Brouie commented 2026-04-27 04:04:48 +00:00 (Migrated from gitlab.com)

mentioned in issue #76

mentioned in issue #76
Brouie commented 2026-04-27 04:05:26 +00:00 (Migrated from gitlab.com)

mentioned in issue #77

mentioned in issue #77
Brouie commented 2026-04-27 04:05:47 +00:00 (Migrated from gitlab.com)

@PlasticDigits per today's process update (file follow-ups, close originals): browser walkthrough items moved to #76, test-coverage gap moved to #77. Source verification (see prior note on this issue) stands.

Closing #63. Live walkthrough proceeds via #76 once a frontend session captures tx receipts; test gap fixes via #77.

@PlasticDigits per today's process update (file follow-ups, close originals): browser walkthrough items moved to #76, test-coverage gap moved to #77. Source verification (see prior note on this issue) stands. Closing #63. Live walkthrough proceeds via #76 once a frontend session captures tx receipts; test gap fixes via #77.
Brouie (Migrated from gitlab.com) closed this issue 2026-04-27 04:05:48 +00:00
Brouie commented 2026-04-27 04:12:19 +00:00 (Migrated from gitlab.com)

mentioned in issue #65

mentioned in issue #65
PlasticDigits commented 2026-05-19 08:06:28 +00:00 (Migrated from gitlab.com)

mentioned in issue #218

mentioned in issue #218
PlasticDigits commented 2026-07-09 07:56:59 +00:00 (Migrated from gitlab.com)

mentioned in issue #370

mentioned in issue #370
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#63
No description provided.