@@ -135,7 +135,7 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
135135 * An amount of `tokens` get unallocated from `subgraphDeploymentID`.
136136 * The `effectiveAllocation` are the tokens allocated from creation to closing.
137137 * This event also emits the POI (proof of indexing) submitted by the indexer.
138- * `isDelegator ` is true if the sender was one of the indexer's delegators .
138+ * `isPublic ` is true if the sender was someone other than the indexer.
139139 */
140140 event AllocationClosed (
141141 address indexed indexer ,
@@ -146,7 +146,7 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
146146 uint256 effectiveAllocation ,
147147 address sender ,
148148 bytes32 poi ,
149- bool isDelegator
149+ bool isPublic
150150 );
151151
152152 /**
@@ -1106,9 +1106,6 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
11061106 ) private {
11071107 require (_isAuth (_indexer), "!auth " );
11081108
1109- // Only allocations with a non-zero token amount are allowed
1110- require (_tokens > 0 , "!tokens " );
1111-
11121109 // Check allocation
11131110 require (_allocationID != address (0 ), "!alloc " );
11141111 require (_getAllocationState (_allocationID) == AllocationState.Null, "!null " );
@@ -1119,8 +1116,16 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
11191116 bytes32 digest = ECDSA.toEthSignedMessageHash (messageHash);
11201117 require (ECDSA.recover (digest, _proof) == _allocationID, "!proof " );
11211118
1122- // Needs to have free capacity not used for other purposes to allocate
1123- require (getIndexerCapacity (_indexer) >= _tokens, "!capacity " );
1119+ if (_tokens > 0 ) {
1120+ // Needs to have free capacity not used for other purposes to allocate
1121+ require (getIndexerCapacity (_indexer) >= _tokens, "!capacity " );
1122+ } else {
1123+ // Allocating zero-tokens still needs to comply with stake requirements
1124+ require (
1125+ stakes[_indexer].tokensSecureStake () >= minimumIndexerStake,
1126+ "!minimumIndexerStake "
1127+ );
1128+ }
11241129
11251130 // Creates an allocation
11261131 // Allocation identifiers are not reused
@@ -1133,18 +1138,23 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
11331138 0 , // closedAtEpoch
11341139 0 , // Initialize collected fees
11351140 0 , // Initialize effective allocation
1136- _updateRewards (_subgraphDeploymentID) // Initialize accumulated rewards per stake allocated
1141+ (_tokens > 0 ) ? _updateRewards (_subgraphDeploymentID) : 0 // Initialize accumulated rewards per stake allocated
11371142 );
11381143 allocations[_allocationID] = alloc;
11391144
1140- // Mark allocated tokens as used
1141- stakes[_indexer].allocate (alloc.tokens);
1145+ // -- Rewards Distribution --
11421146
1143- // Track total allocations per subgraph
1144- // Used for rewards calculations
1145- subgraphAllocations[alloc.subgraphDeploymentID] = subgraphAllocations[
1146- alloc.subgraphDeploymentID
1147- ].add (alloc.tokens);
1147+ // Process non-zero-allocation rewards tracking
1148+ if (_tokens > 0 ) {
1149+ // Mark allocated tokens as used
1150+ stakes[_indexer].allocate (alloc.tokens);
1151+
1152+ // Track total allocations per subgraph
1153+ // Used for rewards calculations
1154+ subgraphAllocations[alloc.subgraphDeploymentID] = subgraphAllocations[
1155+ alloc.subgraphDeploymentID
1156+ ].add (alloc.tokens);
1157+ }
11481158
11491159 emit AllocationCreated (
11501160 _indexer,
@@ -1175,24 +1185,26 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
11751185 require (epochs > 0 , "<epochs " );
11761186
11771187 // Indexer or operator can close an allocation
1178- // Delegators are also allowed but only after maxAllocationEpochs passed
1188+ // Anyone is allowed to close ONLY under two concurrent conditions
1189+ // - After maxAllocationEpochs passed
1190+ // - When the allocation is for non-zero amount of tokens
11791191 bool isIndexer = _isAuth (alloc.indexer);
1180- if (epochs > maxAllocationEpochs) {
1181- require (isIndexer || isDelegator (alloc.indexer, msg .sender ), "!auth-or-del " );
1182- } else {
1192+ if (epochs <= maxAllocationEpochs || alloc.tokens == 0 ) {
11831193 require (isIndexer, "!auth " );
11841194 }
11851195
1196+ // Close the allocation and start counting a period to settle remaining payments from
1197+ // state channels.
1198+ allocations[_allocationID].closedAtEpoch = alloc.closedAtEpoch;
1199+
1200+ // -- Rebate Pool --
1201+
11861202 // Calculate effective allocation for the amount of epochs it remained allocated
11871203 alloc.effectiveAllocation = _getEffectiveAllocation (
11881204 maxAllocationEpochs,
11891205 alloc.tokens,
11901206 epochs
11911207 );
1192-
1193- // Close the allocation and start counting a period to settle remaining payments from
1194- // state channels.
1195- allocations[_allocationID].closedAtEpoch = alloc.closedAtEpoch;
11961208 allocations[_allocationID].effectiveAllocation = alloc.effectiveAllocation;
11971209
11981210 // Account collected fees and effective allocation in rebate pool for the epoch
@@ -1202,21 +1214,26 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
12021214 }
12031215 rebatePool.addToPool (alloc.collectedFees, alloc.effectiveAllocation);
12041216
1205- // Distribute rewards if proof of indexing was presented by the indexer or operator
1206- if (isIndexer && _poi != 0 ) {
1207- _distributeRewards (_allocationID, alloc.indexer);
1208- } else {
1209- _updateRewards (alloc.subgraphDeploymentID);
1210- }
1217+ // -- Rewards Distribution --
12111218
1212- // Free allocated tokens from use
1213- stakes[alloc.indexer].unallocate (alloc.tokens);
1219+ // Process non-zero-allocation rewards tracking
1220+ if (alloc.tokens > 0 ) {
1221+ // Distribute rewards if proof of indexing was presented by the indexer or operator
1222+ if (isIndexer && _poi != 0 ) {
1223+ _distributeRewards (_allocationID, alloc.indexer);
1224+ } else {
1225+ _updateRewards (alloc.subgraphDeploymentID);
1226+ }
12141227
1215- // Track total allocations per subgraph
1216- // Used for rewards calculations
1217- subgraphAllocations[alloc.subgraphDeploymentID] = subgraphAllocations[
1218- alloc.subgraphDeploymentID
1219- ].sub (alloc.tokens);
1228+ // Free allocated tokens from use
1229+ stakes[alloc.indexer].unallocate (alloc.tokens);
1230+
1231+ // Track total allocations per subgraph
1232+ // Used for rewards calculations
1233+ subgraphAllocations[alloc.subgraphDeploymentID] = subgraphAllocations[
1234+ alloc.subgraphDeploymentID
1235+ ].sub (alloc.tokens);
1236+ }
12201237
12211238 emit AllocationClosed (
12221239 alloc.indexer,
@@ -1258,8 +1275,8 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
12581275 // Purge allocation data except for:
12591276 // - indexer: used in disputes and to avoid reusing an allocationID
12601277 // - subgraphDeploymentID: used in disputes
1261- allocations[_allocationID].tokens = 0 ; // This avoid collect(), close() and claim() to be called
1262- allocations[_allocationID].createdAtEpoch = 0 ;
1278+ allocations[_allocationID].tokens = 0 ;
1279+ allocations[_allocationID].createdAtEpoch = 0 ; // This avoid collect(), close() and claim() to be called
12631280 allocations[_allocationID].closedAtEpoch = 0 ;
12641281 allocations[_allocationID].collectedFees = 0 ;
12651282 allocations[_allocationID].effectiveAllocation = 0 ;
@@ -1529,7 +1546,7 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
15291546 if (alloc.indexer == address (0 )) {
15301547 return AllocationState.Null;
15311548 }
1532- if (alloc.tokens == 0 ) {
1549+ if (alloc.createdAtEpoch == 0 ) {
15331550 return AllocationState.Claimed;
15341551 }
15351552
0 commit comments