@@ -1115,9 +1115,13 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking {
1115
1115
bytes32 digest = ECDSA.toEthSignedMessageHash (messageHash);
1116
1116
require (ECDSA.recover (digest, _proof) == _allocationID, "!proof " );
1117
1117
1118
- // Needs to have free capacity not used for other purposes to allocate
1119
1118
if (_tokens > 0 ) {
1119
+ // Needs to have free capacity not used for other purposes to allocate
1120
1120
require (getIndexerCapacity (_indexer) >= _tokens, "!capacity " );
1121
+ } else {
1122
+ // Allocating zero-tokens still needs to have stake
1123
+ // Minimum indexer stake is managed by stake/unstake
1124
+ require (stakes[_indexer].tokensSecureStake () > 0 , "!stake " );
1121
1125
}
1122
1126
1123
1127
// Creates an allocation
@@ -1131,10 +1135,13 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking {
1131
1135
0 , // closedAtEpoch
1132
1136
0 , // Initialize collected fees
1133
1137
0 , // Initialize effective allocation
1134
- _updateRewards (_subgraphDeploymentID) // Initialize accumulated rewards per stake allocated
1138
+ (_tokens > 0 ) ? _updateRewards (_subgraphDeploymentID) : 0 // Initialize accumulated rewards per stake allocated
1135
1139
);
1136
1140
allocations[_allocationID] = alloc;
1137
1141
1142
+ // -- Rewards Distribution --
1143
+
1144
+ // Process non-zero-allocation rewards tracking
1138
1145
if (_tokens > 0 ) {
1139
1146
// Mark allocated tokens as used
1140
1147
stakes[_indexer].allocate (alloc.tokens);
@@ -1175,22 +1182,26 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking {
1175
1182
require (epochs > 0 , "<epochs " );
1176
1183
1177
1184
// Indexer or operator can close an allocation
1178
- // Delegators are also allowed but only after maxAllocationEpochs passed
1185
+ // Anyone is allowed to close ONLY under two concurrent conditions
1186
+ // - After maxAllocationEpochs passed
1187
+ // - When the allocation is for non-zero amount of tokens
1179
1188
bool isIndexer = _isAuth (alloc.indexer);
1180
- if (epochs <= maxAllocationEpochs) {
1189
+ if (epochs <= maxAllocationEpochs || alloc.tokens == 0 ) {
1181
1190
require (isIndexer, "!auth " );
1182
1191
}
1183
1192
1193
+ // Close the allocation and start counting a period to settle remaining payments from
1194
+ // state channels.
1195
+ allocations[_allocationID].closedAtEpoch = alloc.closedAtEpoch;
1196
+
1197
+ // -- Rebate Pool --
1198
+
1184
1199
// Calculate effective allocation for the amount of epochs it remained allocated
1185
1200
alloc.effectiveAllocation = _getEffectiveAllocation (
1186
1201
maxAllocationEpochs,
1187
1202
alloc.tokens,
1188
1203
epochs
1189
1204
);
1190
-
1191
- // Close the allocation and start counting a period to settle remaining payments from
1192
- // state channels.
1193
- allocations[_allocationID].closedAtEpoch = alloc.closedAtEpoch;
1194
1205
allocations[_allocationID].effectiveAllocation = alloc.effectiveAllocation;
1195
1206
1196
1207
// Account collected fees and effective allocation in rebate pool for the epoch
@@ -1200,14 +1211,17 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking {
1200
1211
}
1201
1212
rebatePool.addToPool (alloc.collectedFees, alloc.effectiveAllocation);
1202
1213
1203
- // Distribute rewards if proof of indexing was presented by the indexer or operator
1204
- if (isIndexer && _poi != 0 ) {
1205
- _distributeRewards (_allocationID, alloc.indexer);
1206
- } else {
1207
- _updateRewards (alloc.subgraphDeploymentID);
1208
- }
1214
+ // -- Rewards Distribution --
1209
1215
1216
+ // Process non-zero-allocation rewards tracking
1210
1217
if (alloc.tokens > 0 ) {
1218
+ // Distribute rewards if proof of indexing was presented by the indexer or operator
1219
+ if (isIndexer && _poi != 0 ) {
1220
+ _distributeRewards (_allocationID, alloc.indexer);
1221
+ } else {
1222
+ _updateRewards (alloc.subgraphDeploymentID);
1223
+ }
1224
+
1211
1225
// Free allocated tokens from use
1212
1226
stakes[alloc.indexer].unallocate (alloc.tokens);
1213
1227
0 commit comments