@@ -135,7 +135,7 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
135
135
* An amount of `tokens` get unallocated from `subgraphDeploymentID`.
136
136
* The `effectiveAllocation` are the tokens allocated from creation to closing.
137
137
* 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.
139
139
*/
140
140
event AllocationClosed (
141
141
address indexed indexer ,
@@ -146,7 +146,7 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
146
146
uint256 effectiveAllocation ,
147
147
address sender ,
148
148
bytes32 poi ,
149
- bool isDelegator
149
+ bool isPublic
150
150
);
151
151
152
152
/**
@@ -1106,9 +1106,6 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1106
1106
) private {
1107
1107
require (_isAuth (_indexer), "!auth " );
1108
1108
1109
- // Only allocations with a non-zero token amount are allowed
1110
- require (_tokens > 0 , "!tokens " );
1111
-
1112
1109
// Check allocation
1113
1110
require (_allocationID != address (0 ), "!alloc " );
1114
1111
require (_getAllocationState (_allocationID) == AllocationState.Null, "!null " );
@@ -1119,8 +1116,16 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1119
1116
bytes32 digest = ECDSA.toEthSignedMessageHash (messageHash);
1120
1117
require (ECDSA.recover (digest, _proof) == _allocationID, "!proof " );
1121
1118
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
+ }
1124
1129
1125
1130
// Creates an allocation
1126
1131
// Allocation identifiers are not reused
@@ -1133,18 +1138,23 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1133
1138
0 , // closedAtEpoch
1134
1139
0 , // Initialize collected fees
1135
1140
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
1137
1142
);
1138
1143
allocations[_allocationID] = alloc;
1139
1144
1140
- // Mark allocated tokens as used
1141
- stakes[_indexer].allocate (alloc.tokens);
1145
+ // -- Rewards Distribution --
1142
1146
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
+ }
1148
1158
1149
1159
emit AllocationCreated (
1150
1160
_indexer,
@@ -1175,24 +1185,26 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1175
1185
require (epochs > 0 , "<epochs " );
1176
1186
1177
1187
// 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
1179
1191
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 ) {
1183
1193
require (isIndexer, "!auth " );
1184
1194
}
1185
1195
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
+
1186
1202
// Calculate effective allocation for the amount of epochs it remained allocated
1187
1203
alloc.effectiveAllocation = _getEffectiveAllocation (
1188
1204
maxAllocationEpochs,
1189
1205
alloc.tokens,
1190
1206
epochs
1191
1207
);
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
1208
allocations[_allocationID].effectiveAllocation = alloc.effectiveAllocation;
1197
1209
1198
1210
// Account collected fees and effective allocation in rebate pool for the epoch
@@ -1202,21 +1214,26 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1202
1214
}
1203
1215
rebatePool.addToPool (alloc.collectedFees, alloc.effectiveAllocation);
1204
1216
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 --
1211
1218
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
+ }
1214
1227
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
+ }
1220
1237
1221
1238
emit AllocationClosed (
1222
1239
alloc.indexer,
@@ -1258,8 +1275,8 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1258
1275
// Purge allocation data except for:
1259
1276
// - indexer: used in disputes and to avoid reusing an allocationID
1260
1277
// - 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
1263
1280
allocations[_allocationID].closedAtEpoch = 0 ;
1264
1281
allocations[_allocationID].collectedFees = 0 ;
1265
1282
allocations[_allocationID].effectiveAllocation = 0 ;
@@ -1529,7 +1546,7 @@ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall {
1529
1546
if (alloc.indexer == address (0 )) {
1530
1547
return AllocationState.Null;
1531
1548
}
1532
- if (alloc.tokens == 0 ) {
1549
+ if (alloc.createdAtEpoch == 0 ) {
1533
1550
return AllocationState.Claimed;
1534
1551
}
1535
1552
0 commit comments