Skip to content

Indexing payments management audit fix#1311

Draft
RembrandtK wants to merge 11 commits intoindexing-payments-management-auditfrom
indexing-payments-management-audit-fix-v5
Draft

Indexing payments management audit fix#1311
RembrandtK wants to merge 11 commits intoindexing-payments-management-auditfrom
indexing-payments-management-audit-fix-v5

Conversation

@RembrandtK
Copy link
Copy Markdown
Contributor

@RembrandtK RembrandtK commented Mar 30, 2026

Currently being finalised. Code stable but last commit likely to be tweaked and updated.

Filter for files in audit scope:

git diff indexing-payments-management-audit --name-status --diff-filter=d -- \
    '*.sol' ':!*/test*' ':!*.t.sol' ':!*Helper.sol' ':!*/mock*' ':!*/toolshed/*'`

Add packages/testing with real PaymentsEscrow, RecurringCollector,
IssuanceAllocator, and RecurringAgreementManager deployed together.
Gas results confirm 21-27x headroom on callback budgets.

Improve test coverage across all packages:
- horizon: RecurringCollector 86%→100% lines, 76%→100% functions
- subgraph-service: 92.86%→94.76% branches
- issuance: restore 100% coverage, remove dead getPageBytes16

Fix coverage scripts to print summary tables to stdout.
Trust Security audit (2026-03-03 to 2026-03-19) findings extracted from
Graph_PR1301_v01.pdf into PR1301/ directory with README index.
Includes auditor-supplied staleSnap PoC test.
Replace tempJit/fullReserveMargin with two configurable parameters:
minOnDemandBasisThreshold (default 128) and minFullBasisMargin
(default 16). Effective basis limited based on spare balance relative to
sumMaxNextClaimAll using strict <.

Delete staleSnap.t.sol (PoC scenario no longer applicable after
threshold-based degradation replaces tempJit).
…ST-M-1)

Added configurable `minThawFraction` (uint8, proportion of 256, default
16 = 6.25%) that skips thaws when the excess above max is below
`sumMaxNextClaim * fraction / 256` for the (collector, provider) pair.
Add revertOnIneligible flag to RewardsManager. When true, ineligible
indexers cause takeRewards to revert (blocking POI presentation and
preserving rewards for future collection). When false (default),
ineligible indexers have rewards reclaimed but takeRewards succeeds.
Over-allocated and stale allocations are now resized to zero tokens
instead of being force-closed. This keeps the allocation open so that
any bound indexing agreement remains active. The allocation stays as
a stakeless (altruistic) allocation rather than being deleted.

Changes:
- AllocationHandler.presentPOI: call _resizeAllocation(0) instead of
  _closeAllocation when over-allocated
- SubgraphService.closeStaleAllocation: resize to zero instead of
  _onCloseAllocation + _closeAllocation
- SubgraphService._collectIndexingRewards: remove _onCloseAllocation
  call after over-allocation (allocation stays open)
- Extract _resizeAllocation internal helper from resizeAllocation
Add a governor-controlled guard that prevents closing an allocation
when it has an active indexing agreement. When enabled, stopService
reverts with SubgraphServiceAllocationHasActiveAgreement instead of
auto-canceling the agreement.

- Add SubgraphServiceV2Storage with blockClosingAllocationWithActiveAgreement
- Add setter/getter and BlockClosingAllocationWithActiveAgreementSet event
- IndexingAgreement.onCloseAllocation: revert if active when guard enabled,
  otherwise cancel as ServiceProvider (forceClosed param removed since
  closeStaleAllocation now resizes instead of closing)
Add IEmergencyRoleControl interface and emergencyRevokeRole to
RecurringAgreementManager. Allows PAUSE_ROLE holders to surgically
disable a specific actor (operator, collector, data service) without
halting the entire contract. Governor role is excluded to prevent a
pause guardian from locking out governance.

Tests in following commit (depend on refactored test infrastructure).
Move agreement lifecycle management into RecurringCollector:
- State model: replace AgreementState enum with uint16 bitmask flags
  (REGISTERED, ACCEPTED, NOTICE_GIVEN, SETTLED, BY_PAYER, etc.)
- API: two-phase offer/accept flow replaces single-call accept(rca, sig)
- Drop ECDSA signing and Authorizable from RC
- Callback model: _shouldCallback + _notifyStateChange with gas caps
  (MAX_CALLBACK_GAS) and PayerCallbackFailed events baked in
- Extract RecurringCollectorHashing library
- RAM: nested storage per collector, pair-keyed enumeration
- SS: callback integration (acceptAgreement, afterAgreementStateChange)
  replacing direct accept/update/cancel methods
- Error simplification: drop RecurringCollector prefix from all errors

Includes callback hardening (TRST-H-1, L-1, H-2, H-4, SR-4) as part
of the new callback design rather than as a separate bolt-on.

feat: make RecurringCollector upgradeable

Convert RC to upgradeable proxy pattern:
- Add Initializable
- Move agreements mapping into ERC-7201 namespaced storage
- Split constructor into constructor + initialize()
- Add Ignition deployment modules for RC proxy
- Update test setups to deploy behind TransparentUpgradeableProxy

feat: pause RecurringCollector and RecurringAgreementManager (TRST-L-3)

Add PausableUpgradeable to RC with pause guardian management:
- pause/unpause/setPauseGuardian functions
- whenNotPaused on collect, offer, accept, cancel
- Pause guardian storage in ERC-7201 namespace

Add IEmergencyRoleControl + emergencyRevokeRole to
RecurringAgreementManager for role revocation as alternative to pause.
@RembrandtK RembrandtK force-pushed the indexing-payments-management-audit-fix-v5 branch from 54a4ecc to cab11ef Compare March 30, 2026 12:28
@openzeppelin-code
Copy link
Copy Markdown

openzeppelin-code bot commented Mar 30, 2026

Indexing payments management audit fix

Generated at commit: cab11ef48c877674e238db7378daa817c70e16e5

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
3
5
0
15
39
62
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant