Skip to content

Commit 041bb6a

Browse files
f - Re-implement Upgrade
1 parent de6b7a1 commit 041bb6a

File tree

7 files changed

+233
-73
lines changed

7 files changed

+233
-73
lines changed

IndexingPaymentsTodo.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@
2323
* DONE: ~~Make `agreementId` unique globally so that we don't need the full tuple (`payer`+`indexer`+`agreementId`) as key?~~
2424
* DONE: ~~Maybe IRecurringCollector.cancel(address payer, address serviceProvider, bytes16 agreementId) should only take in agreementId?~~
2525
* DONE: ~~Unify to one error in Decoder.sol~~
26+
* Missing events for accept, cancel, upgrade RCAs.

packages/horizon/contracts/interfaces/IRecurringCollector.sol

+47-6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,35 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
4949
bytes metadata;
5050
}
5151

52+
/// @notice A representation of a signed Recurring Collection Agreement Upgrade (RCAU)
53+
struct SignedRCAU {
54+
// The RCAU
55+
RecurringCollectionAgreementUpgrade rcau;
56+
// Signature - 65 bytes: r (32 Bytes) || s (32 Bytes) || v (1 Byte)
57+
bytes signature;
58+
}
59+
60+
struct RecurringCollectionAgreementUpgrade {
61+
// The agreement ID of the RCA
62+
bytes16 agreementId;
63+
// The deadline for accepting the RCA
64+
uint256 acceptDeadline;
65+
// The duration of the agreement in seconds
66+
uint256 duration;
67+
// The maximum amount of tokens that can be collected in the first collection
68+
// on top of the amount allowed for subsequent collections
69+
uint256 maxInitialTokens;
70+
// The maximum amount of tokens that can be collected per second
71+
// except for the first collection
72+
uint256 maxOngoingTokensPerSecond;
73+
// The minimum amount of seconds that must pass between collections
74+
uint32 minSecondsPerCollection;
75+
// The maximum amount of seconds that can pass between collections
76+
uint32 maxSecondsPerCollection;
77+
// Arbitrary metadata to extend functionality if a data service requires it
78+
bytes metadata;
79+
}
80+
5281
/// @notice The data for an agreement
5382
struct AgreementData {
5483
// The address of the data service
@@ -73,8 +102,6 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
73102
uint32 minSecondsPerCollection;
74103
// The maximum amount of seconds that can pass between collections
75104
uint32 maxSecondsPerCollection;
76-
// The agreement ID of the previous agreement
77-
bytes16 updatedFromAgreementId;
78105
}
79106

80107
/// @notice The params for collecting an agreement
@@ -117,9 +144,9 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
117144
error RecurringCollectorAgreementAcceptanceElapsed(uint256 elapsedAt);
118145

119146
/**
120-
* Thrown when the RCA signer is invalid
147+
* Thrown when the signer is invalid
121148
*/
122-
error RecurringCollectorInvalidRCASigner();
149+
error RecurringCollectorInvalidSigner();
123150

124151
/**
125152
* Thrown when the payment type is not IndexingFee
@@ -194,7 +221,7 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
194221
* @dev Accept an indexing agreement.
195222
* @param signedRCA The signed Recurring Collection Agreement which is to be accepted.
196223
*/
197-
function accept(SignedRCA memory signedRCA) external;
224+
function accept(SignedRCA calldata signedRCA) external;
198225

199226
/**
200227
* @dev Cancel an indexing agreement.
@@ -205,7 +232,7 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
205232
/**
206233
* @dev Upgrade an indexing agreement.
207234
*/
208-
function upgrade() external;
235+
function upgrade(SignedRCAU calldata signedRCAU) external;
209236

210237
/**
211238
* @dev Computes the hash of a RecurringCollectionAgreement (RCA).
@@ -214,10 +241,24 @@ interface IRecurringCollector is IAuthorizable, IPaymentsCollector {
214241
*/
215242
function encodeRCA(RecurringCollectionAgreement calldata rca) external view returns (bytes32);
216243

