Skip to content

Indexing payments management audit#1301

Open
RembrandtK wants to merge 52 commits intomainfrom
indexing-payments-management-audit
Open

Indexing payments management audit#1301
RembrandtK wants to merge 52 commits intomainfrom
indexing-payments-management-audit

Conversation

@RembrandtK
Copy link
Contributor

@RembrandtK RembrandtK commented Mar 1, 2026

Filter for files in audit scope:

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

bf6d4cb Indexing payments (merge)
Previously audited (2025-06-Indexing-Payments.pdf) implementation of indexing agreements for the Graph Protocol's recurring payment system. Adds RecurringCollector, IndexingAgreement library, and integrates indexing fee collection into SubgraphService and DisputeManager.

28edcd7 Post-Horizon migration removal of transition code (merge)
Remove HorizonStakingExtension, ExponentialRebates, LibFixedMath, MathUtils, and all transition-period guard logic. Simplify HorizonStaking to direct implementation without delegatecall split. Clean up RewardsManager multi-issuer model to single subgraphService. Remove legacy allocation/dispute code from SubgraphService.

f4451f1 feat: add back legacy allocation id collision check
fa99514 fix: cap maxSecondsPerCollection instead of reverting
c6836a7 fix: enforce temporal validation on zero-token collections and remove zero-POI special case
8efaec9 feat: add adjustThaw to PaymentsEscrow
3f1578c refactor: rename IRewardsEligibility to IProviderEligibility
(For the purpose of this audit note that the interfaces being changed are not in production.)

d20bc84 feat: contract approver model for RecurringCollector accept/update
ec72360 feat: IDataServiceAgreements interface and SubgraphService integration
89def3d feat: enumerable indexer tracking for REO and issuance constructor cleanup
(Do not need storage compatibility for delpoying an upgrade.)

843fdd2 feat: RecurringAgreementManager with lifecycle, escrow funding, and agreement updates

matiasedgeandnode and others added 30 commits June 11, 2025 14:08
Fixes TRST-M-1 audit finding:

Wrong TYPEHASH string is used for agreement updates, limiting functionality.

* Fixed EIP712_RCAU_TYPEHASH to use correct uint64 types for deadline and endsAt fields (was incorrectly using uint256)
* This prevents signature verification failures for RecurringCollectionAgreementUpdate
Fixes TRST-M-2 audit finding:

Collection for an elapsed or canceled agreement could be wrong due to
temporal calculation inconsistencies between IndexingAgreement and
RecurringCollector layers.

* Replace isCollectable() with getCollectionInfo() that returns both collectability and duration
* Make RecurringCollector the single source of truth for temporal logic
* Update IndexingAgreement to call getCollectionInfo() once and pass duration to _tokensToCollect()
Fixes signature replay attack vulnerability where old signed RecurringCollectionAgreementUpdate
messages could be replayed to revert agreements to previous terms.

 ## Changes

- Add `nonce` field to RecurringCollectionAgreementUpdate struct (uint32)
- Add `updateNonce` field to AgreementData struct to track current nonce
- Add nonce validation in RecurringCollector.update() to ensure sequential updates
- Update EIP712_RCAU_TYPEHASH to include nonce field
- Add comprehensive tests for nonce validation and replay attack prevention
- Add RecurringCollectorInvalidUpdateNonce error for invalid nonce attempts

 ## Implementation Details

- Nonces start at 0 when agreement is accepted
- Each update must use current nonce + 1
- Nonce is incremented after successful update
- Uses uint32 for gas optimization (supports 4B+ updates per agreement)
- Single source of truth: nonce stored in AgreementData struct
Implements slippage protection mechanism to prevent silent token loss
during rate-limited collections in RecurringCollector agreements.

The implementation uses type(uint256).max convention to disable slippage
checks, providing users full control over acceptable token loss during
rate limiting.

Resolves audit finding TRST-L-5: "RecurringCollector silently reduces
collected tokens without user consent"
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
tmigone and others added 21 commits December 1, 2025 15:45
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
…Z L-01)

Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
…tions (OZ L-02)

Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Normalize all Solidity pragmas to caret form (^0.8.27) for forward
compatibility, and bump the compiler version from 0.8.33 to 0.8.34
for the issuance and subgraph-service packages. Archive the now-
obsolete CompilerUpgrade0833.md doc.
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
Replace the hard revert (RecurringCollectorCollectionTooLate) with a
Math.min cap in _getCollectionInfo. Collections past maxSecondsPerCollection
now succeed with tokens capped at maxSecondsPerCollection worth of service,
rather than failing entirely.

