Skip to content

Commit 0b93ba6

Browse files
feat: implement Indexing Agreements
1 parent da95d8b commit 0b93ba6

21 files changed

+2584
-50
lines changed

IndexingPaymentsTodo.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
### Still pending
2+
3+
* Rename `agreementId` to `voucherId`?
4+
* Support indexing agreement upgadeability, so that there is a mechanism to adjust the rates without having to cancel and start over.
5+
* Built-in upgrade path to indexing agreements v2. So that indexers can be paid per byte instead of per entity.
6+
* Support for agreements that end up in `RecurringCollectorCollectionTooLate` or ways to avoid getting to that state.
7+
* Should we deal with zero entities declared as a special case?
8+
* Expose a function that indexers can use to calculate the tokens to be collected and other collection params?
9+
* Since an allocation is required for collecting, do we want to expect that the allocation is not stale? Do we want to add code to collect rewards as part of the collection of fees? Make sure allocation is more than one epoch old if we attempt this.
10+
* Reject Zero POIs?
11+
* What happens if the escrow doesn't have enough funds? Since you can't collect that means you lose out forever?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
pragma solidity 0.8.27;
3+
4+
import { IPaymentsCollector } from "./IPaymentsCollector.sol";
5+
import { IGraphPayments } from "./IGraphPayments.sol";
6+
import { IAuthorizable } from "./IAuthorizable.sol";
7+
8+
/**
9+
* @title Interface for the {RecurringCollector} contract
10+
* @dev Implements the {IPaymentCollector} interface as defined by the Graph
11+
* Horizon payments protocol.
12+
* @notice Implements a payments collector contract that can be used to collect
13+
* recurrent payments.
14+
*/
15+
interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
16+
/// @notice A representation of a signed Recurrent Collection Voucher (RCV)
17+
struct SignedRCV {
18+
// The RCV
19+
RecurrentCollectionVoucher rcv;
20+
// Signature - 65 bytes: r (32 Bytes) || s (32 Bytes) || v (1 Byte)
21+
bytes signature;
22+
}
23+
24+
/// @notice The Recurrent Collection Voucher (RCV)
25+
struct RecurrentCollectionVoucher {
26+
// The agreement ID of the RCV
27+
bytes16 agreementId;
28+
// The deadline for accepting the RCV
29+
uint256 acceptDeadline;
30+
// The duration of the RCV in seconds
31+
uint256 duration;
32+
// The address of the payer the RCV was issued by
33+
address payer;
34+
// The address of the data service the RCV was issued to
35+
address dataService;
36+
// The address of the service provider the RCV was issued to
37+
address serviceProvider;
38+
// The maximum amount of tokens that can be collected in the first collection
39+
// on top of the amount allowed for subsequent collections
40+
uint256 maxInitialTokens;
41+
// The maximum amount of tokens that can be collected in a single collection
42+
// except for the first collection
43+
uint256 maxOngoingTokensPerSecond;
44+
// The minimum amount of seconds that must pass between collections
45+
uint32 minSecondsPerCollection;
46+
// The maximum amount of seconds that can pass between collections
47+
uint32 maxSecondsPerCollection;
48+
// Arbitrary metadata to extend functionality if a data service requires it
49+
bytes metadata;
50+
}
51+
52+
/// @notice The data for an agreement
53+
struct AgreementData {
54+
// The timestamp when the agreement was accepted
55+
uint256 acceptedAt;
56+
// The timestamp when the agreement was last collected at
57+
uint256 lastCollectionAt;
58+
// The duration of the agreement in seconds
59+
uint256 duration;
60+
// The maximum amount of tokens that can be collected in the first collection
61+
// on top of the amount allowed for subsequent collections
62+
uint256 maxInitialTokens;
63+
// The maximum amount of tokens that can be collected in a single collection
64+
// except for the first collection
65+
uint256 maxOngoingTokensPerSecond;
66+
// The minimum amount of seconds that must pass between collections
67+
uint32 minSecondsPerCollection;
68+
// The maximum amount of seconds that can pass between collections
69+
uint32 maxSecondsPerCollection;
70+
}
71+
72+
/// @notice The key for a stored agreement
73+
struct AgreementKey {
74+
// The address of the data service the agreement was issued to
75+
address dataService;
76+
// The address of the payer the agreement was issued by
77+
address payer;
78+
// The address of the service provider the agreement was issued to
79+
address serviceProvider;
80+
// The ID of the agreement
81+
bytes16 agreementId;
82+
}
83+
84+
/// @notice The params for collecting an agreement
85+
struct CollectParams {
86+
// The agreement key that uniquely identifies it
87+
AgreementKey key;
88+
// The collection ID
89+
bytes32 collectionId;
90+
// The amount of tokens to collect
91+
uint256 tokens;
92+
// The data service cut in PPM
93+
uint256 dataServiceCut;
94+
}
95+
96+
/**
97+
* @notice Emitted when an RCV is collected
98+
* @param dataService The address of the data service
99+
* @param payer The address of the payer
100+
* @param serviceProvider The address of the service provider
101+
*/
102+
event RCVCollected(
103+
address indexed dataService,
104+
address indexed payer,
105+
address indexed serviceProvider,
106+
bytes32 collectionId,
107+
uint256 tokens,
108+
uint256 dataServiceCut
109+
);
110+
111+
/**
112+
* Thrown when calling accept() for an agreement with an elapsed acceptance deadline
113+
* @param elapsedAt The timestamp when the acceptance deadline elapsed
114+
*/
115+
error RecurringCollectorAgreementAcceptanceElapsed(uint256 elapsedAt);
116+
117+
/**
118+
* Thrown when the RCV signer is invalid
119+
*/
120+
error RecurringCollectorInvalidRCVSigner();
121+
122+
/**
123+
* Thrown when the payment type is not IndexingFee
124+
* @param paymentType The provided payment type
125+
*/
126+
error RecurringCollectorInvalidPaymentType(IGraphPayments.PaymentTypes paymentType);
127+
128+
/**
129+
* Thrown when the caller is not the data service the RCV was issued to
130+
* @param caller The address of the caller
131+
* @param dataService The address of the data service
132+
*/
133+
error RecurringCollectorCallerNotDataService(address caller, address dataService);
134+
135+
/**
136+
* Thrown when calling collect() with invalid data
137+
* @param data The invalid data
138+
*/
139+
error RecurringCollectorInvalidCollectData(bytes data);
140+
141+
/**
142+
* Thrown when calling accept() for an already accepted agreement
143+
* @param key The agreement key
144+
*/
145+
error RecurringCollectorAgreementAlreadyAccepted(AgreementKey key);
146+
147+
/**
148+
* Thrown when calling cancel() for a never accepted agreement
149+
* @param key The agreement key
150+
*/
151+
error RecurringCollectorAgreementNeverAccepted(AgreementKey key);
152+
153+
/**
154+
* Thrown when calling collect() on an invalid agreement
155+
* @param key The agreement key
156+
* @param acceptedAt The agreement accepted timestamp
157+
*/
158+
error RecurringCollectorAgreementInvalid(AgreementKey key, uint256 acceptedAt);
159+
160+
/**
161+
* Thrown when calling collect() on an elapsed agreement
162+
* @param key The agreement key
163+
* @param agreementEnd The agreement end timestamp
164+
*/
165+
error RecurringCollectorAgreementElapsed(AgreementKey key, uint256 agreementEnd);
166+
167+
/**
168+
* Thrown when calling collect() too soon
169+
* @param key The agreement key
170+
* @param secondsSinceLast Seconds since last collection
171+
* @param minSeconds Minimum seconds between collections
172+
*/
173+
error RecurringCollectorCollectionTooSoon(AgreementKey key, uint256 secondsSinceLast, uint256 minSeconds);
174+
175+
/**
176+
* Thrown when calling collect() too late
177+
* @param key The agreement key
178+
* @param secondsSinceLast Seconds since last collection
179+
* @param maxSeconds Maximum seconds between collections
180+
*/
181+
error RecurringCollectorCollectionTooLate(AgreementKey key, uint256 secondsSinceLast, uint256 maxSeconds);
182+
183+
/**
184+
* Thrown when calling collect() too late
185+
* @param key The agreement key
186+
* @param tokens The amount of tokens to collect
187+
* @param maxTokens The maximum amount of tokens allowed to collect
188+
*/
189+
error RecurringCollectorCollectAmountTooHigh(AgreementKey key, uint256 tokens, uint256 maxTokens);
190+
191+
/**
192+
* @dev Accept an indexing agreement.
193+
* @param signedRCV The signed Recurrent Collection Voucher which is to be accepted.
194+
*/
195+
function accept(SignedRCV memory signedRCV) external;
196+
197+
/**
198+
* @dev Cancel an indexing agreement.
199+
* @param payer The address of the payer for the agreement.
200+
* @param serviceProvider The address of the serviceProvider for the agreement.
201+
* @param agreementId The agreement's ID.
202+
*/
203+
function cancel(address payer, address serviceProvider, bytes16 agreementId) external;
204+
205+
/**
206+
* @dev Computes the hash of a RecurrentCollectionVoucher (RCV).
207+
* @param rcv The RCV for which to compute the hash.
208+
* @return The hash of the RCV.
209+
*/
210+
function encodeRCV(RecurrentCollectionVoucher calldata rcv) external view returns (bytes32);
211+
212+
/**
213+
* @dev Recovers the signer address of a signed RecurrentCollectionVoucher (RCV).
214+
* @param signedRCV The SignedRCV containing the RCV and its signature.
215+
* @return The address of the signer.
216+
*/
217+
function recoverRCVSigner(SignedRCV calldata signedRCV) external view returns (address);
218+
}

0 commit comments

Comments
 (0)