2
2
pragma solidity 0.8.27 ;
3
3
4
4
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol " ;
5
- import { IHorizonStaking } from "@graphprotocol/horizon/contracts/interfaces/IHorizonStaking.sol " ;
5
+ import { IEpochManager } from "@graphprotocol/contracts/contracts/epochs/IEpochManager.sol " ;
6
+ import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol " ;
6
7
import { IRewardsManager } from "@graphprotocol/contracts/contracts/rewards/IRewardsManager.sol " ;
8
+ import { TokenUtils } from "@graphprotocol/contracts/contracts/utils/TokenUtils.sol " ;
9
+ import { IHorizonStakingTypes } from "@graphprotocol/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol " ;
10
+ import { IHorizonStaking } from "@graphprotocol/horizon/contracts/interfaces/IHorizonStaking.sol " ;
11
+ import { IGraphPayments } from "@graphprotocol/horizon/contracts/interfaces/IGraphPayments.sol " ;
7
12
import { ProvisionTracker } from "@graphprotocol/horizon/contracts/data-service/libraries/ProvisionTracker.sol " ;
13
+ import { PPMMath } from "@graphprotocol/horizon/contracts/libraries/PPMMath.sol " ;
8
14
9
15
import { Allocation } from "../libraries/Allocation.sol " ;
10
16
import { LegacyAllocation } from "../libraries/LegacyAllocation.sol " ;
@@ -13,11 +19,10 @@ import { AllocationManager } from "../utilities/AllocationManager.sol";
13
19
library AllocationManagerLib {
14
20
using ProvisionTracker for mapping (address => uint256 );
15
21
using Allocation for mapping (address => Allocation.State);
22
+ using Allocation for Allocation.State;
16
23
using LegacyAllocation for mapping (address => LegacyAllocation.State);
17
-
18
- ///@dev EIP712 typehash for allocation id proof
19
- bytes32 private constant EIP712_ALLOCATION_ID_PROOF_TYPEHASH =
20
- keccak256 ("AllocationIdProof(address indexer,address allocationId) " );
24
+ using PPMMath for uint256 ;
25
+ using TokenUtils for IGraphToken;
21
26
22
27
struct AllocateParams {
23
28
uint256 currentEpoch;
@@ -32,6 +37,23 @@ library AllocationManagerLib {
32
37
uint32 _delegationRatio;
33
38
}
34
39
40
+ struct PresentParams {
41
+ uint256 maxPOIStaleness;
42
+ IEpochManager graphEpochManager;
43
+ IHorizonStaking graphStaking;
44
+ IRewardsManager graphRewardsManager;
45
+ IGraphToken graphToken;
46
+ address _allocationId;
47
+ bytes32 _poi;
48
+ bytes _poiMetadata;
49
+ uint32 _delegationRatio;
50
+ address _paymentsDestination;
51
+ }
52
+
53
+ ///@dev EIP712 typehash for allocation id proof
54
+ bytes32 private constant EIP712_ALLOCATION_ID_PROOF_TYPEHASH =
55
+ keccak256 ("AllocationIdProof(address indexer,address allocationId) " );
56
+
35
57
/**
36
58
* @notice Create an allocation
37
59
* @dev The `_allocationProof` is a 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationId)`
@@ -85,6 +107,173 @@ library AllocationManagerLib {
85
107
);
86
108
}
87
109
110
+ function presentPOI (
111
+ mapping (address allocationId = > Allocation.State allocation ) storage _allocations ,
112
+ mapping (address indexer = > uint256 tokens ) storage allocationProvisionTracker ,
113
+ mapping (bytes32 subgraphDeploymentId = > uint256 tokens ) storage _subgraphAllocatedTokens ,
114
+ PresentParams memory params
115
+ ) external returns (uint256 ) {
116
+ Allocation.State memory allocation = _allocations.get (params._allocationId);
117
+ require (allocation.isOpen (), AllocationManager.AllocationManagerAllocationClosed (params._allocationId));
118
+
119
+ // Mint indexing rewards if all conditions are met
120
+ uint256 tokensRewards = (! allocation.isStale (params.maxPOIStaleness) &&
121
+ ! allocation.isAltruistic () &&
122
+ params._poi != bytes32 (0 )) && params.graphEpochManager.currentEpoch () > allocation.createdAtEpoch
123
+ ? params.graphRewardsManager.takeRewards (params._allocationId)
124
+ : 0 ;
125
+
126
+ // ... but we still take a snapshot to ensure the rewards are not accumulated for the next valid POI
127
+ _allocations.snapshotRewards (
128
+ params._allocationId,
129
+ params.graphRewardsManager.onSubgraphAllocationUpdate (allocation.subgraphDeploymentId)
130
+ );
131
+ _allocations.presentPOI (params._allocationId);
132
+
133
+ // Any pending rewards should have been collected now
134
+ _allocations.clearPendingRewards (params._allocationId);
135
+
136
+ uint256 tokensIndexerRewards = 0 ;
137
+ uint256 tokensDelegationRewards = 0 ;
138
+ if (tokensRewards != 0 ) {
139
+ // Distribute rewards to delegators
140
+ uint256 delegatorCut = params.graphStaking.getDelegationFeeCut (
141
+ allocation.indexer,
142
+ address (this ),
143
+ IGraphPayments.PaymentTypes.IndexingRewards
144
+ );
145
+ IHorizonStakingTypes.DelegationPool memory delegationPool = params.graphStaking.getDelegationPool (
146
+ allocation.indexer,
147
+ address (this )
148
+ );
149
+ // If delegation pool has no shares then we don't need to distribute rewards to delegators
150
+ tokensDelegationRewards = delegationPool.shares > 0 ? tokensRewards.mulPPM (delegatorCut) : 0 ;
151
+ if (tokensDelegationRewards > 0 ) {
152
+ params.graphToken.approve (address (params.graphStaking), tokensDelegationRewards);
153
+ params.graphStaking.addToDelegationPool (allocation.indexer, address (this ), tokensDelegationRewards);
154
+ }
155
+
156
+ // Distribute rewards to indexer
157
+ tokensIndexerRewards = tokensRewards - tokensDelegationRewards;
158
+ if (tokensIndexerRewards > 0 ) {
159
+ if (params._paymentsDestination == address (0 )) {
160
+ params.graphToken.approve (address (params.graphStaking), tokensIndexerRewards);
161
+ params.graphStaking.stakeToProvision (allocation.indexer, address (this ), tokensIndexerRewards);
162
+ } else {
163
+ params.graphToken.pushTokens (params._paymentsDestination, tokensIndexerRewards);
164
+ }
165
+ }
166
+ }
167
+
168
+ emit AllocationManager.IndexingRewardsCollected (
169
+ allocation.indexer,
170
+ params._allocationId,
171
+ allocation.subgraphDeploymentId,
172
+ tokensRewards,
173
+ tokensIndexerRewards,
174
+ tokensDelegationRewards,
175
+ params._poi,
176
+ params._poiMetadata,
177
+ params.graphEpochManager.currentEpoch ()
178
+ );
179
+
180
+ // Check if the indexer is over-allocated and force close the allocation if necessary
181
+ if (
182
+ _isOverAllocated (
183
+ allocationProvisionTracker,
184
+ params.graphStaking,
185
+ allocation.indexer,
186
+ params._delegationRatio
187
+ )
188
+ ) {
189
+ _closeAllocation (
190
+ _allocations,
191
+ allocationProvisionTracker,
192
+ _subgraphAllocatedTokens,
193
+ params.graphRewardsManager,
194
+ params._allocationId,
195
+ true
196
+ );
197
+ }
198
+
199
+ return tokensRewards;
200
+ }
201
+
202
+ function closeAllocation (
203
+ mapping (address allocationId = > Allocation.State allocation ) storage _allocations ,
204
+ mapping (address indexer = > uint256 tokens ) storage allocationProvisionTracker ,
205
+ mapping (bytes32 subgraphDeploymentId = > uint256 tokens ) storage _subgraphAllocatedTokens ,
206
+ IRewardsManager graphRewardsManager ,
207
+ address _allocationId ,
208
+ bool _forceClosed
209
+ ) external {
210
+ _closeAllocation (
211
+ _allocations,
212
+ allocationProvisionTracker,
213
+ _subgraphAllocatedTokens,
214
+ graphRewardsManager,
215
+ _allocationId,
216
+ _forceClosed
217
+ );
218
+ }
219
+
220
+ /**
221
+ * @notice Checks if an allocation is over-allocated
222
+ * @param _indexer The address of the indexer
223
+ * @param _delegationRatio The delegation ratio to consider when locking tokens
224
+ * @return True if the allocation is over-allocated, false otherwise
225
+ */
226
+ function isOverAllocated (
227
+ mapping (address indexer = > uint256 tokens ) storage allocationProvisionTracker ,
228
+ IHorizonStaking graphStaking ,
229
+ address _indexer ,
230
+ uint32 _delegationRatio
231
+ ) external view returns (bool ) {
232
+ return _isOverAllocated (allocationProvisionTracker, graphStaking, _indexer, _delegationRatio);
233
+ }
234
+
235
+ function _closeAllocation (
236
+ mapping (address allocationId = > Allocation.State allocation ) storage _allocations ,
237
+ mapping (address indexer = > uint256 tokens ) storage allocationProvisionTracker ,
238
+ mapping (bytes32 subgraphDeploymentId = > uint256 tokens ) storage _subgraphAllocatedTokens ,
239
+ IRewardsManager graphRewardsManager ,
240
+ address _allocationId ,
241
+ bool _forceClosed
242
+ ) private {
243
+ Allocation.State memory allocation = _allocations.get (_allocationId);
244
+
245
+ // Take rewards snapshot to prevent other allos from counting tokens from this allo
246
+ _allocations.snapshotRewards (
247
+ _allocationId,
248
+ graphRewardsManager.onSubgraphAllocationUpdate (allocation.subgraphDeploymentId)
249
+ );
250
+
251
+ _allocations.close (_allocationId);
252
+ allocationProvisionTracker.release (allocation.indexer, allocation.tokens);
253
+
254
+ // Update total allocated tokens for the subgraph deployment
255
+ _subgraphAllocatedTokens[allocation.subgraphDeploymentId] =
256
+ _subgraphAllocatedTokens[allocation.subgraphDeploymentId] -
257
+ allocation.tokens;
258
+
259
+ emit AllocationManager.AllocationClosed (
260
+ allocation.indexer,
261
+ _allocationId,
262
+ allocation.subgraphDeploymentId,
263
+ allocation.tokens,
264
+ _forceClosed
265
+ );
266
+ }
267
+
268
+ function _isOverAllocated (
269
+ mapping (address indexer = > uint256 tokens ) storage allocationProvisionTracker ,
270
+ IHorizonStaking graphStaking ,
271
+ address _indexer ,
272
+ uint32 _delegationRatio
273
+ ) private view returns (bool ) {
274
+ return ! allocationProvisionTracker.check (graphStaking, _indexer, _delegationRatio);
275
+ }
276
+
88
277
/**
89
278
* @notice Verifies ownership of an allocation id by verifying an EIP712 allocation proof
90
279
* @dev Requirements:
0 commit comments