@bananapus/core-v6 0.0.20 → 0.0.22

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.
Files changed (48) hide show
  1. package/ADMINISTRATION.md +0 -1
  2. package/AUDIT_INSTRUCTIONS.md +1 -1
  3. package/CHANGE_LOG.md +3 -3
  4. package/RISKS.md +3 -3
  5. package/SKILLS.md +8 -8
  6. package/USER_JOURNEYS.md +1 -1
  7. package/foundry.toml +0 -1
  8. package/package.json +1 -1
  9. package/src/JBMultiTerminal.sol +92 -192
  10. package/src/JBTerminalStore.sol +405 -235
  11. package/src/interfaces/IJBMultiTerminal.sol +0 -4
  12. package/src/interfaces/IJBTerminal.sol +4 -4
  13. package/src/interfaces/IJBTerminalStore.sol +65 -33
  14. package/src/libraries/JBPayoutSplitGroupLib.sol +3 -4
  15. package/src/libraries/JBSurplus.sol +3 -4
  16. package/test/ComprehensiveInvariant.t.sol +5 -7
  17. package/test/CoreExploitTests.t.sol +18 -23
  18. package/test/TestCashOut.sol +6 -6
  19. package/test/TestMultiTerminalSurplus.sol +4 -4
  20. package/test/TestMultiTokenSurplus.sol +6 -23
  21. package/test/TestTerminalMigration.sol +2 -7
  22. package/test/fork/TestSequencerPriceFeedFork.sol +1 -1
  23. package/test/fork/TestTerminalPreviewParityFork.sol +0 -1
  24. package/test/invariants/TerminalStoreInvariant.t.sol +5 -7
  25. package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +1 -2
  26. package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +23 -24
  27. package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +79 -119
  28. package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +33 -26
  29. package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +32 -27
  30. package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +22 -4
  31. package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +8 -5
  32. package/test/units/static/JBMultiTerminal/TestPay.sol +41 -33
  33. package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +19 -18
  34. package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +38 -22
  35. package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +9 -6
  36. package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +4 -4
  37. package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +37 -32
  38. package/test/units/static/JBSurplus/TestSurplusFuzz.sol +5 -20
  39. package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +17 -0
  40. package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +120 -246
  41. package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +29 -7
  42. package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +88 -20
  43. package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +30 -29
  44. package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +46 -16
  45. package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +24 -53
  46. package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +24 -4
  47. package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +14 -4
  48. package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +21 -3
@@ -7,7 +7,6 @@ import {IJBFeeTerminal} from "./IJBFeeTerminal.sol";
7
7
  import {IJBPayoutTerminal} from "./IJBPayoutTerminal.sol";
8
8
  import {IJBPermitTerminal} from "./IJBPermitTerminal.sol";
9
9
  import {IJBProjects} from "./IJBProjects.sol";
10
- import {IJBRulesets} from "./IJBRulesets.sol";
11
10
  import {IJBSplits} from "./IJBSplits.sol";
12
11
  import {IJBTerminal} from "./IJBTerminal.sol";
13
12
  import {IJBTerminalStore} from "./IJBTerminalStore.sol";
@@ -22,9 +21,6 @@ interface IJBMultiTerminal is IJBTerminal, IJBFeeTerminal, IJBCashOutTerminal, I
22
21
  /// @notice Mints ERC-721s that represent project ownership and transfers.
23
22
  function PROJECTS() external view returns (IJBProjects);
24
23
 
25
- /// @notice The contract storing and managing project rulesets.
26
- function RULESETS() external view returns (IJBRulesets);
27
-
28
24
  /// @notice The contract that stores splits for each project.
29
25
  function SPLITS() external view returns (IJBSplits);
30
26
 
