@bananapus/core-v6 0.0.17 → 0.0.19

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 (107) hide show
  1. package/CHANGE_LOG.md +53 -13
  2. package/SKILLS.md +6 -0
  3. package/USER_JOURNEYS.md +4 -0
  4. package/package.json +1 -1
  5. package/src/JBTerminalStore.sol +357 -171
  6. package/src/interfaces/IJBTerminalStore.sol +70 -0
  7. package/test/fork/TestSequencerPriceFeedFork.sol +168 -0
  8. package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +215 -0
  9. package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +383 -0
  10. package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +415 -0
  11. package/docs/book.css +0 -13
  12. package/docs/book.toml +0 -12
  13. package/docs/solidity.min.js +0 -74
  14. package/docs/src/README.md +0 -703
  15. package/docs/src/SUMMARY.md +0 -94
  16. package/docs/src/src/JBChainlinkV3PriceFeed.sol/contract.JBChainlinkV3PriceFeed.md +0 -83
  17. package/docs/src/src/JBChainlinkV3SequencerPriceFeed.sol/contract.JBChainlinkV3SequencerPriceFeed.md +0 -88
  18. package/docs/src/src/JBController.sol/contract.JBController.md +0 -1121
  19. package/docs/src/src/JBDeadline.sol/contract.JBDeadline.md +0 -84
  20. package/docs/src/src/JBDirectory.sol/contract.JBDirectory.md +0 -294
  21. package/docs/src/src/JBERC20.sol/contract.JBERC20.md +0 -190
  22. package/docs/src/src/JBFeelessAddresses.sol/contract.JBFeelessAddresses.md +0 -80
  23. package/docs/src/src/JBFundAccessLimits.sol/contract.JBFundAccessLimits.md +0 -253
  24. package/docs/src/src/JBMultiTerminal.sol/contract.JBMultiTerminal.md +0 -1472
  25. package/docs/src/src/JBPermissions.sol/contract.JBPermissions.md +0 -199
  26. package/docs/src/src/JBPrices.sol/contract.JBPrices.md +0 -154
  27. package/docs/src/src/JBProjects.sol/contract.JBProjects.md +0 -131
  28. package/docs/src/src/JBRulesets.sol/contract.JBRulesets.md +0 -677
  29. package/docs/src/src/JBSplits.sol/contract.JBSplits.md +0 -237
  30. package/docs/src/src/JBTerminalStore.sol/contract.JBTerminalStore.md +0 -591
  31. package/docs/src/src/JBTokens.sol/contract.JBTokens.md +0 -353
  32. package/docs/src/src/README.md +0 -25
  33. package/docs/src/src/abstract/JBControlled.sol/abstract.JBControlled.md +0 -64
  34. package/docs/src/src/abstract/JBPermissioned.sol/abstract.JBPermissioned.md +0 -84
  35. package/docs/src/src/abstract/README.md +0 -5
  36. package/docs/src/src/enums/JBApprovalStatus.sol/enum.JBApprovalStatus.md +0 -17
  37. package/docs/src/src/enums/README.md +0 -4
  38. package/docs/src/src/interfaces/IJBCashOutHook.sol/interface.IJBCashOutHook.md +0 -29
  39. package/docs/src/src/interfaces/IJBCashOutTerminal.sol/interface.IJBCashOutTerminal.md +0 -57
  40. package/docs/src/src/interfaces/IJBControlled.sol/interface.IJBControlled.md +0 -12
  41. package/docs/src/src/interfaces/IJBController.sol/interface.IJBController.md +0 -334
  42. package/docs/src/src/interfaces/IJBDirectory.sol/interface.IJBDirectory.md +0 -108
  43. package/docs/src/src/interfaces/IJBDirectoryAccessControl.sol/interface.IJBDirectoryAccessControl.md +0 -19
  44. package/docs/src/src/interfaces/IJBFeeTerminal.sol/interface.IJBFeeTerminal.md +0 -91
  45. package/docs/src/src/interfaces/IJBFeelessAddresses.sol/interface.IJBFeelessAddresses.md +0 -26
  46. package/docs/src/src/interfaces/IJBFundAccessLimits.sol/interface.IJBFundAccessLimits.md +0 -88
  47. package/docs/src/src/interfaces/IJBMigratable.sol/interface.IJBMigratable.md +0 -29
  48. package/docs/src/src/interfaces/IJBMultiTerminal.sol/interface.IJBMultiTerminal.md +0 -50
  49. package/docs/src/src/interfaces/IJBPayHook.sol/interface.IJBPayHook.md +0 -28
  50. package/docs/src/src/interfaces/IJBPayoutTerminal.sol/interface.IJBPayoutTerminal.md +0 -105
  51. package/docs/src/src/interfaces/IJBPermissioned.sol/interface.IJBPermissioned.md +0 -12
  52. package/docs/src/src/interfaces/IJBPermissions.sol/interface.IJBPermissions.md +0 -74
  53. package/docs/src/src/interfaces/IJBPermitTerminal.sol/interface.IJBPermitTerminal.md +0 -15
  54. package/docs/src/src/interfaces/IJBPriceFeed.sol/interface.IJBPriceFeed.md +0 -12
  55. package/docs/src/src/interfaces/IJBPrices.sol/interface.IJBPrices.md +0 -74
  56. package/docs/src/src/interfaces/IJBProjectUriRegistry.sol/interface.IJBProjectUriRegistry.md +0 -19
  57. package/docs/src/src/interfaces/IJBProjects.sol/interface.IJBProjects.md +0 -49
  58. package/docs/src/src/interfaces/IJBRulesetApprovalHook.sol/interface.IJBRulesetApprovalHook.md +0 -35
  59. package/docs/src/src/interfaces/IJBRulesetDataHook.sol/interface.IJBRulesetDataHook.md +0 -97
  60. package/docs/src/src/interfaces/IJBRulesets.sol/interface.IJBRulesets.md +0 -165
  61. package/docs/src/src/interfaces/IJBSplitHook.sol/interface.IJBSplitHook.md +0 -31
  62. package/docs/src/src/interfaces/IJBSplits.sol/interface.IJBSplits.md +0 -35
  63. package/docs/src/src/interfaces/IJBTerminal.sol/interface.IJBTerminal.md +0 -141
  64. package/docs/src/src/interfaces/IJBTerminalStore.sol/interface.IJBTerminalStore.md +0 -198
  65. package/docs/src/src/interfaces/IJBToken.sol/interface.IJBToken.md +0 -54
  66. package/docs/src/src/interfaces/IJBTokenUriResolver.sol/interface.IJBTokenUriResolver.md +0 -12
  67. package/docs/src/src/interfaces/IJBTokens.sol/interface.IJBTokens.md +0 -151
  68. package/docs/src/src/interfaces/README.md +0 -33
  69. package/docs/src/src/libraries/JBCashOuts.sol/library.JBCashOuts.md +0 -40
  70. package/docs/src/src/libraries/JBConstants.sol/library.JBConstants.md +0 -52
  71. package/docs/src/src/libraries/JBCurrencyIds.sol/library.JBCurrencyIds.md +0 -19
  72. package/docs/src/src/libraries/JBFees.sol/library.JBFees.md +0 -52
  73. package/docs/src/src/libraries/JBFixedPointNumber.sol/library.JBFixedPointNumber.md +0 -12
  74. package/docs/src/src/libraries/JBMetadataResolver.sol/library.JBMetadataResolver.md +0 -242
  75. package/docs/src/src/libraries/JBRulesetMetadataResolver.sol/library.JBRulesetMetadataResolver.md +0 -180
  76. package/docs/src/src/libraries/JBSplitGroupIds.sol/library.JBSplitGroupIds.md +0 -14
  77. package/docs/src/src/libraries/JBSurplus.sol/library.JBSurplus.md +0 -44
  78. package/docs/src/src/libraries/README.md +0 -12
  79. package/docs/src/src/periphery/JBDeadline1Day.sol/contract.JBDeadline1Day.md +0 -15
  80. package/docs/src/src/periphery/JBDeadline3Days.sol/contract.JBDeadline3Days.md +0 -15
  81. package/docs/src/src/periphery/JBDeadline3Hours.sol/contract.JBDeadline3Hours.md +0 -15
  82. package/docs/src/src/periphery/JBDeadline7Days.sol/contract.JBDeadline7Days.md +0 -15
  83. package/docs/src/src/periphery/JBMatchingPriceFeed.sol/contract.JBMatchingPriceFeed.md +0 -22
  84. package/docs/src/src/periphery/README.md +0 -8
  85. package/docs/src/src/structs/JBAccountingContext.sol/struct.JBAccountingContext.md +0 -20
  86. package/docs/src/src/structs/JBAfterCashOutRecordedContext.sol/struct.JBAfterCashOutRecordedContext.md +0 -43
  87. package/docs/src/src/structs/JBAfterPayRecordedContext.sol/struct.JBAfterPayRecordedContext.md +0 -42
  88. package/docs/src/src/structs/JBBeforeCashOutRecordedContext.sol/struct.JBBeforeCashOutRecordedContext.md +0 -45
  89. package/docs/src/src/structs/JBBeforePayRecordedContext.sol/struct.JBBeforePayRecordedContext.md +0 -41
  90. package/docs/src/src/structs/JBCashOutHookSpecification.sol/struct.JBCashOutHookSpecification.md +0 -22
  91. package/docs/src/src/structs/JBCurrencyAmount.sol/struct.JBCurrencyAmount.md +0 -17
  92. package/docs/src/src/structs/JBFee.sol/struct.JBFee.md +0 -20
  93. package/docs/src/src/structs/JBFundAccessLimitGroup.sol/struct.JBFundAccessLimitGroup.md +0 -39
  94. package/docs/src/src/structs/JBPayHookSpecification.sol/struct.JBPayHookSpecification.md +0 -22
  95. package/docs/src/src/structs/JBPermissionsData.sol/struct.JBPermissionsData.md +0 -21
  96. package/docs/src/src/structs/JBRuleset.sol/struct.JBRuleset.md +0 -55
  97. package/docs/src/src/structs/JBRulesetConfig.sol/struct.JBRulesetConfig.md +0 -51
  98. package/docs/src/src/structs/JBRulesetMetadata.sol/struct.JBRulesetMetadata.md +0 -79
  99. package/docs/src/src/structs/JBRulesetWeightCache.sol/struct.JBRulesetWeightCache.md +0 -16
  100. package/docs/src/src/structs/JBRulesetWithMetadata.sol/struct.JBRulesetWithMetadata.md +0 -16
  101. package/docs/src/src/structs/JBSingleAllowance.sol/struct.JBSingleAllowance.md +0 -26
  102. package/docs/src/src/structs/JBSplit.sol/struct.JBSplit.md +0 -49
  103. package/docs/src/src/structs/JBSplitGroup.sol/struct.JBSplitGroup.md +0 -17
  104. package/docs/src/src/structs/JBSplitHookContext.sol/struct.JBSplitHookContext.md +0 -29
  105. package/docs/src/src/structs/JBTerminalConfig.sol/struct.JBTerminalConfig.md +0 -16
  106. package/docs/src/src/structs/JBTokenAmount.sol/struct.JBTokenAmount.md +0 -23
  107. package/docs/src/src/structs/README.md +0 -25
