Skip to content

Commit 01db6e5

Browse files
committed
feat: dont issue rewards to allos less than one epoch old
Signed-off-by: Tomás Migone <[email protected]>
1 parent 51976ec commit 01db6e5

File tree

6 files changed

+87
-31
lines changed

6 files changed

+87
-31
lines changed

packages/subgraph-service/contracts/libraries/Allocation.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ library Allocation {
3030
uint256 accRewardsPerAllocatedToken;
3131
// Accumulated rewards that are pending to be claimed due allocation resize
3232
uint256 accRewardsPending;
33+
// Epoch when the allocation was created
34+
uint256 createdAtEpoch;
3335
}
3436

3537
/**
@@ -68,7 +70,8 @@ library Allocation {
6870
address allocationId,
6971
bytes32 subgraphDeploymentId,
7072
uint256 tokens,
71-
uint256 accRewardsPerAllocatedToken
73+
uint256 accRewardsPerAllocatedToken,
74+
uint256 createdAtEpoch
7275
) internal returns (State memory) {
7376
require(!self[allocationId].exists(), AllocationAlreadyExists(allocationId));
7477

@@ -80,7 +83,8 @@ library Allocation {
8083
closedAt: 0,
8184
lastPOIPresentedAt: 0,
8285
accRewardsPerAllocatedToken: accRewardsPerAllocatedToken,
83-
accRewardsPending: 0
86+
accRewardsPending: 0,
87+
createdAtEpoch: createdAtEpoch
8488
});
8589

8690
self[allocationId] = allocation;

packages/subgraph-service/contracts/utilities/AllocationManager.sol

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca
4040
* @param allocationId The id of the allocation
4141
* @param subgraphDeploymentId The id of the subgraph deployment
4242
* @param tokens The amount of tokens allocated
43+
* @param currentEpoch The current epoch
4344
*/
4445
event AllocationCreated(
4546
address indexed indexer,
4647
address indexed allocationId,
4748
bytes32 indexed subgraphDeploymentId,
48-
uint256 tokens
49+
uint256 tokens,
50+
uint256 currentEpoch
4951
);
5052

5153
/**
@@ -157,17 +159,14 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca
157159
// solhint-disable-next-line func-name-mixedcase
158160
function __AllocationManager_init(string memory _name, string memory _version) internal onlyInitializing {
159161
__EIP712_init(_name, _version);
160-
__AllocationManager_init_unchained(_name, _version);
162+
__AllocationManager_init_unchained();
161163
}
162164

163165
/**
164166
* @notice Initializes the contract
165167
*/
166168
// solhint-disable-next-line func-name-mixedcase
167-
function __AllocationManager_init_unchained(
168-
string memory _name,
169-
string memory _version
170-
) internal onlyInitializing {}
169+
function __AllocationManager_init_unchained() internal onlyInitializing {}
171170

