fix: prevent over-allocation double-collection in presentPOI auto-downsize#1342
fix: prevent over-allocation double-collection in presentPOI auto-downsize#1342RembrandtK wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes reward accounting in presentPOI over-allocation auto-downsize by ensuring resize logic reads the current allocation state from storage, preventing stale snapshots from re-crediting already collected rewards.
Changes:
- Updates
_resizeAllocationto load allocation state internally and adjusts callers accordingly. - Adds unit and integration regression coverage for over-allocation reward collection and zero-token resize behavior.
- Cleans up related test comments to describe invariants rather than historical bugs.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
packages/subgraph-service/contracts/libraries/AllocationHandler.sol |
Updates resize internals to avoid stale allocation snapshots and narrows reward distribution inputs. |
packages/subgraph-service/test/unit/subgraphService/SubgraphService.t.sol |
Tightens expected post-collection pending reward invariant. |
packages/subgraph-service/test/unit/subgraphService/collect/indexing/indexing.t.sol |
Adds unit regressions for over-allocation resize and double-collection prevention. |
packages/subgraph-service/test/integration/after-transition-period/subgraph-service/indexer.test.ts |
Adds integration coverage for second POI collection after over-allocation resize. |
packages/subgraph-service/test/integration/after-transition-period/subgraph-service/permisionless.test.ts |
Updates stale-allocation expectations to match resize-to-zero behavior. |
packages/testing/test/harness/FullStackHarness.t.sol |
Makes RewardsManager deployment pluggable for real-rewards integration tests. |
packages/testing/test/harness/RealRewardsHarness.t.sol |
Adds harness support for loading the production RewardsManager artifact. |
packages/testing/test/integration/IndexingRewardsCollection.t.sol |
Adds real RewardsManager regression test for single collection per accrual. |
packages/testing/foundry.toml |
Grants read access to RewardsManager artifact path for bytecode-loaded tests. |
packages/issuance/test/unit/agreement-manager/fundingModes.t.sol |
Reframes escrow deficit test comments around the invariant. |
packages/issuance/test/unit/agreement-manager/cancelWithPendingUpdate.t.sol |
Reframes pending-update cancellation test comments and name. |
packages/contracts-test/tests/unit/rewards/rewards-snapshot-inversion.test.ts |
Cleans up reward snapshot inversion invariant documentation. |
packages/contracts-test/tests/unit/rewards/rewards-signal-allocation-update.test.ts |
Cleans up signal/allocation accounting invariant documentation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fix: prevent over-allocation double-collection in presentPOI auto-downsize
🚨 Report Summary
For more details view the full report in OpenZeppelin Code Inspector |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1342 +/- ##
=======================================
Coverage 91.16% 91.16%
=======================================
Files 81 81
Lines 5318 5319 +1
Branches 1128 1128
=======================================
+ Hits 4848 4849 +1
Misses 448 448
Partials 22 22
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…nsize When an over-allocated indexer presents a POI, presentPOI takes rewards and updates the allocation in storage (snapshotRewards / clearPendingRewards), but passed the stale memory snapshot captured at the top of the function into _resizeAllocation. That snapshot recomputed the reward accumulator delta from the pre-snapshot value and re-credited accRewardsPending with the rewards just paid out, letting the indexer collect them again on the next POI. _resizeAllocation now reads the current allocation state from storage itself instead of accepting a caller-supplied copy, so neither caller has to keep a memory copy in sync with storage. This removes the stale-snapshot hazard for both the auto-downsize and explicit-resize paths. Includes failing-then-passing unit and integration regression tests.
_distributeIndexingRewards only needs the (immutable) indexer, not the full allocation struct. Pass allocation.indexer instead of the whole IAllocation.State, so a post-snapshot distribution path cannot hand a stale allocation copy to a callee. Preventative follow-up to the over-allocation double-collection fix; no behavioural change.
…gression tests
Several regression tests narrated the bug they were born from ("THE BUG: with
the original buggy code...", "Old code: A.sub(S).add(P) reverts...", "the old
buggy ... approach") in comments and assertion messages. Once the underlying fix
lands, that narration is stale and misleading: it describes code that no longer
exists rather than the invariant the test guards.
Reframe the comments and assertion messages to state the expected behaviour as a
permanent property (snapshot-delta distribution regardless of same-block ordering;
underflow-safe A.add(P).sub(S) under an inverted snapshot state; per-provider
escrow deficit; pending-update escrow freed on cancel). Also rename two tests whose
identifiers referenced the old bug: PendingUpdateEscrowNotFreed -> ...Freed (the
test verifies escrow IS freed) and a snapshot test's "post-fix steady state" ->
"synced snapshot steady state".
Comments and message strings only; no test logic, values, or assertions changed.
e3ebef9 to
f3b6a93
Compare
When an over-allocated indexer presents a POI,
presentPOIcollects rewards and updates the allocation in storage (snapshotRewards/clearPendingRewards), then auto-downsizes it. It was passing the stale memory snapshot captured at the top of the function into_resizeAllocation, which recomputed the accumulator delta from the pre-snapshot value and re-creditedaccRewardsPendingwith the rewards just paid out. The indexer could then collect them again on the next POI._resizeAllocationnow reads the current allocation state from storage instead of accepting a caller-supplied copy, removing the stale-snapshot hazard for both the auto-downsize and explicit-resize paths.Includes failing-then-passing unit and integration regression tests.
Incidental, in separate commits:
_distributeIndexingRewardsto take the indexer address rather than the full allocation struct (no behavioural change).