@bananapus/core-v6 0.0.80 → 0.0.82

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.
@@ -66,18 +66,43 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
66
66
  // --------------------------- custom errors ------------------------- //
67
67
  //*********************************************************************//
68
68
 
69
+ /// @notice Thrown when no terminal can be found to pay a fee in the given token.
69
70
  error JBMultiTerminal_FeeTerminalNotFound(address token);
71
+
72
+ /// @notice Thrown when a payout split routes back into the project that is paying out.
70
73
  error JBMultiTerminal_MintNotAllowed(uint256 projectId, address terminal);
74
+
75
+ /// @notice Thrown when native value is sent with an operation that does not accept it.
71
76
  error JBMultiTerminal_NoMsgValueAllowed(uint256 value);
77
+
78
+ /// @notice Thrown when a value exceeds the allowed maximum.
72
79
  error JBMultiTerminal_OverflowAlert(uint256 value, uint256 limit);
80
+
81
+ /// @notice Thrown when the Permit2 allowance is less than the amount being paid.
73
82
  error JBMultiTerminal_PermitAllowanceNotEnough(uint256 amount, uint256 allowance);
83
+
84
+ /// @notice Thrown when no terminal can be found to pay a recipient project's split in the given token.
74
85
  error JBMultiTerminal_RecipientProjectTerminalNotFound(uint256 projectId, address token);
86
+
87
+ /// @notice Thrown when a token transfer reenters during a balance-delta measurement.
75
88
  error JBMultiTerminal_ReentrantTokenTransfer(address token);
89
+
90
+ /// @notice Thrown when a split hook does not support the `IJBSplitHook` interface.
76
91
  error JBMultiTerminal_SplitHookInvalid(IJBSplitHook hook);
92
+
93
+ /// @notice Thrown when an approved temporary allowance was not fully consumed by the spender.
77
94
  error JBMultiTerminal_TemporaryAllowanceNotConsumed(address token, address spender, uint256 allowance);
95
+
96
+ /// @notice Thrown when migrating a terminal to itself.
78
97
  error JBMultiTerminal_TerminalMigrationToSelf(uint256 projectId, address token);
98
+
99
+ /// @notice Thrown when the terminal being migrated to does not accept the same token.
79
100
  error JBMultiTerminal_TerminalTokensIncompatible(uint256 projectId, address token, IJBTerminal terminal);
101
+
102
+ /// @notice Thrown when the terminal does not accept the given token.
80
103
  error JBMultiTerminal_TokenNotAccepted(address token);
104
+
105
+ /// @notice Thrown when a value is less than the required minimum.
81
106
  error JBMultiTerminal_UnderMin(uint256 value, uint256 min);
82
107
 
83
108
  //*********************************************************************//
@@ -109,7 +134,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
109
134
  /// @notice The contract that stores and manages the terminal's data.
110
135
  IJBTerminalStore public immutable override STORE;
111
136
 
112
- /// @notice The contract storing and managing project rulesets.
137
+ /// @notice The contract that manages token minting and burning.
113
138
  IJBTokens public immutable override TOKENS;
114
139
 
115
140
  //*********************************************************************//
@@ -146,25 +171,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
146
171
  /// @custom:param token The token the fees are held in.
147
172
  mapping(uint256 projectId => mapping(address token => uint256)) internal _nextHeldFeeIndexOf;
148
173
 
149
- //*********************************************************************//
150
- // --------------- public transient stored properties --------------- //
151
- //*********************************************************************//
152
-
153
- /// @notice Caller-originated referrer reference for the duration of the current external fee-paying call.
154
- /// @dev Encoded as `(referralChainId << 48) | referralProjectId`: bits [79:48] are the referrer's EIP-155 chain
155
- /// ID (uint32), bits [47:0] are the referrer's project ID on that chain (uint48). The upper bits of the
156
- /// uint256 are reserved for future encoding extensions.
157
- /// @dev The entry points `cashOutTokensOf`, `sendPayoutsOf`, and `useAllowanceOf` auto-resolve a bare project ID
158
- /// (non-zero project, zero chain bits) to the current execution chain via `block.chainid` before writing this
159
- /// slot. So storage (and indexers reading `feeVolumeByReferralOf`) always observe a fully-resolved
160
- /// `(chainId, projectId)` pair — callers can write `referralProjectId: someProjectId` without manually packing
161
- /// their own chain ID.
162
- /// @dev Set by the three entry points via the `_setReferralProjectId` save-restore wrapper. Read inside `_pay`
163
- /// to credit `feeVolumeByReferralOf` when the fee project's pay call is recorded locally.
164
- /// @dev Public so pay, cash out, and split hooks can introspect which referral originated the in-flight call (e.g.
165
- /// to apply referral-specific logic). Reads `0` outside any fee-paying call.
166
- uint256 public transient override currentReferralProjectId;
167
-
168
174
  //*********************************************************************//
