feat: add RecurringCollector data source and Offer entity#4
Closed
MoonBoi9001 wants to merge 1 commit intomainfrom
Closed
feat: add RecurringCollector data source and Offer entity#4MoonBoi9001 wants to merge 1 commit intomainfrom
MoonBoi9001 wants to merge 1 commit intomainfrom
Conversation
530e44a to
dafb4e1
Compare
ff8fe0c to
84247b4
Compare
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>
84247b4 to
5d7e3dd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
The subgraph serves two consumer shapes. Dashboards query current agreement state; dipper's
chain_listenerpolls everyIndexingAgreementAccepted/IndexingAgreementCanceledtransition since the last block it saw, so it can replay state changes into its local DB and move agreements fromCreatedtoAcceptedOnChain. 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 noIndexingAgreementexists yet.Stacks on #5 and layers the deltas needed to keep both consumer patterns working.
Summary
IndexingAgreementAccepted/Canceled/Updatedentities 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.OfferStoredlog and first-winsOfferentity (keyed byagreementId) for dipper's offer idempotency gate. Handler early-returns on duplicateOffer— a second write to an immutable entity halts the subgraph.RecurringCollectordata source with theOfferStoredhandler. Skips thetopic1filter Maikol applies to his four events becauseOfferStored's first indexed arg isagreementId, notdataService.config/hardhat.json— feat: aggregated IndexingAgreement entity #5 leftsubgraphServiceAddressas0x0000....Draft until end-to-end verification on local-network with the rebased branch.