Skip to content

Indexer agent on-chain acceptance and contract deployment #554

Description

@MoonBoi9001

Context

Both the Dipper and indexer-rs sides of this feature are now complete. The remaining work is contract deployment and the indexer agent's on-chain acceptance implementation, both of which are outside these two codebases.

This issue tracked the migration from the epoch-based `IndexingAgreementVoucher` to the seconds-based `RecurringCollectionAgreement` (RCA) — the same type the contract consumes — and the end-to-end on-chain acceptance flow.

Architecture

The Dipper signs an RCA with a short deadline (~5 min) and sends it to indexer-rs via gRPC (fire-and-forget, no off-chain ACCEPT/REJECT). The indexer agent picks it up, validates, ensures an allocation, and calls `SubgraphService.acceptIndexingAgreement(allocationId, signedRCA)` on-chain. The Dipper monitors for the `IndexingAgreementAccepted` event and transitions the agreement to `AcceptedOnChain`. If the deadline passes with no on-chain acceptance, the Dipper expires the agreement and reassigns it.

If an indexer rejects the proposal off-chain but accepts on-chain anyway, the chain listener detects this and Dipper cancels the active agreement via `cancelIndexingAgreementByPayer`.

Dipper                 indexer-rs              indexer-agent           SubgraphService contract
  |                        |                        |                         |
  |-- SignedRCA (gRPC) --> |                        |                         |
  |                        |-- store SignedRCA       |                         |
  |                        |                        |-- read pending           |
  |                        |                        |-- validate terms         |
  |                        |                        |-- ensure allocation      |
  |                        |                        |-- acceptIndexingAgreement(allocationId, SignedRCA) -->|
  |                        |                        |                         |-- emit IndexingAgreementAccepted
  |<-- monitor on-chain event ------------------------------------------------|
  |-- mark ACCEPTED_ON_CHAIN                        |                         |

Agreement status flow:

Created -> AcceptedOnChain (on-chain event observed)
Created -> Expired (deadline passed, no on-chain acceptance)
Created -> DeliveryFailed (gRPC error sending to indexer-rs)
AcceptedOnChain -> CanceledByRequester (Dipper cancels adversarial on-chain acceptance)
AcceptedOnChain -> CanceledByIndexer (indexer cancels on-chain)

Phases

  • Phase 1 - Dipper: Status cleanup -- Remove `Accepted` and `Rejected` statuses. `Created` is the only pre-confirmation active status, `AcceptedOnChain` is the confirmed one. Update active agreement queries, unique constraint, replace `at_epoch` with timestamp. (97af12b, PR feat: periodic reassignment, V2 status cleanup, and AcceptedOnChain (#550) #553)
  • Phase 2 - Dipper: Switch to seconds-based pricing -- Replace epoch-based internal model (voucher fields, pricing config, DB schema) with seconds-based equivalents matching the contract. (22a648e, PR feat: migrate from epoch-based voucher to seconds-based RCA #555)
  • Phase 3 - Dipper: Sign and send SignedRCA -- Replace voucher signing with RCA signing using the contract's EIP-712 domain. Fire-and-forget gRPC call. ABI-encode `AcceptIndexingAgreementMetadata` into the RCA metadata field. Configurable deadline (~5 min). (22a648e, PR feat: migrate from epoch-based voucher to seconds-based RCA #555)
  • Phase 4 - indexer-rs: Store SignedRCA -- Simplified gRPC endpoint stores the `SignedRCA` blob in `pending_rca_proposals` with idempotent writes. Agent queries this table to pick up pending agreements. (indexer-rs PRs #942, #947, #948)
  • Phase 5 - Indexer agent: On-chain acceptance -- New `AgreementAcceptor` background service (follows TapCollector pattern). Read pending agreements from indexer-rs DB, validate terms, ensure allocation, call `SubgraphService.acceptIndexingAgreement()`. Blocked on Phase 6.
  • Phase 6 - Contract deployment -- The `IndexingAgreement.sol` contract has not been deployed yet. It needs to be deployed to Arbitrum Sepolia for testnet integration first, then to Arbitrum mainnet. Phases 5 and 7 depend on the contract being live.
  • Phase 7 - Protocol subgraph: Create and deploy -- No subgraph currently exists that indexes the `IndexingAgreement.sol` contract events. A new subgraph needs to be written, covering `IndexingAgreementAccepted`, `IndexingAgreementCanceled`, and `IndexingFeesCollectedV1` events emitted by the SubgraphService contract. Once the contract is deployed, the subgraph can be published and indexed. This is a prerequisite for Phase 8.
  • Phase 8 - Dipper: On-chain event monitoring -- Chain listener polls the protocol subgraph for agreement events, transitions `Created → AcceptedOnChain`, expires agreements past deadline, triggers reassignment. Also handles the adversarial case: if an indexer accepted on-chain after rejecting off-chain, Dipper cancels the agreement via `cancelIndexingAgreementByPayer`. (PRs feat: periodic reassignment, V2 status cleanup, and AcceptedOnChain (#550) #553, feat: migrate from epoch-based voucher to seconds-based RCA #555, Cancel agreements when indexers accept on-chain after rejecting off-chain #561)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions