Skip to content

feat: add RecurringCollector data source and Offer entity#4

Closed
MoonBoi9001 wants to merge 1 commit intomainfrom
mb9/add-recurring-collector-offer-entity
Closed

feat: add RecurringCollector data source and Offer entity#4
MoonBoi9001 wants to merge 1 commit intomainfrom
mb9/add-recurring-collector-offer-entity

Conversation

@MoonBoi9001
Copy link
Copy Markdown
Member

@MoonBoi9001 MoonBoi9001 commented Apr 15, 2026

Motivation

The subgraph serves two consumer shapes. Dashboards query current agreement state; dipper's chain_listener polls every IndexingAgreementAccepted / IndexingAgreementCanceled transition since the last block it saw, so it can replay state changes into its local DB and move agreements from Created to AcceptedOnChain. That polling is how dipper knows an agreement it proposed actually landed on-chain — without it, agreements expire and dipper over-allocates (BUG-012).

PR #5 consolidates the immutable event-log entities into one mutable IndexingAgreement. Snapshot consumers win; dipper's polling surface disappears and the chain_listener goes silent on redeploy.

Separately, when dipper calls RecurringCollector.offer() and then crashes before persisting the agreement id, a naive restart double-spends gas. Dipper needs a subgraph-backed idempotency gate for the pre-accept window, where no IndexingAgreement exists yet.

Stacks on #5 and layers the deltas needed to keep both consumer patterns working.

Summary

  • Restore the immutable IndexingAgreementAccepted / Canceled / Updated entities and SS handlers so dipper's chain_listener keeps working. State is still owned by the RC handlers; the SS handlers only emit the transition logs.
  • Add immutable OfferStored log and first-wins Offer entity (keyed by agreementId) for dipper's offer idempotency gate. Handler early-returns on duplicate Offer — a second write to an immutable entity halts the subgraph.
  • Extend RecurringCollector data source with the OfferStored handler. Skips the topic1 filter Maikol applies to his four events because OfferStored's first indexed arg is agreementId, not dataService.
  • Fix config/hardhat.jsonfeat: aggregated IndexingAgreement entity #5 left subgraphServiceAddress as 0x0000....
  • Matchstick coverage for all five RC handlers and the new SS log writes (15 tests).

Draft until end-to-end verification on local-network with the rebased branch.

@MoonBoi9001 MoonBoi9001 changed the base branch from main to feat/aggregated-indexing-agreement April 21, 2026 23:55
@MoonBoi9001 MoonBoi9001 force-pushed the mb9/add-recurring-collector-offer-entity branch from 530e44a to dafb4e1 Compare April 22, 2026 00:12
@MoonBoi9001 MoonBoi9001 marked this pull request as ready for review April 22, 2026 00:31
@MoonBoi9001 MoonBoi9001 changed the base branch from feat/aggregated-indexing-agreement to main April 23, 2026 04:03
@MoonBoi9001 MoonBoi9001 force-pushed the mb9/add-recurring-collector-offer-entity branch from ff8fe0c to 84247b4 Compare April 23, 2026 04:03
Adds a second data source for RecurringCollector alongside the existing
SubgraphService data source, with a handleOfferStored mapping that writes
both an immutable OfferStored log entity and an immutable first-wins Offer
entity keyed by agreementId. handleOfferStored early-returns if an Offer
for the same id already exists, guarding dipper crash-recovery resubmissions
and chain reorg re-emissions from halting the subgraph on an immutable rewrite.

The Offer entity lets dipper verify via subgraph query that a stored RCA
offer exists on-chain before accepting a DIPs proposal with an empty
signature. For a given agreementId the RCA identifying fields (payer,
dataService, serviceProvider, deadline, nonce) are fixed by the id
derivation, so any duplicate OfferStored event for the same id carries the
same offerHash by construction -- the entity is modelled as write-only and
first-wins to reflect that.

Alongside the new data source, restores the immutable IndexingAgreementAccepted
/ Canceled / Updated event-log entities that PR #5 removed when it introduced
the aggregated stateful IndexingAgreement entity. Dipper's chain_listener
replays those transitions since-block into its local DB; dropping the logs
broke event-sourcing consumers. Both shapes now coexist so dashboards can
query current state while streaming consumers keep their replay stream.

Config files split the contract address into subgraphServiceAddress and
recurringCollectorAddress. The local-network deploy pipeline pulls the
RecurringCollector address from horizon.json alongside the existing
SubgraphService substitution.

Includes matchstick coverage for the RecurringCollector handlers
(handleAgreementAccepted, handleAgreementCanceled with both canceledBy
variants and early-return, handleAgreementUpdated with early-return and
field updates, handleRCACollected with early-return and accumulation) and
handleOfferStored (first-wins behaviour and duplicate-event guard).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MoonBoi9001 MoonBoi9001 force-pushed the mb9/add-recurring-collector-offer-entity branch from 84247b4 to 5d7e3dd Compare April 23, 2026 04:07
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.

1 participant