@bananapus/distributor-v6 0.0.18 → 0.0.20
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 +3 -3
- package/script/Deploy.s.sol +3 -1
- package/src/JB721Distributor.sol +11 -11
- package/src/JBDistributor.sol +24 -14
- package/src/JBTokenDistributor.sol +8 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/distributor-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"deploy:testnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks testnets"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
30
|
-
"@bananapus/core-v6": "^0.0.
|
|
29
|
+
"@bananapus/721-hook-v6": "^0.0.52",
|
|
30
|
+
"@bananapus/core-v6": "^0.0.55",
|
|
31
31
|
"@bananapus/permission-ids-v6": "^0.0.25",
|
|
32
32
|
"@openzeppelin/contracts": "5.6.1",
|
|
33
33
|
"@prb/math": "4.1.1"
|
package/script/Deploy.s.sol
CHANGED
|
@@ -16,7 +16,9 @@ contract Deploy is Script {
|
|
|
16
16
|
uint256 roundDuration = vm.envUint("ROUND_DURATION");
|
|
17
17
|
uint256 vestingRounds = vm.envUint("VESTING_ROUNDS");
|
|
18
18
|
|
|
19
|
-
new JB721Distributor(
|
|
19
|
+
new JB721Distributor({
|
|
20
|
+
directory: directory, initialRoundDuration: roundDuration, initialVestingRounds: vestingRounds
|
|
21
|
+
});
|
|
20
22
|
|
|
21
23
|
vm.stopBroadcast();
|
|
22
24
|
}
|
package/src/JB721Distributor.sol
CHANGED
|
@@ -77,14 +77,14 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
|
|
|
77
77
|
//*********************************************************************//
|
|
78
78
|
|
|
79
79
|
/// @param directory The JB directory used to verify terminal/controller callers.
|
|
80
|
-
/// @param
|
|
81
|
-
/// @param
|
|
80
|
+
/// @param initialRoundDuration The duration of each round, specified in seconds.
|
|
81
|
+
/// @param initialVestingRounds The number of rounds until tokens are fully vested.
|
|
82
82
|
constructor(
|
|
83
83
|
IJBDirectory directory,
|
|
84
|
-
uint256
|
|
85
|
-
uint256
|
|
84
|
+
uint256 initialRoundDuration,
|
|
85
|
+
uint256 initialVestingRounds
|
|
86
86
|
)
|
|
87
|
-
JBDistributor(
|
|
87
|
+
JBDistributor(initialRoundDuration, initialVestingRounds)
|
|
88
88
|
{
|
|
89
89
|
DIRECTORY = directory;
|
|
90
90
|
}
|
|
@@ -109,7 +109,7 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
|
|
|
109
109
|
function processSplitWith(JBSplitHookContext calldata context) external payable override {
|
|
110
110
|
// Only terminals and controllers for the project can call this.
|
|
111
111
|
if (
|
|
112
|
-
!DIRECTORY.isTerminalOf(context.projectId, IJBTerminal(msg.sender))
|
|
112
|
+
!DIRECTORY.isTerminalOf({projectId: context.projectId, terminal: IJBTerminal(msg.sender)})
|
|
113
113
|
&& DIRECTORY.controllerOf(context.projectId) != IERC165(msg.sender)
|
|
114
114
|
) revert JB721Distributor_Unauthorized({projectId: context.projectId, caller: msg.sender});
|
|
115
115
|
|
|
@@ -266,7 +266,7 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
|
|
|
266
266
|
|
|
267
267
|
// Use the checkpoints module to verify the token's snapshot owner had voting power at the round's snapshot
|
|
268
268
|
// block. If the token did not exist then, ownerOfAt returns zero above and the token is not eligible.
|
|
269
|
-
uint256 pastVotes = IVotes(address(IJB721TiersHook(hook).
|
|
269
|
+
uint256 pastVotes = IVotes(address(IJB721TiersHook(hook).checkpoints()))
|
|
270
270
|
.getPastVotes({account: owner, timepoint: snapshotBlock});
|
|
271
271
|
|
|
272
272
|
// If the owner had no voting power at round start, the token is ineligible.
|
|
@@ -278,14 +278,14 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
/// @notice The total stake at a specific block, using the hook's checkpoints module for historical accuracy.
|
|
281
|
-
/// @dev Uses `IVotes.getPastTotalSupply` from the hook's
|
|
281
|
+
/// @dev Uses `IVotes.getPastTotalSupply` from the hook's checkpoints module. This ensures that only NFTs
|
|
282
282
|
/// that existed (and were delegated) at `blockNumber` are counted, preventing late mints from diluting or
|
|
283
283
|
/// capturing rewards within the current round.
|
|
284
284
|
/// @param hook The hook to get the total stake for.
|
|
285
285
|
/// @param blockNumber The block number to get the total staked amount at.
|
|
286
286
|
/// @return total The total checkpointed voting units at the given block.
|
|
287
287
|
function _totalStake(address hook, uint256 blockNumber) internal view override returns (uint256 total) {
|
|
288
|
-
IJB721Checkpoints checkpoints = IJB721TiersHook(hook).
|
|
288
|
+
IJB721Checkpoints checkpoints = IJB721TiersHook(hook).checkpoints();
|
|
289
289
|
total = IVotes(address(checkpoints)).getPastTotalSupply(blockNumber);
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -311,7 +311,7 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
|
|
|
311
311
|
returns (address owner)
|
|
312
312
|
{
|
|
313
313
|
// The 721 hook owns the checkpoint module; the distributor only trusts that module's historical proof.
|
|
314
|
-
IJB721Checkpoints checkpoints = IJB721TiersHook(hook).
|
|
314
|
+
IJB721Checkpoints checkpoints = IJB721TiersHook(hook).checkpoints();
|
|
315
315
|
|
|
316
316
|
// Use staticcall so older hooks without `ownerOfAt` fail closed instead of reverting the whole distribution.
|
|
317
317
|
(bool success, bytes memory data) =
|
|
@@ -382,7 +382,7 @@ contract JB721Distributor is JBDistributor, IJB721Distributor {
|
|
|
382
382
|
if (owner == address(0)) return (0, newUniqueCount);
|
|
383
383
|
|
|
384
384
|
// Query the owner's checkpointed voting power at the round's snapshot block.
|
|
385
|
-
pastVotes = IVotes(address(IJB721TiersHook(ctx.hook).
|
|
385
|
+
pastVotes = IVotes(address(IJB721TiersHook(ctx.hook).checkpoints()))
|
|
386
386
|
.getPastVotes({account: owner, timepoint: snapshotBlock});
|
|
387
387
|
|
|
388
388
|
// If the snapshot owner had no voting power at round start, the token is ineligible for this round.
|
package/src/JBDistributor.sol
CHANGED
|
@@ -119,13 +119,15 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
119
119
|
// -------------------------- constructor ---------------------------- //
|
|
120
120
|
//*********************************************************************//
|
|
121
121
|
|
|
122
|
-
/// @param
|
|
123
|
-
/// @param
|
|
124
|
-
constructor(uint256
|
|
125
|
-
if (
|
|
122
|
+
/// @param initialRoundDuration The duration of each round, specified in seconds.
|
|
123
|
+
/// @param initialVestingRounds The number of rounds until tokens are fully vested.
|
|
124
|
+
constructor(uint256 initialRoundDuration, uint256 initialVestingRounds) {
|
|
125
|
+
if (initialRoundDuration == 0) {
|
|
126
|
+
revert JBDistributor_InvalidRoundDuration({roundDuration: initialRoundDuration});
|
|
127
|
+
}
|
|
126
128
|
startingTimestamp = block.timestamp;
|
|
127
|
-
roundDuration =
|
|
128
|
-
vestingRounds =
|
|
129
|
+
roundDuration = initialRoundDuration;
|
|
130
|
+
vestingRounds = initialVestingRounds;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
//*********************************************************************//
|
|
@@ -202,7 +204,7 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
202
204
|
}
|
|
203
205
|
// Use balance delta to handle fee-on-transfer tokens correctly.
|
|
204
206
|
uint256 balanceBefore = token.balanceOf(address(this));
|
|
205
|
-
token.safeTransferFrom(msg.sender, address(this), amount);
|
|
207
|
+
token.safeTransferFrom({from: msg.sender, to: address(this), value: amount});
|
|
206
208
|
amount = token.balanceOf(address(this)) - balanceBefore;
|
|
207
209
|
}
|
|
208
210
|
_balanceOf[hook][token] += amount;
|
|
@@ -283,7 +285,7 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
283
285
|
JBVestingData memory vesting = vestingDataOf[hook][tokenId][token][vestedIndex];
|
|
284
286
|
|
|
285
287
|
// Use `original - alreadyPaid` to include rounding dust in the remaining amount.
|
|
286
|
-
tokenAmount += vesting.amount - mulDiv(vesting.amount, vesting.shareClaimed, MAX_SHARE);
|
|
288
|
+
tokenAmount += vesting.amount - mulDiv({x: vesting.amount, y: vesting.shareClaimed, denominator: MAX_SHARE});
|
|
287
289
|
|
|
288
290
|
unchecked {
|
|
289
291
|
++vestedIndex;
|
|
@@ -329,11 +331,14 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
329
331
|
|
|
330
332
|
if (lockedShare == 0 && vesting.shareClaimed < MAX_SHARE) {
|
|
331
333
|
// Final unlock: compute remaining as `original - alreadyPaid` to include dust.
|
|
332
|
-
tokenAmount += vesting.amount
|
|
334
|
+
tokenAmount += vesting.amount
|
|
335
|
+
- mulDiv({x: vesting.amount, y: vesting.shareClaimed, denominator: MAX_SHARE});
|
|
333
336
|
} else {
|
|
334
337
|
uint256 newShareClaimed = MAX_SHARE - lockedShare;
|
|
335
338
|
if (newShareClaimed > vesting.shareClaimed) {
|
|
336
|
-
tokenAmount += mulDiv(
|
|
339
|
+
tokenAmount += mulDiv({
|
|
340
|
+
x: vesting.amount, y: newShareClaimed - vesting.shareClaimed, denominator: MAX_SHARE
|
|
341
|
+
});
|
|
337
342
|
}
|
|
338
343
|
}
|
|
339
344
|
|
|
@@ -542,7 +547,7 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
542
547
|
});
|
|
543
548
|
}
|
|
544
549
|
} else {
|
|
545
|
-
token.safeTransfer(beneficiary, totalTokenAmount);
|
|
550
|
+
token.safeTransfer({to: beneficiary, value: totalTokenAmount});
|
|
546
551
|
}
|
|
547
552
|
}
|
|
548
553
|
// If forfeiture: _balanceOf is NOT decremented so the forfeited tokens
|
|
@@ -598,9 +603,12 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
598
603
|
if (lockedShare == 0 && vesting.shareClaimed < MAX_SHARE) {
|
|
599
604
|
// Final unlock: compute remaining amount as `original - alreadyPaid` to force
|
|
600
605
|
// rounding dust out so nothing is stranded in the entry.
|
|
601
|
-
claimAmount =
|
|
606
|
+
claimAmount =
|
|
607
|
+
vesting.amount - mulDiv({x: vesting.amount, y: vesting.shareClaimed, denominator: MAX_SHARE});
|
|
602
608
|
} else if (MAX_SHARE - lockedShare > vesting.shareClaimed) {
|
|
603
|
-
claimAmount = mulDiv(
|
|
609
|
+
claimAmount = mulDiv({
|
|
610
|
+
x: vesting.amount, y: MAX_SHARE - lockedShare - vesting.shareClaimed, denominator: MAX_SHARE
|
|
611
|
+
});
|
|
604
612
|
}
|
|
605
613
|
|
|
606
614
|
if (claimAmount != 0) {
|
|
@@ -682,7 +690,9 @@ abstract contract JBDistributor is IJBDistributor {
|
|
|
682
690
|
}
|
|
683
691
|
|
|
684
692
|
// Keep a reference to the amount of tokens being claimed.
|
|
685
|
-
uint256 tokenAmount = mulDiv(
|
|
693
|
+
uint256 tokenAmount = mulDiv({
|
|
694
|
+
x: distributable, y: _tokenStake({hook: hook, tokenId: tokenId}), denominator: totalStakeAmount
|
|
695
|
+
});
|
|
686
696
|
|
|
687
697
|
// Skip zero-amount entries to prevent stalling latestVestedIndexOf advancement.
|
|
688
698
|
if (tokenAmount == 0) {
|
|
@@ -49,14 +49,14 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
|
|
|
49
49
|
//*********************************************************************//
|
|
50
50
|
|
|
51
51
|
/// @param directory The JB directory used to verify terminal/controller callers.
|
|
52
|
-
/// @param
|
|
53
|
-
/// @param
|
|
52
|
+
/// @param initialRoundDuration The duration of each round, specified in seconds.
|
|
53
|
+
/// @param initialVestingRounds The number of rounds until tokens are fully vested.
|
|
54
54
|
constructor(
|
|
55
55
|
IJBDirectory directory,
|
|
56
|
-
uint256
|
|
57
|
-
uint256
|
|
56
|
+
uint256 initialRoundDuration,
|
|
57
|
+
uint256 initialVestingRounds
|
|
58
58
|
)
|
|
59
|
-
JBDistributor(
|
|
59
|
+
JBDistributor(initialRoundDuration, initialVestingRounds)
|
|
60
60
|
{
|
|
61
61
|
DIRECTORY = directory;
|
|
62
62
|
}
|
|
@@ -79,7 +79,7 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
|
|
|
79
79
|
function processSplitWith(JBSplitHookContext calldata context) external payable override {
|
|
80
80
|
// Only terminals and controllers for the project can call this.
|
|
81
81
|
if (
|
|
82
|
-
!DIRECTORY.isTerminalOf(context.projectId, IJBTerminal(msg.sender))
|
|
82
|
+
!DIRECTORY.isTerminalOf({projectId: context.projectId, terminal: IJBTerminal(msg.sender)})
|
|
83
83
|
&& DIRECTORY.controllerOf(context.projectId) != IERC165(msg.sender)
|
|
84
84
|
) revert JBTokenDistributor_Unauthorized({projectId: context.projectId, caller: msg.sender});
|
|
85
85
|
|
|
@@ -160,7 +160,8 @@ contract JBTokenDistributor is JBDistributor, IJBTokenDistributor {
|
|
|
160
160
|
if (tokenId >> 160 != 0) revert JBTokenDistributor_InvalidTokenId({tokenId: tokenId});
|
|
161
161
|
// The high bits were checked above, so this cast recovers the encoded address.
|
|
162
162
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
163
|
-
|
|
163
|
+
address account = address(uint160(tokenId));
|
|
164
|
+
tokenStakeAmount = IVotes(hook).getPastVotes({account: account, timepoint: roundSnapshotBlock[currentRound()]});
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
/// @notice The total supply of votes at a specific block.
|