@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
@@ -8,7 +8,6 @@ import {IJBFeeTerminal} from "../../../../src/interfaces/IJBFeeTerminal.sol";
8
8
  import {IJBFeelessAddresses} from "../../../../src/interfaces/IJBFeelessAddresses.sol";
9
9
  import {IJBPayoutTerminal} from "../../../../src/interfaces/IJBPayoutTerminal.sol";
10
10
  import {IJBRulesetApprovalHook} from "../../../../src/interfaces/IJBRulesetApprovalHook.sol";
11
- import {IJBRulesets} from "../../../../src/interfaces/IJBRulesets.sol";
12
11
  import {IJBTerminalStore} from "../../../../src/interfaces/IJBTerminalStore.sol";
13
12
  import {JBAccountingContext} from "../../../../src/structs/JBAccountingContext.sol";
14
13
  import {JBPayHookSpecification} from "../../../../src/structs/JBPayHookSpecification.sol";
@@ -18,7 +17,6 @@ import {JBRulesetMetadataResolver} from "../../../../src/libraries/JBRulesetMeta
18
17
  import {JBConstants} from "../../../../src/libraries/JBConstants.sol";
19
18
  import {JBTokenAmount} from "../../../../src/structs/JBTokenAmount.sol";
20
19
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
21
- import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
22
20
  import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
23
21
  import {JBMultiTerminalSetup} from "./JBMultiTerminalSetup.sol";
24
22
 
@@ -55,7 +53,7 @@ contract TestUseAllowanceOf_Local is JBMultiTerminalSetup {
55
53
  // recordUsedAllowance
56
54
  mockExpect(
57
55
  address(store),
58
- abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, mockTokenContext, 0, 0)),
56
+ abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, mockTokenContext.token, 0, 0)),
59
57
  abi.encode(returnedRuleset, 0)
60
58
  );
61
59
 
@@ -88,12 +86,10 @@ contract TestUseAllowanceOf_Local is JBMultiTerminalSetup {
88
86
  metadata: 0
89
87
  });
90
88
 
91
- JBAccountingContext memory mockTokenContext = JBAccountingContext({token: address(0), decimals: 0, currency: 0});
92
-
93
- // recordUsedAllowance
89
+ // recordUsedAllowance terminal now passes token address directly.
94
90
  mockExpect(
95
91
  address(store),
96
- abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, mockTokenContext, 100, 0)),
92
+ abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, mockToken, 100, 0)),
97
93
  abi.encode(returnedRuleset, 100)
98
94
  );
99
95
 
@@ -140,12 +136,10 @@ contract TestUseAllowanceOf_Local is JBMultiTerminalSetup {
140
136
  metadata: 0
141
137
  });
142
138
 
143
- JBAccountingContext memory mockTokenContext = JBAccountingContext({token: address(0), decimals: 0, currency: 0});
144
-
145
- // recordUsedAllowance
139
+ // recordUsedAllowance terminal now passes token address directly
146
140
  mockExpect(
147
141
  address(store),
148
- abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, mockTokenContext, 100, 0)),
142
+ abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, mockToken, 100, 0)),
149
143
  abi.encode(returnedRuleset, 100)
150
144
  );
151
145
 
@@ -222,24 +216,30 @@ contract TestUseAllowanceOf_Local is JBMultiTerminalSetup {
222
216
  metadata: 0
223
217
  });
224
218
 
225
- // mock call to tokens decimals()
226
- mockExpect(mockToken, abi.encodeCall(IERC20Metadata.decimals, ()), abi.encode(18));
227
-
228
- // mock call to rulesets currentOf returning 0 to bypass ruleset checking
229
- mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(returnedRuleset));
230
-
231
219
  // call params
232
220
  JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
233
221
  _tokens[0] = JBAccountingContext({token: mockToken, decimals: 18, currency: currencyId});
234
222
 
223
+ // Mock recordAccountingContextOf in the store (validation now happens there)
224
+ mockExpect(
225
+ address(store), abi.encodeCall(IJBTerminalStore.recordAccountingContextOf, (_projectId, _tokens)), ""
226
+ );
227
+
235
228
  _terminal.addAccountingContextsFor(_projectId, _tokens);
236
229
 
