Skip to content

feat: dedicated fast-path loop for DIPs proposal acceptance#1200

Open
MoonBoi9001 wants to merge 11 commits intofeat/dips-on-chain-cancelfrom
mb9/dedicated-fast-loop-for-dips-acceptance
Open

feat: dedicated fast-path loop for DIPs proposal acceptance#1200
MoonBoi9001 wants to merge 11 commits intofeat/dips-on-chain-cancelfrom
mb9/dedicated-fast-loop-for-dips-acceptance

Conversation

@MoonBoi9001
Copy link
Copy Markdown
Member

@MoonBoi9001 MoonBoi9001 commented Apr 22, 2026

Motivation

The indexer-agent's DIPs proposal acceptance has been tied to the 120s reconciliation loop, which requires two or more cycles (240-360s) before the agent attempts on-chain acceptance. The RecurringCollectionAgreement deadline is 300s, so the reconciliation cadence leaves little to no slack and agreements can expire before the agent attempts acceptance at all.

This PR adds a dedicated 5s polling loop for pending_rca_proposals, independent of reconciliation, so acceptance happens promptly within the deadline.

Summary

1. Dedicated fast-path loop for proposal acceptance

Added startProposalAcceptanceLoop() which polls pending_rca_proposals every 5 seconds and calls processProposal for each entry. Runs independently of the 120s reconciliation cycle. Reconciliation continues to call acceptPendingProposals on its own cadence as a catch-up path.

2. Wrong contract in getRewards call

AllocationManager.stakeUsageSummary() called RewardsManager.getRewards(HorizonStaking, allocationId) but HorizonStaking is not a registered rewards issuer -- SubgraphService is. Every allocation operation (including DIPs acceptance) reverted with "Not a rewards issuer".

3. Opaque contract revert errors

When on-chain acceptance failed, the log showed error: null because tryParseCustomError couldn't decode unknown custom errors. Added full revert context (reason, data, message, contract target) to the rejection log so the root cause is immediately visible.


See companion PRs: edgeandnode/dipper#583, graphprotocol/indexer-rs#983.

Generated with Claude Code

Maikol and others added 10 commits April 10, 2026 10:23
Three fixes that together enable end-to-end DIPs on-chain acceptance:

1. Pass SubgraphService (not HorizonStaking) to RewardsManager.getRewards
   in the allocation stakeUsageSummary call.

2. Decouple DIPs proposal acceptance from the 120s reconciliation loop
   into a dedicated 5s polling loop (startProposalAcceptanceLoop). The
   300s RCA deadline left insufficient slack with the old 240s+ latency.

3. Log full revert context (reason, data, message, contract target) when
   on-chain acceptance fails, instead of just the parsed error (which was
   null for unknown custom errors).

Test metadata version updated to 0 to match the Solidity enum
IndexingAgreementVersion.V1 (first variant = 0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…greement

The audit-branch SubgraphService contract splits the previous packed
SignedRCA argument into separate rca (now 11 fields including conditions)
and signature arguments. The agent was still building calldata using the
old 2-arg signature, so every acceptIndexingAgreement call resolved to a
non-existent selector on the deployed contract and reverted with
FailedCall() inside the multicall path.

Unpack proposal.signedRca at both call sites (acceptWithExistingAllocation
and acceptWithNewAllocation) and pass rca and signature as the second and
third arguments. Add the conditions field to the two test fixtures so the
mocks match the updated tuple shape.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dips rule was created on the 15s reconciliation loop by iterating
pending_rca_proposals, but the fast 5s accept loop clears that row as
soon as the acceptIndexingAgreement receipt lands. On fast chains the
receipt consistently beats the next reconcile tick, so graph-node never
gets told to deploy the subgraph and the agent spins on unallocate
attempts for the just-created DIPs allocation.

Extract the per-proposal rule logic into ensureDipsRuleForProposal and
call it eagerly from processProposal before executeTransaction fires.
Reconciliation still calls it as defense-in-depth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-project-automation github-project-automation Bot moved this to 🗃️ Inbox in Indexer Apr 22, 2026
@Maikol Maikol force-pushed the feat/dips-on-chain-cancel branch from c4d0d52 to 861cddf Compare April 22, 2026 14:29
The DIPs accept path created the allocation on-chain via
acceptIndexingAgreement without first telling graph-node to deploy the
subgraph. If the main reconcile loop ticked after the allocation was
created but before graph-node had started indexing, it read an
undefined indexingStatus, hit the !indexingStatus branch of
failsHealthCheck, and queued an unallocate for the allocation the
accept path had just created.

Call graphNode.ensure before dispatching to acceptWith*Allocation so
indexingStatus always resolves on the next reconcile tick. ensure is
idempotent for existing deployments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: 🗃️ Inbox

Development

Successfully merging this pull request may close these issues.

2 participants