169
175
  // -------------- internal transient stored properties -------------- //
170
176
  //*********************************************************************//
@@ -177,9 +183,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
177
183
  /// the fee basis to `executePayout`.
178
184
  uint256 internal transient _internalSplitPayProjectId;
179
185
 
180
- /// @notice The in-flight fee amount that can credit `currentReferralProjectId`.
181
- uint256 internal transient _feeReferralCreditAmount;
182
-
183
186
  //*********************************************************************//
184
187
  // -------------------------- constructor ---------------------------- //
185
188
  //*********************************************************************//
@@ -303,10 +306,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
303
306
  /// data hook and cash out hooks if applicable.
304
307
  /// @param metadata Bytes to send along to the emitted event, as well as the data hook and cash out hooks if
305
308
  /// applicable.
306
- /// @param referralProjectId Optional referrer reference to credit with the protocol fee volume taken by this
307
- /// call, encoded as `(referralChainId << 48) | referralProjectId` (chain ID in the upper 32 bits, project ID
308
- /// in the lower 48). A bare project ID (chain bits zero) is auto-resolved to the current chain via `block.chainid`.
309
- /// Pass `0` for no referral credit.
310
309
  /// @return reclaimAmount The amount of **terminal tokens** sent to `beneficiary` in exchange for the burned project
311
310
  /// tokens, as a fixed point number with the same number of decimals as the terminal token's accounting context.
312
311
  function cashOutTokensOf(
@@ -316,8 +315,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
316
315
  address tokenToReclaim,
317
316
  uint256 minTokensReclaimed,
318
317
  address payable beneficiary,
319
- bytes calldata metadata,
320
- uint256 referralProjectId
318
+ bytes calldata metadata
321
319
  )
322
320
  external
323
321
  override
@@ -326,9 +324,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
326
324
  // Enforce permissions.
327
325
  _requirePermissionFrom({account: holder, projectId: projectId, permissionId: JBPermissionIds.CASH_OUT_TOKENS});
328
326
 
329
- // Save-restore the transient referral slot so nested reentrant fee-paying calls don't pollute each other.
330
- uint256 priorReferral = _setReferralProjectId(referralProjectId);
331
-
332
327
  reclaimAmount = _cashOutTokensOf({
333
328
  holder: holder,
334
329
  projectId: projectId,
@@ -338,8 +333,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
338
333
  metadata: metadata
339
334
  });
340
335
 
341
- _setReferralProjectId(priorReferral);
342
-
343
336
  // The amount being reclaimed must be at least as much as was expected.
344
337
  _checkMin({value: reclaimAmount, min: minTokensReclaimed});
345
338
  }
@@ -441,7 +434,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
441
434
  revert JBMultiTerminal_MintNotAllowed({projectId: projectId, terminal: address(terminal)});
442
435
  }
443
436
 
444
- // Send the `projectId` in the metadata as a referral.
437
+ // Send the source `projectId` in the metadata for same-terminal split-pay accounting.
445
438
  bytes memory metadata = bytes(abi.encodePacked(projectId));
446
439
 
447
440
  // Add to balance if preferred.
@@ -724,9 +717,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
724
717
  /// @param token The token to process held fees for.
725
718
  /// @param count The number of fees to process.
726
719
  function processHeldFeesOf(uint256 projectId, address token, uint256 count) external override {
727
- // Preserve any in-flight referral context if this function is reached through reentrancy.
728
- uint256 previousReferralProjectId = currentReferralProjectId;
729
-
730
720
  // Keep a reference to the terminal that'll receive the fees.
731
721
  IJBTerminal feeTerminal = _primaryTerminalOf({projectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID, token: token});
732
722
 
@@ -761,11 +751,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
761
751
  _nextHeldFeeIndexOf[projectId][token] = currentIndex + 1;
762
752
  }
763
753
 
764
- // Restore the originating fee-paying call's referral project for this fee's processing so the credit in
765
- // `_processFee` attributes to the right (chain, project) pair. Reconstructs the packed encoding from the
766
- // two struct halves: `(chainId << 48) | projectId`.
767
- currentReferralProjectId = (uint256(heldFee.referralChainId) << 48) | uint256(heldFee.referralProjectId);
768
-
769
754
  // Process the standard fee on the original gross amount recorded when the held fee was created.
770
755
  _processFee({
771
756
  projectId: projectId,
@@ -781,9 +766,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
781
766
  }
782
767
  }
783
768
 
784
- // Restore the previous transient value so reentrant held-fee processing does not erase an outer referral.
785
- currentReferralProjectId = previousReferralProjectId;
786
-
787
769
  // If all held fees have been processed, reset the array and index entirely to bound storage growth.