230
+ // Mock accountingContextOf for subsequent reads (used by _tokenAmountOf during fee processing)
231
+ mockExpect(
232
+ address(store),
233
+ abi.encodeCall(IJBTerminalStore.accountingContextOf, (address(_terminal), _projectId, mockToken)),
234
+ abi.encode(_tokens[0])
235
+ );
236
+
237
237
  _terminal.accountingContextForTokenOf(_projectId, mockToken);
238
238
 
239
239
  // recordUsedAllowance
240
240
  mockExpect(
241
241
  address(store),
242
- abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, _tokens[0], 100, 0)),
242
+ abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, _tokens[0].token, 100, 0)),
243
243
  abi.encode(returnedRuleset, 100)
244
244
  );
245
245
 
@@ -370,22 +370,21 @@ contract TestUseAllowanceOf_Local is JBMultiTerminalSetup {
370
370
  metadata: packedMetadata
371
371
  });
372
372
 
373
- // mock call to tokens decimals()
374
- mockExpect(mockToken, abi.encodeCall(IERC20Metadata.decimals, ()), abi.encode(18));
375
-
376
- // mock call to rulesets currentOf
377
- mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(returnedRuleset));
378
-
379
373
  // Set up accounting context so the token is recognized
380
374
  JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
381
375
  _tokens[0] = JBAccountingContext({token: mockToken, decimals: 18, currency: currencyId});
382
376
 
377
+ // Mock recordAccountingContextOf in the store (validation now happens there)
378
+ mockExpect(
379
+ address(store), abi.encodeCall(IJBTerminalStore.recordAccountingContextOf, (_projectId, _tokens)), ""
380
+ );
381
+
383
382
  _terminal.addAccountingContextsFor(_projectId, _tokens);
384
383
 
385
384
  // recordUsedAllowance
386
385
  mockExpect(
387
386
  address(store),
388
- abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, _tokens[0], 100, 0)),
387
+ abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, _tokens[0].token, 100, 0)),
389
388
  abi.encode(returnedRuleset, 100)
390
389
  );
391
390
 
@@ -499,22 +498,28 @@ contract TestUseAllowanceOf_Local is JBMultiTerminalSetup {
499
498
  metadata: packedMetadata
500
499
  });
501
500
 
502
- // mock call to tokens decimals()
503
- mockExpect(mockToken, abi.encodeCall(IERC20Metadata.decimals, ()), abi.encode(18));
504
-
505
- // mock call to rulesets currentOf
506
- mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(returnedRuleset));
507
-
508
501
  // Set up accounting context
509
502
  JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
510
503
  _tokens[0] = JBAccountingContext({token: mockToken, decimals: 18, currency: currencyId});
511
504
 
505
+ // Mock recordAccountingContextOf in the store (validation now happens there)
506
+ mockExpect(
507
+ address(store), abi.encodeCall(IJBTerminalStore.recordAccountingContextOf, (_projectId, _tokens)), ""
508
+ );
509
+
512
510
  _terminal.addAccountingContextsFor(_projectId, _tokens);
513
511
 
512
+ // Mock accountingContextOf for subsequent reads (used by _tokenAmountOf during fee processing)
513
+ mockExpect(
514
+ address(store),
515
+ abi.encodeCall(IJBTerminalStore.accountingContextOf, (address(_terminal), _projectId, mockToken)),
516
+ abi.encode(_tokens[0])
517
+ );
518
+
514
519
  // recordUsedAllowance
515
520
  mockExpect(
516
521
  address(store),
517
- abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, _tokens[0], 100, 0)),
522
+ abi.encodeCall(IJBTerminalStore.recordUsedAllowanceOf, (_projectId, _tokens[0].token, 100, 0)),
518
523
  abi.encode(returnedRuleset, 100)
519
524
  );
520
525
 
@@ -19,17 +19,7 @@ contract MockSurplusTerminal is ERC165, IJBCashOutTerminal {
19
19
  surplusAmount = _surplus;
20
20
  }
21
21
 