172171
/**
173172
* @notice Imports a legacy allocation id into the subgraph service
@@ -213,12 +212,15 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca
213212
// Ensure allocation id is not reused
214213
// need to check both subgraph service (on allocations.create()) and legacy allocations
215214
_legacyAllocations.revertIfExists(_graphStaking(), _allocationId);
215+
216+
uint256 currentEpoch = _graphEpochManager().currentEpoch();
216217
Allocation.State memory allocation = _allocations.create(
217218
_indexer,
218219
_allocationId,
219220
_subgraphDeploymentId,
220221
_tokens,
221-
_graphRewardsManager().onSubgraphAllocationUpdate(_subgraphDeploymentId)
222+
_graphRewardsManager().onSubgraphAllocationUpdate(_subgraphDeploymentId),
223+
currentEpoch
222224
);
223225

224226
// Check that the indexer has enough tokens available
@@ -230,23 +232,28 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca
230232
_subgraphAllocatedTokens[allocation.subgraphDeploymentId] +
231233
allocation.tokens;
232234

233-
emit AllocationCreated(_indexer, _allocationId, _subgraphDeploymentId, allocation.tokens);
235+
emit AllocationCreated(_indexer, _allocationId, _subgraphDeploymentId, allocation.tokens, currentEpoch);
234236
return allocation;
235237
}
236238

237239
/**
238240
* @notice Present a POI to collect indexing rewards for an allocation
239241
* This function will mint indexing rewards using the {RewardsManager} and distribute them to the indexer and delegators.
240242
*
241-
* To qualify for indexing rewards:
243+
* Conditions to qualify for indexing rewards:
242244
* - POI must be non-zero
243245
* - POI must not be stale, i.e: older than `maxPOIStaleness`
244246
* - allocation must not be altruistic (allocated tokens = 0)
247+
* - allocation must be open for at least one epoch
245248
*
246249
* Note that indexers are required to periodically (at most every `maxPOIStaleness`) present POIs to collect rewards.
247250
* Rewards will not be issued to stale POIs, which means that indexers are advised to present a zero POI if they are
248251
* unable to present a valid one to prevent being locked out of future rewards.
249252
*
253+
* Note on allocation duration restriction: this is required to ensure that non protocol chains have a valid block number for
254+
* which to calculate POIs. EBO posts once per epoch typically at each epoch change, so we restrict rewards to allocations
255+
* that have gone through at least one epoch change.
256+
*
250257
* Emits a {IndexingRewardsCollected} event.
251258
*
252259
* @param _allocationId The id of the allocation to collect rewards for
@@ -260,10 +267,12 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca
260267
Allocation.State memory allocation = _allocations.get(_allocationId);
261268
require(allocation.isOpen(), AllocationManagerAllocationClosed(_allocationId));
262269

270+
uint256 currentEpoch = _graphEpochManager().currentEpoch();
271+
263272
// Mint indexing rewards if all conditions are met
264273
uint256 tokensRewards = (!allocation.isStale(maxPOIStaleness) &&
265274
!allocation.isAltruistic() &&
266-
_poi != bytes32(0))
275+
_poi != bytes32(0)) && currentEpoch > allocation.createdAtEpoch
267276
? _graphRewardsManager().takeRewards(_allocationId)
268277
: 0;
269278

@@ -318,7 +327,7 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca
318327
tokensIndexerRewards,
319328
tokensDelegationRewards,
320329
_poi,
321-
_graphEpochManager().currentEpoch()
330+
currentEpoch
322331
);
323332

324333
// Check if the indexer is over-allocated and close the allocation if necessary

packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { ISubgraphService } from "../../contracts/interfaces/ISubgraphService.so
1111
import { HorizonStakingSharedTest } from "./HorizonStakingShared.t.sol";
1212

1313
abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
14-
1514
/*
1615
* VARIABLES
1716
*/
@@ -24,7 +23,7 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
2423
* MODIFIERS
2524
*/
2625

27-
modifier useIndexer {
26+
modifier useIndexer() {
2827
vm.startPrank(users.indexer);
2928
_;
3029
vm.stopPrank();
@@ -35,15 +34,20 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
3534
vm.assume(tokens < 10_000_000_000 ether);
3635
_createProvision(users.indexer, tokens, maxSlashingPercentage, disputePeriod);
3736
_register(users.indexer, abi.encode("url", "geoHash", address(0)));
38-
bytes memory data = _createSubgraphAllocationData(users.indexer, subgraphDeployment, allocationIDPrivateKey, tokens);
37+
bytes memory data = _createSubgraphAllocationData(
38+
users.indexer,
39+
subgraphDeployment,
40+
allocationIDPrivateKey,
41+
tokens
42+
);
3943
_startService(users.indexer, data);
4044
_;
4145
}
4246

4347
modifier useDelegation(uint256 tokens) {
4448
vm.assume(tokens > MIN_DELEGATION);
4549
vm.assume(tokens < 10_000_000_000 ether);
46-
(, address msgSender,) = vm.readCallers();
50+
(, address msgSender, ) = vm.readCallers();
4751
resetPrank(users.delegator);
4852
token.approve(address(staking), tokens);
4953
_delegate(users.indexer, address(subgraphService), tokens, 0);
@@ -72,10 +76,7 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
7276
);
7377

7478
vm.expectEmit(address(subgraphService));
75-
emit IDataService.ServiceProviderRegistered(
76-
_indexer,
77-
_data
78-
);
79+
emit IDataService.ServiceProviderRegistered(_indexer, _data);
7980

8081
// Register indexer
8182
subgraphService.register(_indexer, _data);
@@ -91,14 +92,22 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
9192
}
9293

