Verification: #55 pause + signoff — post-end gate live walkthrough #79

Closed
opened 2026-04-27 04:23:29 +00:00 by Brouie · 9 comments
Brouie commented 2026-04-27 04:23:29 +00:00 (Migrated from gitlab.com)

Follow-up tracker for items deferred from #55 source + selective live verification on Sunday/Monday.

Background

#55 closed today on cf6fe19 + f2500ec source + selective live verification:

  • Source review (Sunday note): unified _requireSaleInteractionsEnabled at TimeCurve.sol L295 covers both buy and WarBow paths, charmRedemption + reservePodiumPayouts gates wired at L656-657 and prize-distribution path
  • Live cast verification (today):
    • cast send setBuyFeeRoutingEnabled(false) -> cast call buy(1e18) reverts with exact 'TimeCurve: sale interactions disabled' (matches L295 helper revert string)
    • cast send setBuyFeeRoutingEnabled(true) -> cast call buy(1e18) reverts past gate with custom error 0xfb8f41b2 ERC20InsufficientAllowance (proves buy logic now reachable)
    • cast send setBuyFeeRoutingEnabled(false) -> cast call warbowSteal(addr, false) reverts with same exact 'TimeCurve: sale interactions disabled' (proves L295 helper is unified across both buy and WarBow paths via cf6fe19)
  • Test pass: forge 191/191 (includes pause/signoff suite)

Scope (deferred to follow-up live walkthrough)

These items need a stack where ended=true (sale ended state), which the SKIP_ANVIL_RICH_STATE=1 path doesn't produce. anvil_rich_state.sh provides the post-end state.

  • charmRedemptionEnabled = false on a post-end stack: redeemCharms reverts with 'TimeCurve: charm redemptions disabled' (gates check is at L657 after the L656 ended check; can only be exercised once sale is ended)
  • charmRedemptionEnabled flip to true: redeemCharms succeeds for a buyer with non-zero charmWeight
  • reservePodiumPayoutsEnabled = false on a post-end stack with non-zero prize pool: distributePrizes reverts with the corresponding gate string
  • reservePodiumPayoutsEnabled flip to true: distributePrizes succeeds, prizes flow to podium winners

Out of scope / explicit non-QA items

Per dev framing on #55:

  • Per-buy CL8Y routing scope (TBD product row)
  • Rabbit Treasury Pausable as separate ceremony
  • Implementation child issues per pause-and-final-signoff.md tables

These are stakeholder/governance signoffs and TBD product decisions — not QA-actionable in this issue. They should track as separate issues if/when they become concrete.

Pre-conditions

  • Live Anvil stack with anvil_rich_state.sh executed (post-end state with ended=true)
  • Owner private key for setCharmRedemptionEnabled / setReservePodiumPayoutsEnabled toggles
  • Buyer addresses with non-zero charmWeight from rich-state setup
  • Optional: at least one prize-eligible address for reservePodiumPayouts side

Acceptance

All 4 rows produce evidence (cast tx hash + revert string or success log) and either PASS or have a filed sub-issue. cc @PlasticDigits