22
- function currentSurplusOf(
23
- uint256,
24
- JBAccountingContext[] memory,
25
- uint256,
26
- uint256
27
- )
28
- external
29
- view
30
- override
31
- returns (uint256)
32
- {
22
+ function currentSurplusOf(uint256, address[] calldata, uint256, uint256) external view override returns (uint256) {
33
23
  return surplusAmount;
34
24
  }
35
25
 
@@ -131,9 +121,8 @@ contract TestSurplusFuzz_Local is JBTest {
131
121
  /// @notice Surplus with no terminals is 0.
132
122
  function testFuzz_noTerminals_returnsZero(uint256 projectId) external view {
133
123
  IJBTerminal[] memory terminals = new IJBTerminal[](0);
134
- JBAccountingContext[] memory contexts = new JBAccountingContext[](0);
135
124
 
136
- uint256 surplus = JBSurplus.currentSurplusOf(projectId, terminals, contexts, 18, 1);
125
+ uint256 surplus = JBSurplus.currentSurplusOf(projectId, terminals, new address[](0), 18, 1);
137
126
  assertEq(surplus, 0, "surplus with no terminals should be 0");
138
127
  }
139
128
 
@@ -146,9 +135,7 @@ contract TestSurplusFuzz_Local is JBTest {
146
135
  terminals[0] = terminal1;
147
136
  terminals[1] = terminal2;
148
137
 
149
- JBAccountingContext[] memory contexts = new JBAccountingContext[](0);
150
-
151
- uint256 total = JBSurplus.currentSurplusOf(1, terminals, contexts, 18, 1);
138
+ uint256 total = JBSurplus.currentSurplusOf(1, terminals, new address[](0), 18, 1);
152
139
  assertEq(total, uint256(surplus1) + uint256(surplus2), "surplus should be sum of all terminals");
153
140
  }
154
141
 
@@ -165,10 +152,8 @@ contract TestSurplusFuzz_Local is JBTest {
165
152
  IJBTerminal[] memory terminals2 = new IJBTerminal[](1);
166
153
  terminals2[0] = terminal2;
167
154
 
168
- JBAccountingContext[] memory contexts = new JBAccountingContext[](0);
169
-
170
- uint256 total1 = JBSurplus.currentSurplusOf(1, terminals1, contexts, 18, 1);
171
- uint256 total2 = JBSurplus.currentSurplusOf(1, terminals2, contexts, 18, 1);
155
+ uint256 total1 = JBSurplus.currentSurplusOf(1, terminals1, new address[](0), 18, 1);
156
+ uint256 total2 = JBSurplus.currentSurplusOf(1, terminals2, new address[](0), 18, 1);
172
157
 
173
158
  assertLe(total1, total2, "surplus should be monotonically increasing");
174
159
  }
@@ -4,8 +4,10 @@ pragma solidity 0.8.26;
4
4
  import {JBTerminalStore} from "../../../../src/JBTerminalStore.sol";
5
5
  import {IJBDirectory} from "../../../../src/interfaces/IJBDirectory.sol";
6
6
  import {IJBPrices} from "../../../../src/interfaces/IJBPrices.sol";
7
+ import {IJBRulesetApprovalHook} from "../../../../src/interfaces/IJBRulesetApprovalHook.sol";
7
8
  import {IJBRulesets} from "../../../../src/interfaces/IJBRulesets.sol";
8
9
  import {IJBTerminalStore} from "../../../../src/interfaces/IJBTerminalStore.sol";
10
+ import {JBRuleset} from "../../../../src/structs/JBRuleset.sol";
9
11
  import {JBTest} from "../../../helpers/JBTest.sol";
10
12
 
11
13
  /*
@@ -24,5 +26,20 @@ contract JBTerminalStoreSetup is JBTest {
24
26
  function terminalStoreSetup() public virtual {
25
27
  // Instantiate the contract being tested
26
28
  _store = new JBTerminalStore(directory, prices, rulesets);
29
+
30
+ // Default mock: allow adding accounting contexts (return zero-id ruleset to bypass the
31
+ // allowAddAccountingContext check in recordAccountingContextOf).
32
+ JBRuleset memory _zeroRuleset = JBRuleset({
33
+ cycleNumber: 0,
34
+ id: 0,
35
+ basedOnId: 0,
36
+ start: 0,
37
+ duration: 0,
38
+ weight: 0,
39
+ weightCutPercent: 0,
40
+ approvalHook: IJBRulesetApprovalHook(address(0)),
41
+ metadata: 0
42
+ });
43
+ vm.mockCall(address(rulesets), abi.encodeWithSelector(IJBRulesets.currentOf.selector), abi.encode(_zeroRuleset));
27
44
  }
28
45
  }