Skip to content

Commit 202e8e3

Browse files
f: integration testing WIP
1 parent d5887b1 commit 202e8e3

File tree

5 files changed

+182
-50
lines changed

5 files changed

+182
-50
lines changed

Diff for: packages/horizon/test/payments/recurring-collector/RecurringCollector.t.sol

+5-21
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import { IPaymentsCollector } from "../../../contracts/interfaces/IPaymentsColle
88
import { IRecurringCollector } from "../../../contracts/interfaces/IRecurringCollector.sol";
99
import { RecurringCollector } from "../../../contracts/payments/collectors/RecurringCollector.sol";
1010

11-
import { AuthorizableHelper } from "../../utilities/Authorizable.t.sol";
1211
import { Bounder } from "../../utils/Bounder.t.sol";
1312
import { RecurringCollectorControllerMock } from "./RecurringCollectorControllerMock.t.sol";
1413
import { PaymentsEscrowMock } from "./PaymentsEscrowMock.t.sol";
14+
import { RecurringCollectorHelper } from "./RecurringCollectorHelper.t.sol";
1515

1616
contract RecurringCollectorTest is Test, Bounder {
1717
RecurringCollector private _recurringCollector;
18-
AuthorizableHelper private _authHelper;
1918
PaymentsEscrowMock private _paymentsEscrow;
19+
RecurringCollectorHelper private _recurringCollectorHelper;
2020

2121
function setUp() public {
2222
_paymentsEscrow = new PaymentsEscrowMock();
@@ -26,7 +26,7 @@ contract RecurringCollectorTest is Test, Bounder {
2626
address(new RecurringCollectorControllerMock(address(_paymentsEscrow))),
2727
1
2828
);
29-
_authHelper = new AuthorizableHelper(_recurringCollector, 1);
29+
_recurringCollectorHelper = new RecurringCollectorHelper(_recurringCollector);
3030
}
3131

3232
/*
@@ -404,9 +404,9 @@ contract RecurringCollectorTest is Test, Bounder {
404404
uint256 _signerKey
405405
) private returns (IRecurringCollector.SignedRCV memory) {
406406
vm.assume(_rcv.payer != address(0));
407-
_authHelper.authorizeSignerWithChecks(_rcv.payer, _signerKey);
407+
_recurringCollectorHelper.authorizeSignerWithChecks(_rcv.payer, _signerKey);
408408
_rcv.acceptDeadline = boundTimestampMin(_rcv.acceptDeadline, block.timestamp + 1);
409-
IRecurringCollector.SignedRCV memory signedRCV = _generateSignedRCV(_recurringCollector, _rcv, _signerKey);
409+
IRecurringCollector.SignedRCV memory signedRCV = _recurringCollectorHelper.generateSignedRCV(_rcv, _signerKey);
410410

411411
vm.prank(_rcv.dataService);
412412
_recurringCollector.accept(signedRCV);
@@ -478,22 +478,6 @@ contract RecurringCollectorTest is Test, Bounder {
478478
return (data, collectionSeconds, tokens);
479479
}
480480

481-
function _generateSignedRCV(
482-
IRecurringCollector _collector,
483-
IRecurringCollector.RecurrentCollectionVoucher memory _rcv,
484-
uint256 _signerPrivateKey
485-
) private view returns (IRecurringCollector.SignedRCV memory) {
486-
bytes32 messageHash = _collector.encodeRCV(_rcv);
487-
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPrivateKey, messageHash);
488-
bytes memory signature = abi.encodePacked(r, s, v);
489-
IRecurringCollector.SignedRCV memory signedRCV = IRecurringCollector.SignedRCV({
490-
rcv: _rcv,
491-
signature: signature
492-
});
493-
494-
return signedRCV;
495-
}
496-
497481
function _sensibleRCV(
498482
IRecurringCollector.RecurrentCollectionVoucher memory _rcv
499483
) private pure returns (IRecurringCollector.RecurrentCollectionVoucher memory) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.27;
3+
4+
import { Test } from "forge-std/Test.sol";
5+
6+
import { IRecurringCollector } from "../../../contracts/interfaces/IRecurringCollector.sol";
7+
import { RecurringCollector } from "../../../contracts/payments/collectors/RecurringCollector.sol";
8+
import { AuthorizableHelper } from "../../utilities/Authorizable.t.sol";
9+
10+
contract RecurringCollectorHelper is AuthorizableHelper {
11+
RecurringCollector public collector;
12+
13+
constructor(
14+
RecurringCollector _collector
15+
) AuthorizableHelper(_collector, _collector.REVOKE_AUTHORIZATION_THAWING_PERIOD()) {
16+
collector = _collector;
17+
}
18+
19+
function generateSignedRCV(
20+
IRecurringCollector.RecurrentCollectionVoucher memory rcv,
21+
uint256 signerPrivateKey
22+
) public view returns (IRecurringCollector.SignedRCV memory) {
23+
bytes32 messageHash = collector.encodeRCV(rcv);
24+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, messageHash);
25+
bytes memory signature = abi.encodePacked(r, s, v);
26+
IRecurringCollector.SignedRCV memory signedRCV = IRecurringCollector.SignedRCV({
27+
rcv: rcv,
28+
signature: signature
29+
});
30+
31+
return signedRCV;
32+
}
33+
}

Diff for: packages/subgraph-service/test/subgraphService/SubgraphService.t.sol

+12-29
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest {
202202
uint256 paymentCollected = 0;
203203
address allocationId;
204204
IndexingRewardsData memory indexingRewardsData;
205-
CollectPaymentData memory collectPaymentDataBefore = _collectPaymentDataBefore(_indexer);
205+
CollectPaymentData memory collectPaymentDataBefore = _collectPaymentData(_indexer);
206206

207207
if (_paymentType == IGraphPayments.PaymentTypes.QueryFee) {
208208
paymentCollected = _handleQueryFeeCollection(_indexer, _data);
@@ -216,7 +216,7 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest {
216216
// collect rewards
217217
subgraphService.collect(_indexer, _paymentType, _data);
218218

219-
CollectPaymentData memory collectPaymentDataAfter = _collectPaymentDataAfter(_indexer);
219+
CollectPaymentData memory collectPaymentDataAfter = _collectPaymentData(_indexer);
220220

221221
if (_paymentType == IGraphPayments.PaymentTypes.QueryFee) {
222222
_verifyQueryFeeCollection(
@@ -237,40 +237,23 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest {
237237
}
238238
}
239239

240-
function _collectPaymentDataBefore(address _indexer) private view returns (CollectPaymentData memory) {
240+
function _collectPaymentData(
241+
address _indexer
242+
) internal view returns (CollectPaymentData memory collectPaymentData) {
241243
address rewardsDestination = subgraphService.rewardsDestination(_indexer);
242-
CollectPaymentData memory collectPaymentDataBefore;
243-
collectPaymentDataBefore.rewardsDestinationBalance = token.balanceOf(rewardsDestination);
244-
collectPaymentDataBefore.indexerProvisionBalance = staking.getProviderTokensAvailable(
244+
collectPaymentData.rewardsDestinationBalance = token.balanceOf(rewardsDestination);
245+
collectPaymentData.indexerProvisionBalance = staking.getProviderTokensAvailable(
245246
_indexer,
246247
address(subgraphService)
247248
);
248-
collectPaymentDataBefore.delegationPoolBalance = staking.getDelegatedTokensAvailable(
249+
collectPaymentData.delegationPoolBalance = staking.getDelegatedTokensAvailable(
249250
_indexer,
250251
address(subgraphService)
251252
);
252-
collectPaymentDataBefore.indexerBalance = token.balanceOf(_indexer);
253-
collectPaymentDataBefore.curationBalance = token.balanceOf(address(curation));
254-
collectPaymentDataBefore.lockedTokens = subgraphService.feesProvisionTracker(_indexer);
255-
return collectPaymentDataBefore;
256-
}
257-
258-
function _collectPaymentDataAfter(address _indexer) private view returns (CollectPaymentData memory) {
259-
CollectPaymentData memory collectPaymentDataAfter;
260-
address rewardsDestination = subgraphService.rewardsDestination(_indexer);
261-
collectPaymentDataAfter.rewardsDestinationBalance = token.balanceOf(rewardsDestination);
262-
collectPaymentDataAfter.indexerProvisionBalance = staking.getProviderTokensAvailable(
263-
_indexer,
264-
address(subgraphService)
265-
);
266-
collectPaymentDataAfter.delegationPoolBalance = staking.getDelegatedTokensAvailable(
267-
_indexer,
268-
address(subgraphService)
269-
);
270-
collectPaymentDataAfter.indexerBalance = token.balanceOf(_indexer);
271-
collectPaymentDataAfter.curationBalance = token.balanceOf(address(curation));
272-
collectPaymentDataAfter.lockedTokens = subgraphService.feesProvisionTracker(_indexer);
273-
return collectPaymentDataAfter;
253+
collectPaymentData.indexerBalance = token.balanceOf(_indexer);
254+
collectPaymentData.curationBalance = token.balanceOf(address(curation));
255+
collectPaymentData.lockedTokens = subgraphService.feesProvisionTracker(_indexer);
256+
return collectPaymentData;
274257
}
275258

276259
function _handleQueryFeeCollection(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.27;
3+
4+
import { IRecurringCollector } from "@graphprotocol/horizon/contracts/interfaces/IRecurringCollector.sol";
5+
import { IGraphPayments } from "@graphprotocol/horizon/contracts/interfaces/IGraphPayments.sol";
6+
import { ISubgraphService } from "../../../contracts/interfaces/ISubgraphService.sol";
7+
8+
import { SubgraphServiceIndexingAgreementSharedTest } from "./shared.t.sol";
9+
10+
import { RecurringCollectorHelper } from "@graphprotocol/horizon/test/payments/recurring-collector/RecurringCollectorHelper.t.sol";
11+
12+
contract SubgraphServiceIndexingAgreementCancelTest is SubgraphServiceIndexingAgreementSharedTest {
13+
RecurringCollectorHelper private _recurringCollectorHelper;
14+
15+
function setUp() public override {
16+
super.setUp();
17+
18+
_recurringCollectorHelper = new RecurringCollectorHelper(recurringCollector);
19+
}
20+
21+
/*
22+
* TESTS
23+
*/
24+
25+
/* solhint-disable graph/func-name-mixedcase */
26+
function test_SubgraphService_CollectIndexingFee_Integration(
27+
SetupTestIndexerParams calldata fuzzyParams,
28+
IRecurringCollector.RecurrentCollectionVoucher memory fuzzyRCV,
29+
uint256 escrowTokens
30+
) public {
31+
vm.assume(escrowTokens > 0);
32+
33+
// Authorize Signer to issue Vouchers on behalf of the gateway
34+
(, uint256 signerPrivateKey) = makeAddrAndKey("signer");
35+
_recurringCollectorHelper.authorizeSignerWithChecks(users.gateway, signerPrivateKey);
36+
37+
// Setup Indexer
38+
TestIndexerParams memory params = _setupTestIndexer(fuzzyParams);
39+
deal({ token: address(token), to: params.indexer, give: 1 ether });
40+
vm.startPrank(params.indexer);
41+
_addToProvision(params.indexer, 1 ether);
42+
subgraphService.setRewardsDestination(params.indexer);
43+
vm.stopPrank();
44+
45+
// Gateway deposits tokens to the Escrow
46+
deal({ token: address(token), to: users.gateway, give: 1 ether });
47+
vm.startPrank(users.gateway);
48+
// FIX-ME: sometimes this fails with ERC20InsufficientBalance?!?!?
49+
_escrow(1 ether, params.indexer);
50+
vm.stopPrank();
51+
52+
// Create the Indexing Agreement
53+
fuzzyRCV.acceptDeadline = block.timestamp; // accept now
54+
fuzzyRCV.duration = type(uint256).max; // no expiration
55+
fuzzyRCV.maxInitialTokens = 0; // no initial payment
56+
fuzzyRCV.maxOngoingTokensPerSecond = 1; // 1 token per second
57+
fuzzyRCV.minSecondsPerCollection = 0; // no minimum time between collections
58+
fuzzyRCV.maxSecondsPerCollection = type(uint32).max; // no maximum time between collections
59+
fuzzyRCV.payer = users.gateway; // payer is the gateway
60+
fuzzyRCV.serviceProvider = params.indexer; // service provider is the indexer
61+
fuzzyRCV.dataService = address(subgraphService); // data service is the subgraph service
62+
fuzzyRCV.metadata = abi.encode(
63+
ISubgraphService.RCVMetadata({
64+
tokensPerSecond: 1,
65+
tokensPerEntityPerSecond: 0,
66+
subgraphDeploymentId: params.subgraphDeploymentId
67+
})
68+
);
69+
70+
resetPrank(params.indexer);
71+
subgraphService.acceptIndexingAgreement(
72+
params.allocationId,
73+
_recurringCollectorHelper.generateSignedRCV(fuzzyRCV, signerPrivateKey)
74+
);
75+
76+
// check that indexer stake is enough to cover the fee
77+
// vm.assume(tokensAllocated > minimumProvisionTokens * stakeToFeesRatio);
78+
79+
// uint256 maxTokensPayment = params.tokens / stakeToFeesRatio > type(uint128).max
80+
// ? type(uint128).max
81+
// : tokensAllocated / stakeToFeesRatio;
82+
// tokensPayment = bound(tokensPayment, minimumProvisionTokens, maxTokensPayment);
83+
84+
skip(fuzzyRCV.minSecondsPerCollection + 1000);
85+
ISubgraphService.IndexingAgreementKey memory key = ISubgraphService.IndexingAgreementKey({
86+
payer: users.gateway,
87+
indexer: params.indexer,
88+
agreementId: fuzzyRCV.agreementId
89+
});
90+
uint256 expectedTotalTokensCollected = 1000;
91+
uint256 expectedProtocolTokensBurnt = 10;
92+
uint256 expectedIndexerTokensCollected = expectedTotalTokensCollected - expectedProtocolTokensBurnt;
93+
uint256 entities = 1;
94+
bytes32 poi = keccak256(abi.encodePacked("poi"));
95+
vm.expectEmit();
96+
emit ISubgraphService.IndexingFeesCollected(
97+
key.indexer,
98+
key.payer,
99+
key.agreementId,
100+
params.allocationId,
101+
params.subgraphDeploymentId,
102+
epochManager.currentEpoch(),
103+
expectedTotalTokensCollected,
104+
entities,
105+
poi
106+
);
107+
CollectPaymentData memory collectPaymentDataBefore = _collectPaymentData(params.indexer);
108+
uint256 tokensCollected = subgraphService.collect(
109+
params.indexer,
110+
IGraphPayments.PaymentTypes.IndexingFee,
111+
abi.encode(key, entities, poi)
112+
);
113+
CollectPaymentData memory collectPaymentDataAfter = _collectPaymentData(params.indexer);
114+
115+
assertEq(tokensCollected, expectedTotalTokensCollected);
116+
assertEq(
117+
collectPaymentDataAfter.indexerBalance,
118+
collectPaymentDataBefore.indexerBalance + expectedIndexerTokensCollected
119+
);
120+
assertEq(collectPaymentDataAfter.curationBalance, collectPaymentDataBefore.curationBalance + 0);
121+
assertEq(collectPaymentDataAfter.lockedTokens, collectPaymentDataBefore.lockedTokens + 2000);
122+
}
123+
124+
/* solhint-enable graph/func-name-mixedcase */
125+
126+
function _escrow(uint256 tokens, address _indexer) private {
127+
token.approve(address(escrow), tokens);
128+
escrow.deposit(address(recurringCollector), _indexer, tokens);
129+
}
130+
}

Diff for: packages/subgraph-service/test/subgraphService/indexing-agreement/shared.t.sol

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { SubgraphServiceTest } from "../SubgraphService.t.sol";
1111
contract SubgraphServiceIndexingAgreementSharedTest is SubgraphServiceTest, Bounder {
1212
struct SetupTestIndexerParams {
1313
address indexer;
14+
string indexerLabel;
1415
uint256 unboundedTokens;
1516
uint256 unboundedAllocationPrivateKey;
1617
bytes32 subgraphDeploymentId;
@@ -104,6 +105,7 @@ contract SubgraphServiceIndexingAgreementSharedTest is SubgraphServiceTest, Boun
104105
}
105106

106107
function _setupTestIndexer(SetupTestIndexerParams calldata _params) internal returns (TestIndexerParams memory) {
108+
vm.label(_params.indexer, string.concat("indexer-", _params.indexerLabel));
107109
vm.assume(_isSafeSubgraphServiceCaller(_params.indexer) && !_registeredIndexers[_params.indexer]);
108110
_registeredIndexers[_params.indexer] = true;
109111

0 commit comments

Comments
 (0)