-
Notifications
You must be signed in to change notification settings - Fork 156
/
Copy pathSubgraphService.sol
599 lines (547 loc) · 24.7 KB
/
SubgraphService.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.27;
import { IGraphPayments } from "@graphprotocol/horizon/contracts/interfaces/IGraphPayments.sol";
import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol";
import { IGraphTallyCollector } from "@graphprotocol/horizon/contracts/interfaces/IGraphTallyCollector.sol";
import { IRewardsIssuer } from "@graphprotocol/contracts/contracts/rewards/IRewardsIssuer.sol";
import { ISubgraphService } from "./interfaces/ISubgraphService.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { DataServicePausableUpgradeable } from "@graphprotocol/horizon/contracts/data-service/extensions/DataServicePausableUpgradeable.sol";
import { DataService } from "@graphprotocol/horizon/contracts/data-service/DataService.sol";
import { DataServiceFees } from "@graphprotocol/horizon/contracts/data-service/extensions/DataServiceFees.sol";
import { Directory } from "./utilities/Directory.sol";
import { AllocationManager } from "./utilities/AllocationManager.sol";
import { SubgraphServiceV1Storage } from "./SubgraphServiceStorage.sol";
import { TokenUtils } from "@graphprotocol/contracts/contracts/utils/TokenUtils.sol";
import { PPMMath } from "@graphprotocol/horizon/contracts/libraries/PPMMath.sol";
import { Allocation } from "./libraries/Allocation.sol";
import { LegacyAllocation } from "./libraries/LegacyAllocation.sol";
/**
* @title SubgraphService contract
* @custom:security-contact Please email [email protected] if you find any
* bugs. We may have an active bug bounty program.
*/
contract SubgraphService is
Initializable,
OwnableUpgradeable,
MulticallUpgradeable,
DataService,
DataServicePausableUpgradeable,
DataServiceFees,
Directory,
AllocationManager,
SubgraphServiceV1Storage,
IRewardsIssuer,
ISubgraphService
{
using PPMMath for uint256;
using Allocation for mapping(address => Allocation.State);
using Allocation for Allocation.State;
using TokenUtils for IGraphToken;
/**
* @notice Checks that an indexer is registered
* @param indexer The address of the indexer
*/
modifier onlyRegisteredIndexer(address indexer) {
require(indexers[indexer].registeredAt != 0, SubgraphServiceIndexerNotRegistered(indexer));
_;
}
/**
* @notice Constructor for the SubgraphService contract
* @dev DataService and Directory constructors set a bunch of immutable variables
* @param graphController The address of the Graph Controller contract
* @param disputeManager The address of the DisputeManager contract
* @param graphTallyCollector The address of the GraphTallyCollector contract
* @param curation The address of the Curation contract
*/
constructor(
address graphController,
address disputeManager,
address graphTallyCollector,
address curation
) DataService(graphController) Directory(address(this), disputeManager, graphTallyCollector, curation) {
_disableInitializers();
}
/**
* @notice Initialize the contract
* @dev The thawingPeriod and verifierCut ranges are not set here because they are variables
* on the DisputeManager. We use the {ProvisionManager} overrideable getters to get the ranges.
* @param owner The owner of the contract
* @param minimumProvisionTokens The minimum amount of provisioned tokens required to create an allocation
* @param maximumDelegationRatio The maximum delegation ratio allowed for an allocation
* @param stakeToFeesRatio The ratio of stake to fees to lock when collecting query fees
*/
function initialize(
address owner,
uint256 minimumProvisionTokens,
uint32 maximumDelegationRatio,
uint256 stakeToFeesRatio
) external initializer {
__Ownable_init(owner);
__Multicall_init();
__DataService_init();
__DataServicePausable_init();
__AllocationManager_init("SubgraphService", "1.0");
_setProvisionTokensRange(minimumProvisionTokens, type(uint256).max);
_setDelegationRatio(maximumDelegationRatio);
_setStakeToFeesRatio(stakeToFeesRatio);
}
/**
* @notice
* @dev Implements {IDataService.register}
*
* Requirements:
* - The indexer must not be already registered
* - The URL must not be empty
* - The provision must be valid according to the subgraph service rules
*
* Emits a {ServiceProviderRegistered} event
*
* @param indexer The address of the indexer to register
* @param data Encoded registration data:
* - address `url`: The URL of the indexer
* - string `geohash`: The geohash of the indexer
* - address `rewardsDestination`: The address where the indexer wants to receive indexing rewards.
* Use zero address for automatic reprovisioning to the subgraph service.
*/
function register(
address indexer,
bytes calldata data
) external override onlyAuthorizedForProvision(indexer) onlyValidProvision(indexer) whenNotPaused {
(string memory url, string memory geohash, address rewardsDestination) = abi.decode(
data,
(string, string, address)
);
require(bytes(url).length > 0, SubgraphServiceEmptyUrl());
require(bytes(geohash).length > 0, SubgraphServiceEmptyGeohash());
require(indexers[indexer].registeredAt == 0, SubgraphServiceIndexerAlreadyRegistered());
// Register the indexer
indexers[indexer] = Indexer({ registeredAt: block.timestamp, url: url, geoHash: geohash });
if (rewardsDestination != address(0)) {
_setRewardsDestination(indexer, rewardsDestination);
}
emit ServiceProviderRegistered(indexer, data);
}
/**
* @notice Accept staged parameters in the provision of a service provider
* @dev Implements {IDataService-acceptProvisionPendingParameters}
*
* Requirements:
* - The indexer must be registered
* - Must have previously staged provision parameters, using {IHorizonStaking-setProvisionParameters}
* - The new provision parameters must be valid according to the subgraph service rules
*
* Emits a {ProvisionPendingParametersAccepted} event
*
* @param indexer The address of the indexer to accept the provision for
*/
function acceptProvisionPendingParameters(
address indexer,
bytes calldata
) external override onlyAuthorizedForProvision(indexer) onlyRegisteredIndexer(indexer) whenNotPaused {
_checkProvisionTokens(indexer);
_acceptProvisionParameters(indexer);
emit ProvisionPendingParametersAccepted(indexer);
}
/**
* @notice Allocates tokens to subgraph deployment, manifesting the indexer's commitment to index it
* @dev This is the equivalent of the `allocate` function in the legacy Staking contract.
*
* Requirements:
* - The indexer must be registered
* - The provision must be valid according to the subgraph service rules
* - Allocation id cannot be zero
* - Allocation id cannot be reused from the legacy staking contract
* - The indexer must have enough available tokens to allocate
*
* The `allocationProof` is a 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationId)`.
*
* See {AllocationManager-allocate} for more details.
*
* Emits {ServiceStarted} and {AllocationCreated} events
*
* @param indexer The address of the indexer
* @param data Encoded data:
* - bytes32 `subgraphDeploymentId`: The id of the subgraph deployment
* - uint256 `tokens`: The amount of tokens to allocate
* - address `allocationId`: The id of the allocation
* - bytes `allocationProof`: Signed proof of the allocation id address ownership
*/
function startService(
address indexer,
bytes calldata data
)
external
override
onlyAuthorizedForProvision(indexer)
onlyValidProvision(indexer)
onlyRegisteredIndexer(indexer)
whenNotPaused
{
(bytes32 subgraphDeploymentId, uint256 tokens, address allocationId, bytes memory allocationProof) = abi.decode(
data,
(bytes32, uint256, address, bytes)
);
_allocate(indexer, allocationId, subgraphDeploymentId, tokens, allocationProof, delegationRatio);
emit ServiceStarted(indexer, data);
}
/**
* @notice Close an allocation, indicating that the indexer has stopped indexing the subgraph deployment
* @dev This is the equivalent of the `closeAllocation` function in the legacy Staking contract.
* There are a few notable differences with the legacy function:
* - allocations are nowlong lived. All service payments, including indexing rewards, should be collected periodically
* without the need of closing the allocation. Allocations should only be closed when indexers want to reclaim the allocated
* tokens for other purposes.
* - No POI is required to close an allocation. Indexers should present POIs to collect indexing rewards using {collect}.
*
* Requirements:
* - The indexer must be registered
* - Allocation must exist and be open
*
* Emits {ServiceStopped} and {AllocationClosed} events
*
* @param indexer The address of the indexer
* @param data Encoded data:
* - address `allocationId`: The id of the allocation
*/
function stopService(
address indexer,
bytes calldata data
) external override onlyAuthorizedForProvision(indexer) onlyRegisteredIndexer(indexer) whenNotPaused {
address allocationId = abi.decode(data, (address));
require(
_allocations.get(allocationId).indexer == indexer,
SubgraphServiceAllocationNotAuthorized(indexer, allocationId)
);
_closeAllocation(allocationId);
emit ServiceStopped(indexer, data);
}
/**
* @notice Collects payment for the service provided by the indexer
* Allows collecting different types of payments such as query fees and indexing rewards.
* It uses Graph Horizon payments protocol to process payments.
* Reverts if the payment type is not supported.
* @dev This function is the equivalent of the `collect` function for query fees and the `closeAllocation` function
* for indexing rewards in the legacy Staking contract.
*
* Requirements:
* - The indexer must be registered
* - The provision must be valid according to the subgraph service rules
*
* Emits a {ServicePaymentCollected} event. Emits payment type specific events.
*
* For query fees, see {SubgraphService-_collectQueryFees} for more details.
* For indexing rewards, see {AllocationManager-_collectIndexingRewards} for more details.
*
* @param indexer The address of the indexer
* @param paymentType The type of payment to collect as defined in {IGraphPayments}
* @param data Encoded data to fulfill the payment. The structure of the data depends on the payment type. See above.
*/
function collect(
address indexer,
IGraphPayments.PaymentTypes paymentType,
bytes calldata data
)
external
override
onlyAuthorizedForProvision(indexer)
onlyValidProvision(indexer)
onlyRegisteredIndexer(indexer)
whenNotPaused
returns (uint256)
{
uint256 paymentCollected = 0;
if (paymentType == IGraphPayments.PaymentTypes.QueryFee) {
IGraphTallyCollector.SignedRAV memory signedRav = abi.decode(data, (IGraphTallyCollector.SignedRAV));
require(
signedRav.rav.serviceProvider == indexer,
SubgraphServiceIndexerMismatch(signedRav.rav.serviceProvider, indexer)
);
paymentCollected = _collectQueryFees(signedRav);
} else if (paymentType == IGraphPayments.PaymentTypes.IndexingRewards) {
(address allocationId, bytes32 poi) = abi.decode(data, (address, bytes32));
require(
_allocations.get(allocationId).indexer == indexer,
SubgraphServiceAllocationNotAuthorized(indexer, allocationId)
);
paymentCollected = _collectIndexingRewards(allocationId, poi, delegationRatio);
} else {
revert SubgraphServiceInvalidPaymentType(paymentType);
}
emit ServicePaymentCollected(indexer, paymentType, paymentCollected);
return paymentCollected;
}
/**
* @notice Slash an indexer
* @dev Slashing is delegated to the {DisputeManager} contract which is the only one that can call this
* function.
*
* See {IHorizonStaking-slash} for more details.
*
* Emits a {ServiceProviderSlashed} event.
*
* @param indexer The address of the indexer to be slashed
* @param data Encoded data:
* - uint256 `tokens`: The amount of tokens to slash
* - uint256 `reward`: The amount of tokens to reward the slasher
*/
function slash(address indexer, bytes calldata data) external override onlyDisputeManager {
(uint256 tokens, uint256 reward) = abi.decode(data, (uint256, uint256));
_graphStaking().slash(indexer, tokens, reward, address(_disputeManager()));
emit ServiceProviderSlashed(indexer, tokens);
}
/**
* @notice See {ISubgraphService.closeStaleAllocation}
*/
function closeStaleAllocation(address allocationId) external override whenNotPaused {
Allocation.State memory allocation = _allocations.get(allocationId);
require(allocation.isStale(maxPOIStaleness), SubgraphServiceCannotForceCloseAllocation(allocationId));
require(!allocation.isAltruistic(), SubgraphServiceAllocationIsAltruistic(allocationId));
_closeAllocation(allocationId);
}
/**
* @notice See {ISubgraphService.resizeAllocation}
*/
function resizeAllocation(
address indexer,
address allocationId,
uint256 tokens
)
external
onlyAuthorizedForProvision(indexer)
onlyValidProvision(indexer)
onlyRegisteredIndexer(indexer)
whenNotPaused
{
require(
_allocations.get(allocationId).indexer == indexer,
SubgraphServiceAllocationNotAuthorized(indexer, allocationId)
);
_resizeAllocation(allocationId, tokens, delegationRatio);
}
/**
* @notice See {ISubgraphService.migrateLegacyAllocation}
*/
function migrateLegacyAllocation(
address indexer,
address allocationId,
bytes32 subgraphDeploymentID
) external override onlyOwner {
_migrateLegacyAllocation(indexer, allocationId, subgraphDeploymentID);
}
/**
* @notice See {ISubgraphService.setPauseGuardian}
*/
function setPauseGuardian(address pauseGuardian, bool allowed) external override onlyOwner {
_setPauseGuardian(pauseGuardian, allowed);
}
/**
* @notice See {ISubgraphService.setRewardsDestination}
*/
function setRewardsDestination(address rewardsDestination) external override {
_setRewardsDestination(msg.sender, rewardsDestination);
}
/**
* @notice See {ISubgraphService.setMinimumProvisionTokens}
*/
function setMinimumProvisionTokens(uint256 minimumProvisionTokens) external override onlyOwner {
_setProvisionTokensRange(minimumProvisionTokens, DEFAULT_MAX_PROVISION_TOKENS);
}
/**
* @notice See {ISubgraphService.setDelegationRatio}
*/
function setDelegationRatio(uint32 delegationRatio) external override onlyOwner {
_setDelegationRatio(delegationRatio);
}
/**
* @notice See {ISubgraphService.setStakeToFeesRatio}
*/
function setStakeToFeesRatio(uint256 stakeToFeesRatio_) external override onlyOwner {
_setStakeToFeesRatio(stakeToFeesRatio_);
}
/**
* @notice See {ISubgraphService.setMaxPOIStaleness}
*/
function setMaxPOIStaleness(uint256 maxPOIStaleness) external override onlyOwner {
_setMaxPOIStaleness(maxPOIStaleness);
}
/**
* @notice See {ISubgraphService.setCurationCut}
*/
function setCurationCut(uint256 curationCut) external override onlyOwner {
require(PPMMath.isValidPPM(curationCut), SubgraphServiceInvalidCurationCut(curationCut));
curationFeesCut = curationCut;
emit CurationCutSet(curationCut);
}
/**
* @notice See {ISubgraphService.getAllocation}
*/
function getAllocation(address allocationId) external view override returns (Allocation.State memory) {
return _allocations[allocationId];
}
/**
* @notice Get allocation data to calculate rewards issuance
* @dev Implements {IRewardsIssuer.getAllocationData}
* @dev Note that this is slightly different than {getAllocation}. It returns an
* unstructured subset of the allocation data, which is the minimum required to mint rewards.
*
* Should only be used by the {RewardsManager}.
*
* @param allocationId The allocation Id
* @return indexer The indexer address
* @return subgraphDeploymentId Subgraph deployment id for the allocation
* @return tokens Amount of allocated tokens
* @return accRewardsPerAllocatedToken Rewards snapshot
* @return accRewardsPending Rewards pending to be minted due to allocation resize
*/
function getAllocationData(
address allocationId
) external view override returns (address, bytes32, uint256, uint256, uint256) {
Allocation.State memory allo = _allocations[allocationId];
return (
allo.indexer,
allo.subgraphDeploymentId,
allo.tokens,
allo.accRewardsPerAllocatedToken,
allo.accRewardsPending
);
}
/**
* @notice Return the total amount of tokens allocated to subgraph.
* @dev Implements {IRewardsIssuer.getSubgraphAllocatedTokens}
* @dev To be used by the {RewardsManager}.
* @param subgraphDeploymentId Deployment Id for the subgraph
* @return Total tokens allocated to subgraph
*/
function getSubgraphAllocatedTokens(bytes32 subgraphDeploymentId) external view override returns (uint256) {
return _subgraphAllocatedTokens[subgraphDeploymentId];
}
/**
* @notice See {ISubgraphService.getLegacyAllocation}
*/
function getLegacyAllocation(address allocationId) external view override returns (LegacyAllocation.State memory) {
return _legacyAllocations[allocationId];
}
/**
* @notice See {ISubgraphService.getDisputeManager}
*/
function getDisputeManager() external view override returns (address) {
return address(_disputeManager());
}
/**
* @notice See {ISubgraphService.getGraphTallyCollector}
*/
function getGraphTallyCollector() external view override returns (address) {
return address(_graphTallyCollector());
}
/**
* @notice See {ISubgraphService.getCuration}
*/
function getCuration() external view override returns (address) {
return address(_curation());
}
/**
* @notice See {ISubgraphService.encodeAllocationProof}
*/
function encodeAllocationProof(address indexer, address allocationId) external view override returns (bytes32) {
return _encodeAllocationProof(indexer, allocationId);
}
/**
* @notice See {ISubgraphService.isOverAllocated}
*/
function isOverAllocated(address indexer) external view override returns (bool) {
return _isOverAllocated(indexer, delegationRatio);
}
// -- Data service parameter getters --
/**
* @notice Getter for the accepted thawing period range for provisions
* @dev This override ensures {ProvisionManager} uses the thawing period from the {DisputeManager}
* @return min The minimum thawing period which is defined by {DisputeManager-getDisputePeriod}
* @return max The maximum is unbounded
*/
function _getThawingPeriodRange() internal view override returns (uint64 min, uint64 max) {
return (_disputeManager().getDisputePeriod(), DEFAULT_MAX_THAWING_PERIOD);
}
/**
* @notice Getter for the accepted verifier cut range for provisions
* @return min The minimum verifier cut which is defined by {DisputeManager-getVerifierCut}
* @return max The maximum is 100% in PPM
*/
function _getVerifierCutRange() internal view override returns (uint32 min, uint32 max) {
return (_disputeManager().getVerifierCut(), DEFAULT_MAX_VERIFIER_CUT);
}
/**
* @notice Collect query fees
* Stake equal to the amount being collected times the `stakeToFeesRatio` is locked into a stake claim.
* This claim can be released at a later stage once expired.
*
* It's important to note that before collecting this function will attempt to release any expired stake claims.
* This could lead to an out of gas error if there are too many expired claims. In that case, the indexer will need to
* manually release the claims, see {IDataServiceFees-releaseStake}, before attempting to collect again.
*
* @dev This function is the equivalent of the legacy `collect` function for query fees.
* @dev Uses the {GraphTallyCollector} to collect payment from Graph Horizon payments protocol.
* Fees are distributed to service provider and delegators by {GraphPayments}, though curators
* share is distributed by this function.
*
* Query fees can be collected on closed allocations.
*
* Requirements:
* - Indexer must have enough available tokens to lock as economic security for fees
*
* Emits a {StakeClaimsReleased} event, and a {StakeClaimReleased} event for each claim released.
* Emits a {StakeClaimLocked} event.
* Emits a {QueryFeesCollected} event.
*
* @param _signedRav Signed RAV
*/
function _collectQueryFees(
IGraphTallyCollector.SignedRAV memory _signedRav
) private returns (uint256 feesCollected) {
address indexer = _signedRav.rav.serviceProvider;
// Check that collectionId (256 bits) is a valid address (160 bits)
require(
uint256(_signedRav.rav.collectionId) <= type(uint160).max,
SubgraphServiceInvalidCollectionId(_signedRav.rav.collectionId)
);
address allocationId = address(uint160(uint256(_signedRav.rav.collectionId)));
Allocation.State memory allocation = _allocations.get(allocationId);
// Check RAV is consistent - RAV indexer must match the allocation's indexer
require(allocation.indexer == indexer, SubgraphServiceInvalidRAV(indexer, allocation.indexer));
bytes32 subgraphDeploymentId = allocation.subgraphDeploymentId;
// release expired stake claims
_releaseStake(indexer, 0);
// Collect from GraphPayments - only curators cut is sent back to the subgraph service
uint256 balanceBefore = _graphToken().balanceOf(address(this));
uint256 curationCut = _curation().isCurated(subgraphDeploymentId) ? curationFeesCut : 0;
uint256 tokensCollected = _graphTallyCollector().collect(
IGraphPayments.PaymentTypes.QueryFee,
abi.encode(_signedRav, curationCut)
);
uint256 balanceAfter = _graphToken().balanceOf(address(this));
require(balanceAfter >= balanceBefore, SubgraphServiceInconsistentCollection(balanceBefore, balanceAfter));
uint256 tokensCurators = balanceAfter - balanceBefore;
if (tokensCollected > 0) {
// lock stake as economic security for fees
uint256 tokensToLock = tokensCollected * stakeToFeesRatio;
uint256 unlockTimestamp = block.timestamp + _disputeManager().getDisputePeriod();
_lockStake(indexer, tokensToLock, unlockTimestamp);
if (tokensCurators > 0) {
// curation collection changes subgraph signal so we take rewards snapshot
_graphRewardsManager().onSubgraphSignalUpdate(subgraphDeploymentId);
// Send GRT and bookkeep by calling collect()
_graphToken().pushTokens(address(_curation()), tokensCurators);
_curation().collect(subgraphDeploymentId, tokensCurators);
}
}
emit QueryFeesCollected(indexer, _signedRav.rav.payer, tokensCollected, tokensCurators);
return tokensCollected;
}
function _setStakeToFeesRatio(uint256 _stakeToFeesRatio) private {
require(_stakeToFeesRatio != 0, SubgraphServiceInvalidZeroStakeToFeesRatio());
stakeToFeesRatio = _stakeToFeesRatio;
emit StakeToFeesRatioSet(_stakeToFeesRatio);
}
}