244+
/**
245+
* @dev Computes the hash of a RecurringCollectionAgreementUpgrade (RCAU).
246+
* @param rcau The RCAU for which to compute the hash.
247+
* @return The hash of the RCAU.
248+
*/
249+
function encodeRCAU(RecurringCollectionAgreementUpgrade calldata rcau) external view returns (bytes32);
250+
217251
/**
218252
* @dev Recovers the signer address of a signed RecurringCollectionAgreement (RCA).
219253
* @param signedRCA The SignedRCA containing the RCA and its signature.
220254
* @return The address of the signer.
221255
*/
222256
function recoverRCASigner(SignedRCA calldata signedRCA) external view returns (address);
257+
258+
/**
259+
* @dev Recovers the signer address of a signed RecurringCollectionAgreementUpgrade (RCAU).
260+
* @param signedRCAU The SignedRCAU containing the RCAU and its signature.
261+
* @return The address of the signer.
262+
*/
263+
function recoverRCAUSigner(SignedRCAU calldata signedRCAU) external view returns (address);
223264
}

packages/horizon/contracts/payments/collectors/RecurringCollector.sol

+90-5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
2626
"RecurringCollectionAgreement(bytes16 agreementId,uint256 acceptDeadline,uint256 duration,address payer,address dataService,address serviceProvider,uint256 maxInitialTokens,uint256 maxOngoingTokensPerSecond,uint32 minSecondsPerCollection,uint32 maxSecondsPerCollection,bytes metadata)"
2727
);
2828

29+
/// @notice The EIP712 typehash for the RecurringCollectionAgreementUpgrade struct
30+
bytes32 public constant EIP712_RCAU_TYPEHASH =
31+
keccak256(
32+
"RecurringCollectionAgreementUpgrade(bytes16 agreementId,uint256 acceptDeadline,uint256 duration,uint256 maxInitialTokens,uint256 maxOngoingTokensPerSecond,uint32 minSecondsPerCollection,uint32 maxSecondsPerCollection,bytes metadata)"
33+
);
34+
2935
/// @notice Sentinel value to indicate an agreement has been canceled
3036
uint256 public constant CANCELED = type(uint256).max;
3137

