@bananapus/core-v6 0.0.38 → 0.0.40
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 +67 -58
- package/src/JBDeadline.sol +4 -4
- package/src/JBDirectory.sol +38 -36
- package/src/JBERC20.sol +10 -9
- package/src/JBFeelessAddresses.sol +6 -3
- package/src/JBFundAccessLimits.sol +26 -22
- package/src/JBMultiTerminal.sol +128 -125
- package/src/JBPermissions.sol +35 -38
- package/src/JBPrices.sol +25 -20
- package/src/JBProjects.sol +6 -3
- package/src/JBRulesets.sol +45 -42
- package/src/JBSplits.sol +19 -17
- package/src/JBTerminalStore.sol +57 -50
- package/src/JBTokens.sol +42 -32
- package/src/abstract/JBControlled.sol +3 -1
- package/src/abstract/JBPermissioned.sol +3 -1
- package/src/enums/JBApprovalStatus.sol +7 -1
- package/src/interfaces/IJBCashOutTerminal.sol +5 -5
- package/src/interfaces/IJBController.sol +13 -12
- package/src/interfaces/IJBDirectory.sol +3 -1
- package/src/interfaces/IJBMigratable.sol +5 -5
- package/src/interfaces/IJBMultiTerminal.sol +3 -2
- package/src/interfaces/IJBPayoutTerminal.sol +3 -3
- package/src/interfaces/IJBPermissions.sol +6 -5
- package/src/interfaces/IJBPermitTerminal.sol +1 -1
- package/src/interfaces/IJBPrices.sol +7 -5
- package/src/interfaces/IJBRulesets.sol +2 -1
- package/src/interfaces/IJBSplits.sol +2 -1
- package/src/interfaces/IJBTerminal.sol +13 -11
- package/src/interfaces/IJBTerminalStore.sol +23 -21
- package/src/interfaces/IJBTokens.sol +8 -7
- package/src/libraries/JBCashOuts.sol +8 -3
- 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 +10 -7
- package/src/libraries/JBRulesetMetadataResolver.sol +4 -0
- package/src/libraries/JBSplitGroupIds.sol +2 -1
- package/src/libraries/JBSurplus.sol +4 -2
- package/src/periphery/JBMatchingPriceFeed.sol +2 -0
- package/src/structs/JBAccountingContext.sol +7 -4
- package/src/structs/JBAfterCashOutRecordedContext.sol +8 -8
- package/src/structs/JBAfterPayRecordedContext.sol +5 -5
- package/src/structs/JBBeforeCashOutRecordedContext.sol +7 -7
- package/src/structs/JBBeforePayRecordedContext.sol +5 -5
- package/src/structs/JBFundAccessLimitGroup.sol +10 -17
- package/src/structs/JBPermissionsData.sol +3 -3
- package/src/structs/JBRuleset.sol +18 -26
- package/src/structs/JBRulesetConfig.sol +13 -25
- package/src/structs/JBRulesetMetadata.sol +25 -32
- package/src/structs/JBSplitHookContext.sol +2 -2
package/src/JBPermissions.sol
CHANGED
|
@@ -7,8 +7,11 @@ import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol"
|
|
|
7
7
|
import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
|
|
8
8
|
import {JBPermissionsData} from "./structs/JBPermissionsData.sol";
|
|
9
9
|
|
|
10
|
-
/// @notice
|
|
11
|
-
///
|
|
10
|
+
/// @notice The permission system for Juicebox. Any address can authorize another address (an "operator") to perform
|
|
11
|
+
/// specific actions on its behalf — like queuing rulesets, distributing payouts, or managing terminals.
|
|
12
|
+
/// @dev Permissions are stored as a packed `uint256` bitmap: each of the 256 bits represents one permission's
|
|
13
|
+
/// on/off state. Project ID 0 is a wildcard that grants an operator access across all projects — use with caution.
|
|
14
|
+
/// @dev The ROOT permission (ID 1) implicitly grants every other permission for the scoped project.
|
|
12
15
|
contract JBPermissions is ERC2771Context, IJBPermissions {
|
|
13
16
|
//*********************************************************************//
|
|
14
17
|
// --------------------------- custom errors ------------------------- //
|
|
@@ -23,21 +26,21 @@ contract JBPermissions is ERC2771Context, IJBPermissions {
|
|
|
23
26
|
// ------------------------- public constants ------------------------ //
|
|
24
27
|
//*********************************************************************//
|
|
25
28
|
|
|
26
|
-
/// @notice The project ID
|
|
29
|
+
/// @notice The project ID that acts as a wildcard — granting the operator permissions across all projects owned
|
|
30
|
+
/// by
|
|
31
|
+
/// the account. Setting permissions for project ID 0 is powerful and should be done carefully.
|
|
27
32
|
uint256 public constant override WILDCARD_PROJECT_ID = 0;
|
|
28
33
|
|
|
29
34
|
//*********************************************************************//
|
|
30
35
|
// --------------------- public stored properties -------------------- //
|
|
31
36
|
//*********************************************************************//
|
|
32
37
|
|
|
33
|
-
/// @notice The
|
|
34
|
-
/// @dev
|
|
35
|
-
///
|
|
36
|
-
///
|
|
37
|
-
/// @dev Permissions are stored in a packed `uint256`. Each of the 256 bits represents the on/off state of a
|
|
38
|
-
/// permission. Applications can specify the significance of each permission ID.
|
|
38
|
+
/// @notice The packed permission bitmap that an account has granted to an operator for a specific project.
|
|
39
|
+
/// @dev Each bit in the returned `uint256` corresponds to a permission ID (0–255). A `1` bit means that
|
|
40
|
+
/// permission
|
|
41
|
+
/// is granted. See `JBPermissionIds` for the meaning of each ID.
|
|
39
42
|
/// @custom:param operator The address of the operator.
|
|
40
|
-
/// @custom:param account The address of the account
|
|
43
|
+
/// @custom:param account The address of the account operated on behalf of.
|
|
41
44
|
/// @custom:param projectId The project ID the permissions are scoped to. An ID of 0 grants permissions across all
|
|
42
45
|
/// projects.
|
|
43
46
|
mapping(address operator => mapping(address account => mapping(uint256 projectId => uint256)))
|
|
@@ -55,14 +58,12 @@ contract JBPermissions is ERC2771Context, IJBPermissions {
|
|
|
55
58
|
// ---------------------- external transactions ---------------------- //
|
|
56
59
|
//*********************************************************************//
|
|
57
60
|
|
|
58
|
-
/// @notice
|
|
59
|
-
/// @dev Only the
|
|
60
|
-
///
|
|
61
|
-
///
|
|
62
|
-
///
|
|
63
|
-
///
|
|
64
|
-
/// @param account The account setting its operators' permissions.
|
|
65
|
-
/// @param permissionsData The data which specifies the permissions the operator is being given.
|
|
61
|
+
/// @notice Grant or revoke permissions for an operator on a specific project.
|
|
62
|
+
/// @dev Only the account itself can set permissions without restriction. A ROOT operator on a specific project can
|
|
63
|
+
/// set non-ROOT permissions for that same project on the account's behalf, but cannot grant ROOT or set wildcard
|
|
64
|
+
/// (project ID 0) permissions — preventing privilege escalation.
|
|
65
|
+
/// @param account The account to configure operator permissions for.
|
|
66
|
+
/// @param permissionsData The operator address, project scope, and permission IDs to set.
|
|
66
67
|
function setPermissionsFor(address account, JBPermissionsData calldata permissionsData) external override {
|
|
67
68
|
// Pack the permission IDs into a uint256.
|
|
68
69
|
uint256 packed = _packedPermissions(permissionsData.permissionIds);
|
|
@@ -113,16 +114,14 @@ contract JBPermissions is ERC2771Context, IJBPermissions {
|
|
|
113
114
|
// ------------------------- external views -------------------------- //
|
|
114
115
|
//*********************************************************************//
|
|
115
116
|
|
|
116
|
-
/// @notice Check
|
|
117
|
-
/// @param operator The
|
|
118
|
-
/// @param account The account
|
|
119
|
-
/// @param projectId The project ID
|
|
120
|
-
/// @param permissionIds
|
|
121
|
-
/// @param includeRoot
|
|
122
|
-
///
|
|
123
|
-
/// @
|
|
124
|
-
/// specified permission on the wildcard project ID.
|
|
125
|
-
/// @return A flag indicating whether the operator has all specified permissions.
|
|
117
|
+
/// @notice Check whether an operator has *all* of the specified permissions for an account and project.
|
|
118
|
+
/// @param operator The address to check permissions for.
|
|
119
|
+
/// @param account The account that granted (or didn't grant) the permissions.
|
|
120
|
+
/// @param projectId The project ID to check within. Pass 0 to check the wildcard scope directly.
|
|
121
|
+
/// @param permissionIds The permission IDs that must all be present.
|
|
122
|
+
/// @param includeRoot If `true`, returns `true` immediately when the operator has the ROOT permission.
|
|
123
|
+
/// @param includeWildcardProjectId If `true`, also checks wildcard (project 0) permissions as a fallback.
|
|
124
|
+
/// @return Whether the operator holds every requested permission.
|
|
126
125
|
function hasPermissions(
|
|
127
126
|
address operator,
|
|
128
127
|
address account,
|
|
@@ -187,16 +186,14 @@ contract JBPermissions is ERC2771Context, IJBPermissions {
|
|
|
187
186
|
// -------------------------- public views --------------------------- //
|
|
188
187
|
//*********************************************************************//
|
|
189
188
|
|
|
190
|
-
/// @notice Check
|
|
191
|
-
/// @param operator The
|
|
192
|
-
/// @param account The account
|
|
193
|
-
/// @param projectId The project ID
|
|
194
|
-
/// @param permissionId The permission ID to
|
|
195
|
-
/// @param includeRoot
|
|
196
|
-
///
|
|
197
|
-
/// @
|
|
198
|
-
/// specified permission on the wildcard project ID.
|
|
199
|
-
/// @return A flag indicating whether the operator has the specified permission.
|
|
189
|
+
/// @notice Check whether an operator has a single permission for an account and project.
|
|
190
|
+
/// @param operator The address to check.
|
|
191
|
+
/// @param account The account that granted (or didn't grant) the permission.
|
|
192
|
+
/// @param projectId The project ID to check within. Pass 0 to check the wildcard scope directly.
|
|
193
|
+
/// @param permissionId The specific permission ID to look for (0–255).
|
|
194
|
+
/// @param includeRoot If `true`, returns `true` immediately when the operator has the ROOT permission.
|
|
195
|
+
/// @param includeWildcardProjectId If `true`, also checks wildcard (project 0) permissions as a fallback.
|
|
196
|
+
/// @return Whether the operator holds the requested permission.
|
|
200
197
|
function hasPermission(
|
|
201
198
|
address operator,
|
|
202
199
|
address account,
|
package/src/JBPrices.sol
CHANGED
|
@@ -13,12 +13,13 @@ import {IJBPriceFeed} from "./interfaces/IJBPriceFeed.sol";
|
|
|
13
13
|
import {IJBPrices} from "./interfaces/IJBPrices.sol";
|
|
14
14
|
import {IJBProjects} from "./interfaces/IJBProjects.sol";
|
|
15
15
|
|
|
16
|
-
/// @notice
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
///
|
|
21
|
-
///
|
|
16
|
+
/// @notice Provides currency conversion for the protocol. When a project's payout limits or surplus allowances are
|
|
17
|
+
/// denominated in a currency different from the token held in its terminal (e.g. USD limits with ETH held), this
|
|
18
|
+
/// contract resolves the exchange rate via registered price feeds (typically Chainlink oracles).
|
|
19
|
+
/// @dev Price feeds are immutable once set — they cannot be replaced or removed. This protects against oracle
|
|
20
|
+
/// manipulation via admin-key attacks. If a feed is misconfigured, operations using that pair will revert (DoS, not
|
|
21
|
+
/// fund loss). The inverse of any registered feed is auto-calculated. Projects can have their own feeds; project ID 0
|
|
22
|
+
/// holds protocol-wide defaults.
|
|
22
23
|
contract JBPrices is JBControlled, JBPermissioned, ERC2771Context, Ownable, IJBPrices {
|
|
23
24
|
//*********************************************************************//
|
|
24
25
|
// --------------------------- custom errors ------------------------- //
|
|
@@ -52,7 +53,7 @@ contract JBPrices is JBControlled, JBPermissioned, ERC2771Context, Ownable, IJBP
|
|
|
52
53
|
/// @custom:param projectId The ID of the project the feed applies to. Feeds stored in ID 0 are used by default for
|
|
53
54
|
/// all projects.
|
|
54
55
|
/// @custom:param pricingCurrency The currency the feed's resulting price is in terms of.
|
|
55
|
-
/// @custom:param unitCurrency The currency
|
|
56
|
+
/// @custom:param unitCurrency The currency the feed prices.
|
|
56
57
|
mapping(uint256 projectId => mapping(uint256 pricingCurrency => mapping(uint256 unitCurrency => IJBPriceFeed)))
|
|
57
58
|
public
|
|
58
59
|
override priceFeedFor;
|
|
@@ -85,15 +86,16 @@ contract JBPrices is JBControlled, JBPermissioned, ERC2771Context, Ownable, IJBP
|
|
|
85
86
|
// ---------------------- external transactions ---------------------- //
|
|
86
87
|
//*********************************************************************//
|
|
87
88
|
|
|
88
|
-
/// @notice
|
|
89
|
-
///
|
|
90
|
-
///
|
|
91
|
-
///
|
|
92
|
-
///
|
|
93
|
-
/// @
|
|
94
|
-
/// feed.
|
|
89
|
+
/// @notice Register a price feed that provides the exchange rate between two currencies. For example, registering
|
|
90
|
+
/// an ETH/USD feed allows payout limits denominated in USD to be enforced against ETH balances.
|
|
91
|
+
/// @dev Price feeds are immutable — once set for a currency pair, they cannot be replaced or removed. This
|
|
92
|
+
/// prevents
|
|
93
|
+
/// admin-key oracle manipulation. The inverse rate is auto-calculated, so registering A→B also provides B→A.
|
|
94
|
+
/// @dev Pass `projectId` = 0 to set a protocol-wide default (owner only). Non-zero project IDs require controller
|
|
95
|
+
/// authorization. A default feed for a pair blocks per-project overrides for that same pair.
|
|
96
|
+
/// @param projectId The ID of the project to add a feed for. Pass 0 for a protocol-wide default.
|
|
95
97
|
/// @param pricingCurrency The currency the feed's output price is in terms of.
|
|
96
|
-
/// @param unitCurrency The currency
|
|
98
|
+
/// @param unitCurrency The currency the feed prices.
|
|
97
99
|
/// @param feed The address of the price feed to add.
|
|
98
100
|
function addPriceFeedFor(
|
|
99
101
|
uint256 projectId,
|
|
@@ -156,11 +158,14 @@ contract JBPrices is JBControlled, JBPermissioned, ERC2771Context, Ownable, IJBP
|
|
|
156
158
|
// -------------------------- public views --------------------------- //
|
|
157
159
|
//*********************************************************************//
|
|
158
160
|
|
|
159
|
-
/// @notice
|
|
160
|
-
///
|
|
161
|
-
///
|
|
162
|
-
/// @
|
|
163
|
-
///
|
|
161
|
+
/// @notice Convert between currencies — returns how much of `pricingCurrency` one unit of `unitCurrency` is
|
|
162
|
+
/// worth.
|
|
163
|
+
/// For example, `pricePerUnitOf(id, USD, ETH, 18)` returns the USD price of 1 ETH with 18 decimals.
|
|
164
|
+
/// @dev Lookup order: project-specific feed → inverse of project feed → default feed (project 0) → inverse of
|
|
165
|
+
/// default. Reverts with `JBPrices_PriceFeedNotFound` if no feed exists in any direction.
|
|
166
|
+
/// @param projectId The ID of the project to check the feed for. Falls back to project 0 (protocol defaults).
|
|
167
|
+
/// @param pricingCurrency The currency the result is denominated in.
|
|
168
|
+
/// @param unitCurrency The currency to price.
|
|
164
169
|
/// @param decimals The number of decimals the returned fixed point price should include.
|
|
165
170
|
/// @return The `pricingCurrency` price of 1 `unitCurrency`, as a fixed point number with the specified number of
|
|
166
171
|
/// decimals.
|
package/src/JBProjects.sol
CHANGED
|
@@ -9,8 +9,9 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
|
9
9
|
import {IJBProjects} from "./interfaces/IJBProjects.sol";
|
|
10
10
|
import {IJBTokenUriResolver} from "./interfaces/IJBTokenUriResolver.sol";
|
|
11
11
|
|
|
12
|
-
/// @notice
|
|
13
|
-
///
|
|
12
|
+
/// @notice Each Juicebox project is an ERC-721 NFT. Whoever holds the NFT owns the project and can configure its
|
|
13
|
+
/// rulesets, terminals, and permissions. Projects are created with `createFor` and the resulting token ID is used as
|
|
14
|
+
/// the project's ID across the entire protocol.
|
|
14
15
|
contract JBProjects is ERC721, ERC2771Context, Ownable, IJBProjects {
|
|
15
16
|
//*********************************************************************//
|
|
16
17
|
// --------------------- public stored properties -------------------- //
|
|
@@ -50,7 +51,9 @@ contract JBProjects is ERC721, ERC2771Context, Ownable, IJBProjects {
|
|
|
50
51
|
// ---------------------- external transactions ---------------------- //
|
|
51
52
|
//*********************************************************************//
|
|
52
53
|
|
|
53
|
-
/// @notice
|
|
54
|
+
/// @notice Set the contract that resolves project NFT metadata (the `tokenURI`). This controls what artwork and
|
|
55
|
+
/// JSON metadata is returned for each project's ERC-721 token.
|
|
56
|
+
/// @dev Only this contract's owner can change the resolver.
|
|
54
57
|
/// @param resolver The address of the new resolver.
|
|
55
58
|
function setTokenUriResolver(IJBTokenUriResolver resolver) external override onlyOwner {
|
|
56
59
|
// Store the new resolver.
|
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
|
|
@@ -794,7 +791,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
|
|
|
794
791
|
/// @notice Initializes a ruleset with the specified properties.
|
|
795
792
|
/// @param projectId The ID of the project to initialize the ruleset for.
|
|
796
793
|
/// @param baseRuleset The ruleset struct to base the newly initialized one on.
|
|
797
|
-
/// @param rulesetId The `rulesetId` for the ruleset
|
|
794
|
+
/// @param rulesetId The `rulesetId` for the ruleset to initialize.
|
|
798
795
|
/// @param mustStartAtOrAfter The earliest time the ruleset can start. The ruleset cannot start before this
|
|
799
796
|
/// timestamp.
|
|
800
797
|
/// @param weight The weight to give the newly initialized ruleset.
|
|
@@ -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 to distribute.
|
|
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,
|
|
@@ -160,7 +161,7 @@ contract JBSplits is JBControlled, IJBSplits {
|
|
|
160
161
|
/// @notice Sets the splits for a group given a project, ruleset, and group ID.
|
|
161
162
|
/// @dev The new splits must include any currently set splits that are locked.
|
|
162
163
|
/// @dev The sum of the split `percent`s within one group must be less than 100%.
|
|
163
|
-
/// @param projectId The ID of the project
|
|
164
|
+
/// @param projectId The ID of the project to set splits for.
|
|
164
165
|
/// @param rulesetId The ID of the ruleset the splits should be considered active within.
|
|
165
166
|
/// @param groupId The ID of the group to set the splits within.
|
|
166
167
|
/// @param splits An array of splits to set.
|
|
@@ -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
|
) {
|