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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions