@bananapus/distributor-v6 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,9 +60,12 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
60
60
 
61
61
  /// @notice The next reward round a staker has not yet claimed.
62
62
  /// @custom:param hook The IVotes token whose stakers are claiming.
63
+ /// @custom:param groupId The reward group (0 = the default group).
63
64
  /// @custom:param tokenId The encoded staker address.
64
65
  /// @custom:param token The reward token being claimed.
65
- mapping(address hook => mapping(uint256 tokenId => mapping(IERC20 token => uint256))) public nextClaimRoundOf;
66
+ mapping(
67
+ address hook => mapping(uint256 groupId => mapping(uint256 tokenId => mapping(IERC20 token => uint256)))
68
+ ) public nextClaimRoundOf;
66
69
 
67
70
  //*********************************************************************//
68
71
  // -------------------------- constructor ---------------------------- //
@@ -121,8 +124,8 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
121
124
  }
122
125
 
123
126
  if (msg.value != 0) {
124
- // Assign native split proceeds to the current reward round for this IVotes hook.
125
- _recordRewardFunding({hook: hook, token: IERC20(context.token), amount: msg.value});
127
+ // Split-funded pots go to the default group (0); a split cannot carry a tier set.
128
+ _recordRewardFunding({hook: hook, groupId: 0, token: IERC20(context.token), amount: msg.value});
126
129
  }
127
130
  } else {
128
131
  // Validate that native ETH is not cross-booked under an ERC-20 token.
@@ -139,63 +142,15 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
139
142
  uint256 delta =
140
143
  _acceptErc20FundsFrom({token: IERC20(context.token), from: msg.sender, amount: context.amount});
141
144
 
142
- // Assign only the amount actually received to this round's reward pot.
143
- _recordRewardFunding({hook: hook, token: IERC20(context.token), amount: delta});
145
+ // Assign only the amount actually received to this round's reward pot (default group, 0).
146
+ _recordRewardFunding({hook: hook, groupId: 0, token: IERC20(context.token), amount: delta});
144
147
  }
145
148
  }
146
149
 
147
- /// @notice Snapshot this staker's past reward rounds and start vesting them now.
148
- /// @dev Unlike the shared distributor flow, token claims are owner-initiated. This prevents third parties from
149
- /// starting a staker's vesting clock before the staker actually claims.
150
- /// @param hook The IVotes token whose stakers are vesting.
151
- /// @param tokenIds The encoded staker addresses to claim for.
152
- /// @param tokens The reward tokens to begin vesting.
153
- function beginVesting(
154
- address hook,
155
- uint256[] calldata tokenIds,
156
- IERC20[] calldata tokens
157
- )
158
- external
159
- override(JBDistributor, IJBDistributor)
160
- {
161
- // Do not let reward-token callbacks mutate claim accounting during an inbound transfer.
162
- _requireNotAcceptingToken();
163
- if (tokenIds.length == 0) revert JBDistributor_EmptyTokenIds({tokenIdCount: tokenIds.length});
164
-
165
- // Token IDs encode staker addresses, so only the encoded staker can start their own vesting clock.
166
- _requireCanClaimTokenIds({hook: hook, tokenIds: tokenIds});
167
-
168
- // Materialize all unclaimed historical rewards into fresh vesting entries that start now.
169
- _claimPastRewards({hook: hook, tokenIds: tokenIds, tokens: tokens});
170
- }
171
-
172
- /// @notice Collect already-vested rewards and first start vesting any unclaimed past reward rounds.
173
- /// @param hook The IVotes token whose stakers are collecting.
174
- /// @param tokenIds The encoded staker addresses to collect for.
175
- /// @param tokens The reward tokens to collect.
176
- /// @param beneficiary The recipient of collected vested rewards.
177
- function collectVestedRewards(
178
- address hook,
179
- uint256[] calldata tokenIds,
180
- IERC20[] calldata tokens,
181
- address beneficiary
182
- )
183
- public
184
- override(JBDistributor, IJBDistributor)
185
- {
186
- // Do not let reward-token callbacks mutate claim accounting during an inbound transfer.
187
- _requireNotAcceptingToken();
188
- if (tokenIds.length == 0) revert JBDistributor_EmptyTokenIds({tokenIdCount: tokenIds.length});
189
-
190
- // Only the encoded staker can materialize and collect their token rewards.
191
- _requireCanClaimTokenIds({hook: hook, tokenIds: tokenIds});
192
-
193
- // Before collecting, bring the caller current by starting vesting for any past reward rounds.
194
- _claimPastRewards({hook: hook, tokenIds: tokenIds, tokens: tokens});
195
-
196
- // Release whatever portion of existing vesting entries has unlocked by this round.
197
- _unlockRewards({hook: hook, tokenIds: tokenIds, tokens: tokens, beneficiary: beneficiary, ownerClaim: true});
198
- }
150
+ // `beginVesting` and `collectVestedRewards` are provided by `JBDistributor`. Both distributors share the exact
151
+ // same flow (authorize -> materialize past rounds via `_claimPastRewards` -> optionally release unlocked), so the
152
+ // round-claim logic lives once in the base and dispatches to this contract's `_claimPastRewards` /
153
+ // `_requireCanClaimTokenIds` overrides below.
199
154
 