Follow-up tracker for items deferred from #55 source + selective live verification on Sunday/Monday. ## Background #55 closed today on cf6fe19 + f2500ec source + selective live verification: - **Source review (Sunday note):** unified _requireSaleInteractionsEnabled at TimeCurve.sol L295 covers both buy and WarBow paths, charmRedemption + reservePodiumPayouts gates wired at L656-657 and prize-distribution path - **Live cast verification (today):** - cast send setBuyFeeRoutingEnabled(false) -> cast call buy(1e18) reverts with exact 'TimeCurve: sale interactions disabled' (matches L295 helper revert string) - cast send setBuyFeeRoutingEnabled(true) -> cast call buy(1e18) reverts past gate with custom error 0xfb8f41b2 ERC20InsufficientAllowance (proves buy logic now reachable) - cast send setBuyFeeRoutingEnabled(false) -> cast call warbowSteal(addr, false) reverts with same exact 'TimeCurve: sale interactions disabled' (proves L295 helper is unified across both buy and WarBow paths via cf6fe19) - **Test pass:** forge 191/191 (includes pause/signoff suite) ## Scope (deferred to follow-up live walkthrough) These items need a stack where ended=true (sale ended state), which the SKIP_ANVIL_RICH_STATE=1 path doesn't produce. anvil_rich_state.sh provides the post-end state. - [ ] charmRedemptionEnabled = false on a post-end stack: redeemCharms reverts with 'TimeCurve: charm redemptions disabled' (gates check is at L657 *after* the L656 ended check; can only be exercised once sale is ended) - [ ] charmRedemptionEnabled flip to true: redeemCharms succeeds for a buyer with non-zero charmWeight - [ ] reservePodiumPayoutsEnabled = false on a post-end stack with non-zero prize pool: distributePrizes reverts with the corresponding gate string - [ ] reservePodiumPayoutsEnabled flip to true: distributePrizes succeeds, prizes flow to podium winners ## Out of scope / explicit non-QA items Per dev framing on #55: - Per-buy CL8Y routing scope (TBD product row) - Rabbit Treasury Pausable as separate ceremony - Implementation child issues per pause-and-final-signoff.md tables These are stakeholder/governance signoffs and TBD product decisions — not QA-actionable in this issue. They should track as separate issues if/when they become concrete. ## Pre-conditions - Live Anvil stack with anvil_rich_state.sh executed (post-end state with ended=true) - Owner private key for setCharmRedemptionEnabled / setReservePodiumPayoutsEnabled toggles - Buyer addresses with non-zero charmWeight from rich-state setup - Optional: at least one prize-eligible address for reservePodiumPayouts side ## Acceptance All 4 rows produce evidence (cast tx hash + revert string or success log) and either PASS or have a filed sub-issue. cc @PlasticDigits
Brouie commented 2026-04-27 04:24:01 +00:00 (Migrated from gitlab.com)

mentioned in issue #55

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

mentioned in commit 8803428809

mentioned in commit 88034288091d307de99a7afd1c72e87ce30a8c15
PlasticDigits commented 2026-04-27 05:07:27 +00:00 (Migrated from gitlab.com)

mentioned in commit 734517f277

mentioned in commit 734517f277f72d7933df95757d7ca231c7914290
PlasticDigits commented 2026-04-27 05:09:41 +00:00 (Migrated from gitlab.com)

#79 — automated deferred scope (merged)

Implemented on `main` (e.g. merge at current tip): reproducible post-end Anvil path + `cast` verification script for the four #79 rows, with docs + invariants + `skills/verify-yo-timecurve-post-end-gates/` for 3rd-party agents.

Why it was blocked before: `SimulateAnvilRichStatePart2` turns on `charmRedemptionEnabled` / `reservePodiumPayoutsEnabled` and runs redemptions + `distributePrizes`, and `DeployDev` also pre-enables those flags. That full rich script cannot exercise the “gate off” revert strings on the same chain. The new `ANVIL_RICH_END_SALE_ONLY=1` path ends the sale with false/false (after reset), then the verify script flips them in order.

Script (happy path)

  1. `anvil --code-size-limit 524288` (or repo stack) + `DeployDev` to 31337.
  2. `export RPC_URL=…` then: `ANVIL_RICH_END_SALE_ONLY=1 bash contracts/script/anvil_rich_state.sh`
  3. `bash scripts/verify-timecurve-post-end-gates-anvil.sh` (optional `YIELDOMEGA_TIMECURVE=0x…` or rely on `contracts/deployments/local-anvil-registry.json`)

