Document doubOwedForBuy mutability and TWAP usage (#315) #459

Merged
PlasticDigits merged 1 commit from issue-315-doub-owed-preview into main 2026-06-13 09:27:45 +00:00
PlasticDigits commented 2026-06-13 08:03:09 +00:00 (Migrated from gitlab.com)

Summary

GitLab #315: document and harden doubOwedForBuy for integrators and the buy router.

  • doubOwedForBuy(charmWad) and _sampleCharmAnchor() are now view — safe for eth_call / staticcall (no state writes on the preview path).
  • NatSpec explains when external TWAP/spot reads occur (hard-reset band vs within-epoch growth).
  • Forge tests assert doubOwedForBuy == buy DOUB pull within epoch and at the Last Buy hard-reset boundary.
  • Cross-linked: contracts/PARAMETERS.md, docs/integrations/kumbaya.md, docs/testing/invariants-and-business-logic.md (INV-TIME-ARENA-DOUB-OWED-PREVIEW), skills/play-time-arena-doub/SKILL.md.

Acceptance checklist

Criterion Command / step Result
Documented when external calls occur and why NatSpec on doubOwedForBuy / _sampleCharmAnchor; PARAMETERS.md; kumbaya.md §315 PASS
Integrators have staticcall-safe preview path doubOwedForBuy is view PASS
Forge test covers hard-reset boundary pricing cd contracts && forge test --match-contract TimeArenaEpochCharmPrice PASS
Router still works with view doubOwedForBuy cd contracts && forge test --match-contract TimeArenaBuyRouter PASS

Verification checklist (third parties)

  1. cd contracts && forge test --match-contract TimeArenaEpochCharmPrice — expect 9/9 PASS including test_doubOwedForBuy_matches_buy_at_hard_reset_boundary.
  2. cd contracts && forge test --match-contract TimeArenaBuyRouter — expect all PASS.
  3. Read docs/integrations/kumbaya.md — confirm doubOwedForBuy vs effectiveCharmPriceWad section and router invariant row.
  4. Optional: bash scripts/verify-time-arena-buy-router-anvil.sh if exercising live Anvil router paths.

Follow-ups

  • Frontend submit sizing still reads effectiveCharmPriceWad only; at the hard-reset band UI quotes may lag until a follow-up switches to doubOwedForBuy (router onchain path is already correct).

Note

Medium Risk
Changes onchain pricing preview semantics at the Last Buy hard-reset boundary where Kumbaya router swap sizing must match actual DOUB pulls; mitigated by Forge parity tests and existing router use of doubOwedForBuy.

Overview
GitLab #315 hardens TimeArena.doubOwedForBuy(charmWad) for eth_call / staticcall: it and _sampleCharmAnchor() are now view (no state writes on the preview path).

When Last Buy is in the hard-reset band (remaining < 780s), the preview uses the same TWAP/spot anchor as onchain re-anchor without updating epoch state; otherwise it uses effectiveCharmPriceWad(). NatSpec directs integrators and TimeArenaBuyRouter to prefer this over effectiveCharmPriceWad() alone for exactOutput sizing at the reset boundary.

Forge adds test_doubOwedForBuy_matches_buy_within_epoch and test_doubOwedForBuy_matches_buy_at_hard_reset_boundary. Docs cross-link PARAMETERS.md, kumbaya.md, invariants INV-TIME-ARENA-DOUB-OWED-PREVIEW / INV-TIME-ARENA-DOUB-OWED-ROUTER, and the play skill. Frontend submit sizing still uses effectiveCharmPriceWad only (noted as follow-up).

Reviewed by Cursor Bugbot for commit 1e966591d1. Bugbot is set up for automated code reviews on this repo. Configure here.

## Summary GitLab [#315](https://gitlab.com/PlasticDigits/yieldomega/-/issues/315): document and harden `doubOwedForBuy` for integrators and the buy router. - **`doubOwedForBuy(charmWad)`** and **`_sampleCharmAnchor()`** are now **`view`** — safe for `eth_call` / `staticcall` (no state writes on the preview path). - NatSpec explains when external TWAP/spot reads occur (hard-reset band vs within-epoch growth). - Forge tests assert **`doubOwedForBuy == buy` DOUB pull** within epoch and at the Last Buy hard-reset boundary. - Cross-linked: `contracts/PARAMETERS.md`, `docs/integrations/kumbaya.md`, `docs/testing/invariants-and-business-logic.md` (**`INV-TIME-ARENA-DOUB-OWED-PREVIEW`**), `skills/play-time-arena-doub/SKILL.md`. ## Acceptance checklist | Criterion | Command / step | Result | |-----------|----------------|--------| | Documented when external calls occur and why | NatSpec on `doubOwedForBuy` / `_sampleCharmAnchor`; `PARAMETERS.md`; `kumbaya.md` §315 | PASS | | Integrators have staticcall-safe preview path | `doubOwedForBuy` is `view` | PASS | | Forge test covers hard-reset boundary pricing | `cd contracts && forge test --match-contract TimeArenaEpochCharmPrice` | PASS | | Router still works with view `doubOwedForBuy` | `cd contracts && forge test --match-contract TimeArenaBuyRouter` | PASS | ## Verification checklist (third parties) 1. `cd contracts && forge test --match-contract TimeArenaEpochCharmPrice` — expect 9/9 PASS including `test_doubOwedForBuy_matches_buy_at_hard_reset_boundary`. 2. `cd contracts && forge test --match-contract TimeArenaBuyRouter` — expect all PASS. 3. Read `docs/integrations/kumbaya.md` — confirm `doubOwedForBuy` vs `effectiveCharmPriceWad` section and router invariant row. 4. Optional: `bash scripts/verify-time-arena-buy-router-anvil.sh` if exercising live Anvil router paths. ## Follow-ups - Frontend submit sizing still reads **`effectiveCharmPriceWad`** only; at the hard-reset band UI quotes may lag until a follow-up switches to **`doubOwedForBuy`** (router onchain path is already correct). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes onchain pricing preview semantics at the Last Buy hard-reset boundary where Kumbaya router swap sizing must match actual DOUB pulls; mitigated by Forge parity tests and existing router use of `doubOwedForBuy`. > > **Overview** > GitLab **#315** hardens **`TimeArena.doubOwedForBuy(charmWad)`** for **`eth_call`** / **`staticcall`**: it and **`_sampleCharmAnchor()`** are now **`view`** (no state writes on the preview path). > > When Last Buy is in the **hard-reset band** (`remaining < 780s`), the preview uses the same TWAP/spot anchor as onchain re-anchor **without** updating epoch state; otherwise it uses **`effectiveCharmPriceWad()`**. NatSpec directs integrators and **`TimeArenaBuyRouter`** to prefer this over **`effectiveCharmPriceWad()`** alone for **`exactOutput`** sizing at the reset boundary. > > **Forge** adds **`test_doubOwedForBuy_matches_buy_within_epoch`** and **`test_doubOwedForBuy_matches_buy_at_hard_reset_boundary`**. **Docs** cross-link **`PARAMETERS.md`**, **`kumbaya.md`**, invariants **`INV-TIME-ARENA-DOUB-OWED-PREVIEW`** / **`INV-TIME-ARENA-DOUB-OWED-ROUTER`**, and the play skill. Frontend submit sizing still uses **`effectiveCharmPriceWad`** only (noted as follow-up). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1e966591d1f0036158c16009907e0984170eae8e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
PlasticDigits (Migrated from gitlab.com) approved these changes 2026-06-13 08:03:09 +00:00
project_80561744_bot_5118c68a88bff95eacb6ab1c47c19cae commented 2026-06-13 08:03:16 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
project_80561744_bot_5118c68a88bff95eacb6ab1c47c19cae commented 2026-06-13 08:03:25 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-13 08:07:09 +00:00 (Migrated from gitlab.com)

Security review — MR !459

Commit reviewed: 1e966591d1f0036158c16009907e0984170eae8e
Base: a0d325c5797067494af8ef2f7666856dd39131f0

Scope

Onchain diff: TimeArena.doubOwedForBuy / _sampleCharmAnchor marked view (logic unchanged vs base); NatSpec + Forge parity tests at hard-reset boundary; docs/invariants only elsewhere. TimeArenaBuyRouter unchanged (already sizes from doubOwedForBuy).

Method

Traced attacker-controlled inputs (charmWad, oracle/TWAP reads) through preview (doubOwedForBuy) vs execution (_buyDoub → _prepareBuyBeforeTimer → _pullDoubExact) and router atomic path (buyViaKumbaya → swap → buyFor). Ran forge test --match-contract TimeArenaEpochCharmPrice and TimeArenaBuyRouter — PASS.

Findings

FINDINGS: 0 medium+

No inline threads (nothing to anchor).

Notes (out of scope / not introduced here)

  • TWAP manipulation at hard-reset re-anchor is an existing protocol/oracle trust boundary; ArenaCharmPriceTwap enforces 15m window, min observation cardinality, and 500 bps spot/TWAP deviation — not new in this diff.
  • Cross-block preview drift (eth_call then later buy) is inherent oracle latency; same-tx router path re-reads doubOwedForBuy before swap + buyFor, so no atomic mismatch.
  • Frontend still sizes from effectiveCharmPriceWad (documented follow-up) — UX/quoting gap, not a contract authz or fund-loss bug in this MR.

Outcome: Security review: no medium+ findings on this diff. Safe to merge from a security perspective.

## Security review — MR !459 **Commit reviewed:** `1e966591d1f0036158c16009907e0984170eae8e` **Base:** `a0d325c5797067494af8ef2f7666856dd39131f0` ### Scope Onchain diff: `TimeArena.doubOwedForBuy` / `_sampleCharmAnchor` marked **`view`** (logic unchanged vs base); NatSpec + Forge parity tests at hard-reset boundary; docs/invariants only elsewhere. `TimeArenaBuyRouter` unchanged (already sizes from `doubOwedForBuy`). ### Method Traced attacker-controlled inputs (`charmWad`, oracle/TWAP reads) through preview (`doubOwedForBuy`) vs execution (`_buyDoub` → `_prepareBuyBeforeTimer` → `_pullDoubExact`) and router atomic path (`buyViaKumbaya` → swap → `buyFor`). Ran `forge test --match-contract TimeArenaEpochCharmPrice` and `TimeArenaBuyRouter` — PASS. ### Findings **FINDINGS: 0** medium+ No inline threads (nothing to anchor). ### Notes (out of scope / not introduced here) - **TWAP manipulation** at hard-reset re-anchor is an existing protocol/oracle trust boundary; `ArenaCharmPriceTwap` enforces 15m window, min observation cardinality, and 500 bps spot/TWAP deviation — not new in this diff. - **Cross-block preview drift** (`eth_call` then later `buy`) is inherent oracle latency; same-tx router path re-reads `doubOwedForBuy` before swap + `buyFor`, so no atomic mismatch. - **Frontend** still sizes from `effectiveCharmPriceWad` (documented follow-up) — UX/quoting gap, not a contract authz or fund-loss bug in this MR. **Outcome:** Security review: no medium+ findings on this diff. Safe to merge from a security perspective.
PlasticDigits commented 2026-06-13 09:27:41 +00:00 (Migrated from gitlab.com)

approved this merge request

approved this merge request
PlasticDigits commented 2026-06-13 09:27:46 +00:00 (Migrated from gitlab.com)

mentioned in commit 97d56f93ff

mentioned in commit 97d56f93fff397c2eb12f02837c387355afb671d
PlasticDigits (Migrated from gitlab.com) merged commit 97d56f93ff into main 2026-06-13 09:27:46 +00:00
Sign in to join this conversation.
No reviewers
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!459
No description provided.