@@ -0,0 +1,383 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.26;
3
+
4
+ import {JBTerminalStore} from "../../../../src/JBTerminalStore.sol";
5
+ import {IJBCashOutHook} from "../../../../src/interfaces/IJBCashOutHook.sol";
6
+ import {IJBController} from "../../../../src/interfaces/IJBController.sol";
7
+ import {IJBDirectory} from "../../../../src/interfaces/IJBDirectory.sol";
8
+ import {IJBFundAccessLimits} from "../../../../src/interfaces/IJBFundAccessLimits.sol";
9
+ import {IJBRulesetApprovalHook} from "../../../../src/interfaces/IJBRulesetApprovalHook.sol";
10
+ import {IJBRulesetDataHook} from "../../../../src/interfaces/IJBRulesetDataHook.sol";
11
+ import {IJBRulesets} from "../../../../src/interfaces/IJBRulesets.sol";
12
+ import {IJBTerminal} from "../../../../src/interfaces/IJBTerminal.sol";
13
+ import {IJBToken} from "../../../../src/interfaces/IJBToken.sol";
14
+ import {JBConstants} from "../../../../src/libraries/JBConstants.sol";
15
+ import {JBRulesetMetadataResolver} from "../../../../src/libraries/JBRulesetMetadataResolver.sol";
16
+ import {JBAccountingContext} from "../../../../src/structs/JBAccountingContext.sol";
17
+ import {JBBeforeCashOutRecordedContext} from "../../../../src/structs/JBBeforeCashOutRecordedContext.sol";
18
+ import {JBCashOutHookSpecification} from "../../../../src/structs/JBCashOutHookSpecification.sol";
19
+ import {JBCurrencyAmount} from "../../../../src/structs/JBCurrencyAmount.sol";
20
+ import {JBRuleset} from "../../../../src/structs/JBRuleset.sol";
21
+ import {JBRulesetMetadata} from "../../../../src/structs/JBRulesetMetadata.sol";
22
+ import {JBTokenAmount} from "../../../../src/structs/JBTokenAmount.sol";
23
+ import {mulDiv} from "@prb/math/src/Common.sol";
24
+ import {JBTerminalStoreSetup} from "./JBTerminalStoreSetup.sol";
25
+
26
+ contract TestPreviewCashOutFor_Local is JBTerminalStoreSetup {
27
+ uint64 _projectId = 1;
28
+ uint256 _decimals = 18;
29
+ uint256 _balance = 10e18;
30
+ uint256 _totalSupply = 20e18;
31
+
32
+ // Mocks
33
+ IJBTerminal _terminal1 = IJBTerminal(makeAddr("terminal1"));
34
+ IJBTerminal _terminal2 = IJBTerminal(makeAddr("terminal2"));
35
+ IJBToken _token = IJBToken(makeAddr("token"));
36
+ IJBController _controller = IJBController(makeAddr("controller"));
37
+ IJBFundAccessLimits _accessLimits = IJBFundAccessLimits(makeAddr("funds"));
38
+ IJBRulesetDataHook _dataHook = IJBRulesetDataHook(makeAddr("dataHook"));
39
+ IJBCashOutHook _cashOutHook = IJBCashOutHook(makeAddr("cashOutHook"));
40
+
41
+ uint32 _currency = uint32(uint160(address(_token)));
42
+
43
+ function setUp() public {
44
+ super.terminalStoreSetup();
45
+ }
46
+
47
+ function _setBalance(address terminal, uint256 balance) internal {
48
+ bytes32 balanceOfSlot = keccak256(abi.encode(terminal, uint256(0)));
49
+ bytes32 projectSlot = keccak256(abi.encode(_projectId, uint256(balanceOfSlot)));
50
+ bytes32 slot = keccak256(abi.encode(address(_token), uint256(projectSlot)));
51
+ vm.store(address(_store), slot, bytes32(balance));
52
+ }
53
+
54
+ function _mockUseTotalSurplus() internal {
55
+ IJBTerminal[] memory _terminals = new IJBTerminal[](2);
56
+ _terminals[0] = _terminal1;
57
+ _terminals[1] = _terminal2;
58
+
59
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.terminalsOf, (_projectId)), abi.encode(_terminals));
60
+
61
+ mockExpect(
62
+ address(_terminal1),
63
+ abi.encodeCall(
64
+ IJBTerminal.currentSurplusOf, (_projectId, new JBAccountingContext[](0), _decimals, _currency)
65
+ ),
66
+ abi.encode(1e18)
67
+ );
68
+ mockExpect(
69
+ address(_terminal2),
70
+ abi.encodeCall(
71
+ IJBTerminal.currentSurplusOf, (_projectId, new JBAccountingContext[](0), _decimals, _currency)
72
+ ),
73
+ abi.encode(2e18)
74
+ );
75
+ }
76
+
77
+ function test_MatchesRecordCashOutFor() external {
78
+ // Setup: set balance for address(this) (acting as terminal for both preview and record)
79
+ _setBalance(address(this), _balance);
80
+
81
+ _mockUseTotalSurplus();
82
+
83
+ JBRulesetMetadata memory _metadata = JBRulesetMetadata({
84
+ reservedPercent: 0,
85
+ cashOutTaxRate: 0,
86
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
87
+ pausePay: false,
88
+ pauseCreditTransfers: false,
89
+ allowOwnerMinting: false,
90
+ allowSetCustomToken: false,
91
+ allowTerminalMigration: false,
92
+ allowSetTerminals: false,
93
+ ownerMustSendPayouts: false,
94
+ allowSetController: false,
95
+ allowAddAccountingContext: true,
96
+ allowAddPriceFeed: false,
97
+ holdFees: false,
98
+ useTotalSurplusForCashOuts: true,
99
+ useDataHookForPay: false,
100
+ useDataHookForCashOut: false,
101
+ dataHook: address(0),
102
+ metadata: 0
103
+ });
104
+
105
+ uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
106
+
107
+ JBRuleset memory _returnedRuleset = JBRuleset({
108
+ cycleNumber: uint48(block.timestamp),
109
+ id: uint48(block.timestamp),
110
+ basedOnId: 0,
111
+ start: uint48(block.timestamp),
112
+ duration: uint32(block.timestamp + 1000),
113
+ weight: 1e18,
114
+ weightCutPercent: 0,
115
+ approvalHook: IJBRulesetApprovalHook(address(0)),
116
+ metadata: _packedMetadata
117
+ });
118
+
119
+ uint256 _cashOutCount = 10e18;
120
+
121
+ JBAccountingContext memory _accountingContext =
122
+ JBAccountingContext({token: address(_token), decimals: uint8(_decimals), currency: _currency});
123
+
124
+ JBAccountingContext[] memory _balanceContexts = new JBAccountingContext[](0);
125
+
126
+ // Mock for preview call
127
+ mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
128
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.controllerOf, (_projectId)), abi.encode(_controller));
129
+ mockExpect(
130
+ address(_controller),
131
+ abi.encodeCall(IJBController.totalTokenSupplyWithReservedTokensOf, (_projectId)),
132
+ abi.encode(_totalSupply)
133
+ );
134
+
135
+ (, uint256 previewReclaimAmount, uint256 previewTaxRate, JBCashOutHookSpecification[] memory previewSpecs) = _store.previewCashOutFrom({
136
+ terminal: address(this),
137
+ holder: address(this),
138
+ projectId: _projectId,
139
+ cashOutCount: _cashOutCount,
140
+ accountingContext: _accountingContext,
141
+ balanceAccountingContexts: _balanceContexts,
142
+ beneficiaryIsFeeless: false,
143
+ metadata: ""
144
+ });
145
+
146
+ // Re-mock for record call (same mocks, fresh expectations)
147
+ _mockUseTotalSurplus();
148
+ mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
149
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.controllerOf, (_projectId)), abi.encode(_controller));
150
+ mockExpect(
151
+ address(_controller),
152
+ abi.encodeCall(IJBController.totalTokenSupplyWithReservedTokensOf, (_projectId)),
153
+ abi.encode(_totalSupply)
154
+ );
155
+
156
+ (, uint256 recordReclaimAmount, uint256 recordTaxRate, JBCashOutHookSpecification[] memory recordSpecs) = _store.recordCashOutFor({
157
+ holder: address(this),
158
+ projectId: _projectId,
159
+ cashOutCount: _cashOutCount,
160
+ accountingContext: _accountingContext,
161
+ balanceAccountingContexts: _balanceContexts,
162
+ beneficiaryIsFeeless: false,
163
+ metadata: ""
164
+ });
165
+
166
+ assertEq(previewReclaimAmount, recordReclaimAmount);
167
+ assertEq(previewTaxRate, recordTaxRate);
168
+ assertEq(previewSpecs.length, recordSpecs.length);
169
+ }
170
+
171
+ function test_DoesNotModifyState() external {
172
+ _setBalance(address(this), _balance);
173
+ _mockUseTotalSurplus();
174
+
175
+ JBRulesetMetadata memory _metadata = JBRulesetMetadata({
176
+ reservedPercent: 0,
177
+ cashOutTaxRate: 0,
178
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
179
+ pausePay: false,
180
+ pauseCreditTransfers: false,
181
+ allowOwnerMinting: false,
182
+ allowSetCustomToken: false,
183
+ allowTerminalMigration: false,
184
+ allowSetTerminals: false,
185
+ ownerMustSendPayouts: false,
186
+ allowSetController: false,
187
+ allowAddAccountingContext: true,
188
+ allowAddPriceFeed: false,
189
+ holdFees: false,
190
+ useTotalSurplusForCashOuts: true,
191
+ useDataHookForPay: false,
192
+ useDataHookForCashOut: false,
193
+ dataHook: address(0),
194
+ metadata: 0
195
+ });
196
+
197
+ uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
198
+
199
+ JBRuleset memory _returnedRuleset = JBRuleset({
200
+ cycleNumber: uint48(block.timestamp),
201
+ id: uint48(block.timestamp),
202
+ basedOnId: 0,
203
+ start: uint48(block.timestamp),
204
+ duration: uint32(block.timestamp + 1000),
205
+ weight: 1e18,
206
+ weightCutPercent: 0,
207
+ approvalHook: IJBRulesetApprovalHook(address(0)),
208
+ metadata: _packedMetadata
209
+ });
210
+
211
+ JBAccountingContext memory _accountingContext =
212
+ JBAccountingContext({token: address(_token), decimals: uint8(_decimals), currency: _currency});
213
+
214
+ mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
215
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.controllerOf, (_projectId)), abi.encode(_controller));
216
+ mockExpect(
217
+ address(_controller),
218
+ abi.encodeCall(IJBController.totalTokenSupplyWithReservedTokensOf, (_projectId)),
219
+ abi.encode(_totalSupply)
220
+ );
221
+
222
+ uint256 balanceBefore = _store.balanceOf(address(this), _projectId, address(_token));
223
+
224
+ _store.previewCashOutFrom({
225
+ terminal: address(this),
226
+ holder: address(this),
227
+ projectId: _projectId,
228
+ cashOutCount: 5e18,
229
+ accountingContext: _accountingContext,
230
+ balanceAccountingContexts: new JBAccountingContext[](0),
231
+ beneficiaryIsFeeless: false,
232
+ metadata: ""
233
+ });
234
+
235
+ uint256 balanceAfter = _store.balanceOf(address(this), _projectId, address(_token));
236
+ assertEq(balanceBefore, balanceAfter);
237
+ }
238
+
239
+ function test_RevertsWhenCashOutCountExceedsTotalSupply() external {
240
+ _setBalance(address(this), _balance);
241
+ _mockUseTotalSurplus();
242
+
243
+ JBRulesetMetadata memory _metadata = JBRulesetMetadata({
244
+ reservedPercent: 0,
245
+ cashOutTaxRate: 0,
246
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
247
+ pausePay: false,
248
+ pauseCreditTransfers: false,
249
+ allowOwnerMinting: false,
250
+ allowSetCustomToken: false,
251
+ allowTerminalMigration: false,
252
+ allowSetTerminals: false,
253
+ ownerMustSendPayouts: false,
254
+ allowSetController: false,
255
+ allowAddAccountingContext: true,
256
+ allowAddPriceFeed: false,
257
+ holdFees: false,
258
+ useTotalSurplusForCashOuts: true,
259
+ useDataHookForPay: false,
260
+ useDataHookForCashOut: false,
261
+ dataHook: address(0),
262
+ metadata: 0
263
+ });
264
+
265
+ uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
266
+
267
+ JBRuleset memory _returnedRuleset = JBRuleset({
268
+ cycleNumber: uint48(block.timestamp),
269
+ id: uint48(block.timestamp),
270
+ basedOnId: 0,
271
+ start: uint48(block.timestamp),
272
+ duration: uint32(block.timestamp + 1000),
273
+ weight: 1e18,
274
+ weightCutPercent: 0,
275
+ approvalHook: IJBRulesetApprovalHook(address(0)),
276
+ metadata: _packedMetadata
277
+ });
278
+
279
+ JBAccountingContext memory _accountingContext =
280
+ JBAccountingContext({token: address(_token), decimals: uint8(_decimals), currency: _currency});
281
+
282
+ mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
283
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.controllerOf, (_projectId)), abi.encode(_controller));
284
+ mockExpect(
285
+ address(_controller),
286
+ abi.encodeCall(IJBController.totalTokenSupplyWithReservedTokensOf, (_projectId)),
287
+ abi.encode(_totalSupply)
288
+ );
289
+
290
+ uint256 _excessiveCashOutCount = _totalSupply + 1;
291
+
292
+ vm.expectRevert(
293
+ abi.encodeWithSelector(
294
+ JBTerminalStore.JBTerminalStore_InsufficientTokens.selector, _excessiveCashOutCount, _totalSupply
295
+ )
296
+ );
297
+ _store.previewCashOutFrom({
298
+ terminal: address(this),
299
+ holder: address(this),
300
+ projectId: _projectId,
301
+ cashOutCount: _excessiveCashOutCount,
302
+ accountingContext: _accountingContext,
303
+ balanceAccountingContexts: new JBAccountingContext[](0),
304
+ beneficiaryIsFeeless: false,
305
+ metadata: ""
306
+ });
307
+ }
308
+
309
+ function test_ReturnsZeroWhenSurplusIsZero() external {
310
+ // No balance set, no surplus
311
+
312
+ JBRulesetMetadata memory _metadata = JBRulesetMetadata({
313
+ reservedPercent: 0,
314
+ cashOutTaxRate: 0,
315
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
316
+ pausePay: false,
317
+ pauseCreditTransfers: false,
318
+ allowOwnerMinting: false,
319
+ allowSetCustomToken: false,
320
+ allowTerminalMigration: false,
321
+ allowSetTerminals: false,
322
+ ownerMustSendPayouts: false,
323
+ allowSetController: false,
324
+ allowAddAccountingContext: true,
325
+ allowAddPriceFeed: false,
326
+ holdFees: false,
327
+ useTotalSurplusForCashOuts: true,
328
+ useDataHookForPay: false,
329
+ useDataHookForCashOut: false,
330
+ dataHook: address(0),
331
+ metadata: 0
332
+ });
333
+
334
+ uint256 _packedMetadata = JBRulesetMetadataResolver.packRulesetMetadata(_metadata);
335
+
336
+ JBRuleset memory _returnedRuleset = JBRuleset({
337
+ cycleNumber: uint48(block.timestamp),
338
+ id: uint48(block.timestamp),
339
+ basedOnId: 0,
340
+ start: uint48(block.timestamp),
341
+ duration: uint32(block.timestamp + 1000),
342
+ weight: 1e18,
343
+ weightCutPercent: 0,
344
+ approvalHook: IJBRulesetApprovalHook(address(0)),
345
+ metadata: _packedMetadata
346
+ });
347
+
348
+ JBAccountingContext memory _accountingContext =
349
+ JBAccountingContext({token: address(_token), decimals: uint8(_decimals), currency: _currency});
350
+
351
+ IJBTerminal[] memory _terminals = new IJBTerminal[](1);
352
+ _terminals[0] = _terminal1;
353
+
354
+ mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(_returnedRuleset));
355
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.terminalsOf, (_projectId)), abi.encode(_terminals));
356
+ mockExpect(
357
+ address(_terminal1),
358
+ abi.encodeCall(
359
+ IJBTerminal.currentSurplusOf, (_projectId, new JBAccountingContext[](0), _decimals, _currency)
360
+ ),
361
+ abi.encode(0)
362
+ );
363
+ mockExpect(address(directory), abi.encodeCall(IJBDirectory.controllerOf, (_projectId)), abi.encode(_controller));
364
+ mockExpect(
365
+ address(_controller),
366
+ abi.encodeCall(IJBController.totalTokenSupplyWithReservedTokensOf, (_projectId)),
367
+ abi.encode(_totalSupply)
368
+ );
369
+
370
+ (, uint256 reclaimAmount,,) = _store.previewCashOutFrom({
371
+ terminal: address(this),
372
+ holder: address(this),
373
+ projectId: _projectId,
374
+ cashOutCount: 5e18,
375
+ accountingContext: _accountingContext,
376
+ balanceAccountingContexts: new JBAccountingContext[](0),
377
+ beneficiaryIsFeeless: false,
378
+ metadata: ""
379
+ });
380
+
381
+ assertEq(reclaimAmount, 0);
382
+ }
383
+ }