@@ -68,7 +74,7 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
6874
* See {IRecurringCollector.accept}.
6975
* @dev Caller must be the data service the RCA was issued to.
7076
*/
71-
function accept(SignedRCA memory signedRCA) external {
77+
function accept(SignedRCA calldata signedRCA) external {
7278
require(
7379
msg.sender == signedRCA.rca.dataService,
7480
RecurringCollectorCallerNotDataService(msg.sender, signedRCA.rca.dataService)
@@ -116,10 +122,31 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
116122
/**
117123
* @notice Upgrade an indexing agreement.
118124
* See {IRecurringCollector.upgrade}.
119-
* @dev Caller must be the data service the RCA was issued to.
125+
* @dev Caller must be the data service for the agreement.
120126
*/
121-
function upgrade() external {
122-
// FIX-ME: implement me
127+
function upgrade(SignedRCAU calldata signedRCAU) external {
128+
require(
129+
signedRCAU.rcau.acceptDeadline >= block.timestamp,
130+
RecurringCollectorAgreementAcceptanceElapsed(signedRCAU.rcau.acceptDeadline)
131+
);
132+
133+
AgreementData storage agreement = _getForUpdateAgreement(signedRCAU.rcau.agreementId);
134+
require(agreement.acceptedAt > 0, RecurringCollectorAgreementNeverAccepted(signedRCAU.rcau.agreementId));
135+
require(
136+
agreement.dataService == msg.sender,
137+
RecurringCollectorDataServiceNotAuthorized(signedRCAU.rcau.agreementId, msg.sender)
138+
);
139+
140+
// check that the voucher is signed by the payer (or proxy)
141+
_requireAuthorizedRCAUSigner(signedRCAU, agreement.payer);
142+
143+
// upgrade the agreement
144+
// FIX-ME: These need to be validated to something that makes sense for the contract
145+
agreement.duration = signedRCAU.rcau.duration;
146+
agreement.maxInitialTokens = signedRCAU.rcau.maxInitialTokens;
147+
agreement.maxOngoingTokensPerSecond = signedRCAU.rcau.maxOngoingTokensPerSecond;
148+
agreement.minSecondsPerCollection = signedRCAU.rcau.minSecondsPerCollection;
149+
agreement.maxSecondsPerCollection = signedRCAU.rcau.maxSecondsPerCollection;
123150
}
124151

125152
/**
@@ -129,13 +156,27 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
129156
return _recoverRCASigner(signedRCA);
130157
}
131158

159+
/**
160+
* @notice See {IRecurringCollector.recoverRCAUSigner}
161+
*/
162+
function recoverRCAUSigner(SignedRCAU calldata signedRCAU) external view returns (address) {
163+
return _recoverRCAUSigner(signedRCAU);
164+
}
165+
132166
/**
133167
* @notice See {IRecurringCollector.encodeRCA}
134168
*/
135169
function encodeRCA(RecurringCollectionAgreement calldata rca) external view returns (bytes32) {
136170
return _encodeRCA(rca);
137171
}
138172

173+
/**
174+
* @notice See {IRecurringCollector.encodeRCAU}
175+
*/
176+
function encodeRCAU(RecurringCollectionAgreementUpgrade calldata rcau) external view returns (bytes32) {
177+
return _encodeRCAU(rcau);
178+
}
179+
139180
/**
140181
* @notice Decodes the collect data.
141182
*/
@@ -235,6 +276,14 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
235276
return ECDSA.recover(messageHash, _signedRCA.signature);
236277
}
237278

279+
/**
280+
* @notice See {IRecurringCollector.recoverRCAUSigner}
281+
*/
282+
function _recoverRCAUSigner(SignedRCAU memory _signedRCAU) private view returns (address) {
283+
bytes32 messageHash = _encodeRCAU(_signedRCAU.rcau);
284+
return ECDSA.recover(messageHash, _signedRCAU.signature);
285+
}
286+
238287
/**
239288
* @notice See {IRecurringCollector.encodeRCA}
240289
*/
@@ -260,13 +309,49 @@ contract RecurringCollector is EIP712, GraphDirectory, Authorizable, IRecurringC
260309
);
261310
}
262311

312+
/**
313+
* @notice See {IRecurringCollector.encodeRCAU}
314+
*/
315+
function _encodeRCAU(RecurringCollectionAgreementUpgrade memory _rcau) private view returns (bytes32) {
316+
return
317+
_hashTypedDataV4(
318+
keccak256(
319+
abi.encode(
320+
EIP712_RCAU_TYPEHASH,
321+
_rcau.agreementId,
322+
_rcau.acceptDeadline,
323+
_rcau.duration,
324+
_rcau.maxInitialTokens,
325+
_rcau.maxOngoingTokensPerSecond,
326+
_rcau.minSecondsPerCollection,
327+
_rcau.maxSecondsPerCollection,
328+
keccak256(_rcau.metadata)
329+
)
330+
)
331+
);
332+
}
333+
263334
/**
264335
* @notice Requires that the signer for the RCA is authorized
265336
* by the payer of the RCA.
266337
*/
267338
function _requireAuthorizedRCASigner(SignedRCA memory _signedRCA) private view returns (address) {
268339
address signer = _recoverRCASigner(_signedRCA);
269-
require(_isAuthorized(_signedRCA.rca.payer, signer), RecurringCollectorInvalidRCASigner());
340+
require(_isAuthorized(_signedRCA.rca.payer, signer), RecurringCollectorInvalidSigner());
341+
342+
return signer;
343+
}
344+
345+
/**
346+
* @notice Requires that the signer for the RCAU is authorized
347+
* by the payer.
348+
*/
349+
function _requireAuthorizedRCAUSigner(
350+
SignedRCAU memory _signedRCAU,
351+
address _payer
352+
) private view returns (address) {
353+
address signer = _recoverRCAUSigner(_signedRCAU);
354+
require(_isAuthorized(_payer, signer), RecurringCollectorInvalidSigner());
270355

271356
return signer;
272357
}

packages/subgraph-service/contracts/Decoder.sol

+42-9
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,45 @@ contract Decoder {
99
}
1010

1111
/**
12-
* @notice Decodes the indexing agreement metadata.
12+
* @notice Decodes the RCA metadata.
1313
*
14-
* @param data The data to decode. See {ISubgraphService.RCAIndexingAgreementMetadata}
14+
* @param data The data to decode. See {ISubgraphService.AcceptIndexingAgreementMetadata}
1515
* @return The decoded data
1616
*/
1717
function decodeRCAMetadata(
1818
bytes calldata data
19-
) external pure returns (ISubgraphService.RCAIndexingAgreementMetadata memory) {
20-
return abi.decode(data, (ISubgraphService.RCAIndexingAgreementMetadata));
19+
) external pure returns (ISubgraphService.AcceptIndexingAgreementMetadata memory) {
20+
return abi.decode(data, (ISubgraphService.AcceptIndexingAgreementMetadata));
2121
}
2222

23+
/**
24+
* @notice Decodes the RCAU metadata.
25+
*
26+
* @param data The data to decode. See {ISubgraphService.UpgradeIndexingAgreementMetadata}
27+
* @return The decoded data
28+
*/
29+
function decodeRCAUMetadata(
30+
bytes calldata data
31+
) external pure returns (ISubgraphService.UpgradeIndexingAgreementMetadata memory) {
32+
return abi.decode(data, (ISubgraphService.UpgradeIndexingAgreementMetadata));
33+
}
34+
35+
/**
36+
* @notice Decodes the collect data for indexing fees V1.
37+
*
38+
* @param data The data to decode.
39+
*/
2340
function decodeCollectIndexingFeeDataV1(bytes memory data) external pure returns (uint256 entities, bytes32 poi) {
2441
return abi.decode(data, (uint256, bytes32));
2542
}
2643

27-
function decodeAcceptIndexingAgreementTermsV1(
44+
/**
45+
* @notice Decodes the data for indexing agreement terms V1.
46+
*
47+
* @param data The data to decode. See {ISubgraphService.IndexingAgreementTermsV1}
48+
* @return The decoded data
49+
*/
50+
function decodeIndexingAgreementTermsV1(
2851
bytes memory data
2952
) external pure returns (ISubgraphService.IndexingAgreementTermsV1 memory) {
3053
return abi.decode(data, (ISubgraphService.IndexingAgreementTermsV1));
@@ -40,14 +63,24 @@ contract Decoder {
4063

4164
function _decodeRCAMetadata(
4265
bytes memory _data
43-
) internal view returns (ISubgraphService.RCAIndexingAgreementMetadata memory) {
44-
try this.decodeRCAMetadata(_data) returns (ISubgraphService.RCAIndexingAgreementMetadata memory metadata) {
66+
) internal view returns (ISubgraphService.AcceptIndexingAgreementMetadata memory) {
67+
try this.decodeRCAMetadata(_data) returns (ISubgraphService.AcceptIndexingAgreementMetadata memory metadata) {
4568
return metadata;
4669
} catch {
4770
revert ISubgraphService.SubgraphServiceDecoderInvalidData("_decodeRCAMetadata", _data);
4871
}
4972
}
5073

74+
function _decodeRCAUMetadata(
75+
bytes memory _data
76+
) internal view returns (ISubgraphService.UpgradeIndexingAgreementMetadata memory) {
77+
try this.decodeRCAUMetadata(_data) returns (ISubgraphService.UpgradeIndexingAgreementMetadata memory metadata) {
78+
return metadata;
79+
} catch {
80+
revert ISubgraphService.SubgraphServiceDecoderInvalidData("_decodeRCAUMetadata", _data);
81+
}
82+
}
83+
5184
function _decodeCollectIndexingFeeDataV1(bytes memory _data) internal view returns (uint256, bytes32) {
5285
try this.decodeCollectIndexingFeeDataV1(_data) returns (uint256 entities, bytes32 poi) {
5386
return (entities, poi);
@@ -56,10 +89,10 @@ contract Decoder {
5689
}
5790
}
5891

59-
function _decodeAcceptIndexingAgreementTermsV1(
92+
function _decodeIndexingAgreementTermsV1(
6093
bytes memory _data
6194
) internal view returns (ISubgraphService.IndexingAgreementTermsV1 memory) {
62-
try this.decodeAcceptIndexingAgreementTermsV1(_data) returns (
95+
try this.decodeIndexingAgreementTermsV1(_data) returns (
6396
ISubgraphService.IndexingAgreementTermsV1 memory terms
6497
) {
6598
return terms;

0 commit comments

Comments
 (0)