22pragma solidity 0.8.27 ;
33
44import { 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 " ;
67import { 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 " ;
712import { ProvisionTracker } from "@graphprotocol/horizon/contracts/data-service/libraries/ProvisionTracker.sol " ;
13+ import { PPMMath } from "@graphprotocol/horizon/contracts/libraries/PPMMath.sol " ;
814
915import { Allocation } from "../libraries/Allocation.sol " ;
1016import { LegacyAllocation } from "../libraries/LegacyAllocation.sol " ;
@@ -13,11 +19,10 @@ import { AllocationManager } from "../utilities/AllocationManager.sol";
1319library AllocationManagerLib {
1420 using ProvisionTracker for mapping (address => uint256 );
1521 using Allocation for mapping (address => Allocation.State);
22+ using Allocation for Allocation.State;
1623 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;
2126
2227 struct AllocateParams {
2328 uint256 currentEpoch;
@@ -32,6 +37,23 @@ library AllocationManagerLib {
3237 uint32 _delegationRatio;
3338 }
3439
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+
3557 /**
3658 * @notice Create an allocation
3759 * @dev The `_allocationProof` is a 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationId)`
@@ -85,6 +107,173 @@ library AllocationManagerLib {
85107 );
86108 }
87109
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+
88277 /**
89278 * @notice Verifies ownership of an allocation id by verifying an EIP712 allocation proof
90279 * @dev Requirements:
0 commit comments