@bananapus/core-v6 0.0.38 → 0.0.39
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/foundry.toml +1 -1
- package/package.json +1 -1
- package/src/JBChainlinkV3PriceFeed.sol +4 -1
- package/src/JBChainlinkV3SequencerPriceFeed.sol +4 -2
- package/src/JBController.sol +52 -43
- package/src/JBDeadline.sol +4 -4
- package/src/JBDirectory.sol +34 -32
- package/src/JBERC20.sol +5 -4
- package/src/JBFeelessAddresses.sol +6 -3
- package/src/JBFundAccessLimits.sol +25 -21
- package/src/JBMultiTerminal.sol +53 -50
- package/src/JBPermissions.sol +34 -37
- package/src/JBPrices.sol +23 -18
- package/src/JBProjects.sol +6 -3
- package/src/JBRulesets.sol +44 -41
- package/src/JBSplits.sol +18 -16
- package/src/JBTerminalStore.sol +26 -19
- package/src/JBTokens.sol +36 -26
- package/src/abstract/JBControlled.sol +3 -1
- package/src/abstract/JBPermissioned.sol +3 -1
- package/src/enums/JBApprovalStatus.sol +7 -1
- package/src/interfaces/IJBController.sol +3 -2
- package/src/interfaces/IJBDirectory.sol +3 -1
- package/src/interfaces/IJBMultiTerminal.sol +3 -2
- package/src/interfaces/IJBPermissions.sol +2 -1
- package/src/interfaces/IJBPrices.sol +3 -1
- package/src/interfaces/IJBRulesets.sol +2 -1
- package/src/interfaces/IJBSplits.sol +2 -1
- package/src/interfaces/IJBTerminal.sol +3 -1
- package/src/interfaces/IJBTerminalStore.sol +3 -1
- package/src/interfaces/IJBTokens.sol +2 -1
- package/src/libraries/JBCashOuts.sol +6 -1
- package/src/libraries/JBConstants.sol +12 -3
- package/src/libraries/JBCurrencyIds.sol +2 -0
- package/src/libraries/JBFees.sol +5 -1
- package/src/libraries/JBFixedPointNumber.sol +2 -0
- package/src/libraries/JBPayoutSplitGroupLib.sol +5 -2
- package/src/libraries/JBRulesetMetadataResolver.sol +4 -0
- package/src/libraries/JBSplitGroupIds.sol +2 -1
- package/src/libraries/JBSurplus.sol +3 -1
- package/src/periphery/JBMatchingPriceFeed.sol +2 -0
- package/src/structs/JBAccountingContext.sol +7 -4
- package/src/structs/JBFundAccessLimitGroup.sol +10 -17
- package/src/structs/JBRuleset.sol +18 -26
- package/src/structs/JBRulesetConfig.sol +13 -25
- package/src/structs/JBRulesetMetadata.sol +25 -32
package/src/JBRulesets.sol
CHANGED
|
@@ -12,12 +12,14 @@ import {JBConstants} from "./libraries/JBConstants.sol";
|
|
|
12
12
|
import {JBRuleset} from "./structs/JBRuleset.sol";
|
|
13
13
|
import {JBRulesetWeightCache} from "./structs/JBRulesetWeightCache.sol";
|
|
14
14
|
|
|
15
|
-
/// @notice
|
|
16
|
-
///
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
///
|
|
15
|
+
/// @notice Stores and manages the economic rules for every Juicebox project. A "ruleset" defines how a project behaves
|
|
16
|
+
/// for a period of time: its token issuance weight, cash-out tax rate, payout limits, reserved rate, and more.
|
|
17
|
+
/// Projects queue future rulesets to schedule changes; once a ruleset's duration expires, the next approved ruleset
|
|
18
|
+
/// takes effect automatically.
|
|
19
|
+
/// @dev Rulesets form a linked list via `basedOnId`. Each ruleset ID is the unix timestamp when it was first stored.
|
|
20
|
+
/// Weight decays across cycles using `weightCutPercent` — if many cycles elapse, the decay is computed iteratively
|
|
21
|
+
/// (capped at 20,000 iterations; use `updateRulesetWeightCache` for longer gaps). Approval hooks can gate whether a
|
|
22
|
+
/// queued ruleset actually takes effect.
|
|
21
23
|
contract JBRulesets is JBControlled, IJBRulesets {
|
|
22
24
|
//*********************************************************************//
|
|
23
25
|
// --------------------------- custom errors ------------------------- //
|
|
@@ -86,32 +88,20 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
86
88
|
// ---------------------- external transactions ---------------------- //
|
|
87
89
|
//*********************************************************************//
|
|
88
90
|
|
|
89
|
-
/// @notice Queues
|
|
90
|
-
///
|
|
91
|
+
/// @notice Queues a new ruleset for a project. The ruleset takes effect after the current one ends (or immediately
|
|
92
|
+
/// if the current ruleset has `duration = 0`). Must be approved by the previous ruleset's approval hook.
|
|
93
|
+
/// @dev Only the project's current controller can call this.
|
|
91
94
|
/// @param projectId The ID of the project to queue the ruleset for.
|
|
92
|
-
/// @param duration
|
|
93
|
-
///
|
|
94
|
-
///
|
|
95
|
-
///
|
|
96
|
-
///
|
|
97
|
-
///
|
|
98
|
-
///
|
|
99
|
-
///
|
|
100
|
-
///
|
|
101
|
-
/// @param
|
|
102
|
-
/// `weight`
|
|
103
|
-
/// by.
|
|
104
|
-
/// - If a ruleset specifies a non-zero `weight`, the `weightCutPercent` does not apply.
|
|
105
|
-
/// - If the `weightCutPercent` is 0, the `weight` stays the same.
|
|
106
|
-
/// - If the `weightCutPercent` is 10% of `JBConstants.MAX_WEIGHT_CUT_PERCENT`, next ruleset's `weight` will be 90%
|
|
107
|
-
/// of the
|
|
108
|
-
/// current
|
|
109
|
-
/// one.
|
|
110
|
-
/// @param approvalHook A contract which dictates whether a proposed ruleset should be accepted or rejected. It can
|
|
111
|
-
/// be used to constrain a project owner's ability to change ruleset parameters over time.
|
|
112
|
-
/// @param metadata Arbitrary extra data to associate with this ruleset. This metadata is not used by `JBRulesets`.
|
|
113
|
-
/// @param mustStartAtOrAfter The earliest time the ruleset can start. The ruleset cannot start before this
|
|
114
|
-
/// timestamp.
|
|
95
|
+
/// @param duration How long the ruleset lasts (seconds). 0 = no auto-cycling (stays active until explicitly
|
|
96
|
+
/// replaced). When a duration ends without a queued replacement, the ruleset rolls over with decayed weight.
|
|
97
|
+
/// @param weight Tokens minted per unit paid (18 decimals). Terminals divide payment amount by weight to determine
|
|
98
|
+
/// issuance. A value of 1 means "inherit decayed weight from previous ruleset".
|
|
99
|
+
/// @param weightCutPercent How much to reduce weight each auto-cycle (out of 1,000,000,000). Only applies when no
|
|
100
|
+
/// explicit replacement is queued. 0 = no decay. 100,000,000 = 10% cut per cycle.
|
|
101
|
+
/// @param approvalHook A contract that gates whether the *next* queued ruleset can take effect (e.g. `JBDeadline`
|
|
102
|
+
/// for minimum notice periods). Set to address(0) for no approval gate.
|
|
103
|
+
/// @param metadata Packed 256-bit field decoded by `JBRulesetMetadataResolver`. Not used by `JBRulesets` itself.
|
|
104
|
+
/// @param mustStartAtOrAfter The earliest unix timestamp the ruleset can start.
|
|
115
105
|
/// @return The struct of the new ruleset.
|
|
116
106
|
function queueFor(
|
|
117
107
|
uint256 projectId,
|
|
@@ -169,6 +159,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
169
159
|
uint256 latestId = latestRulesetIdOf[projectId];
|
|
170
160
|
|
|
171
161
|
// The new rulesetId timestamp is now, or an increment from now if the current timestamp is taken.
|
|
162
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
172
163
|
uint256 rulesetId = latestId >= block.timestamp ? latestId + 1 : block.timestamp;
|
|
173
164
|
|
|
174
165
|
// Set up the ruleset by configuring intrinsic properties.
|
|
@@ -239,6 +230,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
239
230
|
* targetRuleset.duration;
|
|
240
231
|
|
|
241
232
|
// Determine the start timestamp to derive a weight from for the cache.
|
|
233
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
242
234
|
uint256 start = block.timestamp < maxStart ? block.timestamp : maxStart;
|
|
243
235
|
|
|
244
236
|
// The difference between the start of the latest queued ruleset and the start of the ruleset we're caching the
|
|
@@ -275,7 +267,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
275
267
|
// ------------------------- external views -------------------------- //
|
|
276
268
|
//*********************************************************************//
|
|
277
269
|
|
|
278
|
-
/// @notice
|
|
270
|
+
/// @notice Returns a paginated history of a project's rulesets, sorted newest-first.
|
|
279
271
|
/// @param projectId The ID of the project to get the rulesets of.
|
|
280
272
|
/// @param startingId The ID of the ruleset to begin with. This will be the latest ruleset in the result. If 0 is
|
|
281
273
|
/// passed, the project's latest ruleset will be used.
|
|
@@ -336,7 +328,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
336
328
|
}
|
|
337
329
|
}
|
|
338
330
|
|
|
339
|
-
/// @notice
|
|
331
|
+
/// @notice Whether the project's most recently queued ruleset has been approved, rejected, or is still pending.
|
|
340
332
|
/// @param projectId The ID of the project to check the approval status of.
|
|
341
333
|
/// @return The project's current approval status.
|
|
342
334
|
function currentApprovalStatusForLatestRulesetOf(uint256 projectId)
|
|
@@ -354,12 +346,11 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
354
346
|
return _approvalStatusOf({projectId: projectId, ruleset: ruleset});
|
|
355
347
|
}
|
|
356
348
|
|
|
357
|
-
/// @notice
|
|
358
|
-
///
|
|
359
|
-
/// @dev
|
|
360
|
-
///
|
|
361
|
-
///
|
|
362
|
-
/// ruleset it is based on.
|
|
349
|
+
/// @notice Returns the ruleset currently governing a project. If the stored ruleset has auto-cycled, simulates
|
|
350
|
+
/// cycling with weight decay and returns the simulated current cycle.
|
|
351
|
+
/// @dev Returns an empty ruleset (all zeros) if the project has never had a ruleset queued.
|
|
352
|
+
/// @dev Payout limits reset each cycle (keyed by `cycleNumber`), but the `rulesetId` stays the same across
|
|
353
|
+
/// auto-cycles of the same stored ruleset.
|
|
363
354
|
/// @param projectId The ID of the project to get the current ruleset of.
|
|
364
355
|
/// @return ruleset The project's current ruleset.
|
|
365
356
|
function currentOf(uint256 projectId) external view override returns (JBRuleset memory ruleset) {
|
|
@@ -411,6 +402,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
411
402
|
// the ruleset that the latest is based on, which has the latest approved configuration.
|
|
412
403
|
while (
|
|
413
404
|
(approvalStatus != JBApprovalStatus.Approved && approvalStatus != JBApprovalStatus.Empty)
|
|
405
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
414
406
|
|| block.timestamp < ruleset.start
|
|
415
407
|
) {
|
|
416
408
|
rulesetId = ruleset.basedOnId;
|
|
@@ -466,8 +458,9 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
466
458
|
approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
|
|
467
459
|
}
|
|
468
460
|
|
|
469
|
-
/// @notice
|
|
470
|
-
///
|
|
461
|
+
/// @notice Returns the ruleset that will take effect after the current one ends. This could be an explicitly queued
|
|
462
|
+
/// ruleset or a simulated auto-cycle of the current one (with decayed weight).
|
|
463
|
+
/// @dev Returns an empty ruleset (all zeros) if there is no upcoming ruleset.
|
|
471
464
|
/// @param projectId The ID of the project to get the upcoming ruleset of.
|
|
472
465
|
/// @return ruleset The struct for the project's upcoming ruleset.
|
|
473
466
|
function upcomingOf(uint256 projectId) external view override returns (JBRuleset memory ruleset) {
|
|
@@ -509,6 +502,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
509
502
|
// If the latest ruleset starts in the future, it must start in the distant future
|
|
510
503
|
// Since its not the upcoming approvable ruleset. In this case, base the upcoming ruleset on the base
|
|
511
504
|
// ruleset.
|
|
505
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
512
506
|
while (ruleset.start > block.timestamp) {
|
|
513
507
|
ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId, withMetadata: true});
|
|
514
508
|
}
|
|
@@ -747,12 +741,15 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
747
741
|
// OR it hasn't started but it is likely to be approved and takes place before the proposed one,
|
|
748
742
|
// set the struct to be the ruleset it's based on, which carries the latest approved ruleset.
|
|
749
743
|
if (
|
|
744
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
750
745
|
(block.timestamp >= baseRuleset.start
|
|
751
746
|
&& approvalStatus != JBApprovalStatus.Approved
|
|
752
747
|
&& approvalStatus != JBApprovalStatus.Empty)
|
|
748
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
753
749
|
|| (block.timestamp < baseRuleset.start
|
|
754
750
|
&& mustStartAtOrAfter < baseRuleset.start + baseRuleset.duration
|
|
755
751
|
&& approvalStatus != JBApprovalStatus.Approved)
|
|
752
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
756
753
|
|| (block.timestamp < baseRuleset.start
|
|
757
754
|
&& mustStartAtOrAfter >= baseRuleset.start + baseRuleset.duration
|
|
758
755
|
&& approvalStatus != JBApprovalStatus.Approved
|
|
@@ -955,11 +952,13 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
955
952
|
do {
|
|
956
953
|
// If the latest ruleset is expired, return an empty ruleset.
|
|
957
954
|
// A ruleset with a duration of 0 cannot expire.
|
|
955
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
958
956
|
if (ruleset.duration != 0 && block.timestamp >= ruleset.start + ruleset.duration) {
|
|
959
957
|
return 0;
|
|
960
958
|
}
|
|
961
959
|
|
|
962
960
|
// Return the ruleset's `rulesetId` if it has started.
|
|
961
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
963
962
|
if (block.timestamp >= ruleset.start) {
|
|
964
963
|
return ruleset.id;
|
|
965
964
|
}
|
|
@@ -1047,6 +1046,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
1047
1046
|
// future.
|
|
1048
1047
|
uint256 mustStartAtOrAfter = !allowMidRuleset
|
|
1049
1048
|
? block.timestamp + 1
|
|
1049
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
1050
1050
|
: baseRuleset.duration >= block.timestamp ? 1 : block.timestamp - baseRuleset.duration + 1;
|
|
1051
1051
|
|
|
1052
1052
|
// Calculate what the start time should be.
|
|
@@ -1103,6 +1103,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
1103
1103
|
|
|
1104
1104
|
// There is no upcoming ruleset if the latest ruleset has already started.
|
|
1105
1105
|
// slither-disable-next-line incorrect-equality
|
|
1106
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
1106
1107
|
if (block.timestamp >= ruleset.start) return 0;
|
|
1107
1108
|
|
|
1108
1109
|
// If this is the first ruleset, it is queued.
|
|
@@ -1120,6 +1121,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
1120
1121
|
baseRuleset = _getStructFor({projectId: projectId, rulesetId: basedOnId, withMetadata: false});
|
|
1121
1122
|
|
|
1122
1123
|
// If the base ruleset starts in the future,
|
|
1124
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
1123
1125
|
if (block.timestamp < baseRuleset.start) {
|
|
1124
1126
|
// Set the `rulesetId` to the one found.
|
|
1125
1127
|
rulesetId = baseRuleset.id;
|
|
@@ -1135,6 +1137,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
1135
1137
|
ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId, withMetadata: false});
|
|
1136
1138
|
|
|
1137
1139
|
// If the latest ruleset doesn't start until after another base ruleset return 0.
|
|
1140
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
1138
1141
|
if (baseRuleset.duration != 0 && block.timestamp < ruleset.start - baseRuleset.duration) {
|
|
1139
1142
|
return 0;
|
|
1140
1143
|
}
|
package/src/JBSplits.sol
CHANGED
|
@@ -9,7 +9,11 @@ import {JBConstants} from "./libraries/JBConstants.sol";
|
|
|
9
9
|
import {JBSplit} from "./structs/JBSplit.sol";
|
|
10
10
|
import {JBSplitGroup} from "./structs/JBSplitGroup.sol";
|
|
11
11
|
|
|
12
|
-
/// @notice
|
|
12
|
+
/// @notice Manages how a project distributes its payouts and reserved tokens. Each "split" specifies a recipient and
|
|
13
|
+
/// their share (as a fraction of 1,000,000,000). Splits can be locked until a timestamp — locked splits cannot be
|
|
14
|
+
/// removed or reduced until the lock expires, providing recipients with guaranteed revenue streams.
|
|
15
|
+
/// @dev Splits are organized by project, ruleset, and group. Ruleset 0 is the fallback used when no splits are set for
|
|
16
|
+
/// the active ruleset. The payout group ID is derived from the token address being distributed.
|
|
13
17
|
contract JBSplits is JBControlled, IJBSplits {
|
|
14
18
|
//*********************************************************************//
|
|
15
19
|
// --------------------------- custom errors ------------------------- //
|
|
@@ -75,16 +79,15 @@ contract JBSplits is JBControlled, IJBSplits {
|
|
|
75
79
|
// ---------------------- external transactions ---------------------- //
|
|
76
80
|
//*********************************************************************//
|
|
77
81
|
|
|
78
|
-
/// @notice
|
|
79
|
-
///
|
|
80
|
-
///
|
|
81
|
-
///
|
|
82
|
-
///
|
|
83
|
-
///
|
|
82
|
+
/// @notice Configure how a project distributes funds to its split recipients. Each split group defines a list of
|
|
83
|
+
/// recipients (with percentage shares) for a specific purpose (e.g. payouts in ETH, reserved token distribution).
|
|
84
|
+
/// @dev Only the project's controller can set splits — unless the group ID encodes `msg.sender` in its lower 160
|
|
85
|
+
/// bits with non-zero upper 96 bits, which enables self-managed splits (e.g. hooks managing their own groups).
|
|
86
|
+
/// @dev Locked splits cannot be removed or reduced until their lock expires. The new groups must include all
|
|
87
|
+
/// currently-locked splits.
|
|
84
88
|
/// @param projectId The ID of the project to set the split groups of.
|
|
85
|
-
/// @param rulesetId The ID of the ruleset the split groups should be active in.
|
|
86
|
-
///
|
|
87
|
-
/// project's owner.
|
|
89
|
+
/// @param rulesetId The ID of the ruleset the split groups should be active in. Pass 0 to set the default splits
|
|
90
|
+
/// (used when no ruleset-specific splits are configured). The default's default is the project's owner.
|
|
88
91
|
/// @param splitGroups An array of split groups to set.
|
|
89
92
|
function setSplitGroupsOf(
|
|
90
93
|
uint256 projectId,
|
|
@@ -127,13 +130,11 @@ contract JBSplits is JBControlled, IJBSplits {
|
|
|
127
130
|
// ------------------------- external views -------------------------- //
|
|
128
131
|
//*********************************************************************//
|
|
129
132
|
|
|
130
|
-
/// @notice Get the split
|
|
131
|
-
///
|
|
132
|
-
/// ruleset aren't set.
|
|
133
|
-
/// @dev If splits aren't found at the given `rulesetId`, they'll be sought in the FALLBACK_RULESET_ID of 0.
|
|
133
|
+
/// @notice Get the list of split recipients for a project's group within a given ruleset. Falls back to the
|
|
134
|
+
/// default splits (ruleset 0) if none are set for the specific ruleset.
|
|
134
135
|
/// @param projectId The ID of the project to get splits for.
|
|
135
|
-
/// @param rulesetId
|
|
136
|
-
/// @param groupId The
|
|
136
|
+
/// @param rulesetId The ruleset to look up splits in. Falls back to ruleset 0 if none are found.
|
|
137
|
+
/// @param groupId The split group ID (e.g. token address for payout groups, or a custom ID for reserved tokens).
|
|
137
138
|
/// @return splits An array of all splits for the project.
|
|
138
139
|
function splitsOf(
|
|
139
140
|
uint256 projectId,
|
|
@@ -175,6 +176,7 @@ contract JBSplits is JBControlled, IJBSplits {
|
|
|
175
176
|
for (uint256 i; i < numberOfCurrentSplits;) {
|
|
176
177
|
// If not locked, continue.
|
|
177
178
|
if (
|
|
179
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
178
180
|
block.timestamp < currentSplits[i].lockedUntil
|
|
179
181
|
&& !_includesLockedSplits({splits: splits, lockedSplit: currentSplits[i]})
|
|
180
182
|
) {
|
package/src/JBTerminalStore.sol
CHANGED
|
@@ -25,8 +25,12 @@ import {JBPayHookSpecification} from "./structs/JBPayHookSpecification.sol";
|
|
|
25
25
|
import {JBRuleset} from "./structs/JBRuleset.sol";
|
|
26
26
|
import {JBTokenAmount} from "./structs/JBTokenAmount.sol";
|
|
27
27
|
|
|
28
|
-
/// @notice
|
|
29
|
-
///
|
|
28
|
+
/// @notice The accounting engine behind every terminal. Records project balances, enforces payout limits and surplus
|
|
29
|
+
/// allowances, calculates how many tokens to mint per payment (based on the ruleset's weight and currency), and
|
|
30
|
+
/// determines how much a token holder receives when cashing out (via the bonding curve in `JBCashOuts`).
|
|
31
|
+
/// @dev Terminals call `recordPaymentFrom`, `recordPayoutFor`, `recordUsedAllowanceFrom`, and `recordCashOutFor` to
|
|
32
|
+
/// update state. This contract also handles data hook integration — if a ruleset specifies a data hook, it is called
|
|
33
|
+
/// before recording to potentially override token counts or specify pay/cash-out hooks.
|
|
30
34
|
contract JBTerminalStore is IJBTerminalStore {
|
|
31
35
|
// A library that parses the packed ruleset metadata into a friendlier format.
|
|
32
36
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
@@ -161,8 +165,9 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
161
165
|
// ---------------------- external transactions ---------------------- //
|
|
162
166
|
//*********************************************************************//
|
|
163
167
|
|
|
164
|
-
/// @notice
|
|
165
|
-
///
|
|
168
|
+
/// @notice Registers which tokens a terminal can accept for a project, along with their decimal precision and
|
|
169
|
+
/// currency. Called by the terminal when `addAccountingContextsFor` is invoked.
|
|
170
|
+
/// @dev Uses `msg.sender` as the terminal address. Reverts if the current ruleset disallows adding contexts.
|
|
166
171
|
/// @param projectId The ID of the project.
|
|
167
172
|
/// @param contexts The accounting contexts to record.
|
|
168
173
|
function recordAccountingContextOf(uint256 projectId, JBAccountingContext[] calldata contexts) external override {
|
|
@@ -232,12 +237,11 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
232
237
|
balanceOf[msg.sender][projectId][token] = balanceOf[msg.sender][projectId][token] + amount;
|
|
233
238
|
}
|
|
234
239
|
|
|
235
|
-
/// @notice Records a cash out
|
|
236
|
-
/// @dev
|
|
237
|
-
///
|
|
238
|
-
/// burned.
|
|
240
|
+
/// @notice Records a cash out — calculates how many terminal tokens a holder receives for burning project tokens.
|
|
241
|
+
/// @dev Uses the data hook if configured, otherwise applies the bonding curve formula based on cash out tax rate,
|
|
242
|
+
/// surplus, and supply. The terminal calls this before actually burning tokens and transferring funds.
|
|
239
243
|
/// @param holder The account that is cashing out tokens.
|
|
240
|
-
/// @param projectId The ID of the project being
|
|
244
|
+
/// @param projectId The ID of the project being cashed out from.
|
|
241
245
|
/// @param cashOutCount The number of project tokens to cash out, as supplied by the caller and later burned by the
|
|
242
246
|
/// terminal, as a fixed point number with 18 decimals.
|
|
243
247
|
/// @param tokenToReclaim The token being reclaimed by the cash out.
|
|
@@ -310,9 +314,9 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
310
314
|
}
|
|
311
315
|
}
|
|
312
316
|
|
|
313
|
-
/// @notice Records a payment to
|
|
314
|
-
///
|
|
315
|
-
///
|
|
317
|
+
/// @notice Records a payment — calculates how many project tokens to mint based on the payment amount and the
|
|
318
|
+
/// current ruleset's weight. Uses the data hook if configured, otherwise mints proportionally.
|
|
319
|
+
/// @dev Called by the terminal after accepting funds. Updates the project's recorded balance.
|
|
316
320
|
/// @param payer The address that made the payment to the terminal.
|
|
317
321
|
/// @param amount The amount of tokens being paid. Includes the token being paid, their value, the number of
|
|
318
322
|
/// decimals included, and the currency of the amount.
|
|
@@ -353,10 +357,10 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
353
357
|
}
|
|
354
358
|
}
|
|
355
359
|
|
|
356
|
-
/// @notice Records a payout
|
|
357
|
-
///
|
|
358
|
-
///
|
|
359
|
-
///
|
|
360
|
+
/// @notice Records a payout — decrements the project's balance and enforces the payout limit. Called by the
|
|
361
|
+
/// terminal during `sendPayoutsOf`.
|
|
362
|
+
/// @dev Reverts if the total payouts for this cycle would exceed the ruleset's payout limit. The balance is
|
|
363
|
+
/// decremented before validation (safe because the entire tx reverts atomically on failure).
|
|
360
364
|
/// @param projectId The ID of the project that is paying out funds.
|
|
361
365
|
/// @param token The token being paid out.
|
|
362
366
|
/// @param amount The amount to pay out (use from the payout limit), as a fixed point number.
|
|
@@ -428,7 +432,8 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
428
432
|
usedPayoutLimitOf[msg.sender][projectId][token][ruleset.cycleNumber][currency] = newUsedPayoutLimitOf;
|
|
429
433
|
}
|
|
430
434
|
|
|
431
|
-
/// @notice Records
|
|
435
|
+
/// @notice Records a terminal migration — zeros out the project's balance and returns the amount being moved to
|
|
436
|
+
/// the new terminal. The current ruleset must allow terminal migration.
|
|
432
437
|
/// @param projectId The ID of the project being migrated.
|
|
433
438
|
/// @param token The token being migrated.
|
|
434
439
|
/// @return balance The project's current balance (which is being migrated), as a fixed point number with the same
|
|
@@ -449,8 +454,10 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
449
454
|
balanceOf[msg.sender][projectId][token] = 0;
|
|
450
455
|
}
|
|
451
456
|
|
|
452
|
-
/// @notice Records a
|
|
453
|
-
///
|
|
457
|
+
/// @notice Records a surplus allowance withdrawal — takes funds from the project's surplus (the balance above
|
|
458
|
+
/// payout limits) and enforces the surplus allowance cap.
|
|
459
|
+
/// @dev Called by the terminal during `useAllowanceOf`. Unlike payouts, surplus withdrawals go directly to a
|
|
460
|
+
/// beneficiary rather than through splits.
|
|
454
461
|
/// @param projectId The ID of the project to use the surplus allowance of.
|
|
455
462
|
/// @param token The token whose balances should contribute to the surplus allowance being reclaimed from.
|
|
456
463
|
/// @param amount The amount to use from the surplus allowance, as a fixed point number.
|
package/src/JBTokens.sol
CHANGED
|
@@ -8,12 +8,12 @@ import {IJBDirectory} from "./interfaces/IJBDirectory.sol";
|
|
|
8
8
|
import {IJBToken} from "./interfaces/IJBToken.sol";
|
|
9
9
|
import {IJBTokens} from "./interfaces/IJBTokens.sol";
|
|
10
10
|
|
|
11
|
-
/// @notice Manages
|
|
12
|
-
///
|
|
13
|
-
///
|
|
14
|
-
/// @dev The total supply
|
|
15
|
-
///
|
|
16
|
-
///
|
|
11
|
+
/// @notice Manages the dual-token system for every Juicebox project. When someone pays a project, the controller mints
|
|
12
|
+
/// tokens here — initially as internal "credits" tracked by this contract. Once a project deploys or attaches an
|
|
13
|
+
/// ERC-20, holders can claim their credits into transferable ERC-20 tokens. Burns always consume credits first.
|
|
14
|
+
/// @dev The total supply reported by `totalSupplyOf` is credits + ERC-20 supply combined, and is used by the terminal
|
|
15
|
+
/// to calculate cash-out values. Projects can deploy a new ERC-20 via `deployERC20For` or bring their own via
|
|
16
|
+
/// `setTokenFor` (must be 18 decimals).
|
|
17
17
|
contract JBTokens is JBControlled, IJBTokens {
|
|
18
18
|
//*********************************************************************//
|
|
19
19
|
// --------------------------- custom errors ------------------------- //
|
|
@@ -74,11 +74,11 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
74
74
|
// ---------------------- external transactions ---------------------- //
|
|
75
75
|
//*********************************************************************//
|
|
76
76
|
|
|
77
|
-
/// @notice
|
|
78
|
-
///
|
|
79
|
-
/// @dev Only a project's current controller can burn its tokens.
|
|
77
|
+
/// @notice Destroy a holder's tokens for a project. Credits (internal balance) are burned first; if more tokens
|
|
78
|
+
/// need burning, the remaining amount is burned from the holder's ERC-20 balance.
|
|
79
|
+
/// @dev Only a project's current controller can burn its tokens. Called during cash outs and manual burns.
|
|
80
80
|
/// @param holder The address that owns the tokens which are being burned.
|
|
81
|
-
/// @param projectId The ID of the project
|
|
81
|
+
/// @param projectId The ID of the project the burned tokens belong to.
|
|
82
82
|
/// @param count The number of tokens to burn.
|
|
83
83
|
function burnFrom(address holder, uint256 projectId, uint256 count) external override onlyControllerOf(projectId) {
|
|
84
84
|
// Get a reference to the project's current token.
|
|
@@ -131,7 +131,9 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
131
131
|
if (tokensToBurn > 0) token.burn({account: holder, amount: tokensToBurn});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
/// @notice
|
|
134
|
+
/// @notice Convert internal credits into transferable ERC-20 tokens. The credits are subtracted from the holder's
|
|
135
|
+
/// balance and the equivalent ERC-20 tokens are minted to the beneficiary. The project must have an ERC-20
|
|
136
|
+
/// deployed or attached.
|
|
135
137
|
/// @dev Only a project's controller can claim that project's tokens.
|
|
136
138
|
/// @param holder The owner of the credits being redeemed.
|
|
137
139
|
/// @param projectId The ID of the project whose tokens are being claimed.
|
|
@@ -180,8 +182,9 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
180
182
|
token.mint({account: beneficiary, amount: count});
|
|
181
183
|
}
|
|
182
184
|
|
|
183
|
-
/// @notice
|
|
184
|
-
///
|
|
185
|
+
/// @notice Deploy a new ERC-20 token for a project (cloned from the `TOKEN` implementation). Once deployed, holders
|
|
186
|
+
/// can claim their credits into this transferable token. A project can only have one token — this reverts if one
|
|
187
|
+
/// already exists.
|
|
185
188
|
/// @dev Only a project's controller can deploy its token.
|
|
186
189
|
/// @param projectId The ID of the project to deploy an ERC-20 token for.
|
|
187
190
|
/// @param name The ERC-20's name.
|
|
@@ -231,8 +234,10 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
231
234
|
token.initialize({name: name, symbol: symbol, tokens: address(this)});
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
/// @notice
|
|
235
|
-
///
|
|
237
|
+
/// @notice Create new tokens for a holder. If the project has an ERC-20 deployed, tokens are minted directly to
|
|
238
|
+
/// the holder's wallet. Otherwise, they're tracked as internal credits that can be claimed later.
|
|
239
|
+
/// @dev Only a project's current controller can mint its tokens. Called during payments and reserved token
|
|
240
|
+
/// distribution.
|
|
236
241
|
/// @param holder The address receiving the new tokens.
|
|
237
242
|
/// @param projectId The ID of the project to which the tokens belong.
|
|
238
243
|
/// @param count The number of tokens to mint.
|
|
@@ -276,11 +281,12 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
276
281
|
});
|
|
277
282
|
}
|
|
278
283
|
|
|
279
|
-
/// @notice
|
|
284
|
+
/// @notice Attach an existing ERC-20 token to a project (instead of deploying a new one). The token must use 18
|
|
285
|
+
/// decimals and must not already be attached to another project. A project can only have one token.
|
|
280
286
|
/// @dev Only a project's controller can set its token.
|
|
281
|
-
/// @dev If the
|
|
282
|
-
///
|
|
283
|
-
///
|
|
287
|
+
/// @dev WARNING: If the ERC-20 has supply minted outside this contract, that supply will be included in
|
|
288
|
+
/// `totalSupplyOf` and dilute cash-out values for all holders. Ensure the token's supply is appropriate before
|
|
289
|
+
/// calling.
|
|
284
290
|
/// @param projectId The ID of the project to set the token of.
|
|
285
291
|
/// @param token The new token's address.
|
|
286
292
|
function setTokenFor(uint256 projectId, IJBToken token) external override onlyControllerOf(projectId) {
|
|
@@ -308,7 +314,8 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
308
314
|
emit SetToken({projectId: projectId, token: token, caller: msg.sender});
|
|
309
315
|
}
|
|
310
316
|
|
|
311
|
-
/// @notice
|
|
317
|
+
/// @notice Update the name and symbol of a project's ERC-20 token. The project must already have a token deployed
|
|
318
|
+
/// or attached.
|
|
312
319
|
/// @dev Only a project's controller can set the token's name and symbol.
|
|
313
320
|
/// @param projectId The ID of the project whose token is being updated.
|
|
314
321
|
/// @param name The new name.
|
|
@@ -340,7 +347,8 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
340
347
|
token.setMetadata({name: name, symbol: symbol});
|
|
341
348
|
}
|
|
342
349
|
|
|
343
|
-
/// @notice
|
|
350
|
+
/// @notice Move internal credits from one account to another. Credits are non-transferable on their own (they're
|
|
351
|
+
/// just a balance in this contract), so this function enables transfers via the controller.
|
|
344
352
|
/// @dev Only a project's controller can transfer credits for that project.
|
|
345
353
|
/// @param holder The address to transfer credits from.
|
|
346
354
|
/// @param projectId The ID of the project whose credits are being transferred.
|
|
@@ -379,7 +387,8 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
379
387
|
// ------------------------- external views -------------------------- //
|
|
380
388
|
//*********************************************************************//
|
|
381
389
|
|
|
382
|
-
/// @notice
|
|
390
|
+
/// @notice Get a holder's complete balance for a project — both their internal credits and their ERC-20 token
|
|
391
|
+
/// balance combined.
|
|
383
392
|
/// @param holder The holder to get a balance for.
|
|
384
393
|
/// @param projectId The project to get the `holder`'s balance for.
|
|
385
394
|
/// @return balance The combined token and token credit balance of the `holder`.
|
|
@@ -400,11 +409,12 @@ contract JBTokens is JBControlled, IJBTokens {
|
|
|
400
409
|
// --------------------------- public views -------------------------- //
|
|
401
410
|
//*********************************************************************//
|
|
402
411
|
|
|
403
|
-
/// @notice
|
|
404
|
-
///
|
|
412
|
+
/// @notice Get the total token supply for a project — internal credits plus the ERC-20's `totalSupply()`. This is
|
|
413
|
+
/// the denominator used in cash-out bonding curve calculations.
|
|
414
|
+
/// @dev WARNING: Projects using `setTokenFor` with an external ERC-20 inherit that token's supply manipulation
|
|
405
415
|
/// surface. If the external token has a separate minting authority, `totalSupply()` can be inflated outside of
|
|
406
|
-
/// this contract,
|
|
407
|
-
///
|
|
416
|
+
/// this contract, diluting cash-out values for all holders. Projects using `deployERC20For` are safe because the
|
|
417
|
+
/// resulting `JBERC20` is exclusively owned by this `JBTokens` contract.
|
|
408
418
|
/// @param projectId The ID of the project to get the total supply of.
|
|
409
419
|
/// @return totalSupply The total supply of the project's tokens and token credits.
|
|
410
420
|
function totalSupplyOf(uint256 projectId) public view override returns (uint256 totalSupply) {
|
|
@@ -4,7 +4,9 @@ pragma solidity 0.8.28;
|
|
|
4
4
|
import {IJBControlled} from "./../interfaces/IJBControlled.sol";
|
|
5
5
|
import {IJBDirectory} from "./../interfaces/IJBDirectory.sol";
|
|
6
6
|
|
|
7
|
-
/// @notice
|
|
7
|
+
/// @notice Base contract that restricts certain functions to the project's current controller (as registered in
|
|
8
|
+
/// `JBDirectory`). Used by `JBTokens`, `JBSplits`, `JBFundAccessLimits`, `JBRulesets`, and `JBPrices` to ensure only
|
|
9
|
+
/// the controller can update project state.
|
|
8
10
|
abstract contract JBControlled is IJBControlled {
|
|
9
11
|
//*********************************************************************//
|
|
10
12
|
// --------------------------- custom errors -------------------------- //
|
|
@@ -6,7 +6,9 @@ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
|
|
|
6
6
|
import {IJBPermissioned} from "./../interfaces/IJBPermissioned.sol";
|
|
7
7
|
import {IJBPermissions} from "./../interfaces/IJBPermissions.sol";
|
|
8
8
|
|
|
9
|
-
/// @notice
|
|
9
|
+
/// @notice Base contract that provides permission-checking helpers. Contracts that inherit this can require that the
|
|
10
|
+
/// caller either *is* the account or has been granted a specific permission by that account (via `JBPermissions`).
|
|
11
|
+
/// @dev Used by `JBController`, `JBMultiTerminal`, `JBDirectory`, and others to enforce operator authorization.
|
|
10
12
|
abstract contract JBPermissioned is Context, IJBPermissioned {
|
|
11
13
|
//*********************************************************************//
|
|
12
14
|
// --------------------------- custom errors -------------------------- //
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
/// @notice
|
|
4
|
+
/// @notice The lifecycle states a queued ruleset can be in relative to its approval hook.
|
|
5
|
+
/// @dev `Empty` — no ruleset exists. `Upcoming` — queued but not yet eligible for approval check. `Active` —
|
|
6
|
+
/// currently
|
|
7
|
+
/// governing the project. `ApprovalExpected` — the deadline hasn't passed yet, expected to be approved.
|
|
8
|
+
/// `Approved` — passed the approval hook and will take effect. `Failed` — rejected by the approval hook; the
|
|
9
|
+
/// previous
|
|
10
|
+
/// ruleset continues.
|
|
5
11
|
enum JBApprovalStatus {
|
|
6
12
|
Empty,
|
|
7
13
|
Upcoming,
|
|
@@ -23,8 +23,9 @@ import {JBSplit} from "./../structs/JBSplit.sol";
|
|
|
23
23
|
import {JBSplitGroup} from "./../structs/JBSplitGroup.sol";
|
|
24
24
|
import {JBTerminalConfig} from "./../structs/JBTerminalConfig.sol";
|
|
25
25
|
|
|
26
|
-
/// @notice
|
|
27
|
-
/// project
|
|
26
|
+
/// @notice The interface for the protocol's project controller — launch projects, queue rulesets, mint/burn tokens,
|
|
27
|
+
/// deploy ERC-20s, distribute reserved tokens, and manage all project configuration. This is the primary contract
|
|
28
|
+
/// project owners and frontends interact with.
|
|
28
29
|
interface IJBController is IERC165, IJBProjectUriRegistry, IJBDirectoryAccessControl {
|
|
29
30
|
/// @notice Tokens were burned from a holder's balance.
|
|
30
31
|
/// @param holder The address whose tokens were burned.
|
|
@@ -6,7 +6,9 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
|
6
6
|
import {IJBProjects} from "./IJBProjects.sol";
|
|
7
7
|
import {IJBTerminal} from "./IJBTerminal.sol";
|
|
8
8
|
|
|
9
|
-
/// @notice
|
|
9
|
+
/// @notice Interface for the protocol's routing table. Tracks which terminals accept payments for each project and
|
|
10
|
+
/// which controller manages each project's rulesets and tokens. Used by frontends and contracts to discover where to
|
|
11
|
+
/// send funds.
|
|
10
12
|
interface IJBDirectory {
|
|
11
13
|
/// @notice A terminal was added to a project.
|
|
12
14
|
/// @param projectId The ID of the project the terminal was added to.
|
|
@@ -12,8 +12,9 @@ import {IJBTerminal} from "./IJBTerminal.sol";
|
|
|
12
12
|
import {IJBTerminalStore} from "./IJBTerminalStore.sol";
|
|
13
13
|
import {IJBTokens} from "./IJBTokens.sol";
|
|
14
14
|
|
|
15
|
-
/// @notice
|
|
16
|
-
///
|
|
15
|
+
/// @notice The interface for the protocol's multi-token payment terminal. Accepts ETH and ERC-20 payments, processes
|
|
16
|
+
/// cash outs (token redemptions), distributes payouts to splits, manages surplus allowance withdrawals, and handles
|
|
17
|
+
/// the 2.5% protocol fee lifecycle. The primary entry point for all fund movement in Juicebox.
|
|
17
18
|
interface IJBMultiTerminal is IJBTerminal, IJBFeeTerminal, IJBCashOutTerminal, IJBPayoutTerminal, IJBPermitTerminal {
|
|
18
19
|
/// @notice The directory of terminals and controllers for projects.
|
|
19
20
|
function DIRECTORY() external view returns (IJBDirectory);
|