Changes:
- _getCollectionInfo caps elapsed seconds at maxSecondsPerCollection
- Remove RecurringCollectorCollectionTooLate error from interface
- Replace test_Collect_Revert_WhenCollectingTooLate with
  test_Collect_OK_WhenCollectingPastMaxSeconds
- Update maxSecondsPerCollection NatSpec to reflect cap semantics
- Fix zero-token test to use correct _sensibleAuthorizeAndAccept API
… zero-POI special case

Move _requireValidCollect() call outside the tokens != 0 guard so
temporal constraints (min/maxSecondsPerCollection) are always enforced,
even for zero-token collections. This prevents advancing lastCollectionAt
without passing temporal validation.

Remove the zero-POI special case in IndexingAgreement that bypassed
token calculation when entities == 0 && poi == bytes32(0). The temporal
validation now handles this consistently.
Add adjustThaw for timer-aware escrow thaw management: caps at
balance, preserves timer on decrease, and optionally skips increases
that would reset the timer. Expose escrowAccounts mapping in
IPaymentsEscrow interface.
Generalise the provider eligibility interface so it is not
rewards-specific:

- IRewardsEligibility → IProviderEligibility (same isEligible selector)
- New IProviderEligibilityManagement with shared setter/getter/event
- RewardsManager implements IProviderEligibilityManagement; remove
  bespoke setter/getter/event from IRewardsManager
- Update RewardsEligibilityOracle, mocks, tests, deployment scripts
  and docs accordingly
Add IAgreementOwner interface enabling contracts to approve RCA
accept/update by implementing an on-chain callback. When the
signature parameter is empty, accept() and update() fall back to
calling IAgreementOwner.approveAgreement() on the payer contract
instead of verifying an ECDSA signature. Also adds getMaxNextClaim()
view and removes the SignedRCA/SignedRCAU wrapper structs in favor
of separate (struct, bytes) parameters.
Extract IDataServiceAgreements from ISubgraphService with
cancelIndexingAgreementByPayer and bitmask-dispatched enforce pattern.
Add ProvisionManager support for data-service callback verification.
…eanup

Add EnumerableSet-based indexer tracking to RewardsEligibilityOracle with
a helper contract for paginated queries. Introduce retention-based cleanup
that removes indexers only after a configurable idle period. Split REO
interface into focused sub-interfaces for administration, status, events,
maintenance, and helper operations.

Also refactor issuance constructors from address to IGraphToken type
and normalise pragma to ^0.8.27 across issuance test files.
@RembrandtK RembrandtK self-assigned this Mar 1, 2026
@openzeppelin-code
Copy link

openzeppelin-code bot commented Mar 1, 2026

Indexing payments management audit

Generated at commit: a23ad681eaa1188b5f0b807629ea22eccba7a3f9

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
3
5
0
14
37
59
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

@codecov
Copy link

codecov bot commented Mar 1, 2026

Codecov Report

❌ Patch coverage is 97.65840% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.24%. Comparing base (ddee12b) to head (a23ad68).

Files with missing lines Patch % Lines
...ntracts/payments/collectors/RecurringCollector.sol 93.33% 6 Missing and 6 partials ⚠️
...s/horizon/contracts/staking/HorizonStakingBase.sol 87.50% 2 Missing ⚠️
...ges/contracts/contracts/rewards/RewardsManager.sol 93.33% 1 Missing ⚠️
...n/contracts/data-service/libraries/StakeClaims.sol 96.15% 0 Missing and 1 partial ⚠️
...e/contracts/agreement/RecurringAgreementHelper.sol 98.24% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1301      +/-   ##
==========================================
+ Coverage   88.55%   90.24%   +1.68%     
==========================================
  Files          75       81       +6     
  Lines        4615     5113     +498     
  Branches      981     1079      +98     
==========================================
+ Hits         4087     4614     +527     
+ Misses        507      465      -42     
- Partials       21       34      +13     
Flag Coverage Δ
unittests 90.24% <97.65%> (+1.68%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…greement updates

Add RecurringAgreementManager with configurable escrow funding modes,
enumerable agreement tracking, lifecycle management, and escrow
reconciliation. Extends IAgreementOwner with beforeCollection/
afterCollection callbacks. Includes revokeAgreementUpdate and
pending update escrow cleanup on cancel.
@RembrandtK RembrandtK force-pushed the indexing-payments-management-audit branch from 843fdd2 to a23ad68 Compare March 1, 2026 23:58
@RembrandtK RembrandtK marked this pull request as ready for review March 2, 2026 00:03
@RembrandtK RembrandtK mentioned this pull request Mar 2, 2026
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.

3 participants