788
770
  if (
789
771
  _nextHeldFeeIndexOf[projectId][token] >= _heldFeesOf[projectId][token].length
@@ -810,29 +792,20 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
810
792
  /// in terms of the token's accounting context currency), as a fixed point number with the same number of decimals
811
793
  /// as the token's accounting context. If the amount of tokens paid out would be less than this amount, the send is
812
794
  /// reverted.
813
- /// @param referralProjectId Optional referrer reference to credit with the protocol fee volume taken by this
814
- /// call, encoded as `(referralChainId << 48) | referralProjectId` (chain ID in the upper 32 bits, project ID
815
- /// in the lower 48). A bare project ID (chain bits zero) is auto-resolved to the current chain via `block.chainid`.
816
- /// Pass `0` for no referral credit.
817
795
  /// @return amountPaidOut The total amount paid out.
818
796
  function sendPayoutsOf(
819
797
  uint256 projectId,
820
798
  address token,
821
799
  uint256 amount,
822
800
  uint256 currency,
823
- uint256 minTokensPaidOut,
824
- uint256 referralProjectId
801
+ uint256 minTokensPaidOut
825
802
  )
826
803
  external
827
804
  override
828
805
  returns (uint256 amountPaidOut)
829
806
  {
830
- uint256 priorReferral = _setReferralProjectId(referralProjectId);
831
-
832
807
  amountPaidOut = _sendPayoutsOf({projectId: projectId, token: token, amount: amount, currency: currency});
833
808
 
834
- _setReferralProjectId(priorReferral);
835
-
836
809
  // The amount being paid out must be at least as much as was expected.
837
810
  _checkMin({value: amountPaidOut, min: minTokensPaidOut});
838
811
  }
@@ -855,10 +828,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
855
828
  /// @param feeBeneficiary The address that receives the **project tokens** minted by the fee project in exchange
856
829
  /// for the protocol fee paid in terminal tokens.
857
830
  /// @param memo A memo to pass along to the emitted event.
858
- /// @param referralProjectId Optional referrer reference to credit with the protocol fee volume taken by this
859
- /// call, encoded as `(referralChainId << 48) | referralProjectId` (chain ID in the upper 32 bits, project ID
860
- /// in the lower 48). A bare project ID (chain bits zero) is auto-resolved to the current chain via `block.chainid`.
861
- /// Pass `0` for no referral credit.
862
831
  /// @return netAmountPaidOut The number of **terminal tokens** sent to `beneficiary`, net of the protocol fee, as a
863
832
  /// fixed point number with the same number of decimals as the terminal token's accounting context.
864
833
  function useAllowanceOf(
@@ -869,8 +838,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
869
838
  uint256 minTokensPaidOut,
870
839
  address payable beneficiary,
871
840
  address payable feeBeneficiary,
872
- string calldata memo,
873
- uint256 referralProjectId
841
+ string calldata memo
874
842
  )
875
843
  external
876
844
  override
@@ -882,8 +850,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
882
850
  // Enforce permissions.
883
851
  _requirePermissionFrom({account: owner, projectId: projectId, permissionId: JBPermissionIds.USE_ALLOWANCE});
884
852
 
885
- uint256 priorReferral = _setReferralProjectId(referralProjectId);
886
-
887
853
  netAmountPaidOut = _useAllowanceOf({
888
854
  projectId: projectId,
889
855
  owner: owner,
@@ -895,8 +861,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
895
861
  memo: memo
896
862
  });
897
863
 
898
- _setReferralProjectId(priorReferral);
899
-
900
864
  // The amount being withdrawn must be at least as much as was expected.
901
865
  _checkMin({value: netAmountPaidOut, min: minTokensPaidOut});
902
866
  }
@@ -1766,17 +1730,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1766
1730
  // Keep a reference to the token amount to forward to the store.
1767
1731
  JBTokenAmount memory tokenAmount = _tokenAmountOf({projectId: projectId, token: token, value: amount});
1768
1732
 
1769
- // Credit only the one pay call currently being made by `_processFee`. Ordinary split pays to project #1 can
1770
- // happen while `currentReferralProjectId` is set, but they are not protocol-fee payments. Consume this before
1771
- // recording the payment so fee-project data hooks cannot reenter and use the in-flight credit.
1772
- if (
1773
- projectId == JBConstants.FEE_BENEFICIARY_PROJECT_ID && currentReferralProjectId != 0
1774
- && _feeReferralCreditAmount != 0
1775
- ) {
1776
- delete _feeReferralCreditAmount;
1777
- STORE.recordFeeReferralCreditOf({referralProjectId: currentReferralProjectId, amount: tokenAmount});
1778
- }
1779
-
1780
1733
  // Record the payment.
1781
1734
  // Keep a reference to the ruleset the payment is being made during.
1782
1735
  // Keep a reference to the pay hook specifications.
@@ -1866,11 +1819,9 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1866
1819
  )
1867
1820
  internal