@@ -88,15 +88,15 @@ interface IJBTerminal is IERC165 {
88
88
  /// @return The accounting contexts for the project.
89
89
  function accountingContextsOf(uint256 projectId) external view returns (JBAccountingContext[] memory);
90
90
 
91
- /// @notice Returns a project's current surplus for a set of accounting contexts.
91
+ /// @notice Returns a project's current surplus in this terminal.
92
92
  /// @param projectId The ID of the project to get the surplus of.
93
- /// @param accountingContexts The accounting contexts to include in the surplus calculation.
93
+ /// @param tokens The tokens to include in the surplus calculation. If empty, all tokens are included.
94
94
  /// @param decimals The number of decimals to express the surplus with.
95
95
  /// @param currency The currency to express the surplus in.
96
- /// @return The project's current surplus.
96
+ /// @return The project's current surplus for the given tokens.
97
97
  function currentSurplusOf(
98
98
  uint256 projectId,
99
- JBAccountingContext[] memory accountingContexts,
99
+ address[] calldata tokens,
100
100
  uint256 decimals,
101
101
  uint256 currency
102
102
  )
@@ -5,11 +5,11 @@ import {IJBDirectory} from "./IJBDirectory.sol";
5
5
  import {IJBPrices} from "./IJBPrices.sol";
6
6
  import {IJBRulesets} from "./IJBRulesets.sol";
7
7
  import {IJBTerminal} from "./IJBTerminal.sol";
8
- import {JBAccountingContext} from "./../structs/JBAccountingContext.sol";
9
- import {JBCashOutHookSpecification} from "./../structs/JBCashOutHookSpecification.sol";
10
- import {JBPayHookSpecification} from "./../structs/JBPayHookSpecification.sol";
11
- import {JBRuleset} from "./../structs/JBRuleset.sol";
12
- import {JBTokenAmount} from "./../structs/JBTokenAmount.sol";
8
+ import {JBAccountingContext} from "../structs/JBAccountingContext.sol";
9
+ import {JBCashOutHookSpecification} from "../structs/JBCashOutHookSpecification.sol";
10
+ import {JBPayHookSpecification} from "../structs/JBPayHookSpecification.sol";
11
+ import {JBRuleset} from "../structs/JBRuleset.sol";
12
+ import {JBTokenAmount} from "../structs/JBTokenAmount.sol";
13
13
 
14
14
  /// @notice Manages the bookkeeping for payments, cash outs, payouts, and surplus allowance usage for terminals.
15
15
  interface IJBTerminalStore {
@@ -22,6 +22,32 @@ interface IJBTerminalStore {
22
22
  /// @notice The contract storing and managing project rulesets.
23
23
  function RULESETS() external view returns (IJBRulesets);
24
24
 
25
+ /// @notice Returns the accounting context for a terminal's project token.
26
+ /// @param terminal The terminal the accounting context applies to.
27
+ /// @param projectId The ID of the project.
28
+ /// @param token The token to get the accounting context for.
29
+ /// @return The accounting context.
30
+ function accountingContextOf(
31
+ address terminal,
32
+ uint256 projectId,
33
+ address token
34
+ )
35
+ external
36
+ view
37
+ returns (JBAccountingContext memory);
38
+
39
+ /// @notice Returns all accounting contexts for a terminal's project.
40
+ /// @param terminal The terminal the accounting contexts apply to.
41
+ /// @param projectId The ID of the project.
42
+ /// @return The accounting contexts.
43
+ function accountingContextsOf(
44
+ address terminal,
45
+ uint256 projectId
46
+ )
47
+ external
48
+ view
49
+ returns (JBAccountingContext[] memory);
50
+
25
51
  /// @notice Returns the balance of a terminal for a project and token.
26
52
  /// @param terminal The terminal to get the balance of.
27
53
  /// @param projectId The ID of the project to get the balance for.
@@ -45,11 +71,12 @@ interface IJBTerminalStore {
45
71
  view
46
72
  returns (uint256);
47
73
 
48
- /// @notice Returns the reclaimable surplus for a project across multiple terminals.
74
+ /// @notice Returns the reclaimable surplus for a project across multiple terminals, considering only specific
75
+ /// tokens.
49
76
  /// @param projectId The ID of the project.
50
77
  /// @param cashOutCount The number of tokens being cashed out.
51
- /// @param terminals The terminals to include in the surplus calculation.
52
- /// @param accountingContexts The accounting contexts to include.
78
+ /// @param terminals The terminals to include in the surplus calculation. If empty, all project terminals are used.
79
+ /// @param tokens The tokens to include in the surplus calculation.
53
80
  /// @param decimals The number of decimals to express the result with.
54
81
  /// @param currency The currency to express the result in.
55
82
  /// @return The reclaimable surplus amount.
@@ -57,7 +84,7 @@ interface IJBTerminalStore {
57
84
  uint256 projectId,
58
85
  uint256 cashOutCount,
59
86
  IJBTerminal[] calldata terminals,
60
- JBAccountingContext[] calldata accountingContexts,
87
+ address[] calldata tokens,
61
88
  uint256 decimals,
62
89
  uint256 currency
63
90
  )
@@ -65,15 +92,17 @@ interface IJBTerminalStore {
65
92
  view
66
93
  returns (uint256);
67
94
 
68
- /// @notice Returns the reclaimable surplus for a project across all terminals using all accounting contexts.
95
+ /// @notice Returns the current surplus for a project across specified terminals and tokens.
69
96
  /// @param projectId The ID of the project.
70
- /// @param cashOutCount The number of tokens being cashed out.
97
+ /// @param terminals The terminals to include. If empty, all project terminals are used.
98
+ /// @param tokens The tokens to include. If empty, all tokens per terminal are used.
71
99
  /// @param decimals The number of decimals to express the result with.
72
100
  /// @param currency The currency to express the result in.
73
- /// @return The reclaimable surplus amount.
74
- function currentTotalReclaimableSurplusOf(
101
+ /// @return The current surplus.
102
+ function currentSurplusOf(
75
103
  uint256 projectId,
76
- uint256 cashOutCount,
104
+ IJBTerminal[] calldata terminals,
105
+ address[] calldata tokens,
77
106
  uint256 decimals,
78
107
  uint256 currency
79
108
  )
@@ -81,17 +110,15 @@ interface IJBTerminalStore {
81
110
  view
82
111
  returns (uint256);
83
112
 
84
- /// @notice Returns the current surplus for a terminal and project.
85
- /// @param terminal The terminal to get the surplus of.
113
+ /// @notice Returns the reclaimable surplus for a project across all terminals using all accounting contexts.
86
114
  /// @param projectId The ID of the project.
87
- /// @param accountingContexts The accounting contexts to include.
115
+ /// @param cashOutCount The number of tokens being cashed out.
88
116
  /// @param decimals The number of decimals to express the result with.
89
117
  /// @param currency The currency to express the result in.
90
- /// @return The current surplus.
91
- function currentSurplusOf(
92
- address terminal,
118
+ /// @return The reclaimable surplus amount.
119
+ function currentTotalReclaimableSurplusOf(
93
120
  uint256 projectId,
94
- JBAccountingContext[] calldata accountingContexts,
121
+ uint256 cashOutCount,
95
122
  uint256 decimals,
96
123
  uint256 currency
97
124
  )
@@ -114,11 +141,11 @@ interface IJBTerminalStore {
114
141
  returns (uint256);
115
142
 
116
143
  /// @notice Simulates a cash out without modifying state.
144
+ /// @param terminal The terminal to simulate the cash out from.
117
145
  /// @param holder The address cashing out.
118
146
  /// @param projectId The ID of the project being cashed out from.
119
147
  /// @param cashOutCount The number of project tokens being cashed out.
120
- /// @param accountingContext The accounting context of the token being reclaimed.
121
- /// @param balanceAccountingContexts The accounting contexts to include in the balance calculation.
148
+ /// @param tokenToReclaim The token being reclaimed.
122
149
  /// @param beneficiaryIsFeeless Whether the cash out's beneficiary is a feeless address.
123
150
  /// @param metadata Extra data to pass along to the data hook.
124
151
  /// @return ruleset The project's current ruleset.
@@ -126,11 +153,11 @@ interface IJBTerminalStore {
126
153
  /// @return cashOutTaxRate The cash out tax rate that would be applied.
127
154
  /// @return hookSpecifications Any cash out hook specifications from the data hook.
128
155
  function previewCashOutFrom(
156
+ address terminal,
129
157
  address holder,
130
158
  uint256 projectId,
131
159
  uint256 cashOutCount,
132
- JBAccountingContext calldata accountingContext,
133
- JBAccountingContext[] calldata balanceAccountingContexts,
160
+ address tokenToReclaim,
134
161
  bool beneficiaryIsFeeless,
135
162
  bytes calldata metadata
136
163
  )
@@ -144,6 +171,7 @@ interface IJBTerminalStore {
144
171
  );
145
172
 
146
173
  /// @notice Simulates a payment without modifying state.
174
+ /// @param terminal The terminal to simulate the payment from.
147
175
  /// @param payer The address of the payer.
148
176
  /// @param amount The amount being paid.
149
177
  /// @param projectId The ID of the project being paid.
@@ -153,6 +181,7 @@ interface IJBTerminalStore {
153
181
  /// @return tokenCount The number of project tokens that would be minted, including reserved tokens.
154
182
  /// @return hookSpecifications Any pay hook specifications from the data hook.
155
183
  function previewPayFrom(
184
+ address terminal,
156
185
  address payer,
157
186
  JBTokenAmount memory amount,
158
187
  uint256 projectId,
@@ -199,6 +228,11 @@ interface IJBTerminalStore {
199
228
  view
200
229
  returns (uint256);
201
230
 
231
+ /// @notice Records accounting contexts for a terminal's project tokens.
232
+ /// @param projectId The ID of the project.
233
+ /// @param contexts The accounting contexts to record.
234
+ function recordAccountingContextOf(uint256 projectId, JBAccountingContext[] calldata contexts) external;
235
+
202
236
  /// @notice Records a balance addition for a project.
203
237
  /// @param projectId The ID of the project.
204
238
  /// @param token The token being added.
@@ -209,8 +243,7 @@ interface IJBTerminalStore {
209
243
  /// @param holder The address cashing out.
210
244
  /// @param projectId The ID of the project being cashed out from.
211
245
  /// @param cashOutCount The number of project tokens being cashed out.
212
- /// @param accountingContext The accounting context of the token being reclaimed.
213
- /// @param balanceAccountingContexts The accounting contexts to include in the balance calculation.
246
+ /// @param tokenToReclaim The token being reclaimed.
214
247
  /// @param beneficiaryIsFeeless Whether the cash out's beneficiary is a feeless address. Passed through to data
215
248
  /// hooks so they can skip their own fees when value stays in the protocol (e.g. project-to-project routing).
216
249
  /// @param metadata Extra data to pass along to the data hook.
@@ -222,8 +255,7 @@ interface IJBTerminalStore {
222
255
  address holder,
223
256
  uint256 projectId,
224
257
  uint256 cashOutCount,
225
- JBAccountingContext calldata accountingContext,
226
- JBAccountingContext[] calldata balanceAccountingContexts,
258
+ address tokenToReclaim,
227
259
  bool beneficiaryIsFeeless,
228
260
  bytes calldata metadata
229
261
  )
@@ -256,14 +288,14 @@ interface IJBTerminalStore {
256
288
 
257
289
  /// @notice Records a payout from a project.
258
290
  /// @param projectId The ID of the project paying out.
259
- /// @param accountingContext The accounting context of the token being paid out.
291
+ /// @param token The token being paid out.
260
292
  /// @param amount The amount being paid out.
261
293
  /// @param currency The currency the amount is denominated in.
262
294
  /// @return ruleset The project's current ruleset.
263
295
  /// @return amountPaidOut The amount paid out in the token's native decimals.
264
296
  function recordPayoutFor(
265
297
  uint256 projectId,
266
- JBAccountingContext calldata accountingContext,
298
+ address token,
267
299
  uint256 amount,
268
300
  uint256 currency
269
301
  )
@@ -278,14 +310,14 @@ interface IJBTerminalStore {
278
310
 
279
311
  /// @notice Records surplus allowance usage for a project.
280
312
  /// @param projectId The ID of the project using surplus allowance.
281
- /// @param accountingContext The accounting context of the token being used.
313
+ /// @param token The token being used.
282
314
  /// @param amount The amount of surplus allowance to use.
283
315
  /// @param currency The currency the amount is denominated in.
284
316
  /// @return ruleset The project's current ruleset.
285
317
  /// @return usedAmount The amount used in the token's native decimals.
286
318
  function recordUsedAllowanceOf(
287
319
  uint256 projectId,
288
- JBAccountingContext calldata accountingContext,
320
+ address token,
289
321
  uint256 amount,
290
322
  uint256 currency
291
323
  )
@@ -3,7 +3,6 @@ pragma solidity 0.8.26;
3
3
 
4
4
  import {mulDiv} from "@prb/math/src/Common.sol";
5
5
 
6
- import {IJBPayoutTerminal} from "../interfaces/IJBPayoutTerminal.sol";
7
6
  import {IJBSplits} from "../interfaces/IJBSplits.sol";
8
7
  import {IJBTerminalStore} from "../interfaces/IJBTerminalStore.sol";
9
8
  import {JBSplit} from "../structs/JBSplit.sol";
@@ -62,10 +61,10 @@ library JBPayoutSplitGroupLib {
62
61
  {
63
62
  // The total percentage available to split.
64
63
  uint256 leftoverPercentage = JBConstants.SPLITS_TOTAL_PERCENT;
65
- uint256 group = uint256(uint160(token));
66
64
 
67
65
  // Get a reference to the project's payout splits.
68
- JBSplit[] memory payoutSplits = splits.splitsOf({projectId: projectId, rulesetId: rulesetId, groupId: group});
66
+ JBSplit[] memory payoutSplits =
67
+ splits.splitsOf({projectId: projectId, rulesetId: rulesetId, groupId: uint256(uint160(token))});
69
68
 
70
69
  leftoverAmount = amount;
71
70
 
@@ -102,7 +101,7 @@ library JBPayoutSplitGroupLib {
102
101
  emit SendPayoutToSplit({
103
102
  projectId: projectId,
104
103
  rulesetId: rulesetId,
105
- group: group,
104
+ group: uint256(uint160(token)),
106
105
  split: split,
107
106
  amount: payoutAmount,
108
107
  netAmount: netPayoutAmount,
@@ -2,7 +2,6 @@
2
2
  pragma solidity ^0.8.17;
3
3
 
4
4
  import {IJBTerminal} from "../interfaces/IJBTerminal.sol";
5
- import {JBAccountingContext} from "../structs/JBAccountingContext.sol";
6
5
 
7
6
  /// @notice Surplus calculations.
8
7
  library JBSurplus {
@@ -11,7 +10,7 @@ library JBSurplus {
11
10
  /// the project's payout limits.
12
11
  /// @param projectId The ID of the project to get the total surplus for.
13
12
  /// @param terminals The terminals to look for surplus within.
14
- /// @param accountingContexts The accounting contexts to use to calculate the surplus.
13
+ /// @param tokens The tokens to include in the surplus calculation. If empty, all tokens are included.
15
14
  /// @param decimals The number of decimals that the fixed point surplus result should include.
16
15
  /// @param currency The currency that the surplus result should be in terms of.
17
16
  /// @return surplus The total surplus of a project's funds in terms of `currency`, as a fixed point number with the
@@ -19,7 +18,7 @@ library JBSurplus {
19
18
  function currentSurplusOf(
20
19
  uint256 projectId,
21
20
  IJBTerminal[] memory terminals,
22
- JBAccountingContext[] memory accountingContexts,
21
+ address[] memory tokens,
23
22
  uint256 decimals,
24
23
  uint256 currency
25
24
  )
@@ -33,7 +32,7 @@ library JBSurplus {
33
32
  // Add the current surplus for each terminal.
34
33
  for (uint256 i; i < numberOfTerminals; i++) {
35
34
  surplus += terminals[i].currentSurplusOf({
36
- projectId: projectId, accountingContexts: accountingContexts, decimals: decimals, currency: currency
35
+ projectId: projectId, tokens: tokens, decimals: decimals, currency: currency
37
36
  });
38
37
  }
39
38
  }
@@ -4,6 +4,7 @@ pragma solidity ^0.8.6;
4
4
  import {StdInvariant} from "forge-std/StdInvariant.sol";
5
5
  import {TestBaseWorkflow} from "./helpers/TestBaseWorkflow.sol";
6
6
  import {IJBRulesetApprovalHook} from "../src/interfaces/IJBRulesetApprovalHook.sol";
7
+ import {IJBTerminal} from "../src/interfaces/IJBTerminal.sol";
7
8
  import {IJBSplitHook} from "../src/interfaces/IJBSplitHook.sol";
8
9
  import {IJBToken} from "../src/interfaces/IJBToken.sol";
9
10
  import {JBConstants} from "../src/libraries/JBConstants.sol";
@@ -236,16 +237,13 @@ contract ComprehensiveInvariant_Local is StdInvariant, TestBaseWorkflow {
236
237
  uint256 totalSupply = jbTokens().totalSupplyOf(projectId);
237
238
  if (totalSupply == 0) return;
238
239
 
239
- JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
240
- contexts[0] = JBAccountingContext({
241
- token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
242
- });
243
-
240
+ IJBTerminal[] memory _terminals = new IJBTerminal[](1);
241
+ _terminals[0] = IJBTerminal(jbMultiTerminal());
244
242
  uint256 surplus = jbTerminalStore()
245
243
  .currentSurplusOf({
246
- terminal: address(jbMultiTerminal()),
247
244
  projectId: projectId,
248
- accountingContexts: contexts,
245
+ terminals: _terminals,
246
+ tokens: new address[](0),
249
247
  decimals: 18,
250
248
  currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
251
249
  });
@@ -5,6 +5,7 @@ import {TestBaseWorkflow} from "./helpers/TestBaseWorkflow.sol";
5
5
  import {MetadataResolverHelper} from "./helpers/MetadataResolverHelper.sol";
6
6
  import {MockERC20} from "./mock/MockERC20.sol";
7
7
  import {JBMultiTerminal} from "../src/JBMultiTerminal.sol";
8
+ import {JBTerminalStore} from "../src/JBTerminalStore.sol";
8
9
  import {JBTokens} from "../src/JBTokens.sol";
9
10
  import {JBApprovalStatus} from "../src/enums/JBApprovalStatus.sol";
10
11
  import {IJBController} from "../src/interfaces/IJBController.sol";
@@ -624,16 +625,13 @@ contract EdgeCases_Local is TestBaseWorkflow {
624
625
  });
625
626
 
626
627
  // The surplus should be payAmount - payoutLimit = 15 ether.
627
- JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
628
- contexts[0] = JBAccountingContext({
629
- token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
630
- });
631
-
628
+ IJBTerminal[] memory _surplusTerminals = new IJBTerminal[](1);
629
+ _surplusTerminals[0] = IJBTerminal(_terminal);
632
630
  uint256 surplus = jbTerminalStore()
633
631
  .currentSurplusOf({
634
- terminal: address(_terminal),
635
632
  projectId: projectId,
636
- accountingContexts: contexts,
633
+ terminals: _surplusTerminals,
634
+ tokens: new address[](0),
637
635
  decimals: 18,
638
636
  currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
639
637
  });
@@ -2461,7 +2459,7 @@ contract EdgeCases_Local is TestBaseWorkflow {
2461
2459
  });
2462
2460
 
2463
2461
  vm.prank(_projectOwner);
2464
- vm.expectRevert(JBMultiTerminal.JBMultiTerminal_AccountingContextDecimalsMismatch.selector);
2462
+ vm.expectRevert(JBTerminalStore.JBTerminalStore_AccountingContextDecimalsMismatch.selector);
2465
2463
  _terminal.addAccountingContextsFor(projectId, wrongContexts);
2466
2464
  }
2467
2465
 
@@ -2492,15 +2490,13 @@ contract EdgeCases_Local is TestBaseWorkflow {
2492
2490
  });
2493
2491
 
2494
2492
  // Check surplus — should equal the full balance since no payout limits.
2495
- JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
2496
- contexts[0] =
2497
- JBAccountingContext({token: address(token), decimals: 6, currency: uint32(uint160(address(token)))});
2498
-
2493
+ IJBTerminal[] memory _surplusTerminals2 = new IJBTerminal[](1);
2494
+ _surplusTerminals2[0] = IJBTerminal(_terminal);
2499
2495
  uint256 surplus = jbTerminalStore()
2500
2496
  .currentSurplusOf({
2501
- terminal: address(_terminal),
2502
2497
  projectId: projectId,
2503
- accountingContexts: contexts,
2498
+ terminals: _surplusTerminals2,
2499
+ tokens: new address[](0),
2504
2500
  decimals: 6,
2505
2501
  currency: uint32(uint160(address(token)))
2506
2502
  });
@@ -2572,17 +2568,14 @@ contract EdgeCases_Local is TestBaseWorkflow {
2572
2568
  uint256 pendingReserved = _controller.pendingReservedTokenBalanceOf(projectId);
2573
2569
 
2574
2570
  // Record surplus before distributing reserves
2575
- JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
2576
- contexts[0] = JBAccountingContext({
2577
- token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
2578
- });
2579
-
2580
2571
  uint256 supplyBefore = _tokens.totalSupplyOf(projectId);
2572
+ IJBTerminal[] memory _surplusTerminals3 = new IJBTerminal[](1);
2573
+ _surplusTerminals3[0] = IJBTerminal(_terminal);
2581
2574
  uint256 surplusBefore = jbTerminalStore()
2582
2575
  .currentSurplusOf({
2583
- terminal: address(_terminal),
2584
2576
  projectId: projectId,
2585
- accountingContexts: contexts,
2577
+ terminals: _surplusTerminals3,
2578
+ tokens: new address[](0),
2586
2579
  decimals: 18,
2587
2580
  currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
2588
2581
  });
@@ -2599,11 +2592,13 @@ contract EdgeCases_Local is TestBaseWorkflow {
2599
2592
  }
2600
2593
 
2601
2594
  uint256 supplyAfter = _tokens.totalSupplyOf(projectId);
2595
+ IJBTerminal[] memory _surplusTerminals4 = new IJBTerminal[](1);
2596
+ _surplusTerminals4[0] = IJBTerminal(_terminal);
2602
2597
  uint256 surplusAfter = jbTerminalStore()
2603
2598
  .currentSurplusOf({
2604
- terminal: address(_terminal),
2605
2599
  projectId: projectId,
2606
- accountingContexts: contexts,
2600
+ terminals: _surplusTerminals4,
2601
+ tokens: new address[](0),
2607
2602
  decimals: 18,
2608
2603
  currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
2609
2604
  });
@@ -131,15 +131,15 @@ contract TestCashOut_Local is TestBaseWorkflow {
131
131
  // Fuzz 1 to full balance cash out.
132
132
  _tokenAmountToCashOut = bound(_tokenAmountToCashOut, 1, _beneficiaryTokenBalance);
133
133
 
134
- JBAccountingContext[] memory _tokensContext = new JBAccountingContext[](1);
135
- _tokensContext[0] = JBAccountingContext({
136
- token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
137
- });
138
-
139
134
  // Get the expected gross per a different view.
140
135
  uint256 _grossPerReclaimable = jbTerminalStore()
141
136
  .currentReclaimableSurplusOf(
142
- _projectId, _tokenAmountToCashOut, new IJBTerminal[](0), _tokensContext, 18, _tokensContext[0].currency
137
+ _projectId,
138
+ _tokenAmountToCashOut,
139
+ new IJBTerminal[](0),
140
+ new address[](0),
141
+ 18,
142
+ uint32(uint160(JBConstants.NATIVE_TOKEN))
143
143
  );
144
144
 
145
145
  // Test: cash out.
@@ -162,13 +162,13 @@ contract TestMultiTerminalSurplus_Local is TestBaseWorkflow {
162
162
  // Check ETH-only surplus from terminal 1.
163
163
  JBAccountingContext[] memory ethCtx = new JBAccountingContext[](1);
164
164
  ethCtx[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: _nativeCurrency});
165
- uint256 ethSurplus = _terminal1.currentSurplusOf(_projectId, ethCtx, 18, _nativeCurrency);
165
+ uint256 ethSurplus = _terminal1.currentSurplusOf(_projectId, new address[](0), 18, _nativeCurrency);
166
166
  assertEq(ethSurplus, ethAmount, "terminal1 ETH surplus should match payment");
167
167
 
168
168
  // Check USDC-only surplus from terminal 2.
169
169
  JBAccountingContext[] memory usdcCtx = new JBAccountingContext[](1);
170
170
  usdcCtx[0] = JBAccountingContext({token: address(_usdc), decimals: 6, currency: _usdcCurrency});
171
- uint256 usdcSurplus = _terminal2.currentSurplusOf(_projectId, usdcCtx, 6, _usdcCurrency);
171
+ uint256 usdcSurplus = _terminal2.currentSurplusOf(_projectId, new address[](0), 6, _usdcCurrency);
172
172
  assertEq(usdcSurplus, usdcAmount, "terminal2 USDC surplus should match payment");
173
173
 
174
174
  // Check total surplus in ETH terms from the store.
@@ -330,12 +330,12 @@ contract TestMultiTerminalSurplus_Local is TestBaseWorkflow {
330
330
  // Get individual surpluses in ETH terms.
331
331
  JBAccountingContext[] memory ethCtx = new JBAccountingContext[](1);
332
332
  ethCtx[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: _nativeCurrency});
333
- uint256 t1Surplus = _terminal1.currentSurplusOf(_projectId, ethCtx, 18, _nativeCurrency);
333
+ uint256 t1Surplus = _terminal1.currentSurplusOf(_projectId, new address[](0), 18, _nativeCurrency);
334
334
 
335
335
  JBAccountingContext[] memory usdcCtx = new JBAccountingContext[](1);
336
336
  usdcCtx[0] = JBAccountingContext({token: address(_usdc), decimals: 6, currency: _usdcCurrency});
337
337
  // Get terminal 2 surplus in ETH terms.
338
- uint256 t2SurplusInEth = _terminal2.currentSurplusOf(_projectId, usdcCtx, 18, _nativeCurrency);
338
+ uint256 t2SurplusInEth = _terminal2.currentSurplusOf(_projectId, new address[](0), 18, _nativeCurrency);
339
339
 
340
340
  // Get total surplus from the store.
341
341
  uint256 totalSurplus = jbTerminalStore().currentTotalSurplusOf(_projectId, 18, _nativeCurrency);
@@ -114,7 +114,7 @@ contract TestMultiTokenSurplus_Local is TestBaseWorkflow {
114
114
  JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
115
115
  contexts[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: _nativeCurrency});
116
116
 
117
- uint256 surplus = _terminal.currentSurplusOf(_projectId, contexts, 18, _nativeCurrency);
117
+ uint256 surplus = _terminal.currentSurplusOf(_projectId, new address[](0), 18, _nativeCurrency);
118
118
  assertEq(surplus, payAmount, "ETH surplus should match payment");
119
119
  }
120
120
 
@@ -137,7 +137,7 @@ contract TestMultiTokenSurplus_Local is TestBaseWorkflow {
137
137
  JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
138
138
  contexts[0] = JBAccountingContext({token: address(_usdc), decimals: 6, currency: _usdcCurrency});
139
139
 
140
- uint256 surplus = _terminal.currentSurplusOf(_projectId, contexts, 6, _usdcCurrency);
140
+ uint256 surplus = _terminal.currentSurplusOf(_projectId, new address[](0), 6, _usdcCurrency);
141
141
  assertEq(surplus, usdcAmount, "USDC surplus should match payment");
142
142
  }
143
143
 
@@ -162,30 +162,13 @@ contract TestMultiTokenSurplus_Local is TestBaseWorkflow {
162
162
  vm.prank(_beneficiary);
163
163
  _terminal.pay(_projectId, address(_usdc), usdcAmount, _beneficiary, 0, "", "");
164
164
 
165
- // Check surplus of each token individually
166
- JBAccountingContext[] memory ethContext = new JBAccountingContext[](1);
167
- ethContext[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: _nativeCurrency});
165
+ // Check surplus (now includes all registered contexts automatically)
166
+ uint256 ethSurplus = _terminal.currentSurplusOf(_projectId, new address[](0), 18, _nativeCurrency);
168
167
 
169
- JBAccountingContext[] memory usdcContext = new JBAccountingContext[](1);
170
- usdcContext[0] = JBAccountingContext({token: address(_usdc), decimals: 6, currency: _usdcCurrency});
171
-
172
- uint256 ethSurplus = _terminal.currentSurplusOf(_projectId, ethContext, 18, _nativeCurrency);
173
- assertEq(ethSurplus, ethAmount, "ETH surplus should match");
174
-
175
- uint256 usdcSurplus = _terminal.currentSurplusOf(_projectId, usdcContext, 6, _usdcCurrency);
176
- assertEq(usdcSurplus, usdcAmount, "USDC surplus should match");
177
-
178
- // Check aggregated surplus in ETH terms (both contexts)
179
- JBAccountingContext[] memory bothContexts = new JBAccountingContext[](2);
180
- bothContexts[0] = ethContext[0];
181
- bothContexts[1] = usdcContext[0];
182
-
183
- uint256 totalSurplus = _terminal.currentSurplusOf(_projectId, bothContexts, 18, _nativeCurrency);
184
-
185
- // Total should be ETH amount + USDC converted to ETH
168
+ // Total should include ETH amount + USDC converted to ETH
186
169
  // 1 ETH + (2000 USDC / 2000 per ETH) = 2 ETH
187
170
  // But the conversion depends on the feed direction
188
- assertGt(totalSurplus, ethAmount, "total surplus should include both tokens");
171
+ assertGt(ethSurplus, ethAmount, "total surplus should include both tokens");
189
172
  }
190
173
 
191
174
  /// @notice Balance tracking is per-token.
@@ -144,13 +144,8 @@ contract TestTerminalMigration_Local is TestBaseWorkflow {
144
144
  _terminalA.pay{value: payAmount}(_projectId, JBConstants.NATIVE_TOKEN, payAmount, _beneficiary, 0, "", "");
145
145
 
146
146
  // Record surplus before migration
147
- JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
148
- contexts[0] = JBAccountingContext({
149
- token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
150
- });
151
-
152
147
  uint256 surplusBefore =
153
- _terminalA.currentSurplusOf(_projectId, contexts, 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
148
+ _terminalA.currentSurplusOf(_projectId, new address[](0), 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
154
149
  assertGt(surplusBefore, 0, "should have surplus before migration");
155
150
 
156
151
  // Migrate
@@ -159,7 +154,7 @@ contract TestTerminalMigration_Local is TestBaseWorkflow {
159
154
 
160
155
  // Check surplus from terminal B
161
156
  uint256 surplusAfter =
162
- _terminalB.currentSurplusOf(_projectId, contexts, 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
157
+ _terminalB.currentSurplusOf(_projectId, new address[](0), 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
163
158
  assertEq(surplusAfter, surplusBefore, "surplus should be preserved after migration");
164
159
  }
165
160
 
@@ -59,7 +59,7 @@ contract TestSequencerPriceFeedFork is Test {
59
59
  // ------------------------------------------------------------------
60
60
 
61
61
  /// @notice Under normal conditions (sequencer up, grace period elapsed), currentUnitPrice returns a sane price.
62
- function test_normalOperation_returnsValidPrice() public skipIfNoRpc {
62
+ function test_normalOperation_returnsValidPrice() public view skipIfNoRpc {
63
63
  uint256 price18 = feed.currentUnitPrice(18);
64
64
 
65
65
  // ETH price should be between $500 and $50,000.
@@ -2,7 +2,6 @@
2
2
  pragma solidity ^0.8.26;
3
3
 
4
4
  import {TestTerminalPreviewParity_Local} from "../TestTerminalPreviewParity.sol";
5
- import {IJBController} from "../../src/interfaces/IJBController.sol";
6
5
  import {IJBTerminal} from "../../src/interfaces/IJBTerminal.sol";
7
6
  import {IJBCashOutTerminal} from "../../src/interfaces/IJBCashOutTerminal.sol";
8
7
  import {JBConstants} from "../../src/libraries/JBConstants.sol";
@@ -4,6 +4,7 @@ pragma solidity ^0.8.6;
4
4
  import {StdInvariant} from "forge-std/StdInvariant.sol";
5
5
  import {TestBaseWorkflow} from "../helpers/TestBaseWorkflow.sol";
6
6
  import {IJBRulesetApprovalHook} from "../../src/interfaces/IJBRulesetApprovalHook.sol";
7
+ import {IJBTerminal} from "../../src/interfaces/IJBTerminal.sol";
7
8
  import {JBConstants} from "../../src/libraries/JBConstants.sol";
8
9
  import {JBRulesetMetadataResolver} from "../../src/libraries/JBRulesetMetadataResolver.sol";
9
10
  import {JBFundAccessLimitGroup} from "../../src/structs/JBFundAccessLimitGroup.sol";
@@ -153,16 +154,13 @@ contract TerminalStoreInvariant_Local is StdInvariant, TestBaseWorkflow {
153
154
  uint256 totalSupply = jbTokens().totalSupplyOf(projectId);
154
155
  if (totalSupply == 0) return; // Skip when no tokens exist
155
156
 
156
- JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
157
- contexts[0] = JBAccountingContext({
158
- token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
159
- });
160
-
157
+ IJBTerminal[] memory _terminals = new IJBTerminal[](1);
158
+ _terminals[0] = IJBTerminal(jbMultiTerminal());
161
159
  uint256 surplus = jbTerminalStore()
162
160
  .currentSurplusOf({
163
- terminal: address(jbMultiTerminal()),
164
161
  projectId: projectId,
165
- accountingContexts: contexts,
162
+ terminals: _terminals,
163
+ tokens: new address[](0),
166
164
  decimals: 18,
167
165
  currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
168
166
  });