Skip to content

Post indexing payments audit#1338

Open
RembrandtK wants to merge 19 commits into
mainfrom
post-indexing-payments-audit
Open

Post indexing payments audit#1338
RembrandtK wants to merge 19 commits into
mainfrom
post-indexing-payments-audit

Conversation

@RembrandtK
Copy link
Copy Markdown
Contributor

Follow-ups on top of indexing-payments-management-audit-fix-3. 16 commits across deployment infrastructure, dips-publish prep, address-book, root tooling, and config-convention cleanup. No contract logic changes — pragma/source files untouched outside of the data-edge ethers-v6 toolchain refresh.

What's in it

GIP-0088 deployment infrastructure (8baf424b3)
New packages/deployment with deploy scripts, network configs, rocketh-based orchestration, and supporting tasks. Bundles the RM revertOnIneligible config wiring, the sync rocketh-record artifact-verification gate (with shouldSeedRocketh truth-table tests), the cross-package config-reconciliation test, and the mock REO sync/label fixes.

Audit-fix-3 dips-publish prep

  • dc6f9fb3a interfaces re-export of IRecurringCollector; toolshed recurring-collector.ts helpers + DIPs decoder conditions; deployment registries for issuance and REO.
  • b034dbc5f MockRewardsEligibilityOracle test mock.
  • 807c03332 SubgraphService now wires RecurringCollector + StakeClaims/IndexingAgreement libraries via deployImplementation().
  • 76546e1b7 version bumps interfaces 0.7.1-dips.0 / toolshed 1.2.1-dips.2.

Address book

  • 38828bc8d consolidate publish-source list (collapses three duplicated package enumerations into one SOURCES array); adds the missing src/issuance/addresses.json symlink.
  • 247b49d62 1.2.0 changelog + publishing guide.
  • 87d5abbb3 arbitrumSepolia address + deployment-metadata refresh.

Tooling

  • d1495a660 root tooling compat: rocketh@0.17.13 patch (re-enables deployScript.skip()), pnpm packageExtensions for @nomiclabs/hardhat-waffle peer deps, ARBISCAN_API_KEY env fallback.
  • a9682e183 data-edge: ethers v6 + @nomicfoundation/hardhat-* plugins (resolves peer-dep clashes on local-network deploys).
  • f7c9e99f5 json5 files in lint:json glob + baseline prettier pass across 17 ignition/deployment configs.
  • 25052413f solhint catalog ^6.0.3^6.2.1, simplifies per-package .solhint.json extends to a single string (fixes compiler-version override propagation under 6.2.1).

Config / convention cleanup

  • 16e7db9b4 localNetwork governor/pauseGuardian/SAO alignment to ACCOUNT1 across horizon and subgraph-service configs.
  • d8c79acea horizon ignition modules drive governor from m.getParameter('governor') (6 of 8 modules; 2 keep m.getAccount(1) for from: signer because ignition's from: only accepts AccountRuntimeValue). Adds governor field to horizon's protocol.{default,localNetwork}.json5.

Documentation

  • 56ec1e9fb REO testing plans (baseline, indexer test guide, rewards-conditions, subgraph-denial, mainnet/testnet details, support tooling) and docs/RewardsBehaviourChanges.md.
  • 0bf5d9cbe introduces a *.todo.md sidecar convention for capturing pending edits next to contract source. Initial entries: IHorizonStakingTypes NatSpec drift, BaseUpgradeable unused MILLION, AllocationHandler legacy-allo cleanup, RecurringCollector bytecode-headroom / max-next-claim / unreachable-defensive-branches / offer-keyed-terms-storage + pointers to preserved L-7 alternative tags.

RembrandtK added 16 commits May 11, 2026 14:13
…ment registries

Exposes the RecurringCollector contract across interfaces and toolshed
so downstream DIPs consumers can build directly against it:

- Re-export IRecurringCollector from @graphprotocol/interfaces under
  both the bare name and the conventional toolshed alias
  (RecurringCollector), matching the pattern used for other contracts.
- Add toolshed/src/core/recurring-collector.ts (DIPs helpers, including
  conditions in RCA decoder) and a covering test.
- Update toolshed deployment registries for issuance and REO contracts.
The set of packages exported by @graphprotocol/address-book was duplicated
three times: the exports map in package.json, FILES_TO_COPY in
copy-addresses-for-publish.js, and SYMLINKS_TO_RESTORE in restore-symlinks.js.
Drift had already crept in -- issuance was referenced everywhere but
src/issuance/ was missing on disk, which would break prepublishOnly.

Collapse to a single SOURCES array in scripts/sources.js consumed by both
scripts. Use a subpath pattern in package.json exports
("./*/addresses.json": "./src/*/addresses.json") so the exports map no
longer enumerates names.

Both scripts now mkdir src/<name>/ recursively and use rmSync({ force: true })
in place of the existsSync/unlinkSync dance, so a missing dir or stale entry
is handled silently. The copy step ends with a drift check that catches
src/<name>/ dirs not listed in SOURCES (stale leftovers from removed entries).

Verified end-to-end: pnpm pack produces a tarball containing real files
under src/{horizon,issuance,subgraph-service}/addresses.json, and
restore-symlinks puts the symlinks back at the original relative targets.
…an env fallback)