1868
1821
  {
1869
- if (feeTerminal == IJBTerminal(address(this))) _feeReferralCreditAmount = amount;
1870
1822
  try this.executeProcessFee({
1871
1823
  projectId: projectId, token: token, amount: amount, beneficiary: beneficiary, feeTerminal: feeTerminal
1872
1824
  }) {
1873
- delete _feeReferralCreditAmount;
1874
1825
  emit ProcessFee({
1875
1826
  projectId: projectId,
1876
1827
  token: token,
@@ -1880,7 +1831,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1880
1831
  caller: _msgSender()
1881
1832
  });
1882
1833
  } catch (bytes memory reason) {
1883
- delete _feeReferralCreditAmount;
1884
1834
  // Fee processing is fail-open for project liveness: a broken project #1 terminal or fee route must not
1885
1835
  // trap payouts, cash outs, allowances, held-fee processing, or terminal migration. The fee is forgiven,
1886
1836
  // credited back to the originating project on this terminal, and surfaced through `FeeReverted`.
@@ -2074,22 +2024,13 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
2074
2024
  _checkFitsIn({value: amount, max: type(uint224).max});
2075
2025
 
2076
2026
  // Store the gross amount so future repayments can recover the corresponding fee.
2077
- // Capture the in-flight `currentReferralProjectId` so attribution survives the 28-day hold window —
2078
- // by the time `processHeldFeesOf` runs, the transient slot has been cleared. The encoded
2079
- // `(chainId << 48) | projectId` pair in the transient slot is decomposed into its two halves so the
2080
- // struct stays in 2 storage slots.
2081
- uint256 encodedReferral = currentReferralProjectId;
2082
2027
  _heldFeesOf[projectId][token].push(
2083
2028
  JBFee({
2084
2029
  // forge-lint: disable-next-line(unsafe-typecast)
2085
2030
  amount: uint224(amount),
2086
- // forge-lint: disable-next-line(unsafe-typecast)
2087
- referralChainId: uint32(encodedReferral >> 48),
2088
2031
  beneficiary: beneficiary,
2089
2032
  // forge-lint: disable-next-line(unsafe-typecast)
2090
- unlockTimestamp: uint48(block.timestamp + _FEE_HOLDING_SECONDS),
2091
- // forge-lint: disable-next-line(unsafe-typecast)
2092
- referralProjectId: uint48(encodedReferral)
2033
+ unlockTimestamp: uint48(block.timestamp + _FEE_HOLDING_SECONDS)
2093
2034
  })
2094
2035
  );
2095
2036
 
@@ -2360,22 +2301,4 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
2360
2301
  function _feeAmountFrom(uint256 amount) private pure returns (uint256) {
2361
2302
  return JBFees.standardFeeAmountFrom(amount);
2362
2303
  }
2363
-
2364
- /// @notice Set the transient `currentReferralProjectId` slot and return the prior value (for save-restore).
2365
- /// @dev Returning the prior value lets the caller restore it after the inner call completes, which is required
2366
- /// to keep nested reentrant fee-paying calls from polluting each other. Per EIP-1153, a revert in the inner
2367
- /// call also reverts the transient write, so no explicit cleanup on the failure path is needed.
2368
- /// @dev Backfills `block.chainid` into the chain-bits half of the encoding when the caller passed a bare
2369
- /// project ID (non-zero project, zero chain). This lets callers write `referralProjectId: someProjectId`
2370
- /// without manually packing their own chain ID — the entry point resolves it to the current execution chain,
2371
- /// so storage and indexers always see a fully-resolved `(chainId, projectId)` pair.
2372
- /// @param referralProjectId The new value to write into the transient slot.
2373
- /// @return prior The value previously stored in the slot.
2374
- function _setReferralProjectId(uint256 referralProjectId) private returns (uint256 prior) {
2375
- if (referralProjectId != 0 && referralProjectId >> 48 == 0) {
2376
- referralProjectId |= block.chainid << 48;
2377
- }
2378
- prior = currentReferralProjectId;
2379
- currentReferralProjectId = referralProjectId;
2380
- }
2381
2304
  }
@@ -17,8 +17,13 @@ contract JBPermissions is ERC2771Context, IJBPermissions {
17
17
  // --------------------------- custom errors ------------------------- //
18
18
  //*********************************************************************//
19
19
 
20
+ /// @notice Thrown when attempting to set the reserved permission ID 0.
20
21
  error JBPermissions_NoZeroPermission(address account, address operator, uint256 projectId);
22
+
23
+ /// @notice Thrown when a permission ID is greater than the maximum of 255.
21
24
  error JBPermissions_PermissionIdOutOfBounds(uint256 permissionId);
25
+
26
+ /// @notice Thrown when the caller is not the account and lacks ROOT permission to set permissions on its behalf.
22
27
  error JBPermissions_Unauthorized(address account, address operator, uint256 projectId, uint256 permissionId);
23
28
 
24
29
  //*********************************************************************//
package/src/JBPrices.sol CHANGED
@@ -23,10 +23,19 @@ contract JBPrices is JBControlled, JBPermissioned, ERC2771Context, Ownable, IJBP
23
23
  // --------------------------- custom errors ------------------------- //
24
24
  //*********************************************************************//
25
25
 
26
+ /// @notice Thrown when adding a price feed that is already registered for the same currency pair.
26
27
  error JBPrices_PriceFeedAlreadyAdded(IJBPriceFeed feed);
28
+
29
+ /// @notice Thrown when no configured price feed can be found for the requested currency pair.
27
30
  error JBPrices_PriceFeedNotFound(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency);
31
+
32
+ /// @notice Thrown when the provided price feed is the zero address.
28
33
  error JBPrices_ZeroPriceFeed();
34
+
35
+ /// @notice Thrown when the pricing currency provided is zero.
29
36
  error JBPrices_ZeroPricingCurrency(uint256 projectId, uint256 pricingCurrency);
37
+
38
+ /// @notice Thrown when the unit currency provided is zero.
30
39
  error JBPrices_ZeroUnitCurrency(uint256 projectId, uint256 unitCurrency);
31
40
 
32
41
  //*********************************************************************//
@@ -18,8 +18,13 @@ contract JBProjects is ERC721, ERC2771Context, Ownable, IJBProjects {
18
18
  // --------------------------- custom errors ------------------------- //
19
19
  //*********************************************************************//
20
20
 
21
+ /// @notice Thrown when the configured creation fee exceeds the allowed maximum.
21
22
  error JBProjects_CreationFeeExceedsMax(uint256 fee, uint256 max);
23
+
24
+ /// @notice Thrown when the native value sent does not equal the required creation fee.
22
25
  error JBProjects_InvalidCreationFee(uint256 value, uint256 requiredFee);
26
+
27
+ /// @notice Thrown when a non-zero creation fee is configured without a fee receiver.
23
28
  error JBProjects_ZeroCreationFeeReceiver();
24
29
 
25
30
  //*********************************************************************//
@@ -25,11 +25,23 @@ contract JBRulesets is JBControlled, IJBRulesets {
25
25
  // --------------------------- custom errors ------------------------- //
26
26
  //*********************************************************************//
27
27
 
28
+ /// @notice Thrown when the approval hook is not a contract or does not support the `IJBRulesetApprovalHook`
29
+ /// interface.
28
30
  error JBRulesets_InvalidRulesetApprovalHook(IJBRulesetApprovalHook hook);
31
+
32
+ /// @notice Thrown when the ruleset duration exceeds the uint32 maximum.
29
33
  error JBRulesets_InvalidRulesetDuration(uint256 duration, uint256 limit);
34
+
35
+ /// @notice Thrown when the ruleset's start plus duration exceeds the uint48 maximum.
30
36
  error JBRulesets_InvalidRulesetEndTime(uint256 timestamp, uint256 limit);
37
+
38
+ /// @notice Thrown when the ruleset weight exceeds the uint112 maximum.
31
39
  error JBRulesets_InvalidWeight(uint256 weight, uint256 limit);
40
+
41
+ /// @notice Thrown when the weight cut percent exceeds 100%.
32
42
  error JBRulesets_InvalidWeightCutPercent(uint256 percent);
43
+
44
+ /// @notice Thrown when too many weight-cut iterations remain and the weight cache must be populated first.
33
45
  error JBRulesets_WeightCacheRequired(uint256 projectId);
34
46
 
35
47
  //*********************************************************************//
@@ -396,8 +408,8 @@ contract JBRulesets is JBControlled, IJBRulesets {
396
408
  // Get a reference to the approval status.
397
409
  JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
398
410
 
399
- // While the ruleset has a approval hook that isn't approved or if it hasn't yet started, get a reference to
400
- // the ruleset that the latest is based on, which has the latest approved configuration.
411
+ // While the ruleset has an approval hook that isn't approved or if it hasn't yet started, get a reference
412
+ // to the ruleset that the latest is based on, which has the latest approved configuration.
401
413
  while (
402
414
  (approvalStatus != JBApprovalStatus.Approved && approvalStatus != JBApprovalStatus.Empty)
403
415
  // forge-lint: disable-next-line(block-timestamp)
@@ -472,7 +484,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
472
484
  // Keep a reference to its approval status.
473
485
  JBApprovalStatus approvalStatus;
474
486
 
475
- // If an upcoming approvable ruleset has been queued, and it's approval status is Approved or ApprovalExpected,
487
+ // If an upcoming approvable ruleset has been queued, and its approval status is Approved or ApprovalExpected,
476
488
  // return its ruleset struct
477
489
  if (upcomingApprovableRulesetId != 0) {
478
490
  ruleset = _getStructFor({projectId: projectId, rulesetId: upcomingApprovableRulesetId, withMetadata: true});
@@ -495,7 +507,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
495
507
  ruleset = _getStructFor({projectId: projectId, rulesetId: latestRulesetIdOf[projectId], withMetadata: true});
496
508
 
497
509
  // If the latest ruleset starts in the future, it must start in the distant future
498
- // Since its not the upcoming approvable ruleset. In this case, base the upcoming ruleset on the base
510
+ // Since it's not the upcoming approvable ruleset. In this case, base the upcoming ruleset on the base
499
511
  // ruleset.
500
512
  // forge-lint: disable-next-line(block-timestamp)
501
513
  while (ruleset.start > block.timestamp) {
@@ -723,7 +735,7 @@ contract JBRulesets is JBControlled, IJBRulesets {
723
735
  // Get a reference to the approval status.
724
736
  JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: baseRuleset});
725
737
 
726
- // If the base ruleset has started but wasn't approved if a approval hook exists
738
+ // If the base ruleset has started but wasn't approved if an approval hook exists
727
739
  // OR it hasn't started but is currently approved
728
740
  // OR it hasn't started but it is likely to be approved and takes place before the proposed one,
729
741
  // set the struct to be the ruleset it's based on, which carries the latest approved ruleset.
package/src/JBSplits.sol CHANGED
@@ -19,8 +19,13 @@ contract JBSplits is JBControlled, IJBSplits {
19
19
  // --------------------------- custom errors ------------------------- //
20
20
  //*********************************************************************//
21
21
 
22
+ /// @notice Thrown when a new set of splits omits a still-locked split from the previous set.
22
23
  error JBSplits_PreviousLockedSplitsNotIncluded(uint256 projectId, uint256 rulesetId);
24
+
25
+ /// @notice Thrown when the splits in a group sum to more than 100%.
23
26
  error JBSplits_TotalPercentExceeds100(uint256 projectId, uint256 rulesetId, uint256 groupId, uint256 percentTotal);
27
+
28
+ /// @notice Thrown when a split is configured with a percent of zero.
24
29
  error JBSplits_ZeroSplitPercent(uint256 projectId, uint256 rulesetId, uint256 groupId, uint256 splitIndex);
25
30
 
26
31
  //*********************************************************************//
@@ -39,22 +39,48 @@ contract JBTerminalStore is IJBTerminalStore {
39
39
  // --------------------------- custom errors ------------------------- //
40
40
  //*********************************************************************//
41
41
 
42
+ /// @notice Thrown when setting an accounting context for a token that already has one.
42
43
  error JBTerminalStore_AccountingContextAlreadySet(address token);
44
+
45
+ /// @notice Thrown when a token's provided decimals do not match the decimals it reports.
43
46
  error JBTerminalStore_AccountingContextDecimalsMismatch(
44
47
  address token, uint256 providedDecimals, uint256 expectedDecimals
45
48
  );
49
+
50
+ /// @notice Thrown when a token's decimals are outside the supported range.
46
51
  error JBTerminalStore_AccountingContextDecimalsOutOfRange(address token, uint256 decimals);
52
+
53
+ /// @notice Thrown when the project's current ruleset does not allow adding accounting contexts.
47
54
  error JBTerminalStore_AddingAccountingContextNotAllowed(uint256 projectId, uint256 rulesetId, address terminal);
55
+
56
+ /// @notice Thrown when the used surplus allowance exceeds the amount the controller granted.
48
57
  error JBTerminalStore_InadequateControllerAllowance(uint256 amount, uint256 allowance);
49
58
 
59
+ /// @notice Thrown when the amount to use exceeds the terminal's recorded balance or surplus.
50
60
  error JBTerminalStore_InadequateTerminalStoreBalance(uint256 amount, uint256 balance);
61
+
62
+ /// @notice Thrown when cashing out more tokens than the effective total supply.
51
63
  error JBTerminalStore_InsufficientTokens(uint256 count, uint256 totalSupply);
64
+
65
+ /// @notice Thrown when a hook specification asks to forward more than the amount paid.
52
66
  error JBTerminalStore_InvalidAmountToForwardHook(uint256 amount, uint256 paidAmount);
67
+
68
+ /// @notice Thrown when a hook specification is flagged as a no-op but carries a non-zero amount.
53
69
  error JBTerminalStore_NoopHookSpecHasAmount(uint256 amount);
70
+
71
+ /// @notice Thrown when the project has no current ruleset.
54
72
  error JBTerminalStore_RulesetNotFound(uint256 projectId);
73
+
74
+ /// @notice Thrown when paying a project whose current ruleset pauses payments.
55
75
  error JBTerminalStore_RulesetPaymentPaused(uint256 projectId, uint256 rulesetId);
76
+
77
+ /// @notice Thrown when the project's current ruleset does not allow terminal migration.
56
78
  error JBTerminalStore_TerminalMigrationNotAllowed(uint256 projectId, uint256 rulesetId);
79
+
80
+ /// @notice Thrown when a value to record exceeds the uint224 maximum.
57
81
  error JBTerminalStore_Uint224Overflow(uint256 value);
82
+
83
+ /// @notice Thrown when an accounting context's currency is zero.
58
84
  error JBTerminalStore_ZeroAccountingContextCurrency(address token);
59
85
 
60
86
  //*********************************************************************//
@@ -92,26 +118,6 @@ contract JBTerminalStore is IJBTerminalStore {
92
118
  public
93
119
  override balanceOf;
94
120
 
95
- /// @notice Cumulative fee payment amount credited to a referrer (chainId, projectId) pair as a result of
96
- /// fee-paying calls that originated through a given terminal.
97
- /// @dev Written by terminals via `recordFeeReferralCreditOf`; the writing terminal is `msg.sender`, so a caller
98
- /// can only pollute their own bucket.
99
- /// @dev Nested by chain ID then project ID so off-chain consumers can read the credit for a specific
100
- /// `(chainId, projectId)` directly without re-packing the encoded form themselves.
101
- /// @custom:param terminal The terminal that originated the fee-paying call.
102
- /// @custom:param referralChainId The EIP-155 chain ID of the referrer's home chain.
103
- /// @custom:param referralProjectId The referrer's bare project ID on `referralChainId`.
104
- mapping(address terminal => mapping(uint256 referralChainId => mapping(uint256 referralProjectId => uint256)))
105
- public
106
- override feeVolumeByReferralOf;
107
-
108
- /// @notice Cumulative fee payment amount credited across all referral projects for a given terminal.
109
- /// @dev Updated in lockstep with `feeVolumeByReferralOf` so consumers can compute a referrer's pro-rata share
110
- /// in a single SLOAD pair without enumerating referrers. Used as the denominator by split hooks that distribute
111
- /// rewards proportional to attributed fee volume.
112
- /// @custom:param terminal The terminal that originated the fee-paying calls.
113
- mapping(address terminal => uint256) public override totalFeeVolumeOf;
114
-
115
121
  /// @notice The currency-denominated amount of funds that a project has already paid out from its payout limit
116
122
  /// during the current ruleset for each terminal, in terms of the payout limit's currency.
117
123
  /// @dev Increases as projects pay out funds.
@@ -365,22 +371,6 @@ contract JBTerminalStore is IJBTerminalStore {
365
371
  }
366
372
  }
367
373
 
368
- /// @notice Credit a referral project with a fee payment amount routed through `msg.sender` (the calling terminal).
369
- /// @dev Called by `JBMultiTerminal._pay` after a payment lands on the fee project. Permissionless: writes are
370
- /// scoped to `msg.sender`'s slots so an arbitrary caller can only pollute their own buckets — off-chain
371
- /// consumers should filter on known terminal addresses. The amount is normalized to `NATIVE_TOKEN` units
372
- /// (18 decimals) here in the store (where `PRICES` is available) so all credits share a common denominator.
373
- /// @param referralProjectId The referral project to credit.
374
- /// @param amount The fee amount paid by the originating fee-take call (raw value, decimals, currency).
375
- function recordFeeReferralCreditOf(uint256 referralProjectId, JBTokenAmount calldata amount) external override {
376
- _creditFeeReferral({
377
- referralProjectId: referralProjectId,
378
- amount: _normalizeToNativeTokenUnits({
379
- value: amount.value, decimals: amount.decimals, currency: amount.currency
380
- })
381
- });
382
- }
383
-
384
374
  /// @notice Records a payment — calculates how many project tokens to mint based on the payment amount and the
385
375
  /// current ruleset's weight. Uses the data hook if configured, otherwise mints proportionally.
386
376
  /// @dev Called by the terminal after accepting funds. Updates the project's recorded balance.
@@ -501,7 +491,7 @@ contract JBTerminalStore is IJBTerminalStore {
501
491
  revert JBTerminalStore_InadequateTerminalStoreBalance({amount: amountPaidOut, balance: currentBalance});
502
492
  }
503
493
 
504
- // Removed the paid out funds from the project's token balance.
494
+ // Remove the paid out funds from the project's token balance.
505
495
  unchecked {
506
496
  balanceOf[msg.sender][projectId][token] = currentBalance - amountPaidOut;
507
497
  }
@@ -1173,7 +1163,7 @@ contract JBTerminalStore is IJBTerminalStore {
1173
1163
  if (weight == 0) return (ruleset, 0, hookSpecifications, balanceDiff);
1174
1164
 
1175
1165
  // If the terminal should base its weight on a currency other than the terminal's currency, determine the
1176
- // factor. The weight is always a fixed point mumber with 18 decimals. To ensure this, the ratio should use the
1166
+ // factor. The weight is always a fixed point number with 18 decimals. To ensure this, the ratio should use the
1177
1167
  // same
1178
1168
  // number of decimals as the `amount`.
1179
1169
  uint256 weightRatio = amount.currency == ruleset.baseCurrency()
@@ -1366,7 +1356,7 @@ contract JBTerminalStore is IJBTerminalStore {
1366
1356
  : mulDiv({
1367
1357
  x: surplus,
1368
1358
  y: 10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the
1369
- // `_payoutLimitRemaining`'s fidelity as possible when converting.
1359
+ // surplus's fidelity as possible when converting.
1370
1360
  denominator: PRICES.pricePerUnitOf({
1371
1361
  projectId: projectId,
1372
1362
  pricingCurrency: accountingContext.currency,
@@ -1442,80 +1432,4 @@ contract JBTerminalStore is IJBTerminalStore {
1442
1432
  }
1443
1433
  }
1444
1434
  }
1445
-
1446
- //*********************************************************************//
1447
- // -------------------------- private helpers ------------------------ //
1448
- //*********************************************************************//
1449
-
1450
- /// @notice Credit a referrer with a fee payment amount. Internal counterpart of `recordFeeReferralCreditOf` —
1451
- /// both write to the same slots and emit the same event.
1452
- /// @dev No-op when `referralProjectId == 0` or `amount == 0`.
1453
- /// @dev Unpacks the encoded `(chainId << 48) | projectId` form into the nested mapping at write-time so the
1454
- /// public getter exposes `(terminal, chainId, projectId) → cumulative` directly. The event topics carry the
1455
- /// two halves separately as well.
1456
- /// @param referralProjectId The packed `(chainId << 48) | projectId` referrer reference to credit.
1457
- /// @param amount The fee amount to credit.
1458
- function _creditFeeReferral(uint256 referralProjectId, uint256 amount) private {
1459
- if (referralProjectId == 0 || amount == 0) return;
1460
-
1461
- uint256 referralChainId = referralProjectId >> 48;
1462
- uint256 bareProjectId = referralProjectId & ((1 << 48) - 1);
1463
-
1464
- feeVolumeByReferralOf[msg.sender][referralChainId][bareProjectId] += amount;
1465
- uint256 newTotal = totalFeeVolumeOf[msg.sender] + amount;
1466
- totalFeeVolumeOf[msg.sender] = newTotal;
1467
-
1468
- emit ReferralCredit({
1469
- terminal: msg.sender,
1470
- referralChainId: referralChainId,
1471
- referralProjectId: bareProjectId,
1472
- amount: amount,
1473
- newTotal: newTotal,
1474
- caller: msg.sender
1475
- });
1476
- }
1477
-
1478
- /// @notice Normalize a fee-token amount to `JBConstants.NATIVE_TOKEN` units at 18 decimals.
1479
- /// @dev Two-step: first adjust decimals to 18 via `JBFixedPointNumber.adjustDecimals`, then convert currency
1480
- /// via `PRICES.pricePerUnitOf` using the fee project's price feeds. If no price feed exists for the pair, the
1481
- /// `try` block catches the revert and the credit is silently skipped — the payment itself still succeeds.
1482
- /// @param value The amount in the source token's native decimals.
1483
- /// @param decimals The source token's decimals.
1484
- /// @param currency The source token's accounting-context currency (`uint32(uint160(token))`).
1485
- /// @return normalized The amount expressed in `NATIVE_TOKEN` units (18 decimals), or 0 if conversion failed.
1486
- function _normalizeToNativeTokenUnits(
1487
- uint256 value,
1488
- uint256 decimals,
1489
- uint256 currency
1490
- )
1491
- private
1492
- view
1493
- returns (uint256 normalized)
1494
- {
1495
- // Adjust the source amount up/down to 18 decimals so all credits share a common precision.
1496
- normalized = decimals == _MAX_FIXED_POINT_FIDELITY
1497
- ? value
1498
- : JBFixedPointNumber.adjustDecimals({
1499
- value: value, decimals: decimals, targetDecimals: _MAX_FIXED_POINT_FIDELITY
1500
- });
1501
-
1502
- if (normalized == 0 || currency == JBConstants.NATIVE_TOKEN_CURRENCY) return normalized;
1503
-
1504
- // Convert from the source currency to NATIVE_TOKEN via the fee project's price feeds. A missing feed
1505
- // reverts inside `PRICES.pricePerUnitOf` — caught here so the payment is not blocked.
1506
- try PRICES.pricePerUnitOf({
1507
- projectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID,
1508
- pricingCurrency: currency,
1509
- unitCurrency: JBConstants.NATIVE_TOKEN_CURRENCY,
1510
- decimals: _MAX_FIXED_POINT_FIDELITY
1511
- }) returns (
1512
- uint256 price
1513
- ) {
1514
- normalized = price == 0
1515
- ? 0
1516
- : mulDiv({x: normalized, y: 10 ** _MAX_FIXED_POINT_FIDELITY, denominator: price});
1517
- } catch {
1518
- normalized = 0;
1519
- }
1520
- }
1521
1435
  }