@bananapus/distributor-v6 0.0.8 → 0.0.10

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/distributor-v6",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -169,7 +169,7 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
169
169
  /// Silently skips burned tokens, already-vested tokens, and tokens whose owner had no snapshot voting power.
170
170
  /// @param hook The address of the 721 hook whose stakers are vesting.
171
171
  /// @param tokenIds The NFT token IDs to vest rewards for.
172
- /// @param token The ERC-20 reward token being distributed.
172
+ /// @param token The ERC-20 reward token to distribute.
173
173
  /// @param distributable The total distributable amount of `token` for this round.
174
174
  /// @param totalStakeAmount The aggregate voting power at the round's snapshot block.
175
175
  /// @param vestingReleaseRound The round number at which the vesting period ends and tokens become fully claimable.
@@ -10,7 +10,13 @@ import {IJBDistributor} from "./interfaces/IJBDistributor.sol";
10
10
  import {JBTokenSnapshotData} from "./structs/JBTokenSnapshotData.sol";
11
11
  import {JBVestingData} from "./structs/JBVestingData.sol";
12
12
 
13
- /// @notice A contract managing distributions of tokens to be claimed and vested by stakers of any other token.
13
+ /// @notice Abstract base for reward distributors. Manages round-based distribution of ERC-20 tokens (or native ETH)
14
+ /// to stakers with linear vesting. Each round, a snapshot is taken of the distributable balance, and stakers can
15
+ /// claim their pro-rata share based on their stake weight at the snapshot block. Claimed tokens vest linearly over
16
+ /// `vestingRounds` rounds and can be collected as they unlock.
17
+ /// @dev Subclasses define how stake is measured (`_tokenStake`, `_totalStake`), who can claim (`_canClaim`), and
18
+ /// what "burned" means (`_tokenBurned`). Two concrete implementations exist: `JBTokenDistributor` (IVotes tokens)
19
+ /// and `JB721Distributor` (Juicebox 721 NFTs).
14
20
  abstract contract JBDistributor is IJBDistributor {
15
21
  using SafeERC20 for IERC20;
16
22
 
@@ -66,7 +72,7 @@ abstract contract JBDistributor is IJBDistributor {
66
72
  /// @notice The index within `vestingDataOf` of the latest vest.
67
73
  /// @custom:param hook The hook the tokenId belongs to.
68
74
  /// @custom:param tokenId The ID of the token to which the vests belong.
69
- /// @custom:param token The address of the token being vested.
75
+ /// @custom:param token The address of the token vested.
70
76
  mapping(address hook => mapping(uint256 tokenId => mapping(IERC20 token => uint256))) public latestVestedIndexOf;
71
77
 
72
78
  /// @notice The block number recorded as the snapshot point for each round.
@@ -81,7 +87,7 @@ abstract contract JBDistributor is IJBDistributor {
81
87
  /// @notice All vesting data of a tokenId for any number of vesting tokens.
82
88
  /// @custom:param hook The hook the tokenId belongs to.
83
89
  /// @custom:param tokenId The ID of the token to which the vests belong.
84
- /// @custom:param token The address of the token being vested.
90
+ /// @custom:param token The address of the token vested.
85
91
  // slither-disable-next-line uninitialized-state
86
92
  mapping(address hook => mapping(uint256 tokenId => mapping(IERC20 token => JBVestingData[]))) public vestingDataOf;
87
93
 
@@ -100,7 +106,7 @@ abstract contract JBDistributor is IJBDistributor {
100
106
 
101
107
  /// @notice The snapshot data of the token information for each round.
102
108
  /// @custom:param hook The hook the snapshot is for.
103
- /// @custom:param token The address of the token being claimed and vested.
109
+ /// @custom:param token The address of the token claimed and vested.
104
110
  /// @custom:param round The round to which the data applies.
105
111
  mapping(address hook => mapping(IERC20 token => mapping(uint256 round => JBTokenSnapshotData snapshot))) internal
106
112
  _snapshotAtRoundOf;
@@ -122,10 +128,12 @@ abstract contract JBDistributor is IJBDistributor {
122
128
  // ---------------------- external transactions ---------------------- //
123
129
  //*********************************************************************//
124
130
 
125
- /// @notice Claims tokens and begins vesting.
126
- /// @param hook The hook whose stakers are vesting.
127
- /// @param tokenIds The IDs to claim rewards for.
128
- /// @param tokens The tokens to claim.
131
+ /// @notice Snapshot the current round's distributable balance and begin vesting for the specified token IDs.
132
+ /// Each token ID's share is proportional to its stake weight relative to the total stake at the snapshot block.
133
+ /// Vesting completes after `vestingRounds` rounds. Reverts if there's nothing to distribute.
134
+ /// @param hook The hook (IVotes token or 721 hook) whose stakers are vesting.
135
+ /// @param tokenIds The staker token IDs to claim rewards for.
136
+ /// @param tokens The reward tokens to begin vesting.
129
137
  function beginVesting(address hook, uint256[] calldata tokenIds, IERC20[] calldata tokens) external override {
130
138
  // Revert if no token IDs are provided.
131
139
  if (tokenIds.length == 0) revert JBDistributor_EmptyTokenIds();
@@ -167,11 +175,13 @@ abstract contract JBDistributor is IJBDistributor {
167
175
  }
168
176
  }
169
177
 
170
- /// @notice Fund the distributor for a specific hook by pulling tokens from the caller.
171
- /// @dev For native ETH, send `msg.value` and pass `IERC20(JBConstants.NATIVE_TOKEN)` as the token.
172
- /// @param hook The hook to fund.
178
+ /// @notice Directly fund the distributor for a specific hook by pulling tokens from the caller. An alternative
179
+ /// to split-based funding useful for one-off deposits or external reward sources.
180
+ /// @dev For native ETH, send `msg.value` and pass `IERC20(JBConstants.NATIVE_TOKEN)` as the token. Uses balance
181
+ /// delta to handle fee-on-transfer tokens correctly.
182
+ /// @param hook The hook to fund (determines which staker pool receives the tokens).
173
183
  /// @param token The token to fund with.
174
- /// @param amount The amount to fund.
184
+ /// @param amount The amount to fund (ignored for native ETH — `msg.value` is used instead).
175
185
  function fund(address hook, IERC20 token, uint256 amount) external payable override {
176
186
  if (address(token) == JBConstants.NATIVE_TOKEN) {
177
187
  amount = msg.value;
@@ -186,16 +196,19 @@ abstract contract JBDistributor is IJBDistributor {
186
196
  _accountedBalanceOf[token] += amount;
187
197
  }
188
198
 
189
- /// @notice Record the snapshot block for the current round. Callable by anyone (keepers, frontends).
199
+ /// @notice Record the snapshot block for the current round (and eagerly for the next round). Callable by anyone
200
+ /// keepers or frontends can call this early in a round to lock the snapshot block before any claims occur.
190
201
  function poke() external override {
191
202
  _ensureSnapshotBlock(currentRound());
192
203
  }
193
204
 
194
- /// @notice Release vested rewards in the case that a token was burned.
205
+ /// @notice Release unvested rewards tied to burned tokens. When an NFT is burned, its pending vesting entries
206
+ /// become stranded — this function unlocks them and returns them to the hook's distributable pool (they are NOT
207
+ /// sent to the beneficiary). Anyone can call this for burned tokens.
195
208
  /// @param hook The hook whose tokens were burned.
196
- /// @param tokenIds The IDs of the burned tokens.
197
- /// @param tokens The address of the tokens being released.
198
- /// @param beneficiary The recipient of the released tokens.
209
+ /// @param tokenIds The IDs of the burned tokens (reverts if any are not actually burned).
210
+ /// @param tokens The reward tokens to release.
211
+ /// @param beneficiary Unused for forfeiture — tokens return to the pool. Kept for interface compatibility.
199
212
  function releaseForfeitedRewards(
200
213
  address hook,
201
214
  uint256[] calldata tokenIds,
@@ -228,11 +241,12 @@ abstract contract JBDistributor is IJBDistributor {
228
241
  return _balanceOf[hook][token];
229
242
  }
230
243
 
231
- /// @notice Calculate how much of the token has been claimed for the given tokenId.
244
+ /// @notice Calculate the total amount of a reward token that has been claimed (began vesting) for a given
245
+ /// staker token ID but has not yet been collected. Includes both locked (still vesting) and unlocked amounts.
232
246
  /// @param hook The hook the tokenId belongs to.
233
- /// @param tokenId The ID of the token to calculate the token amount for.
234
- /// @param token The address of the token being claimed.
235
- /// @return tokenAmount The amount of tokens that can be claimed once they have vested.
247
+ /// @param tokenId The ID of the staker token to calculate for.
248
+ /// @param token The reward token to check.
249
+ /// @return tokenAmount The total uncollected amount (vesting + vested-but-uncollected).
236
250
  function claimedFor(
237
251
  address hook,
238
252
  uint256 tokenId,
@@ -261,11 +275,12 @@ abstract contract JBDistributor is IJBDistributor {
261
275
  }
262
276
  }
263
277
 
264
- /// @notice Calculate how much of the token is currently ready to be collected for the given tokenId.
278
+ /// @notice Calculate how much of a reward token is currently unlocked and ready to be collected for a given
279
+ /// staker token ID. Only includes the vested portion — excludes amounts still locked in vesting.
265
280
  /// @param hook The hook the tokenId belongs to.
266
- /// @param tokenId The ID of the token to calculate the token amount for.
267
- /// @param token The address of the token being claimed.
268
- /// @return tokenAmount The amount of tokens that can be claimed right now.
281
+ /// @param tokenId The ID of the staker token to calculate for.
282
+ /// @param token The reward token to check.
283
+ /// @return tokenAmount The amount of tokens that can be collected right now via `collectVestedRewards`.
269
284
  function collectableFor(
270
285
  address hook,
271
286
  uint256 tokenId,
@@ -306,7 +321,7 @@ abstract contract JBDistributor is IJBDistributor {
306
321
 
307
322
  /// @notice The snapshot data of the token information for each round.
308
323
  /// @param hook The hook the snapshot is for.
309
- /// @param token The address of the token being claimed and vested.
324
+ /// @param token The address of the token claimed and vested.
310
325
  /// @param round The round to which the data applies.
311
326
  function snapshotAtRoundOf(
312
327
  address hook,
@@ -340,10 +355,12 @@ abstract contract JBDistributor is IJBDistributor {
340
355
  // ----------------------- public transactions ----------------------- //
341
356
  //*********************************************************************//
342
357
 
343
- /// @notice Collect vested tokens. Auto-vests for the current round if not already vested.
358
+ /// @notice Collect tokens that have vested (partially or fully) and transfer them to the beneficiary. Also
359
+ /// auto-vests for the current round if rewards haven't been claimed yet — so callers don't need to separately
360
+ /// call `beginVesting`. Only the token owner (verified via `_canClaim`) can collect.
344
361
  /// @param hook The hook whose stakers are collecting.
345
- /// @param tokenIds The IDs of the tokens to collect for.
346
- /// @param tokens The address of the tokens being claimed.
362
+ /// @param tokenIds The IDs of the tokens to collect for (caller must own all of them).
363
+ /// @param tokens The reward tokens to collect vested amounts of.
347
364
  /// @param beneficiary The recipient of the collected tokens.
348
365
  function collectVestedRewards(
349
366
  address hook,
@@ -449,7 +466,7 @@ abstract contract JBDistributor is IJBDistributor {
449
466
  /// @notice Unlocks rewards for the given token IDs and tokens, either for collection or forfeiture.
450
467
  /// @param hook The hook the tokens belong to.
451
468
  /// @param tokenIds The IDs of the tokens to unlock rewards for.
452
- /// @param tokens The address of the tokens being unlocked.
469
+ /// @param tokens The addresses of the tokens to unlock.
453
470
  /// @param beneficiary The recipient of the unlocked tokens.
454
471
  /// @param ownerClaim Whether this is a claim by the owner (true) or a forfeiture release (false).
455
472
  function _unlockRewards(
@@ -504,7 +521,7 @@ abstract contract JBDistributor is IJBDistributor {
504
521
  /// @notice Unlocks rewards for a set of token IDs for a single reward token.
505
522
  /// @param hook The hook the tokens belong to.
506
523
  /// @param tokenIds The IDs of the tokens to unlock rewards for.
507
- /// @param token The reward token being unlocked.
524
+ /// @param token The reward token to unlock.
508
525
  /// @param round The current round.
509
526
  /// @return totalTokenAmount The total amount of reward tokens unlocked.
510
527
  function _unlockTokenIds(
@@ -633,28 +650,33 @@ abstract contract JBDistributor is IJBDistributor {
633
650
  // ----------------------- internal views ---------------------------- //
634
651
  //*********************************************************************//
635
652
 
636
- /// @notice A flag indicating if an account can currently claim their tokens.
653
+ /// @notice Check whether an account is authorized to collect vested rewards for the given token ID. For 721
654
+ /// distributors this is ownership; for token distributors this is address-encoding match.
637
655
  /// @param hook The hook the token belongs to.
638
656
  /// @param tokenId The ID of the token to check.
639
- /// @param account The account to check if it can claim.
640
- /// @return canClaim A flag indicating if claiming is allowed.
657
+ /// @param account The account to check authorization for.
658
+ /// @return canClaim True if the account can collect rewards for this token ID.
641
659
  function _canClaim(address hook, uint256 tokenId, address account) internal view virtual returns (bool canClaim);
642
660
 
643
- /// @notice Checks if the given token was burned or not.
661
+ /// @notice Check whether a staker token has been burned. Burned tokens are excluded from stake calculations
662
+ /// and their unvested rewards can be released via `releaseForfeitedRewards`.
644
663
  /// @param hook The hook the token belongs to.
645
- /// @param tokenId The tokenId to check.
646
- /// @return tokenWasBurned A boolean that is true if the token was burned.
664
+ /// @param tokenId The token ID to check.
665
+ /// @return tokenWasBurned True if the token has been burned.
647
666
  function _tokenBurned(address hook, uint256 tokenId) internal view virtual returns (bool tokenWasBurned);
648
667
 
649
- /// @notice The amount of tokens staked for the given token ID.
668
+ /// @notice The stake weight of a specific token ID, used to calculate its pro-rata share of distributions.
669
+ /// For 721 distributors this is the tier's voting units; for token distributors this is delegated voting power.
650
670
  /// @param hook The hook the token belongs to.
651
- /// @param tokenId The ID of the token to get the staked value of.
652
- /// @return tokenStakeAmount The amount of staked tokens that is being represented by the token.
671
+ /// @param tokenId The ID of the token to get the stake weight of.
672
+ /// @return tokenStakeAmount The stake weight represented by this token ID.
653
673
  function _tokenStake(address hook, uint256 tokenId) internal view virtual returns (uint256 tokenStakeAmount);
654
674
 
655
- /// @notice The total amount staked at the given block.
675
+ /// @notice The total stake across all token IDs at a given block. Used as the denominator when calculating each
676
+ /// token ID's pro-rata share. For 721 distributors this is `getPastTotalSupply` from the checkpoints module;
677
+ /// for token distributors this is `getPastTotalSupply` from the IVotes token.
656
678
  /// @param hook The hook to get the total stake for.
657
- /// @param blockNumber The block number to get the total staked amount at.
658
- /// @return totalStakedAmount The total amount staked at a block number.
679
+ /// @param blockNumber The block number to query (must be strictly in the past).
680
+ /// @return totalStakedAmount The total stake at the given block.
659
681
  function _totalStake(address hook, uint256 blockNumber) internal view virtual returns (uint256 totalStakedAmount);
660
682
  }
@@ -5,7 +5,9 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
5
 
6
6
  import {JBTokenSnapshotData} from "../structs/JBTokenSnapshotData.sol";
7
7
 
8
- /// @notice A contract managing distributions of tokens to be claimed and vested by stakers of any other token.
8
+ /// @notice Interface for round-based reward distributors with linear vesting. Stakers claim their share of a
9
+ /// distributable balance each round, and claimed amounts vest linearly over a configurable number of rounds.
10
+ /// Two implementations exist: `JBTokenDistributor` (IVotes token stakers) and `JB721Distributor` (NFT holders).
9
11
  interface IJBDistributor {
10
12
  //*********************************************************************//
11
13
  // -------------------------------- events --------------------------- //
@@ -14,8 +16,8 @@ interface IJBDistributor {
14
16
  /// @notice Emitted when a staker begins vesting tokens.
15
17
  /// @param hook The hook whose stakers are vesting.
16
18
  /// @param tokenId The ID of the staked token that is claiming.
17
- /// @param token The address of the token being vested.
18
- /// @param amount The amount of tokens being vested.
19
+ /// @param token The address of the token to vest.
20
+ /// @param amount The amount of tokens to vest.
19
21
  /// @param vestingReleaseRound The round at which the tokens will be fully released.
20
22
  event Claimed(
21
23
  address indexed hook, uint256 indexed tokenId, IERC20 token, uint256 amount, uint256 vestingReleaseRound
@@ -24,7 +26,7 @@ interface IJBDistributor {
24
26
  /// @notice Emitted when vested tokens are collected.
25
27
  /// @param hook The hook whose stakers are collecting.
26
28
  /// @param tokenId The ID of the staked token collecting.
27
- /// @param token The address of the token being collected.
29
+ /// @param token The address of the token collected.
28
30
  /// @param amount The amount of tokens collected.
29
31
  /// @param vestingReleaseRound The round at which the tokens will be fully released.
30
32
  event Collected(
@@ -58,13 +60,13 @@ interface IJBDistributor {
58
60
  /// @notice Calculate how much of the token has been claimed for the given tokenId.
59
61
  /// @param hook The hook the tokenId belongs to.
60
62
  /// @param tokenId The ID of the token to calculate the token amount for.
61
- /// @param token The address of the token being claimed.
63
+ /// @param token The address of the token to check.
62
64
  function claimedFor(address hook, uint256 tokenId, IERC20 token) external view returns (uint256);
63
65
 
64
66
  /// @notice Calculate how much of the token is currently ready to be collected for the given tokenId.
65
67
  /// @param hook The hook the tokenId belongs to.
66
68
  /// @param tokenId The ID of the token to calculate the token amount for.
67
- /// @param token The address of the token being claimed.
69
+ /// @param token The address of the token to check.
68
70
  function collectableFor(address hook, uint256 tokenId, IERC20 token) external view returns (uint256);
69
71
 
70
72
  /// @notice The number of the current round.
@@ -84,7 +86,7 @@ interface IJBDistributor {
84
86
 
85
87
  /// @notice The snapshot data of the token information for each round.
86
88
  /// @param hook The hook the snapshot is for.
87
- /// @param token The address of the token being claimed and vested.
89
+ /// @param token The address of the token to check.
88
90
  /// @param round The round to which the data applies.
89
91
  function snapshotAtRoundOf(
90
92
  address hook,
@@ -116,7 +118,7 @@ interface IJBDistributor {
116
118
  /// @notice Collect vested tokens.
117
119
  /// @param hook The hook whose stakers are collecting.
118
120
  /// @param tokenIds The IDs of the tokens to collect for.
119
- /// @param tokens The address of the tokens being claimed.
121
+ /// @param tokens The addresses of the tokens to collect.
120
122
  /// @param beneficiary The recipient of the collected tokens.
121
123
  function collectVestedRewards(
122
124
  address hook,
@@ -139,7 +141,7 @@ interface IJBDistributor {
139
141
  /// @notice Release vested rewards for burned tokens.
140
142
  /// @param hook The hook whose tokens were burned.
141
143
  /// @param tokenIds The IDs of the burned tokens.
142
- /// @param tokens The address of the tokens being released.
144
+ /// @param tokens The addresses of the tokens to release.
143
145
  /// @param beneficiary The recipient of the released tokens.
144
146
  function releaseForfeitedRewards(
145
147
  address hook,
@@ -1,8 +1,10 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.0;
3
3
 
4
- /// @custom:member balance The token balance at the time of the snapshot.
5
- /// @custom:member vestingAmount The amount of tokens vesting at the time of the snapshot.
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).
6
8
  struct JBTokenSnapshotData {
7
9
  uint256 balance;
8
10
  uint256 vestingAmount;
@@ -1,9 +1,12 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.0;
3
3
 
4
- /// @custom:member releaseRound The round at which the vesting tokens are fully released.
5
- /// @custom:member amount The original amount of tokens that were claimed.
6
- /// @custom:member shareClaimed The share of the amount that has already been claimed (out of `MAX_SHARE`).
4
+ /// @notice Tracks a single vesting entry for a staker token ID. Tokens vest linearly from the claim round to
5
+ /// `releaseRound`, and `shareClaimed` tracks how much has been collected so far (out of `MAX_SHARE = 100,000`).
6
+ /// @custom:member releaseRound The round at which the tokens are fully vested and 100% claimable.
7
+ /// @custom:member amount The original amount of reward tokens that were claimed (before any collection).
8
+ /// @custom:member shareClaimed The cumulative share collected so far (out of `MAX_SHARE`). Increases each
9
+ /// time `collectVestedRewards` is called.
7
10
  struct JBVestingData {
8
11
  uint256 releaseRound;
9
12
  uint256 amount;