Three small workspace-root fixes needed to make the deployment package
and the legacy contracts test suite coexist:

- patches/rocketh@0.17.13.patch re-enables the deployScript skip()
  hook. Upstream 0.17.13 has the skip path commented out, so any
  deploy script defining `func.skip` was executed unconditionally;
  the deployment package relies on skip() to short-circuit no-op
  steps. Patch restores the call and removes the commented-out
  scaffolding.

- pnpm packageExtensions for @nomiclabs/hardhat-waffle. Under pnpm
  strict mode the package's missing peer declarations on
  @ethereum-waffle/{chai,provider} cause hardhat-waffle to fail to
  resolve them. The legacy contracts test suite still imports
  hardhat-waffle, so declare the deps via packageExtensions rather
  than upgrading the test suite off waffle.

- ARBISCAN_API_KEY env fallback in packages/contracts/hardhat.config.ts.
  Hardhat keystore (`vars`) is the documented path, but CI and local
  scripts often only set the environment variable; fall back to
  process.env so verification works in both contexts.
The rest of the workspace had moved to ethers v6 and the
@nomicfoundation/hardhat-* plugin generation; data-edge was the last
holdout on ethers v5, @nomiclabs/hardhat-*, ethereum-waffle, and the
@ethersproject/* sub-packages. The mismatch produced peer-dependency
clashes that broke local network deploys whenever data-edge was pulled
into the same Hardhat run as the rest of the workspace.

Safe to do as a straight bump:

- The package's deployed contracts only need recompilation to match a
  later toolchain; any new deployment naturally targets the newer
  versions, so there is no historical compatibility to preserve.
- The migration is mechanical (ethers v6 API renames, BigNumber →
  bigint, chai-matchers move under @NomicFoundation, etc.) and stays
  contained to hardhat.config.ts, tasks, and tests — no contract code
  is touched.

Also drops dead testnet entries (ropsten, rinkeby, kovan,
arbitrum-goerli) and unused dependencies (tenderly, ethereum-waffle,
ethlint, husky/lint-staged) on the way through.
…aims/IndexingAgreement libraries via deployImplementation

Add recurringCollectorAddress as a module parameter and pass it into the
SubgraphService constructor alongside the existing collector / curation /
dispute-manager addresses, and link the StakeClaims, AllocationHandler,
IndexingAgreementDecoder(Raw), and IndexingAgreement libraries.

Uses the deployImplementation() helper from @graphprotocol/horizon/ignition
rather than m.contract() — matches the pattern every other core module
already uses (HorizonStaking, GraphPayments, PaymentsEscrow,
RecurringCollector, RewardsManager, Curation). SubgraphService was the odd
one out.
Align localNetwork config across horizon and subgraph-service so
addresses match the deploy modules' hardcoded ACCOUNT1 convention.
Previously caused OwnableUnauthorizedAccount reverts mid-batch when
run-scripts signed with ACCOUNT1_SECRET.

Covers:
- horizon localNetwork migrate/protocol governor → ACCOUNT1
- horizon localNetwork pauseGuardian and subgraphAvailabilityOracle
- subgraph-service localNetwork migrate/protocol governor → ACCOUNT1
Introduces packages/deployment with the deploy/ scripts, network configs,
ABI generation, rocketh-based orchestration, and supporting tasks for the
GIP-0088 testnet upgrade.

Core infra:
- governor registered as a rocketh named account so local/test signing
  works without explicit private-key plumbing
- localNetwork tweaks to HorizonStaking ignition module
- tag-deployment task annotation cleaned up (no commit-sha noise)

Also includes:

- drive RM revertOnIneligible from network config through ResolvedSettings
  into the upgrade governance batch; remove vestigial RecurringCollector
  block from network configs

- gate sync rocketh-record seeding on artifact verification. When syncing
  a non-proxy address-book entry that has no rocketh deployment record,
  seed rocketh's record from the local artifact's bytecode only when the
  artifact's hash matches the address book's stored hash. Otherwise leave
  the record absent so the next deployFn sees no prior bytecode and
  produces newlyDeployed=true, propagating the new impl address through
  to proxy pending-upgrade detection naturally. Extract the gate into a
  pure shouldSeedRocketh helper and pin its truth table.

- cross-package config reconciliation test that catches drift between
  per-network Ignition config files in horizon/ and subgraph-service/:
  sibling agreement, localNetwork all-files $global agreement,
  same-package cross-prefix sub-object agreement, and mnemonic-index
  correctness for accounts referenced in localNetwork configs.

- sync RewardsManager in mock REO integrate path (otherwise stale on
  subsequent runs); namespace mock REO governance-tx label.
Introduces a `*.todo.md` sidecar convention for capturing pending edits
next to the contract source they target. Tracks deferred items so anyone
working on the contract sees the pending list without having to touch
the contract just to record an intent.

Initial entries cover four files:

- IHorizonStakingTypes.sol — LegacyAllocationState.Active NatSpec drift
  from PR #1331 nits.
- BaseUpgradeable.sol — unused MILLION public constant; ABI-impact note
  on removal.
- AllocationHandler.sol — legacy-allo migration block (~L170) confirmed
  unused; deferred to a post-horizon cleanup pass.
- RecurringCollector.sol — EIP-170 bytecode-headroom refactor candidates,
  max-next-claim consolidation, the two remaining unreachable defensive
  branches (with disposition direction), the offer-keyed-terms storage
  restructure sketch, and pointers to the three preserved L-7 alternative
  tags (audit-l7-alt-{cascade-delete,independent-cancel,document-only}).
Extends the root lint:json prettier glob and the lint-staged config to
cover *.json5 alongside *.json, then runs prettier across the 17
ignition / deployment-config files in the repo to baseline against the
standard prettier style (unquoted keys, single quotes, trailing commas).

The cross-package config-reconciliation test (14 cases) still passes —
content is unchanged, only quote style + trailing commas shift.

Going forward, the json5 ignition configs go through the same
pre-commit prettier pass as everything else, so trailing-comma drift,
key-quote drift, and bracketing irregularities get caught passively
instead of accumulating in hand-edited config files.
…ules

Six of the eight horizon deploy modules hardcoded
`const governor = m.getAccount(1)` for transferOwnership targets, while
the subgraph-service side and all migrate modules read
`m.getParameter('governor')` from config. The dual convention required
anyone touching the system to know which one applied where, and it left
horizon's protocol.<network>.json5 configs silently missing a governor
field where SS's were not — exactly the gap that hid the SS-side
localNetwork governor drift fixed earlier on this branch.

Unify on `m.getParameter('governor')` for the six modules where
governor is only used as a target argument:

- core/{GraphPayments,PaymentsEscrow,RecurringCollector}
- periphery/{Controller,GNS,GraphProxyAdmin}

Two remaining call sites still need `m.getAccount(1)` because the
`from:` option on `m.call` only accepts an AccountRuntimeValue
(returned by `m.getAccount`) and not the ModuleParameterRuntimeValue
that `m.getParameter` returns:

- deploy.ts — Controller / GraphProxyAdmin acceptOwnership
- periphery/GraphToken.ts — L2GraphToken acceptOwnership

In both files the kept `m.getAccount(1)` carries a comment recording
the constraint and the expectation that configs set the 'governor'
parameter to the index-1 address of the deploying mnemonic.

Adds the `governor` field to the two horizon protocol configs that
didn't have one (matching the corresponding SS-side values):

- protocol.localNetwork.json5 → 0x70997970…79C8 (test mnemonic index 1)
- protocol.default.json5 → 0xFFcf8FDE…409f0 (legacy default mnemonic
  index 1, matching SS-side protocol.default.json5)

All 14 cross-package config-reconciliation tests still pass, plus all
574 horizon, 290 subgraph-service, and 101 deployment unit/foundry
tests. Production deploys (arbitrumOne, arbitrumSepolia) go through
migrate which already used `m.getParameter('governor')`, so no
production behaviour changes.
Bumps the workspace solhint catalog entry within the major and refreshes
the lockfile. All eight packages (contracts, horizon, issuance,
subgraph-service, interfaces, data-edge, token-distribution, root) pick
up the new version through the catalog.

Also restructures the per-package .solhint.json files. Previously each
used `"extends": ["solhint:recommended", "./../../.solhint.json"]`,
which under solhint 6.2.1's reduceRight-based extend composition let
solhint:recommended's rules win over the root override and re-enabled
`compiler-version` (which the root config sets to `off` to accommodate
the workspace's mix of pragmas — legacy interfaces on
`^0.7.6 || ^0.8.0`, horizon/issuance/subgraph-service on `^0.8.27`,
data-edge on `^0.8.12`).

Switch each package to a single-string extends pointing at the root
config: `"extends": "./../../.solhint.json"`. The root config itself
extends solhint:recommended, so the rule chain is equivalent — but the
single-indirection form propagates root overrides correctly under
6.2.1.

After the bump + config fix, `pnpm lint:sol` runs clean across all
packages: zero errors, zero warnings beyond the pre-existing pattern set.
No contract source files were modified (verified — solhint --fix
touched none).
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 11, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedconsola@​2.15.39910010082100
Addedconsole-table-printer@​2.14.610010010085100

View full report

@RembrandtK RembrandtK requested a review from Copilot May 11, 2026 13:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@openzeppelin-code
Copy link
Copy Markdown

openzeppelin-code Bot commented May 11, 2026

Post indexing payments audit

Generated at commit: c8101b8865980337b842a5400644e31fd1069917

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
3
5
0
14
38
60
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
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.16%. Comparing base (22042cd) to head (c8101b8).

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1338    +/-   ##
========================================
  Coverage   91.16%   91.16%            
========================================
  Files          81       81            
  Lines        5318     5318            
  Branches      969     1128   +159     
========================================
  Hits         4848     4848            
  Misses        448      448            
  Partials       22       22            
Flag Coverage Δ
unittests 91.16% <ø> (ø)

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.

@RembrandtK RembrandtK requested review from Maikol and MoonBoi9001 May 11, 2026 15:49
@RembrandtK RembrandtK marked this pull request as ready for review May 11, 2026 16:11
Base automatically changed from indexing-payments-management-audit-fix-3 to main May 12, 2026 15:22
…rings

IssuanceAllocator.todo.md sidecar captures pending NatSpec fixes:

- _advanceSelfMintingBlock and distributeIssuance docstrings still
  describe the pre-Self-Minting-Accumulation behaviour.
- _distributeIssuance and _distributePendingIssuance both call
  _advanceSelfMintingBlock; one is redundant.

Reword "selfMintingOffset == 0" framing on the
test_DistributePendingIssuance_NoOffset_* tests to "fresh catch-up
case": unconditional accumulation makes the literal phrasing
inaccurate for the elapsed period inside the call.
Keep Graph_PR1334_v05.pdf as the canonical Trust Security audit
report. Remove the v01–v04 intermediate fix-round PDFs, the
per-finding TRST-*.md extracts, and the directory README that
indexed them — all superseded by v05.
…om PR1301/

Adds YYYY-MM-DD_ prefix (date the report is signed on the cover
page) and flattens packages/issuance/audits/PR1301/ — that sub-
directory now contained a single PDF after the intermediate
markdown was dropped.

Renames:
- Graph_PR1242_1243_v02.pdf            → 2025-11-17_…
- Graph_EligibilityOracle_v02.pdf      → 2025-12-13_…
- Graph_IssuanceAllocator_v03.pdf      → 2025-12-28_…
- Graph_PR1279_v02.pdf                 → 2026-02-15_…
- PR1301/Graph_PR1334_v05.pdf          → 2026-05-09_…
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.

2 participants