9394
function _startService(address _indexer, bytes memory _data) internal {
94-
(bytes32 subgraphDeploymentId, uint256 tokens, address allocationId,) = abi.decode(
95+
(bytes32 subgraphDeploymentId, uint256 tokens, address allocationId, ) = abi.decode(
9596
_data,
9697
(bytes32, uint256, address, bytes)
9798
);
9899
uint256 previousSubgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens(subgraphDeploymentId);
100+
uint256 currentEpoch = epochManager.currentEpoch();
99101

100102
vm.expectEmit(address(subgraphService));
101103
emit IDataService.ServiceStarted(_indexer, _data);
104+
emit AllocationManager.AllocationCreated(
105+
_indexer,
106+
allocationId,
107+
subgraphDeploymentId,
108+
tokens,
109+
currentEpoch
110+
);
102111

103112
// TODO: improve this
104113
uint256 accRewardsPerAllocatedToken = 0;
@@ -116,9 +125,10 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
116125
assertEq(allocation.subgraphDeploymentId, subgraphDeploymentId);
117126
assertEq(allocation.createdAt, block.timestamp);
118127
assertEq(allocation.closedAt, 0);
119-
assertEq(allocation.lastPOIPresentedAt, 0);
128+
assertEq(allocation.lastPOIPresentedAt, 0);
120129
assertEq(allocation.accRewardsPerAllocatedToken, accRewardsPerAllocatedToken);
121130
assertEq(allocation.accRewardsPending, 0);
131+
assertEq(allocation.createdAtEpoch, currentEpoch);
122132

123133
// Check subgraph deployment allocated tokens
124134
uint256 subgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens(subgraphDeploymentId);
@@ -130,10 +140,17 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
130140
assertTrue(subgraphService.isActiveAllocation(allocationId));
131141

132142
Allocation.State memory allocation = subgraphService.getAllocation(allocationId);
133-
uint256 previousSubgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens(allocation.subgraphDeploymentId);
134-
143+
uint256 previousSubgraphAllocatedTokens = subgraphService.getSubgraphAllocatedTokens(
144+
allocation.subgraphDeploymentId
145+
);
146+
135147
vm.expectEmit(address(subgraphService));
136-
emit AllocationManager.AllocationClosed(_indexer, allocationId, allocation.subgraphDeploymentId, allocation.tokens);
148+
emit AllocationManager.AllocationClosed(
149+
_indexer,
150+
allocationId,
151+
allocation.subgraphDeploymentId,
152+
allocation.tokens
153+
);
137154
emit IDataService.ServiceStopped(_indexer, _data);
138155

139156
// stop allocation
@@ -178,10 +195,6 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
178195

179196
function _getIndexer(address _indexer) private view returns (ISubgraphService.Indexer memory) {
180197
(uint256 registeredAt, string memory url, string memory geoHash) = subgraphService.indexers(_indexer);
181-
return ISubgraphService.Indexer({
182-
registeredAt: registeredAt,
183-
url: url,
184-
geoHash: geoHash
185-
});
198+
return ISubgraphService.Indexer({ registeredAt: registeredAt, url: url, geoHash: geoHash });
186199
}
187200
}

packages/subgraph-service/test/subgraphService/SubgraphService.t.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ contract SubgraphServiceTest is SubgraphServiceSharedTest {
302302
// Calculate the payment collected by the indexer for this transaction
303303
paymentCollected = accRewardsPerTokens - allocation.accRewardsPerAllocatedToken;
304304

305+
uint256 currentEpoch = epochManager.currentEpoch();
306+
paymentCollected = currentEpoch > allocation.createdAtEpoch ? paymentCollected : 0;
307+
305308
uint256 delegatorCut = staking.getDelegationFeeCut(
306309
allocation.indexer,
307310
address(subgraphService),

packages/subgraph-service/test/subgraphService/allocation/resize.t.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ contract SubgraphServiceAllocationResizeTest is SubgraphServiceTest {
2929
vm.assume(resizeTokens != tokens);
3030

3131
mint(users.indexer, resizeTokens);
32+
33+
// skip time to ensure allocation gets rewards
34+
vm.roll(block.number + EPOCH_LENGTH);
35+
3236
IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes.IndexingRewards;
3337
bytes memory data = abi.encode(allocationID, bytes32("POI1"));
3438
_collect(users.indexer, paymentType, data);

packages/subgraph-service/test/subgraphService/collect/indexing/indexing.t.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ contract SubgraphServiceCollectIndexingTest is SubgraphServiceTest {
1919
) public useIndexer useAllocation(tokens) {
2020
IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes.IndexingRewards;
2121
bytes memory data = abi.encode(allocationID, bytes32("POI"));
22+
23+
// skip time to ensure allocation gets rewards
24+
vm.roll(block.number + EPOCH_LENGTH);
25+
2226
_collect(users.indexer, paymentType, data);
2327
}
2428

@@ -34,6 +38,11 @@ contract SubgraphServiceCollectIndexingTest is SubgraphServiceTest {
3438
IGraphPayments.PaymentTypes.IndexingRewards,
3539
delegationFeeCut
3640
);
41+
42+
43+
// skip time to ensure allocation gets rewards
44+
vm.roll(block.number + EPOCH_LENGTH);
45+
3746
IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes.IndexingRewards;
3847
bytes memory data = abi.encode(allocationID, bytes32("POI"));
3948
_collect(users.indexer, paymentType, data);
@@ -54,6 +63,10 @@ contract SubgraphServiceCollectIndexingTest is SubgraphServiceTest {
5463
// Undelegate
5564
resetPrank(users.delegator);
5665
staking.undelegate(users.indexer, address(subgraphService), delegationTokens);
66+
67+
// skip time to ensure allocation gets rewards
68+
vm.roll(block.number + EPOCH_LENGTH);
69+
5770
resetPrank(users.indexer);
5871
IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes.IndexingRewards;
5972
bytes memory data = abi.encode(allocationID, bytes32("POI"));
@@ -63,6 +76,9 @@ contract SubgraphServiceCollectIndexingTest is SubgraphServiceTest {
6376
function test_SubgraphService_Collect_Indexing_RewardsDestination(
6477
uint256 tokens
6578
) public useIndexer useAllocation(tokens) useRewardsDestination {
79+
// skip time to ensure allocation gets rewards
80+
vm.roll(block.number + EPOCH_LENGTH);
81+
6682
IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes.IndexingRewards;
6783
bytes memory data = abi.encode(allocationID, bytes32("POI"));
6884
_collect(users.indexer, paymentType, data);
@@ -121,6 +137,9 @@ contract SubgraphServiceCollectIndexingTest is SubgraphServiceTest {
121137
// thaw some tokens to become over allocated
122138
staking.thaw(users.indexer, address(subgraphService), tokens / 2);
123139

140+
// skip time to ensure allocation gets rewards
141+
vm.roll(block.number + EPOCH_LENGTH);
142+
124143
// this collection should close the allocation
125144
IGraphPayments.PaymentTypes paymentType = IGraphPayments.PaymentTypes.IndexingRewards;
126145
bytes memory collectData = abi.encode(allocationID, bytes32("POI"));
@@ -135,6 +154,10 @@ contract SubgraphServiceCollectIndexingTest is SubgraphServiceTest {
135154
address newIndexer = makeAddr("newIndexer");
136155
_createAndStartAllocation(newIndexer, tokens);
137156
bytes memory data = abi.encode(allocationID, bytes32("POI"));
157+
158+
// skip time to ensure allocation gets rewards
159+
vm.roll(block.number + EPOCH_LENGTH);
160+
138161
// Attempt to collect from other indexer's allocation
139162
vm.expectRevert(
140163
abi.encodeWithSelector(ISubgraphService.SubgraphServiceAllocationNotAuthorized.selector, newIndexer, allocationID)

0 commit comments

Comments
 (0)