Fix buyWithCred epoch CRED pool accrual (#311) #491

Merged
PlasticDigits merged 2 commits from fix/issue-311-buywithcred-epoch-pool into main 2026-06-14 07:12:08 +00:00
PlasticDigits commented 2026-06-14 05:58:01 +00:00 (Migrated from gitlab.com)

Summary

  • buyWithCred now calls _accrueCharmAndCred (with zero referral hash) so each CRED buy adds CRED_PER_BUY (35e18) to epochCredPool[epoch], matching DOUB buy economics.
  • Removed unused _accrueCharmOnly; CHARM weight accrual unchanged in intent.
  • Added Forge tests for pool delta, mixed DOUB/CRED pro-rata, and epoch-boundary pool credit.
  • Replaced INV-TIME-ARENA-CRED-NO-POOL with INV-TIME-ARENA-CRED-POOL-ACCRUE in invariants doc.
  • Extended verify-cred-buy-anvil.sh with on-chain epochCredPool assertion (+35 CRED after first buyWithCred); corrected starter-buy level expectation (10 CHARM → level 2).

Verification checklist

Acceptance item Command Result
Each buyWithCred adds 35 CRED to epochCredPool[epoch] forge test --match-test test_buy_with_cred PASS — asserts poolBefore + 35e18
CHARM weight accrual unchanged forge test --match-test cred (19 tests) PASS
claimCred pro-rata fair for mixed DOUB/CRED buyers forge test --match-test test_cred_pro_rata_mixed_doub_and_cred_buyers PASS
Invariant doc updated; CRED-NO-POOL revised docs/testing/invariants-and-business-logic.md PASS — INV-TIME-ARENA-CRED-POOL-ACCRUE
Forge pool delta after buyWithCred forge test --match-test "cred|buy_with_cred|buyWithCred" PASS (19/19)
verify-cred-buy-anvil.sh bash scripts/verify-cred-buy-anvil.sh PASS (=== verify-cred-buy-anvil: OK ===)
No regression in DOUB pool accrual forge test --match-test test_cred_accrue_on_doub_buy PASS
CRED-only buyer fair share (attack) test_cred_pro_rata_mixed_doub_and_cred_buyers PASS
Epoch boundary pool credit test_buyWithCred_epoch_boundary_credits_correct_pool PASS
Double-claim prevention forge test --match-test test_claimCred_cannot_double_claim PASS
Indexer pool fields cd indexer && cargo test SKIP — indexer does not decode epochCredPool

Note

Medium Risk
Changes on-chain epoch CRED accounting for all CRED buys and shifts pro-rata claims, but behavior mirrors the existing DOUB path and is covered by Forge and Anvil scripts.

Overview
CRED buys now fund the epoch pool. Each buyWithCred calls _accrueCharmAndCred (zero referral hash) so epochCredPool[lastBuyEpoch] increases by CRED_PER_BUY (35e18), matching DOUB buy economics for claimCred pro-rata. CHARM weight accrual on the CRED path is unchanged in intent; _accrueCharmOnly is removed.

Tests and ops: Forge coverage includes pool delta, mixed DOUB/CRED pro-rata, and epoch-boundary pool credit. verify-cred-buy-anvil.sh asserts epochCredPool +35 after the first buyWithCred and fixes the starter-buy level expectation (10 CHARM → level 2). Invariants doc replaces INV-TIME-ARENA-CRED-NO-POOL with INV-TIME-ARENA-CRED-POOL-ACCRUE / §311 parity IDs.

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

## Summary - `buyWithCred` now calls `_accrueCharmAndCred` (with zero referral hash) so each CRED buy adds `CRED_PER_BUY` (35e18) to `epochCredPool[epoch]`, matching DOUB buy economics. - Removed unused `_accrueCharmOnly`; CHARM weight accrual unchanged in intent. - Added Forge tests for pool delta, mixed DOUB/CRED pro-rata, and epoch-boundary pool credit. - Replaced `INV-TIME-ARENA-CRED-NO-POOL` with `INV-TIME-ARENA-CRED-POOL-ACCRUE` in invariants doc. - Extended `verify-cred-buy-anvil.sh` with on-chain `epochCredPool` assertion (+35 CRED after first `buyWithCred`); corrected starter-buy level expectation (10 CHARM → level 2). ## Verification checklist | Acceptance item | Command | Result | |-----------------|---------|--------| | Each `buyWithCred` adds 35 CRED to `epochCredPool[epoch]` | `forge test --match-test test_buy_with_cred` | **PASS** — asserts `poolBefore + 35e18` | | CHARM weight accrual unchanged | `forge test --match-test cred` (19 tests) | **PASS** | | `claimCred` pro-rata fair for mixed DOUB/CRED buyers | `forge test --match-test test_cred_pro_rata_mixed_doub_and_cred_buyers` | **PASS** | | Invariant doc updated; `CRED-NO-POOL` revised | `docs/testing/invariants-and-business-logic.md` | **PASS** — `INV-TIME-ARENA-CRED-POOL-ACCRUE` | | Forge pool delta after `buyWithCred` | `forge test --match-test "cred\|buy_with_cred\|buyWithCred"` | **PASS** (19/19) | | `verify-cred-buy-anvil.sh` | `bash scripts/verify-cred-buy-anvil.sh` | **PASS** (`=== verify-cred-buy-anvil: OK ===`) | | No regression in DOUB pool accrual | `forge test --match-test test_cred_accrue_on_doub_buy` | **PASS** | | CRED-only buyer fair share (attack) | `test_cred_pro_rata_mixed_doub_and_cred_buyers` | **PASS** | | Epoch boundary pool credit | `test_buyWithCred_epoch_boundary_credits_correct_pool` | **PASS** | | Double-claim prevention | `forge test --match-test test_claimCred_cannot_double_claim` | **PASS** | | Indexer pool fields | `cd indexer && cargo test` | **SKIP** — indexer does not decode `epochCredPool` | <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes on-chain epoch CRED accounting for all CRED buys and shifts pro-rata claims, but behavior mirrors the existing DOUB path and is covered by Forge and Anvil scripts. > > **Overview** > **CRED buys now fund the epoch pool.** Each `buyWithCred` calls **`_accrueCharmAndCred`** (zero referral hash) so **`epochCredPool[lastBuyEpoch]`** increases by **`CRED_PER_BUY` (35e18)**, matching DOUB buy economics for **`claimCred`** pro-rata. CHARM weight accrual on the CRED path is unchanged in intent; **`_accrueCharmOnly`** is removed. > > **Tests and ops:** Forge coverage includes pool delta, mixed DOUB/CRED pro-rata, and epoch-boundary pool credit. **`verify-cred-buy-anvil.sh`** asserts **`epochCredPool +35`** after the first **`buyWithCred`** and fixes the starter-buy level expectation (10 CHARM → level 2). Invariants doc replaces **`INV-TIME-ARENA-CRED-NO-POOL`** with **`INV-TIME-ARENA-CRED-POOL-ACCRUE`** / §311 parity IDs. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7fffda6109c7252debe41fa5437b1e6ebe725358. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
project_80561744_bot_5118c68a88bff95eacb6ab1c47c19cae commented 2026-06-14 05:58:09 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
project_80561744_bot_5118c68a88bff95eacb6ab1c47c19cae commented 2026-06-14 05:58:18 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-14 06:02:14 +00:00 (Migrated from gitlab.com)

Security review — 34c8c13d

Scope: MR !491 — buyWithCred epoch CRED pool accrual (#311). Reviewed diff in TimeArena.sol (_buyCred → _accrueCharmAndCred), new Forge tests, invariants doc, and verify-cred-buy-anvil.sh.

Method: Traced attacker-controlled inputs (charmWad, buyer address, epoch) through _buyCred → _accrueCharmAndCred → epochCredPool / epochCharmWad → claimCred (playCred.mint). Compared against DOUB path (_buyDoub). Re-checked referral branch (bytes32(0) skips mint), reentrancy (nonReentrant, burn-before-accrue, mint-after-zeroing), epoch-boundary pool assignment, and mixed-buyer pro-rata economics.

Outcome: FINDINGS: 0 medium+

Security review: no medium+ findings on this diff.

Notes (informational, below threshold):

  • This change closes the pre-existing fairness gap where buyWithCred accrued epoch CHARM weight without adding CRED_PER_BUY to epochCredPool, letting CRED buyers dilute DOUB-funded pro-rata claims. The fix aligns CRED accrual with the DOUB path; claimCred still mints from a virtual pool with per-epoch weight accounting and double-claim guards unchanged.
  • bytes32(0) referral hash correctly prevents self-service referral CRED mint on the CRED rail.
  • Off-chain changes (doc, verify script) have no production auth/injection surface.

Inline threads: none

## Security review — `34c8c13d` **Scope:** MR !491 — `buyWithCred` epoch CRED pool accrual (#311). Reviewed diff in `TimeArena.sol` (`_buyCred` → `_accrueCharmAndCred`), new Forge tests, invariants doc, and `verify-cred-buy-anvil.sh`. **Method:** Traced attacker-controlled inputs (`charmWad`, buyer address, epoch) through `_buyCred` → `_accrueCharmAndCred` → `epochCredPool` / `epochCharmWad` → `claimCred` (`playCred.mint`). Compared against DOUB path (`_buyDoub`). Re-checked referral branch (`bytes32(0)` skips mint), reentrancy (`nonReentrant`, burn-before-accrue, mint-after-zeroing), epoch-boundary pool assignment, and mixed-buyer pro-rata economics. **Outcome:** **FINDINGS: 0** medium+ Security review: no medium+ findings on this diff. **Notes (informational, below threshold):** - This change **closes** the pre-existing fairness gap where `buyWithCred` accrued epoch CHARM weight without adding `CRED_PER_BUY` to `epochCredPool`, letting CRED buyers dilute DOUB-funded pro-rata claims. The fix aligns CRED accrual with the DOUB path; `claimCred` still mints from a virtual pool with per-epoch weight accounting and double-claim guards unchanged. - `bytes32(0)` referral hash correctly prevents self-service referral CRED mint on the CRED rail. - Off-chain changes (doc, verify script) have no production auth/injection surface. **Inline threads:** none
PlasticDigits commented 2026-06-14 06:57:48 +00:00 (Migrated from gitlab.com)

added 100 commits

  • 34c8c13d...862964db - 99 commits from branch main
  • 7fffda61 - Merge origin/main into fix/issue-311-buywithcred-epoch-pool

Compare with previous version

added 100 commits <ul><li>34c8c13d...862964db - 99 commits from branch <code>main</code></li><li>7fffda61 - Merge origin/main into fix/issue-311-buywithcred-epoch-pool</li></ul> [Compare with previous version](/PlasticDigits/yieldomega/-/merge_requests/121/diffs?diff_id=1850611911&start_sha=34c8c13d4d45993884b91a087cfcd9809d063617)
PlasticDigits commented 2026-06-14 06:57:52 +00:00 (Migrated from gitlab.com)

Resolved merge conflicts with main in 4 files:

  • contracts/src/arena/TimeArena.sol — kept main's _applyBuyWarBowBp signature (prevDsBuyer / prevDsStreak for defended-streak WarBow BP).
  • contracts/test/TimeArena.t.sol — kept main's expanded #311 test suite (pool accrual, mixed pro-rata, epoch-boundary, fair-share).
  • docs/testing/invariants-and-business-logic.md — aligned INV-TIME-ARENA-CRED-POOL-ACCRUE evidence list with main.
  • scripts/verify-cred-buy-anvil.sh — kept delta-based epochCredPool assertion; dropped duplicate absolute pool check.

Sanity: forge test --match-test "cred|buy_with_cred|buyWithCred" — 25 passed.

Resolved merge conflicts with `main` in 4 files: - **`contracts/src/arena/TimeArena.sol`** — kept main's `_applyBuyWarBowBp` signature (`prevDsBuyer` / `prevDsStreak` for defended-streak WarBow BP). - **`contracts/test/TimeArena.t.sol`** — kept main's expanded #311 test suite (pool accrual, mixed pro-rata, epoch-boundary, fair-share). - **`docs/testing/invariants-and-business-logic.md`** — aligned `INV-TIME-ARENA-CRED-POOL-ACCRUE` evidence list with main. - **`scripts/verify-cred-buy-anvil.sh`** — kept delta-based `epochCredPool` assertion; dropped duplicate absolute pool check. Sanity: `forge test --match-test "cred|buy_with_cred|buyWithCred"` — **25 passed**.
project_80561744_bot_5118c68a88bff95eacb6ab1c47c19cae commented 2026-06-14 06:58:11 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-14 07:01:24 +00:00 (Migrated from gitlab.com)

Security review — 7fffda6109c7252debe41fa5437b1e6ebe725358

Scope: MR !491 — buyWithCred epoch CRED pool accrual (#311). Re-reviewed after merge with main (7fffda61). Focused diff: TimeArena._buyCred now calls _accrueCharmAndCred(buyer, charmWad, bytes32(0)) (removes _accrueCharmOnly), plus Forge tests, invariants doc, and verify-cred-buy-anvil.sh pool-delta assertion.

Method: Traced attacker-controlled inputs (charmWad, msg.sender, lastBuyEpoch) through _buyCred → playCred.burn → _accrueCharmAndCred → epochCredPool / epochCharmWad → claimCred (playCred.mint). Compared parity with DOUB _buyDoub path. Re-checked post-merge conflict resolution in TimeArena.sol (WarBow BP signature unchanged; pool accrual line preserved). Verified referral branch (codeHash == 0 skips mint), reentrancy (nonReentrant, burn-before-accrue, claim zeroes weight before mint), epoch-boundary pool assignment (_accrueCharmAndCred before _finishBuy hard-reset — same ordering as DOUB), and mixed-buyer / CRED-only pro-rata economics (test_cred_pro_rata_mixed_doub_and_cred_buyers, test_cred_only_buyer_fair_share).

Outcome: FINDINGS: 0 medium+

Security review: no medium+ findings on this diff.

Notes (informational, below threshold):

  • This change closes the pre-existing fairness gap where buyWithCred accrued epoch CHARM weight without crediting epochCredPool, allowing CRED buyers to dilute DOUB-funded pro-rata claims. The fix mirrors DOUB economics (CRED_PER_BUY per buy); claimCred mint authority and double-claim guards are unchanged.
  • bytes32(0) referral hash correctly prevents unsolicited referral CRED mint on the CRED rail.
  • Off-chain changes (doc, verify script) have no production auth, injection, or secret-handling surface.

Inline threads: none

## Security review — `7fffda6109c7252debe41fa5437b1e6ebe725358` **Scope:** MR !491 — `buyWithCred` epoch CRED pool accrual (#311). Re-reviewed after merge with `main` (`7fffda61`). Focused diff: `TimeArena._buyCred` now calls `_accrueCharmAndCred(buyer, charmWad, bytes32(0))` (removes `_accrueCharmOnly`), plus Forge tests, invariants doc, and `verify-cred-buy-anvil.sh` pool-delta assertion. **Method:** Traced attacker-controlled inputs (`charmWad`, `msg.sender`, `lastBuyEpoch`) through `_buyCred` → `playCred.burn` → `_accrueCharmAndCred` → `epochCredPool` / `epochCharmWad` → `claimCred` (`playCred.mint`). Compared parity with DOUB `_buyDoub` path. Re-checked post-merge conflict resolution in `TimeArena.sol` (WarBow BP signature unchanged; pool accrual line preserved). Verified referral branch (`codeHash == 0` skips mint), reentrancy (`nonReentrant`, burn-before-accrue, claim zeroes weight before mint), epoch-boundary pool assignment (`_accrueCharmAndCred` before `_finishBuy` hard-reset — same ordering as DOUB), and mixed-buyer / CRED-only pro-rata economics (`test_cred_pro_rata_mixed_doub_and_cred_buyers`, `test_cred_only_buyer_fair_share`). **Outcome:** **FINDINGS: 0** medium+ Security review: no medium+ findings on this diff. **Notes (informational, below threshold):** - This change closes the pre-existing fairness gap where `buyWithCred` accrued epoch CHARM weight without crediting `epochCredPool`, allowing CRED buyers to dilute DOUB-funded pro-rata claims. The fix mirrors DOUB economics (`CRED_PER_BUY` per buy); `claimCred` mint authority and double-claim guards are unchanged. - `bytes32(0)` referral hash correctly prevents unsolicited referral CRED mint on the CRED rail. - Off-chain changes (doc, verify script) have no production auth, injection, or secret-handling surface. **Inline threads:** none
PlasticDigits (Migrated from gitlab.com) merged commit 1b4f62603a into main 2026-06-14 07:12:08 +00:00
PlasticDigits commented 2026-06-14 07:12:09 +00:00 (Migrated from gitlab.com)

mentioned in commit 1b4f62603a

mentioned in commit 1b4f62603ad71a96e2acde0b5efc0f0d7b33a6dd
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!491
No description provided.