If the script is borked (manual / debug): ensure `ended==true`, both gates false, `prizesDistributed==false`, non-zero CL8Y on `podiumPool`, Alice (Anvil key #1) has `charmWeight>0` and not redeemed; then: `redeemCharms` (expect `TimeCurve: charm redemptions disabled`); `setCharmRedemptionEnabled(true)`; `redeemCharms` (success); `distributePrizes` (expect `TimeCurve: reserve podium payouts disabled`); `setReservePodiumPayoutsEnabled(true)`; `distributePrizes` (success). If you already ran full `anvil_rich_state.sh`, use a fresh anvil + deploy.

Docs / cross-links: `docs/operations/final-signoff-and-value-movement.md` (post-end walkthrough), `docs/testing/anvil-rich-state.md`, `docs/testing/invariants-and-business-logic.md` (issue #79 section), `.cursor/skills/yieldomega-guardrails/SKILL.md` item 20, `docs/agents/metadata-and-skills.md` play skill row.

@brouie Please verify using this checklist:

  • `ANVIL_RICH_END_SALE_ONLY=1` + `anvil_rich_state.sh` completes, then `verify-timecurve-post-end-gates-anvil.sh` prints PASS for all four rows.
  • Row 1 revert string is exactly `TimeCurve: charm redemptions disabled`.
  • Row 3 revert string is exactly `TimeCurve: reserve podium payouts disabled` with non-zero podium pool.
  • After row 4, `cast call` `prizesDistributed()(bool)` is `true` on the TimeCurve proxy (not implementation in `run-latest.json`).
  • Optional: `FOUNDRY_OUT=/tmp/…` (if `contracts/out` is not writable) and `forge test` for `test_redeemCharms_reverts_while_charm_redemption_disabled` + `test_distributePrizes_reverts_while_reserve_podium_payouts_disabled` still pass.

Issue left open for your signoff.

## #79 — automated deferred scope (merged) Implemented on \`main\` (e.g. merge at current tip): reproducible **post-end** Anvil path + **\`cast\` verification script** for the four #79 rows, with docs + invariants + **\`skills/verify-yo-timecurve-post-end-gates/\`** for 3rd-party agents. **Why it was blocked before:** \`SimulateAnvilRichStatePart2\` turns **on** \`charmRedemptionEnabled\` / \`reservePodiumPayoutsEnabled\` and runs redemptions + \`distributePrizes\`, and \`DeployDev\` also pre-enables those flags. That full rich script cannot exercise the **“gate off”** revert strings on the same chain. The new \`ANVIL_RICH_END_SALE_ONLY=1\` path ends the sale with **false/false** (after reset), then the verify script flips them in order. **Script (happy path)** 1. \`anvil --code-size-limit 524288\` (or repo stack) + \`DeployDev\` to 31337. 2. \`export RPC_URL=…\` then: \`ANVIL_RICH_END_SALE_ONLY=1 bash contracts/script/anvil_rich_state.sh\` 3. \`bash scripts/verify-timecurve-post-end-gates-anvil.sh\` (optional \`YIELDOMEGA_TIMECURVE=0x…\` or rely on \`contracts/deployments/local-anvil-registry.json\`) **If the script is borked (manual / debug):** ensure \`ended==true\`, both gates **false**, \`prizesDistributed==false\`, non-zero CL8Y on \`podiumPool\`, Alice (Anvil key #1) has \`charmWeight>0\` and not redeemed; then: \`redeemCharms\` (expect \`TimeCurve: charm redemptions disabled\`); \`setCharmRedemptionEnabled(true)\`; \`redeemCharms\` (success); \`distributePrizes\` (expect \`TimeCurve: reserve podium payouts disabled\`); \`setReservePodiumPayoutsEnabled(true)\`; \`distributePrizes\` (success). If you already ran **full** \`anvil_rich_state.sh\`, use a **fresh** anvil + deploy. **Docs / cross-links:** \`docs/operations/final-signoff-and-value-movement.md\` (post-end walkthrough), \`docs/testing/anvil-rich-state.md\`, \`docs/testing/invariants-and-business-logic.md\` (issue #79 section), \`.cursor/skills/yieldomega-guardrails/SKILL.md\` item 20, \`docs/agents/metadata-and-skills.md\` play skill row. @brouie **Please verify** using this checklist: - [ ] \`ANVIL_RICH_END_SALE_ONLY=1\` + \`anvil_rich_state.sh\` completes, then \`verify-timecurve-post-end-gates-anvil.sh\` prints PASS for all four rows. - [ ] Row 1 revert string is exactly \`TimeCurve: charm redemptions disabled\`. - [ ] Row 3 revert string is exactly \`TimeCurve: reserve podium payouts disabled\` with non-zero podium pool. - [ ] After row 4, \`cast call\` \`prizesDistributed()(bool)\` is \`true\` on the **TimeCurve proxy** (not implementation in \`run-latest.json\`). - [ ] Optional: \`FOUNDRY_OUT=/tmp/…\` (if \`contracts/out\` is not writable) and \`forge test\` for \`test_redeemCharms_reverts_while_charm_redemption_disabled\` + \`test_distributePrizes_reverts_while_reserve_podium_payouts_disabled\` still pass. Issue left **open** for your signoff.
Brouie commented 2026-04-27 05:16:27 +00:00 (Migrated from gitlab.com)

@PlasticDigits verified on 734517f / 8803428.

Verify-script execution (clean stack, end-sale-only state)

pkill -f timecurve_bot; pkill -9 -f anvil; sleep 2
SKIP_ANVIL_RICH_STATE=1 bash scripts/start-local-anvil-stack.sh
ANVIL_RICH_END_SALE_ONLY=1 bash contracts/script/anvil_rich_state.sh
bash scripts/verify-timecurve-post-end-gates-anvil.sh

Output

=== TimeCurve (proxy) 0x59b670e9fA9D0A427751Af201D676719a970857b  alice=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 ===
  Podium pool CL8Y balance: 8000296296296296296 (wei) — non-zero, gate will apply to distributePrizes
=== Row 1: redeemCharms reverts when charmRedemptionEnabled is false ===
PASS: redeemCharms (alice) (revert contains: TimeCurve: charm redemptions disabled)
=== Row 2: setCharmRedemptionEnabled(true) -> redeemCharms succeeds (alice) ===
PASS: redeemCharms (alice) succeeded with gate on
=== Row 3: distributePrizes reverts when reservePodiumPayoutsEnabled is false and pool > 0 ===
PASS: distributePrizes (revert contains: TimeCurve: reserve podium payouts disabled)
=== Row 4: setReservePodiumPayoutsEnabled(true) -> distributePrizes succeeds ===
PASS: distributePrizes succeeded with gate on
=== Done — all #79 rows passed for this Anvil. Capture tx output above for evidence. ===

All 4 rows green. Exact revert strings match the issue spec:

  • 'TimeCurve: charm redemptions disabled' (redeem gate at TimeCurve.sol L657)
  • 'TimeCurve: reserve podium payouts disabled' (distributePrizes gate)

Closing #79.

@PlasticDigits verified on 734517f / 8803428. ## Verify-script execution (clean stack, end-sale-only state) ``` pkill -f timecurve_bot; pkill -9 -f anvil; sleep 2 SKIP_ANVIL_RICH_STATE=1 bash scripts/start-local-anvil-stack.sh ANVIL_RICH_END_SALE_ONLY=1 bash contracts/script/anvil_rich_state.sh bash scripts/verify-timecurve-post-end-gates-anvil.sh ``` ## Output ``` === TimeCurve (proxy) 0x59b670e9fA9D0A427751Af201D676719a970857b alice=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 === Podium pool CL8Y balance: 8000296296296296296 (wei) — non-zero, gate will apply to distributePrizes === Row 1: redeemCharms reverts when charmRedemptionEnabled is false === PASS: redeemCharms (alice) (revert contains: TimeCurve: charm redemptions disabled) === Row 2: setCharmRedemptionEnabled(true) -> redeemCharms succeeds (alice) === PASS: redeemCharms (alice) succeeded with gate on === Row 3: distributePrizes reverts when reservePodiumPayoutsEnabled is false and pool > 0 === PASS: distributePrizes (revert contains: TimeCurve: reserve podium payouts disabled) === Row 4: setReservePodiumPayoutsEnabled(true) -> distributePrizes succeeds === PASS: distributePrizes succeeded with gate on === Done — all #79 rows passed for this Anvil. Capture tx output above for evidence. === ``` All 4 rows green. Exact revert strings match the issue spec: - 'TimeCurve: charm redemptions disabled' (redeem gate at TimeCurve.sol L657) - 'TimeCurve: reserve podium payouts disabled' (distributePrizes gate) Closing #79.
Brouie (Migrated from gitlab.com) closed this issue 2026-04-27 05:16:29 +00:00
PlasticDigits commented 2026-04-30 05:25:10 +00:00 (Migrated from gitlab.com)

mentioned in issue #100

mentioned in issue #100
PlasticDigits commented 2026-05-04 10:56:49 +00:00 (Migrated from gitlab.com)

mentioned in commit aa4907475e

mentioned in commit aa4907475e4234960b9ae4d47f89e56625b0c7c3
PlasticDigits commented 2026-05-05 05:59:55 +00:00 (Migrated from gitlab.com)

mentioned in issue #149

mentioned in issue #149
PlasticDigits commented 2026-05-29 03:24:01 +00:00 (Migrated from gitlab.com)

mentioned in issue #263

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