200
155
  //*********************************************************************//
201
156
  // -------------------------- public views --------------------------- //
@@ -217,14 +172,28 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
217
172
  /// @param hook The IVotes token whose stakers are claiming.
218
173
  /// @param tokenIds The encoded staker addresses to claim for.
219
174
  /// @param tokens The reward tokens to claim.
220
- function _claimPastRewards(address hook, uint256[] calldata tokenIds, IERC20[] calldata tokens) internal override {
175
+ function _claimPastRewards(
176
+ address hook,
177
+ uint256 groupId,
178
+ uint256[] calldata tokenIds,
179
+ IERC20[] calldata tokens
180
+ )
181
+ internal
182
+ override
183
+ {
221
184
  // Round 0 has no completed reward rounds behind it, so nothing can be claimed yet.
222
185
  uint256 round = currentRound();
223
186
  if (round == 0) return;
224
187
 
225
- // Current-round funding is excluded. It becomes claimable only after a later round starts.
226
- JBClaimContext memory ctx =
227
- JBClaimContext({hook: hook, lastClaimableRound: round - 1, vestingReleaseRound: round + VESTING_ROUNDS});
188
+ // Current-round funding is excluded. It becomes claimable only after a later round starts. Token distributors
189
+ // have no tier concept, so `tierIds` stays empty; the group only isolates reward storage.
190
+ JBClaimContext memory ctx = JBClaimContext({
191
+ hook: hook,
192
+ groupId: groupId,
193
+ tierIds: new uint256[](0),
194
+ lastClaimableRound: round - 1,
195
+ vestingReleaseRound: round + VESTING_ROUNDS
196
+ });
228
197
 
229
198
  // Process each reward token independently because each token has its own round funding and claim cursor.
230
199
  for (uint256 i; i < tokens.length;) {
@@ -267,7 +236,7 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
267
236
  returns (uint256 tokenAmount)
268
237
  {
269
238
  // Load this staker's cursor for the reward token. All earlier rounds have already been settled.
270
- uint256 nextClaimRound = nextClaimRoundOf[ctx.hook][tokenId][token];
239
+ uint256 nextClaimRound = nextClaimRoundOf[ctx.hook][ctx.groupId][tokenId][token];
271
240
 
272
241
  // If the cursor is already past the last completed round, this staker is current.
273
242
  if (nextClaimRound > ctx.lastClaimableRound) return 0;
@@ -275,6 +244,7 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
275
244
  // Sum this staker's pro-rata share from every unclaimed completed reward round.
276
245
  tokenAmount = _claimRewardsFor({
277
246
  hook: ctx.hook,
247
+ groupId: ctx.groupId,
278
248
  tokenId: tokenId,
279
249
  token: token,
280
250
  firstRound: nextClaimRound,
@@ -282,17 +252,18 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
282
252
  });
283
253
 
284
254
  // Advance the cursor even when the amount is zero, so empty or zero-stake rounds are not rescanned forever.
285
- nextClaimRoundOf[ctx.hook][tokenId][token] = ctx.lastClaimableRound + 1;
255
+ nextClaimRoundOf[ctx.hook][ctx.groupId][tokenId][token] = ctx.lastClaimableRound + 1;
286
256
  if (tokenAmount == 0) return 0;
287
257
 
288
258
  // All accumulated past rewards start a single fresh vesting schedule at the claim round.
289
- vestingDataOf[ctx.hook][tokenId][token].push(
259
+ vestingDataOf[ctx.hook][ctx.groupId][tokenId][token].push(
290
260
  JBVestingData({releaseRound: ctx.vestingReleaseRound, amount: tokenAmount, shareClaimed: 0})
291
261
  );
292
262
 
293
263
  emit Claimed({
294
264
  hook: ctx.hook,
295
265
  tokenId: tokenId,
266
+ groupId: ctx.groupId,
296
267
  token: token,
297
268
  amount: tokenAmount,
298
269
  vestingReleaseRound: ctx.vestingReleaseRound,
@@ -313,6 +284,7 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
313
284
  /// @return tokenAmount The cumulative unclaimed reward amount.
314
285
  function _claimRewardsFor(
315
286
  address hook,
287
+ uint256 groupId,
316
288
  uint256 tokenId,
317
289
  IERC20 token,
318
290
  uint256 firstRound,
@@ -323,14 +295,14 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
323
295
  {
324
296
  // Walk every unclaimed historical round. The caller bounds this to completed rounds only.
325
297
  for (uint256 rewardRoundNumber = firstRound; rewardRoundNumber <= lastRound;) {
326
- // Load this round's reward data for the hook and reward token.
327
- JBRewardRoundData storage rewardRound = rewardRoundOf[hook][token][rewardRoundNumber];
298
+ // Load this round's reward data for the hook, group, and reward token.
299
+ JBRewardRoundData storage rewardRound = rewardRoundOf[hook][groupId][token][rewardRoundNumber];
328
300
 
329
301
  // Skip rounds that never received funding.
330
302
  if (rewardRound.amount != 0) {
331
303
  // Expired rounds can no longer be claimed as-is; recycle their unclaimed remainder instead.
332
304
  if (_rewardRoundExpired(rewardRound)) {
333
- _recycleExpiredRewardRound({hook: hook, token: token, round: rewardRoundNumber});
305
+ _recycleExpiredRewardRound({hook: hook, groupId: groupId, token: token, round: rewardRoundNumber});
334
306
  } else if (rewardRound.totalStake != 0) {
335
307
  // Use the funding round's snapshot block, not the block at which the staker finally claims.
336
308
  uint256 tokenStakeAmount =
@@ -398,11 +370,23 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
398
370
  }
399
371
 
400
372
  /// @notice The total supply of votes at a specific block.
401
- /// @dev Uses `IVotes.getPastTotalSupply` for checkpointed lookups.
373
+ /// @dev Uses `IVotes.getPastTotalSupply` for checkpointed lookups. Token distributors have no tier concept, so the
374
+ /// group only isolates reward storage and does not change the stake denominator.
402
375
  /// @param hook The IVotes-compatible token contract.
376
+ /// @param groupId The reward group (unused for token distributors — kept for base-hook conformance).
403
377
  /// @param blockNumber The block number to get the total supply at.
404
378
  /// @return totalStakedAmount The total supply of votes at the given block.
405
- function _totalStake(address hook, uint256 blockNumber) internal view override returns (uint256 totalStakedAmount) {
379
+ function _totalStake(
380
+ address hook,
381
+ uint256 groupId,
382
+ uint256 blockNumber
383
+ )
384
+ internal
385
+ view
386
+ override
387
+ returns (uint256 totalStakedAmount)
388
+ {
389
+ groupId; // Silence unused variable warning — token distributors are group-agnostic in weight.
406
390
  totalStakedAmount = IVotes(hook).getPastTotalSupply(blockNumber);
407
391
  }
408
392
 
@@ -3,13 +3,152 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
5
5
  import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
6
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
7
 
7
8
  import {IJBDistributor} from "./IJBDistributor.sol";
8
9
 
9
10
  /// @notice A singleton distributor that distributes ERC-20 rewards to JB 721 NFT stakers with linear vesting.
10
11
  /// @dev Also implements `IJBSplitHook` to receive tokens from payout splits.
11
12
  /// @dev Projects configure their split with `hook = distributor` and `beneficiary = their721Hook`.
13
+ /// @dev Adds tier-scoped reward groups on top of the generic group plumbing: a group is a strictly-increasing tier
14
+ /// set, and only NFTs whose tier is in the set can claim that group's pot.
12
15
  interface IJB721Distributor is IJBDistributor, IJBSplitHook {
16
+ //*********************************************************************//
17
+ // ----------------------------- views ------------------------------- //
18
+ //*********************************************************************//
19
+
13
20
  /// @notice The JB directory used to verify terminal/controller callers.
14
21
  function DIRECTORY() external view returns (IJBDirectory);
22
+
23
+ /// @notice Calculate how much of the token has been claimed for the given tokenId in a tier-scoped group.
24
+ /// @param hook The hook the tokenId belongs to.
25
+ /// @param tierIds The strictly-increasing tier set defining the group.
26
+ /// @param tokenId The ID of the token to calculate the token amount for.
27
+ /// @param token The address of the token to check.
28
+ function claimedFor(
29
+ address hook,
30
+ uint256[] calldata tierIds,
31
+ uint256 tokenId,
32
+ IERC20 token
33
+ )
34
+ external
35
+ view
36
+ returns (uint256);
37
+
38
+ /// @notice Calculate how much of the token is currently ready to be collected for the given tokenId in a
39
+ /// tier-scoped group.
40
+ /// @param hook The hook the tokenId belongs to.
41
+ /// @param tierIds The strictly-increasing tier set defining the group.
42
+ /// @param tokenId The ID of the token to calculate the token amount for.
43
+ /// @param token The address of the token to check.
44
+ function collectableFor(
45
+ address hook,
46
+ uint256[] calldata tierIds,
47
+ uint256 tokenId,
48
+ IERC20 token
49
+ )
50
+ external
51
+ view
52
+ returns (uint256);
53
+
54
+ /// @notice The tier set that defines a reward group, recorded when the group is first funded.
55
+ /// @dev Empty for the all-tiers group (0).
56
+ /// @param hook The hook the group belongs to.
57
+ /// @param groupId The reward group.
58
+ /// @return tierIds The strictly-increasing tier set defining the group.
59
+ function tierIdsOf(address hook, uint256 groupId) external view returns (uint256[] memory tierIds);
60
+
61
+ //*********************************************************************//
62
+ // ---------------------------- transactions ------------------------- //
63
+ //*********************************************************************//
64
+
65
+ /// @notice Claims tokens and begins vesting from a tier-scoped reward group.
66
+ /// @param hook The hook whose stakers are vesting.
67
+ /// @param tierIds The strictly-increasing tier set defining the group.
68
+ /// @param tokenIds The IDs to claim rewards for.
69
+ /// @param tokens The tokens to claim.
70
+ function beginVesting(
71
+ address hook,
72
+ uint256[] calldata tierIds,
73
+ uint256[] calldata tokenIds,
74
+ IERC20[] calldata tokens
75
+ )
76
+ external;
77
+
78
+ /// @notice Borrow against one token ID's uncollected vesting rewards in a tier-scoped group.
79
+ /// @param hook The hook whose staker is borrowing against vesting rewards.
80
+ /// @param tierIds The strictly-increasing tier set defining the group.
81
+ /// @param tokenIds The single token ID to borrow against.
82
+ /// @param tokens The single revnet reward token to collateralize.
83
+ /// @param sourceToken The token to borrow from the revnet.
84
+ /// @param minBorrowAmount The minimum amount to borrow, denominated in `sourceToken`.
85
+ /// @param prepaidFeePercent The fee percent to charge upfront.
86
+ /// @param beneficiary The recipient of the borrowed funds.
87
+ /// @return loanId The Revnet loan NFT ID held by this distributor.
88
+ /// @return collateralCount The amount of vesting rewards used as collateral.
89
+ function borrowAgainstVesting(
90
+ address hook,
91
+ uint256[] calldata tierIds,
92
+ uint256[] calldata tokenIds,
93
+ IERC20[] calldata tokens,
94
+ address sourceToken,
95
+ uint256 minBorrowAmount,
96
+ uint256 prepaidFeePercent,
97
+ address payable beneficiary
98
+ )
99
+ external
100
+ returns (uint256 loanId, uint256 collateralCount);
101
+
102
+ /// @notice Recycle unclaimed rewards from expired tier-scoped reward rounds into the current reward round.
103
+ /// @param hook The hook whose expired reward rounds should be recycled.
104
+ /// @param tierIds The strictly-increasing tier set defining the group.
105
+ /// @param token The reward token to recycle.
106
+ /// @param rounds The reward rounds to recycle.
107
+ /// @return amount The total amount recycled.
108
+ function burnExpiredRewards(
109
+ address hook,
110
+ uint256[] calldata tierIds,
111
+ IERC20 token,
112
+ uint256[] calldata rounds
113
+ )
114
+ external
115
+ returns (uint256 amount);
116
+
117
+ /// @notice Collect vested tokens from a tier-scoped reward group.
118
+ /// @param hook The hook whose stakers are collecting.
119
+ /// @param tierIds The strictly-increasing tier set defining the group.
120
+ /// @param tokenIds The IDs of the tokens to collect for.
121
+ /// @param tokens The addresses of the tokens to collect.
122
+ /// @param beneficiary The recipient of the collected tokens.
123
+ function collectVestedRewards(
124
+ address hook,
125
+ uint256[] calldata tierIds,
126
+ uint256[] calldata tokenIds,
127
+ IERC20[] calldata tokens,
128
+ address beneficiary
129
+ )
130
+ external;
131
+
132
+ /// @notice Fund a tier-scoped reward group: only holders of the given tiers can claim this pot.
133
+ /// @dev For native ETH, send `msg.value` and pass `IERC20(NATIVE_TOKEN)` as the token.
134
+ /// @param hook The hook to fund.
135
+ /// @param tierIds The strictly-increasing tier set defining the group.
136
+ /// @param token The token to fund with.
137
+ /// @param amount The amount to fund.
138
+ function fund(address hook, uint256[] calldata tierIds, IERC20 token, uint256 amount) external payable;
139
+
140
+ /// @notice Recycle unlocked rewards from burned tokens in a tier-scoped group into the current reward round.
141
+ /// @param hook The hook whose tokens were burned.
142
+ /// @param tierIds The strictly-increasing tier set defining the group.
143
+ /// @param tokenIds The IDs of the burned tokens.
144
+ /// @param tokens The reward tokens to recycle.
145
+ /// @param beneficiary Unused for forfeiture.
146
+ function releaseForfeitedRewards(
147
+ address hook,
148
+ uint256[] calldata tierIds,
149
+ uint256[] calldata tokenIds,
150
+ IERC20[] calldata tokens,
151
+ address beneficiary
152
+ )
153
+ external;
15
154
  }
@@ -6,7 +6,6 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
6
  import {IREVLoans} from "@rev-net/core-v6/src/interfaces/IREVLoans.sol";
7
7
  import {IREVOwner} from "@rev-net/core-v6/src/interfaces/IREVOwner.sol";
8
8
 
9
- import {JBTokenSnapshotData} from "../structs/JBTokenSnapshotData.sol";
10
9
  import {JBVestingLoan} from "../structs/JBVestingLoan.sol";
11
10
 
12
11
  /// @notice Interface for round-based reward distributors with linear vesting. Stakers claim their share of funded
@@ -46,6 +45,7 @@ interface IJBDistributor {
46
45
  /// @notice Emitted when a staker begins vesting tokens.
47
46
  /// @param hook The hook whose stakers are vesting.
48
47
  /// @param tokenId The ID of the staked token that is claiming.
48
+ /// @param groupId The reward group claimed from (0 = the default group).
49
49
  /// @param token The address of the token to vest.
50
50
  /// @param amount The amount of tokens to vest.
51
51
  /// @param vestingReleaseRound The round at which the tokens will be fully released.
@@ -53,6 +53,7 @@ interface IJBDistributor {
53
53
  event Claimed(
54
54
  address indexed hook,
55
55
  uint256 indexed tokenId,
56
+ uint256 groupId,
56
57
  IERC20 token,
57
58
  uint256 amount,
58
59
  uint256 vestingReleaseRound,
@@ -62,6 +63,7 @@ interface IJBDistributor {
62
63
  /// @notice Emitted when vested tokens are collected.
63
64
  /// @param hook The hook whose stakers are collecting.
64
65
  /// @param tokenId The ID of the staked token collecting.
66
+ /// @param groupId The reward group collected from (0 = the default group).
65
67
  /// @param token The address of the token collected.
66
68
  /// @param amount The amount of tokens collected.
67
69
  /// @param vestingReleaseRound The round at which the tokens will be fully released.
@@ -69,6 +71,7 @@ interface IJBDistributor {
69
71
  event Collected(
70
72
  address indexed hook,
71
73
  uint256 indexed tokenId,
74
+ uint256 groupId,
72
75
  IERC20 token,
73
76
  uint256 amount,
74
77
  uint256 vestingReleaseRound,
@@ -139,22 +142,6 @@ interface IJBDistributor {
139
142
  address caller
140
143
  );
141
144
 
142
- /// @notice Emitted when a snapshot is created for a round.
143
- /// @param hook The hook the snapshot is for.
144
- /// @param round The round the snapshot was created for.
145
- /// @param token The token the snapshot is of.
146
- /// @param balance The token balance at the time of the snapshot.
147
- /// @param vestingAmount The amount of tokens vesting at the time of the snapshot.
148
- /// @param caller The address that triggered the snapshot.
149
- event SnapshotCreated(
150
- address indexed hook,
151
- uint256 indexed round,
152
- IERC20 indexed token,
153
- uint256 balance,
154
- uint256 vestingAmount,
155
- address caller
156
- );
157
-
158
145
  //*********************************************************************//
159
146
  // ----------------------------- views ------------------------------- //
160
147
  //*********************************************************************//
@@ -188,17 +175,27 @@ interface IJBDistributor {
188
175
 
189
176
  /// @notice The active Revnet loan using one token ID's vesting rewards as collateral.
190
177
  /// @param hook The hook the token ID belongs to.
178
+ /// @param groupId The reward group (0 = the default group).
191
179
  /// @param tokenId The token ID whose vesting rewards are collateralized.
192
180
  /// @param token The reward token used as loan collateral.
193
- function activeVestingLoanIdOf(address hook, uint256 tokenId, IERC20 token) external view returns (uint256);
181
+ function activeVestingLoanIdOf(
182
+ address hook,
183
+ uint256 groupId,
184
+ uint256 tokenId,
185
+ IERC20 token
186
+ )
187
+ external
188
+ view
189
+ returns (uint256);
194
190
 
195
- /// @notice Calculate how much of the token has been claimed for the given tokenId.
191
+ /// @notice Calculate how much of the token has been claimed for the given tokenId in the default group.
196
192
  /// @param hook The hook the tokenId belongs to.
197
193
  /// @param tokenId The ID of the token to calculate the token amount for.
198
194
  /// @param token The address of the token to check.
199
195
  function claimedFor(address hook, uint256 tokenId, IERC20 token) external view returns (uint256);
200
196
 
201
- /// @notice Calculate how much of the token is currently ready to be collected for the given tokenId.
197
+ /// @notice Calculate how much of the token is currently ready to be collected for the given tokenId in the
198
+ /// default group.
202
199
  /// @param hook The hook the tokenId belongs to.
203
200
  /// @param tokenId The ID of the token to calculate the token amount for.
204
201
  /// @param token The address of the token to check.
@@ -216,19 +213,6 @@ interface IJBDistributor {
216
213
  /// @param round The round to get the start timestamp of.
217
214
  function roundStartTimestamp(uint256 round) external view returns (uint256);
218
215
 
219
- /// @notice The snapshot data of the token information for each round.
220
- /// @param hook The hook the snapshot is for.
221
- /// @param token The address of the token to check.
222
- /// @param round The round to which the data applies.
223
- function snapshotAtRoundOf(
224
- address hook,
225
- IERC20 token,
226
- uint256 round
227
- )
228
- external
229
- view
230
- returns (JBTokenSnapshotData memory);
231
-
232
216
  /// @notice The amount of a token that is currently vesting for a hook's stakers.
233
217
  /// @param hook The hook whose vesting amount to check.
234
218
  /// @param token The address of the token that is vesting.
@@ -247,7 +231,7 @@ interface IJBDistributor {
247
231
  // ---------------------------- transactions ------------------------- //
248
232
  //*********************************************************************//
249
233
 
250
- /// @notice Claims tokens and begins vesting.
234
+ /// @notice Claims tokens and begins vesting from the default group.
251
235
  /// @param hook The hook whose stakers are vesting.
252
236
  /// @param tokenIds The IDs to claim rewards for.
253
237
  /// @param tokens The tokens to claim.
@@ -275,7 +259,7 @@ interface IJBDistributor {
275
259
  external
276
260
  returns (uint256 loanId, uint256 collateralCount);
277
261
 
278
- /// @notice Collect vested tokens.
262
+ /// @notice Collect vested tokens from the default group.
279
263
  /// @param hook The hook whose stakers are collecting.
280
264
  /// @param tokenIds The IDs of the tokens to collect for.
281
265
  /// @param tokens The addresses of the tokens to collect.
@@ -288,14 +272,14 @@ interface IJBDistributor {
288
272
  )
289
273
  external;
290
274
 
291
- /// @notice Fund the distributor for a specific hook.
275
+ /// @notice Fund the distributor's default group for a specific hook.
292
276
  /// @dev For native ETH, send `msg.value` and pass `IERC20(NATIVE_TOKEN)` as the token.
293
277
  /// @param hook The hook to fund.
294
278
  /// @param token The token to fund with.
295
279
  /// @param amount The amount to fund.
296
280
  function fund(address hook, IERC20 token, uint256 amount) external payable;
297
281
 
298
- /// @notice Recycle unclaimed rewards from expired reward rounds into the current reward round.
282
+ /// @notice Recycle unclaimed rewards from expired default-group reward rounds into the current reward round.
299
283
  /// @param hook The hook whose expired reward rounds should be recycled.
300
284
  /// @param token The reward token to recycle.
301
285
  /// @param rounds The reward rounds to recycle.
@@ -317,7 +301,7 @@ interface IJBDistributor {
317
301
  payable
318
302
  returns (uint256 paidOffLoanId);
319
303
 
320
- /// @notice Recycle unlocked rewards from burned tokens into the current reward round.
304
+ /// @notice Recycle unlocked rewards from burned tokens in the default group into the current reward round.
321
305
  /// @param hook The hook whose tokens were burned.
322
306
  /// @param tokenIds The IDs of the burned tokens.
323
307
  /// @param tokens The reward tokens to recycle.
@@ -5,6 +5,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
5
 
6
6
  /// @notice Parameters used while opening a distributor-owned Revnet loan.
7
7
  /// @custom:member hook The hook whose token ID owns the vesting rewards.
8
+ /// @custom:member groupId The reward group whose vesting rewards are collateralized (0 = the default group).
8
9
  /// @custom:member tokenId The token ID whose vesting rewards are collateralized.
9
10
  /// @custom:member token The revnet reward token used as loan collateral.
10
11
  /// @custom:member sourceToken The token borrowed from the revnet.
@@ -14,6 +15,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
14
15
  /// @custom:member revnetId The revnet whose project token is collateralized.
15
16
  struct JBBorrowContext {
16
17
  address hook;
18
+ uint256 groupId;
17
19
  uint256 tokenId;
18
20
  IERC20 token;
19
21
  address sourceToken;
@@ -2,10 +2,15 @@
2
2
  pragma solidity ^0.8.0;
3
3
 
4
4
  /// @custom:member hook The stake source whose historical rewards are being claimed.
5
+ /// @custom:member groupId The reward group being claimed (0 = the default group).
6
+ /// @custom:member tierIds The tier set defining the group (empty for the default group); used to filter eligible
7
+ /// token IDs on the tier-scoped path.
5
8
  /// @custom:member lastClaimableRound The last completed reward round included in the claim.
6
9
  /// @custom:member vestingReleaseRound The round at which newly materialized rewards finish vesting.
7
10
  struct JBClaimContext {
8
11
  address hook;
12
+ uint256 groupId;
13
+ uint256[] tierIds;
9
14
  uint256 lastClaimableRound;
10
15
  uint256 vestingReleaseRound;
11
16
  }
@@ -4,6 +4,9 @@ pragma solidity ^0.8.0;
4
4
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
5
 
6
6
  /// @custom:member hook The stake source whose rewards are being vested.
7
+ /// @custom:member groupId The reward group being claimed (0 = the default group).
8
+ /// @custom:member tierIds The tier set defining the group (empty for the default group); used to filter eligible
9
+ /// token IDs on the tier-scoped path.
7
10
  /// @custom:member token The reward token being vested.
8
11
  /// @custom:member distributable The reward amount assigned to the round.
9
12
  /// @custom:member totalStakeAmount The total checkpointed stake sharing the round's rewards.
@@ -12,6 +15,8 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
12
15
  /// @custom:member snapshotBlock The block used for historical stake and ownership lookups.
13
16
  struct JBVestContext {
14
17
  address hook;
18
+ uint256 groupId;
19
+ uint256[] tierIds;
15
20
  IERC20 token;
16
21
  uint256 distributable;
17
22
  uint256 totalStakeAmount;
@@ -5,12 +5,14 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
5
 
6
6
  /// @notice Tracks a Revnet loan collateralized by one token ID's vesting rewards.
7
7
  /// @custom:member hook The hook the token ID belongs to.
8
+ /// @custom:member groupId The reward group whose vesting rewards are collateralized (0 = the default group).
8
9
  /// @custom:member tokenId The token ID whose vesting rewards are collateralized.
9
10
  /// @custom:member token The revnet reward token used as loan collateral.
10
11
  /// @custom:member vestingDataCount The vesting-entry boundary collateralized by the loan.
11
12
  /// @custom:member collateralCount The amount of vesting rewards collateralized.
12
13
  struct JBVestingLoan {
13
14
  address hook;
15
+ uint256 groupId;
14
16
  uint256 tokenId;
15
17
  IERC20 token;
16
18
  uint48 vestingDataCount;
@@ -1,11 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.0;
3
-
4
- /// @notice A point-in-time snapshot of a reward token's state for a specific hook and round. The distributable
5
- /// amount for the round is `balance - vestingAmount`.
6
- /// @custom:member balance The total token balance held for the hook's stakers at snapshot time.
7
- /// @custom:member vestingAmount The amount currently locked in vesting at snapshot time (not yet distributable).
8
- struct JBTokenSnapshotData {
9
- uint256 balance;
10
- uint256 vestingAmount;
11
- }