@bananapus/core-v6 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ADMINISTRATION.md +3 -0
- package/ARCHITECTURE.md +24 -0
- package/AUDIT_INSTRUCTIONS.md +4 -2
- package/CHANGE_LOG.md +17 -4
- package/README.md +12 -2
- package/RISKS.md +10 -2
- package/SKILLS.md +5 -2
- package/USER_JOURNEYS.md +4 -2
- package/foundry.toml +1 -0
- package/package.json +1 -1
- package/src/JBController.sol +52 -5
- package/src/JBMultiTerminal.sol +197 -179
- package/src/JBTerminalStore.sol +17 -7
- package/src/interfaces/IJBCashOutTerminal.sol +30 -0
- package/src/interfaces/IJBController.sol +15 -0
- package/src/interfaces/IJBTerminal.sol +28 -0
- package/src/interfaces/IJBTerminalStore.sol +1 -5
- package/src/libraries/JBPayoutSplitGroupLib.sol +157 -0
- package/src/structs/JBCashOutHookSpecification.sol +2 -0
- package/src/structs/JBPayHookSpecification.sol +2 -0
- package/test/CoreExploitTests.t.sol +21 -10
- package/test/TestCashOutHooks.sol +6 -4
- package/test/TestDataHookFuzzing.sol +6 -2
- package/test/TestPayHooks.sol +1 -1
- package/test/TestRulesetQueueing.sol +4 -5
- package/test/TestRulesetQueuingStress.sol +5 -3
- package/test/TestTerminalPreviewParity.sol +208 -0
- package/test/fork/TestTerminalPreviewParityFork.sol +109 -0
- package/test/units/static/JBController/TestPreviewMintOf.sol +116 -0
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +144 -25
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +11 -1
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +15 -2
- package/test/units/static/JBMultiTerminal/TestPay.sol +64 -2
- package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +116 -0
- package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +98 -0
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +11 -2
- package/test/units/static/JBRulesets/TestCurrentOf.sol +8 -6
- package/test/units/static/JBRulesets/TestRulesets.sol +25 -24
- package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +4 -17
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +49 -2
- package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +96 -4
- package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +81 -32
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +113 -2
- package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +227 -5
|
@@ -258,7 +258,8 @@ contract TestPay_Local is JBMultiTerminalSetup {
|
|
|
258
258
|
value: _defaultAmount
|
|
259
259
|
});
|
|
260
260
|
JBPayHookSpecification[] memory hookSpecifications = new JBPayHookSpecification[](1);
|
|
261
|
-
hookSpecifications[0] =
|
|
261
|
+
hookSpecifications[0] =
|
|
262
|
+
JBPayHookSpecification({hook: _mockHook, noop: false, amount: _defaultAmount, metadata: ""});
|
|
262
263
|
|
|
263
264
|
JBRuleset memory returnedRuleset = JBRuleset({
|
|
264
265
|
cycleNumber: 1,
|
|
@@ -349,7 +350,8 @@ contract TestPay_Local is JBMultiTerminalSetup {
|
|
|
349
350
|
JBTokenAmount memory tokenAmount =
|
|
350
351
|
JBTokenAmount({token: _native, decimals: 18, currency: uint32(_nativeCurrency), value: _defaultAmount});
|
|
351
352
|
JBPayHookSpecification[] memory hookSpecifications = new JBPayHookSpecification[](1);
|
|
352
|
-
hookSpecifications[0] =
|
|
353
|
+
hookSpecifications[0] =
|
|
354
|
+
JBPayHookSpecification({hook: _mockHook, noop: false, amount: _defaultAmount, metadata: ""});
|
|
353
355
|
|
|
354
356
|
JBRuleset memory returnedRuleset = JBRuleset({
|
|
355
357
|
cycleNumber: 1,
|
|
@@ -527,6 +529,66 @@ contract TestPay_Local is JBMultiTerminalSetup {
|
|
|
527
529
|
});
|
|
528
530
|
}
|
|
529
531
|
|
|
532
|
+
function test_GivenThePayHookSpecIsNoop() external whenNativeTokenIsAccepted {
|
|
533
|
+
JBTokenAmount memory tokenAmount =
|
|
534
|
+
JBTokenAmount({token: _native, decimals: 18, currency: uint32(_nativeCurrency), value: _defaultAmount});
|
|
535
|
+
JBPayHookSpecification[] memory hookSpecifications = new JBPayHookSpecification[](1);
|
|
536
|
+
hookSpecifications[0] =
|
|
537
|
+
JBPayHookSpecification({hook: IJBPayHook(address(this)), noop: true, amount: 0, metadata: "info"});
|
|
538
|
+
|
|
539
|
+
JBRuleset memory returnedRuleset = JBRuleset({
|
|
540
|
+
cycleNumber: 1,
|
|
541
|
+
id: 1,
|
|
542
|
+
basedOnId: 0,
|
|
543
|
+
start: 0,
|
|
544
|
+
duration: 0,
|
|
545
|
+
weight: 0,
|
|
546
|
+
weightCutPercent: 0,
|
|
547
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
548
|
+
metadata: 0
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
mockExpect(
|
|
552
|
+
address(store),
|
|
553
|
+
abi.encodeCall(
|
|
554
|
+
IJBTerminalStore.recordPaymentFrom, (address(this), tokenAmount, _projectId, _bene, bytes(""))
|
|
555
|
+
),
|
|
556
|
+
abi.encode(returnedRuleset, 0, hookSpecifications)
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
bytes[] memory subsequentReturns = new bytes[](2);
|
|
560
|
+
subsequentReturns[0] = abi.encode(0);
|
|
561
|
+
subsequentReturns[1] = abi.encode(0);
|
|
562
|
+
|
|
563
|
+
mockExpectSubsequent(
|
|
564
|
+
address(tokens), abi.encodeCall(IJBTokens.totalBalanceOf, (_bene, _projectId)), subsequentReturns
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
vm.expectEmit();
|
|
568
|
+
emit IJBTerminal.Pay(
|
|
569
|
+
returnedRuleset.id,
|
|
570
|
+
returnedRuleset.cycleNumber,
|
|
571
|
+
_projectId,
|
|
572
|
+
address(this),
|
|
573
|
+
_bene,
|
|
574
|
+
_defaultAmount,
|
|
575
|
+
0,
|
|
576
|
+
"",
|
|
577
|
+
bytes(""),
|
|
578
|
+
address(this)
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
_terminal.pay{value: _defaultAmount}({
|
|
582
|
+
projectId: _projectId,
|
|
583
|
+
token: _native,
|
|
584
|
+
amount: _defaultAmount,
|
|
585
|
+
beneficiary: _bene,
|
|
586
|
+
minReturnedTokens: 0,
|
|
587
|
+
memo: "",
|
|
588
|
+
metadata: ""
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
530
592
|
// accept funds with permit2 has been extensively tested in other units
|
|
531
593
|
/* modifier whenPayMetadataContainsPermitData() {
|
|
532
594
|
_;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {JBMultiTerminal} from "../../../../src/JBMultiTerminal.sol";
|
|
5
|
+
import {IJBCashOutHook} from "../../../../src/interfaces/IJBCashOutHook.sol";
|
|
6
|
+
import {IJBDirectory} from "../../../../src/interfaces/IJBDirectory.sol";
|
|
7
|
+
import {IJBFeelessAddresses} from "../../../../src/interfaces/IJBFeelessAddresses.sol";
|
|
8
|
+
import {IJBRulesets} from "../../../../src/interfaces/IJBRulesets.sol";
|
|
9
|
+
import {IJBRulesetApprovalHook} from "../../../../src/interfaces/IJBRulesetApprovalHook.sol";
|
|
10
|
+
import {IJBTerminalStore} from "../../../../src/interfaces/IJBTerminalStore.sol";
|
|
11
|
+
import {JBConstants} from "../../../../src/libraries/JBConstants.sol";
|
|
12
|
+
import {JBAccountingContext} from "../../../../src/structs/JBAccountingContext.sol";
|
|
13
|
+
import {JBCashOutHookSpecification} from "../../../../src/structs/JBCashOutHookSpecification.sol";
|
|
14
|
+
import {JBRuleset} from "../../../../src/structs/JBRuleset.sol";
|
|
15
|
+
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
16
|
+
import {JBMultiTerminalSetup} from "./JBMultiTerminalSetup.sol";
|
|
17
|
+
|
|
18
|
+
contract TestPreviewCashOutFrom_Local is JBMultiTerminalSetup {
|
|
19
|
+
uint256 _projectId = 1;
|
|
20
|
+
uint256 _cashOutCount = 1e18;
|
|
21
|
+
address _holder = makeAddr("holder");
|
|
22
|
+
address payable _beneficiary = payable(makeAddr("beneficiary"));
|
|
23
|
+
address _token = JBConstants.NATIVE_TOKEN;
|
|
24
|
+
|
|
25
|
+
function setUp() public {
|
|
26
|
+
super.multiTerminalSetup();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _acceptToken(address token, uint8 decimals, uint32 currency) internal {
|
|
30
|
+
mockExpect(address(projects), abi.encodeCall(IERC721.ownerOf, (_projectId)), abi.encode(address(0)));
|
|
31
|
+
mockExpect(
|
|
32
|
+
address(directory), abi.encodeCall(IJBDirectory.controllerOf, (_projectId)), abi.encode(address(this))
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
JBRuleset memory returnedRuleset = JBRuleset({
|
|
36
|
+
cycleNumber: 1,
|
|
37
|
+
id: 0,
|
|
38
|
+
basedOnId: 0,
|
|
39
|
+
start: 0,
|
|
40
|
+
duration: 0,
|
|
41
|
+
weight: 0,
|
|
42
|
+
weightCutPercent: 0,
|
|
43
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
44
|
+
metadata: 0
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
mockExpect(address(rulesets), abi.encodeCall(IJBRulesets.currentOf, (_projectId)), abi.encode(returnedRuleset));
|
|
48
|
+
|
|
49
|
+
JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
|
|
50
|
+
contexts[0] = JBAccountingContext({token: token, decimals: decimals, currency: currency});
|
|
51
|
+
|
|
52
|
+
vm.prank(address(this));
|
|
53
|
+
_terminal.addAccountingContextsFor(_projectId, contexts);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function test_RevertsWhenTokenIsNotAccepted() external {
|
|
57
|
+
vm.expectRevert(abi.encodeWithSelector(JBMultiTerminal.JBMultiTerminal_TokenNotAccepted.selector, _token));
|
|
58
|
+
JBMultiTerminal(address(_terminal))
|
|
59
|
+
.previewCashOutFrom(_holder, _projectId, _cashOutCount, _token, _beneficiary, "");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function test_ReturnsRulesetAndCashOutPreviewValues() external {
|
|
63
|
+
_acceptToken(_token, 18, uint32(uint160(_token)));
|
|
64
|
+
|
|
65
|
+
JBRuleset memory ruleset = JBRuleset({
|
|
66
|
+
cycleNumber: 1,
|
|
67
|
+
id: 1,
|
|
68
|
+
basedOnId: 0,
|
|
69
|
+
start: uint48(block.timestamp),
|
|
70
|
+
duration: 0,
|
|
71
|
+
weight: 0,
|
|
72
|
+
weightCutPercent: 0,
|
|
73
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
74
|
+
metadata: 0
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
JBCashOutHookSpecification[] memory specs = new JBCashOutHookSpecification[](1);
|
|
78
|
+
specs[0] = JBCashOutHookSpecification({
|
|
79
|
+
hook: IJBCashOutHook(makeAddr("hook")), noop: false, amount: 321, metadata: hex"5678"
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
JBAccountingContext memory accountingContext =
|
|
83
|
+
JBAccountingContext({token: _token, decimals: 18, currency: uint32(uint160(_token))});
|
|
84
|
+
JBAccountingContext[] memory accountingContexts = new JBAccountingContext[](1);
|
|
85
|
+
accountingContexts[0] = accountingContext;
|
|
86
|
+
|
|
87
|
+
mockExpect(
|
|
88
|
+
address(feelessAddresses), abi.encodeCall(IJBFeelessAddresses.isFeeless, (_beneficiary)), abi.encode(true)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
mockExpect(
|
|
92
|
+
address(store),
|
|
93
|
+
abi.encodeCall(
|
|
94
|
+
IJBTerminalStore.previewCashOutFrom,
|
|
95
|
+
(_holder, _projectId, _cashOutCount, accountingContext, accountingContexts, true, bytes(""))
|
|
96
|
+
),
|
|
97
|
+
abi.encode(ruleset, 999, 1234, specs)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
(
|
|
101
|
+
JBRuleset memory previewRuleset,
|
|
102
|
+
uint256 reclaimAmount,
|
|
103
|
+
uint256 cashOutTaxRate,
|
|
104
|
+
JBCashOutHookSpecification[] memory previewSpecs
|
|
105
|
+
) = JBMultiTerminal(address(_terminal))
|
|
106
|
+
.previewCashOutFrom(_holder, _projectId, _cashOutCount, _token, _beneficiary, "");
|
|
107
|
+
|
|
108
|
+
assertEq(previewRuleset.id, ruleset.id);
|
|
109
|
+
assertEq(reclaimAmount, 999);
|
|
110
|
+
assertEq(cashOutTaxRate, 1234);
|
|
111
|
+
assertEq(previewSpecs.length, 1);
|
|
112
|
+
assertEq(address(previewSpecs[0].hook), address(specs[0].hook));
|
|
113
|
+
assertEq(previewSpecs[0].amount, specs[0].amount);
|
|
114
|
+
assertEq(previewSpecs[0].metadata, specs[0].metadata);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {JBMultiTerminal} from "../../../../src/JBMultiTerminal.sol";
|
|
5
|
+
import {IJBController} from "../../../../src/interfaces/IJBController.sol";
|
|
6
|
+
import {IJBDirectory} from "../../../../src/interfaces/IJBDirectory.sol";
|
|
7
|
+
import {IJBPayHook} from "../../../../src/interfaces/IJBPayHook.sol";
|
|
8
|
+
import {IJBRulesetApprovalHook} from "../../../../src/interfaces/IJBRulesetApprovalHook.sol";
|
|
9
|
+
import {IJBTerminalStore} from "../../../../src/interfaces/IJBTerminalStore.sol";
|
|
10
|
+
import {JBConstants} from "../../../../src/libraries/JBConstants.sol";
|
|
11
|
+
import {JBAccountingContext} from "../../../../src/structs/JBAccountingContext.sol";
|
|
12
|
+
import {JBPayHookSpecification} from "../../../../src/structs/JBPayHookSpecification.sol";
|
|
13
|
+
import {JBRuleset} from "../../../../src/structs/JBRuleset.sol";
|
|
14
|
+
import {JBTokenAmount} from "../../../../src/structs/JBTokenAmount.sol";
|
|
15
|
+
import {JBMultiTerminalSetup} from "./JBMultiTerminalSetup.sol";
|
|
16
|
+
|
|
17
|
+
contract TestPreviewPayFor_Local is JBMultiTerminalSetup {
|
|
18
|
+
uint256 _projectId = 1;
|
|
19
|
+
uint256 _amount = 1e18;
|
|
20
|
+
address _token = JBConstants.NATIVE_TOKEN;
|
|
21
|
+
address _beneficiary = makeAddr("beneficiary");
|
|
22
|
+
address _payer = makeAddr("payer");
|
|
23
|
+
IJBController _controller = IJBController(makeAddr("controller"));
|
|
24
|
+
|
|
25
|
+
function setUp() public {
|
|
26
|
+
super.multiTerminalSetup();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _setAccountingContext(address token, uint8 decimals, uint32 currency) internal {
|
|
30
|
+
bytes32 contextSlot = keccak256(abi.encode(_projectId, uint256(0)));
|
|
31
|
+
bytes32 slot = keccak256(abi.encode(token, contextSlot));
|
|
32
|
+
bytes32 packed = bytes32(uint256(uint160(token)) | (uint256(decimals) << 160) | (uint256(currency) << 168));
|
|
33
|
+
vm.store(address(_terminal), slot, packed);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function test_RevertsWhenTokenIsNotAccepted() external {
|
|
37
|
+
vm.prank(_payer);
|
|
38
|
+
vm.expectRevert(abi.encodeWithSelector(JBMultiTerminal.JBMultiTerminal_TokenNotAccepted.selector, _token));
|
|
39
|
+
JBMultiTerminal(address(_terminal)).previewPayFor(_projectId, _token, _amount, _beneficiary, "");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function test_ReturnsRulesetMintSplitAndHookSpecifications() external {
|
|
43
|
+
_setAccountingContext(_token, 18, uint32(uint160(_token)));
|
|
44
|
+
|
|
45
|
+
JBRuleset memory ruleset = JBRuleset({
|
|
46
|
+
cycleNumber: 1,
|
|
47
|
+
id: 1,
|
|
48
|
+
basedOnId: 0,
|
|
49
|
+
start: uint48(block.timestamp),
|
|
50
|
+
duration: 0,
|
|
51
|
+
weight: 0,
|
|
52
|
+
weightCutPercent: 0,
|
|
53
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
54
|
+
metadata: 0
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
JBPayHookSpecification[] memory specs = new JBPayHookSpecification[](1);
|
|
58
|
+
specs[0] =
|
|
59
|
+
JBPayHookSpecification({hook: IJBPayHook(makeAddr("hook")), noop: false, amount: 123, metadata: hex"1234"});
|
|
60
|
+
|
|
61
|
+
JBTokenAmount memory tokenAmount =
|
|
62
|
+
JBTokenAmount({token: _token, decimals: 18, currency: uint32(uint160(_token)), value: _amount});
|
|
63
|
+
|
|
64
|
+
mockExpect(
|
|
65
|
+
address(store),
|
|
66
|
+
abi.encodeCall(IJBTerminalStore.previewPayFrom, (_payer, tokenAmount, _projectId, _beneficiary, bytes(""))),
|
|
67
|
+
abi.encode(ruleset, 1000, specs)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
mockExpect(
|
|
71
|
+
address(directory),
|
|
72
|
+
abi.encodeCall(IJBDirectory.controllerOf, (_projectId)),
|
|
73
|
+
abi.encode(address(_controller))
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
mockExpect(
|
|
77
|
+
address(_controller),
|
|
78
|
+
abi.encodeCall(IJBController.previewMintOf, (_projectId, 1000, true)),
|
|
79
|
+
abi.encode(750, 250)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
vm.prank(_payer);
|
|
83
|
+
(
|
|
84
|
+
JBRuleset memory previewRuleset,
|
|
85
|
+
uint256 beneficiaryTokenCount,
|
|
86
|
+
uint256 reservedTokenCount,
|
|
87
|
+
JBPayHookSpecification[] memory previewSpecs
|
|
88
|
+
) = JBMultiTerminal(address(_terminal)).previewPayFor(_projectId, _token, _amount, _beneficiary, "");
|
|
89
|
+
|
|
90
|
+
assertEq(previewRuleset.id, ruleset.id);
|
|
91
|
+
assertEq(beneficiaryTokenCount, 750);
|
|
92
|
+
assertEq(reservedTokenCount, 250);
|
|
93
|
+
assertEq(previewSpecs.length, 1);
|
|
94
|
+
assertEq(address(previewSpecs[0].hook), address(specs[0].hook));
|
|
95
|
+
assertEq(previewSpecs[0].amount, specs[0].amount);
|
|
96
|
+
assertEq(previewSpecs[0].metadata, specs[0].metadata);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -59,10 +59,18 @@ contract TestProcessHeldFeesOf_Local is JBTest {
|
|
|
59
59
|
IPermit2 public permit2 = IPermit2(makeAddr("permit2"));
|
|
60
60
|
address trustedForwarder = makeAddr("forwarder");
|
|
61
61
|
|
|
62
|
+
uint256 _feeProjectId = 1;
|
|
62
63
|
uint256 _projectId = 2;
|
|
63
64
|
address _mockToken = makeAddr("token");
|
|
64
65
|
address _beneficiary = makeAddr("beneficiary");
|
|
65
66
|
|
|
67
|
+
function _setAccountingContext(uint256 projectId, address token, uint8 decimals, uint32 currency) internal {
|
|
68
|
+
bytes32 contextSlot = keccak256(abi.encode(projectId, uint256(0)));
|
|
69
|
+
bytes32 slot = keccak256(abi.encode(token, contextSlot));
|
|
70
|
+
bytes32 packed = bytes32(uint256(uint160(token)) | (uint256(decimals) << 160) | (uint256(currency) << 168));
|
|
71
|
+
vm.store(address(_terminal), slot, packed);
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
function setUp() public {
|
|
67
75
|
// Constructor will call to find directory and rulesets from the terminal store
|
|
68
76
|
mockExpect(address(store), abi.encodeCall(IJBTerminalStore.DIRECTORY, ()), abi.encode(address(directory)));
|
|
@@ -118,6 +126,9 @@ contract TestProcessHeldFeesOf_Local is JBTest {
|
|
|
118
126
|
// The fee amount that will be calculated from the held amount
|
|
119
127
|
uint256 expectedFeeAmount = JBFees.feeAmountFrom({amountBeforeFee: heldAmount, feePercent: _terminal.FEE()});
|
|
120
128
|
|
|
129
|
+
// Set up accounting context for the fee beneficiary project (project 1) so _pay can build the token amount.
|
|
130
|
+
_setAccountingContext(_feeProjectId, _mockToken, 0, uint32(uint160(_mockToken)));
|
|
131
|
+
|
|
121
132
|
// Mock the directory call to find the fee terminal - return _terminal itself so it uses internal _pay
|
|
122
133
|
mockExpect(
|
|
123
134
|
address(directory),
|
|
@@ -128,8 +139,6 @@ contract TestProcessHeldFeesOf_Local is JBTest {
|
|
|
128
139
|
// Mock executeProcessFee: when the terminal calls itself, it will call recordPaymentFrom on the store.
|
|
129
140
|
// Since executeProcessFee is external and calls pay on the feeTerminal (which is _terminal itself),
|
|
130
141
|
// we need to mock the internal pay path: recordPaymentFrom on the store.
|
|
131
|
-
// The token amount struct for the fee payment
|
|
132
|
-
// Note: accounting context for project 1 on _mockToken is unset, so decimals=0 and currency=0.
|
|
133
142
|
vm.mockCall(
|
|
134
143
|
address(store),
|
|
135
144
|
abi.encodeWithSelector(IJBTerminalStore.recordPaymentFrom.selector),
|
|
@@ -191,12 +191,13 @@ contract TestCurrentOf_Local is JBRulesetsSetup {
|
|
|
191
191
|
{
|
|
192
192
|
// it will return the ruleset the pending approval ruleset is basedOn
|
|
193
193
|
|
|
194
|
-
|
|
195
|
-
uint256
|
|
194
|
+
// Capture IDs from actual storage (avoid via_ir reordering of block.timestamp).
|
|
195
|
+
uint256 _firstRulesetId = _rulesets.currentOf(_projectId).id;
|
|
196
|
+
uint256 _rulesetWithHookId = _firstRulesetId + 1;
|
|
196
197
|
|
|
197
198
|
JBRuleset memory _queuedRuleset = _rulesets.getRulesetOf(_projectId, _rulesetWithHookId);
|
|
198
199
|
|
|
199
|
-
vm.warp(
|
|
200
|
+
vm.warp(_firstRulesetId + 3 days);
|
|
200
201
|
|
|
201
202
|
// mock approvalStatusOf to return Pending
|
|
202
203
|
mockExpect(
|
|
@@ -215,12 +216,13 @@ contract TestCurrentOf_Local is JBRulesetsSetup {
|
|
|
215
216
|
{
|
|
216
217
|
// it will return the basedOn of the latest ruleset
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
uint256
|
|
219
|
+
// Capture IDs from actual storage (avoid via_ir reordering of block.timestamp).
|
|
220
|
+
uint256 _firstRulesetId = _rulesets.currentOf(_projectId).id;
|
|
221
|
+
uint256 _rulesetWithHookId = _firstRulesetId + 1;
|
|
220
222
|
|
|
221
223
|
JBRuleset memory _queuedRuleset = _rulesets.getRulesetOf(_projectId, _rulesetWithHookId);
|
|
222
224
|
|
|
223
|
-
vm.warp(
|
|
225
|
+
vm.warp(_firstRulesetId + 4 days);
|
|
224
226
|
|
|
225
227
|
// mock approvalStatusOf to return Pending
|
|
226
228
|
mockExpect(
|
|
@@ -403,7 +403,8 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
403
403
|
mustStartAtOrAfter: _mustStartAt
|
|
404
404
|
});
|
|
405
405
|
|
|
406
|
-
|
|
406
|
+
// Capture firstId from actual storage (avoid via_ir reordering of block.timestamp).
|
|
407
|
+
uint256 firstId = _rulesets.latestRulesetIdOf(_projectId);
|
|
407
408
|
|
|
408
409
|
// Mock call to approval hook duration
|
|
409
410
|
bytes memory _encodedDurationCall = abi.encodeCall(IJBRulesetApprovalHook.DURATION, ());
|
|
@@ -412,9 +413,9 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
412
413
|
mockExpect(address(_mockApprovalHook), _encodedDurationCall, _willReturnDuration);
|
|
413
414
|
|
|
414
415
|
// avoid overwrite
|
|
415
|
-
vm.warp(
|
|
416
|
+
vm.warp(firstId + 1);
|
|
416
417
|
|
|
417
|
-
uint256 latestId
|
|
418
|
+
uint256 latestId;
|
|
418
419
|
|
|
419
420
|
// Send: Anotha One! Call from this contract as it's been mock authorized above.
|
|
420
421
|
_rulesets.queueFor({
|
|
@@ -424,12 +425,14 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
424
425
|
weightCutPercent: _weightCutPercent,
|
|
425
426
|
approvalHook: _mockApprovalHook,
|
|
426
427
|
metadata: _packedWithApprovalHook,
|
|
427
|
-
mustStartAtOrAfter:
|
|
428
|
+
mustStartAtOrAfter: 0
|
|
428
429
|
});
|
|
429
430
|
|
|
431
|
+
// Capture latestId from storage (avoid via_ir reordering of block.timestamp).
|
|
432
|
+
latestId = _rulesets.latestRulesetIdOf(_projectId);
|
|
433
|
+
|
|
430
434
|
// avoid overwrite
|
|
431
|
-
vm.warp(
|
|
432
|
-
uint256 previouslyApprovedDurationEnds = block.timestamp + 3 days - 2 days - 1;
|
|
435
|
+
vm.warp(latestId + 2 days);
|
|
433
436
|
|
|
434
437
|
// Get the ruleset.
|
|
435
438
|
JBRuleset memory latesetQueuedRuleset = _rulesets.getRulesetOf(_projectId, latestId);
|
|
@@ -449,16 +452,14 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
449
452
|
weightCutPercent: _weightCutPercent,
|
|
450
453
|
approvalHook: _mockApprovalHook,
|
|
451
454
|
metadata: _packedWithApprovalHook,
|
|
452
|
-
mustStartAtOrAfter:
|
|
455
|
+
mustStartAtOrAfter: 0
|
|
453
456
|
});
|
|
454
457
|
|
|
455
|
-
latestId =
|
|
458
|
+
latestId = _rulesets.latestRulesetIdOf(_projectId);
|
|
456
459
|
latesetQueuedRuleset = _rulesets.getRulesetOf(_projectId, latestId);
|
|
457
460
|
|
|
458
461
|
// avoid overwrite
|
|
459
|
-
vm.warp(
|
|
460
|
-
|
|
461
|
-
previouslyApprovedDurationEnds = block.timestamp + 6 days - 2 days - 2;
|
|
462
|
+
vm.warp(latestId + 1);
|
|
462
463
|
|
|
463
464
|
// Mock call to approvalStatusOf and return an approvalExpected status
|
|
464
465
|
_encodedApprovalCall = abi.encodeCall(IJBRulesetApprovalHook.approvalStatusOf, (1, latesetQueuedRuleset));
|
|
@@ -474,15 +475,14 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
474
475
|
weightCutPercent: _weightCutPercent,
|
|
475
476
|
approvalHook: _mockApprovalHook,
|
|
476
477
|
metadata: _packedWithApprovalHook,
|
|
477
|
-
mustStartAtOrAfter:
|
|
478
|
+
mustStartAtOrAfter: 0
|
|
478
479
|
});
|
|
479
480
|
|
|
480
|
-
latestId =
|
|
481
|
+
latestId = _rulesets.latestRulesetIdOf(_projectId);
|
|
481
482
|
latesetQueuedRuleset = _rulesets.getRulesetOf(_projectId, latestId);
|
|
482
483
|
|
|
483
484
|
// avoid overwrite
|
|
484
|
-
vm.warp(
|
|
485
|
-
previouslyApprovedDurationEnds = block.timestamp + 6 days - 2 days - 3;
|
|
485
|
+
vm.warp(latestId + 1);
|
|
486
486
|
|
|
487
487
|
// Mock call to approvalStatusOf and return a failed status
|
|
488
488
|
_encodedApprovalCall = abi.encodeCall(IJBRulesetApprovalHook.approvalStatusOf, (1, latesetQueuedRuleset));
|
|
@@ -498,16 +498,14 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
498
498
|
weightCutPercent: _weightCutPercent,
|
|
499
499
|
approvalHook: _mockApprovalHook,
|
|
500
500
|
metadata: _packedWithApprovalHook,
|
|
501
|
-
mustStartAtOrAfter:
|
|
501
|
+
mustStartAtOrAfter: 0
|
|
502
502
|
});
|
|
503
503
|
|
|
504
|
-
latestId =
|
|
504
|
+
latestId = _rulesets.latestRulesetIdOf(_projectId);
|
|
505
505
|
latesetQueuedRuleset = _rulesets.getRulesetOf(_projectId, latestId);
|
|
506
506
|
|
|
507
507
|
// avoid overwrite
|
|
508
|
-
vm.warp(
|
|
509
|
-
|
|
510
|
-
previouslyApprovedDurationEnds = block.timestamp + 6 days - 2 days - 4;
|
|
508
|
+
vm.warp(latestId + 1);
|
|
511
509
|
|
|
512
510
|
// Mock call to approvalStatusOf and return an empty status
|
|
513
511
|
_encodedApprovalCall = abi.encodeCall(IJBRulesetApprovalHook.approvalStatusOf, (1, latesetQueuedRuleset));
|
|
@@ -523,14 +521,17 @@ contract TestJBRulesetsUnits_Local is JBTest {
|
|
|
523
521
|
weightCutPercent: _weightCutPercent,
|
|
524
522
|
approvalHook: _mockApprovalHook,
|
|
525
523
|
metadata: _packedWithApprovalHook,
|
|
526
|
-
mustStartAtOrAfter:
|
|
524
|
+
mustStartAtOrAfter: 0
|
|
527
525
|
});
|
|
528
526
|
|
|
529
|
-
|
|
527
|
+
// Capture final latestId from storage.
|
|
528
|
+
latestId = _rulesets.latestRulesetIdOf(_projectId);
|
|
529
|
+
|
|
530
|
+
JBRuleset[] memory queuedRulesetsOf = _rulesets.allOf(_projectId, latestId, 3);
|
|
530
531
|
|
|
531
|
-
// check:
|
|
532
|
+
// check: 3 rulesets will be enqueued
|
|
532
533
|
assertEq(queuedRulesetsOf.length, 3);
|
|
533
|
-
assertEq(queuedRulesetsOf[0].id,
|
|
534
|
+
assertEq(queuedRulesetsOf[0].id, latestId);
|
|
534
535
|
|
|
535
536
|
// check first timestamp
|
|
536
537
|
assertEq(queuedRulesetsOf[2].id, firstId);
|
|
@@ -457,8 +457,6 @@ contract TestUpcomingOf_Local is JBRulesetsSetup {
|
|
|
457
457
|
function test_baseRulesetDurationDNEQZero() external {
|
|
458
458
|
// it will simulate a ruleset basedOn
|
|
459
459
|
|
|
460
|
-
uint256 ogTimestamp = block.timestamp;
|
|
461
|
-
|
|
462
460
|
// put code at hook address
|
|
463
461
|
vm.etch(address(_mockApprovalHook), abi.encode(1));
|
|
464
462
|
|
|
@@ -476,20 +474,6 @@ contract TestUpcomingOf_Local is JBRulesetsSetup {
|
|
|
476
474
|
|
|
477
475
|
mockExpect(address(directory), _encodedCall, _willReturn);
|
|
478
476
|
|
|
479
|
-
// Setup: expect ruleset event (RulesetQueued) is emitted
|
|
480
|
-
vm.expectEmit();
|
|
481
|
-
emit IJBRulesets.RulesetQueued(
|
|
482
|
-
block.timestamp,
|
|
483
|
-
_projectId,
|
|
484
|
-
_duration,
|
|
485
|
-
_weight,
|
|
486
|
-
_weightCutPercent,
|
|
487
|
-
_mockApprovalHook,
|
|
488
|
-
_packedWithApprovalHook,
|
|
489
|
-
block.timestamp,
|
|
490
|
-
address(this)
|
|
491
|
-
);
|
|
492
|
-
|
|
493
477
|
// Send: Call from this contract as it's been mock authorized above.
|
|
494
478
|
_rulesets.queueFor({
|
|
495
479
|
projectId: _projectId,
|
|
@@ -501,6 +485,9 @@ contract TestUpcomingOf_Local is JBRulesetsSetup {
|
|
|
501
485
|
mustStartAtOrAfter: _mustStartAt
|
|
502
486
|
});
|
|
503
487
|
|
|
488
|
+
// Capture the first ruleset's id from storage (avoid via_ir reordering of block.timestamp).
|
|
489
|
+
uint256 ogTimestamp = _rulesets.currentOf(_projectId).id;
|
|
490
|
+
|
|
504
491
|
// mock call to hook duration
|
|
505
492
|
mockExpect(
|
|
506
493
|
address(_mockApprovalHook), abi.encodeCall(IJBRulesetApprovalHook.DURATION, ()), abi.encode(_hookDuration)
|
|
@@ -517,7 +504,7 @@ contract TestUpcomingOf_Local is JBRulesetsSetup {
|
|
|
517
504
|
mustStartAtOrAfter: 0
|
|
518
505
|
});
|
|
519
506
|
|
|
520
|
-
vm.warp(
|
|
507
|
+
vm.warp(ogTimestamp + 3 days);
|
|
521
508
|
|
|
522
509
|
uint256 _latestQueuedId = _rulesets.latestRulesetIdOf(_projectId);
|
|
523
510
|
JBRuleset memory _queuedRuleset = _rulesets.getRulesetOf(_projectId, _latestQueuedId);
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
+
import {IJBCashOutTerminal} from "../../../../src/interfaces/IJBCashOutTerminal.sol";
|
|
4
5
|
import {IJBTerminal} from "../../../../src/interfaces/IJBTerminal.sol";
|
|
5
6
|
import {JBAccountingContext} from "../../../../src/structs/JBAccountingContext.sol";
|
|
7
|
+
import {JBCashOutHookSpecification} from "../../../../src/structs/JBCashOutHookSpecification.sol";
|
|
8
|
+
import {JBPayHookSpecification} from "../../../../src/structs/JBPayHookSpecification.sol";
|
|
9
|
+
import {JBRuleset} from "../../../../src/structs/JBRuleset.sol";
|
|
6
10
|
import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
7
11
|
import {JBTest} from "../../../helpers/JBTest.sol";
|
|
8
12
|
import {JBSurplus} from "../../../../src/libraries/JBSurplus.sol";
|
|
9
13
|
|
|
10
14
|
/// @notice Mock terminal that returns a fixed surplus for testing JBSurplus.
|
|
11
|
-
contract MockSurplusTerminal is ERC165,
|
|
15
|
+
contract MockSurplusTerminal is ERC165, IJBCashOutTerminal {
|
|
12
16
|
uint256 public surplusAmount;
|
|
13
17
|
|
|
14
18
|
constructor(uint256 _surplus) {
|
|
@@ -30,7 +34,8 @@ contract MockSurplusTerminal is ERC165, IJBTerminal {
|
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
|
|
33
|
-
return interfaceId == type(IJBTerminal).interfaceId ||
|
|
37
|
+
return interfaceId == type(IJBTerminal).interfaceId || interfaceId == type(IJBCashOutTerminal).interfaceId
|
|
38
|
+
|| super.supportsInterface(interfaceId);
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
// Stub implementations for IJBTerminal
|
|
@@ -41,6 +46,31 @@ contract MockSurplusTerminal is ERC165, IJBTerminal {
|
|
|
41
46
|
returns (JBAccountingContext memory)
|
|
42
47
|
{}
|
|
43
48
|
function accountingContextsOf(uint256) external pure override returns (JBAccountingContext[] memory) {}
|
|
49
|
+
function previewCashOutFrom(
|
|
50
|
+
address,
|
|
51
|
+
uint256,
|
|
52
|
+
uint256,
|
|
53
|
+
address,
|
|
54
|
+
address payable,
|
|
55
|
+
bytes calldata
|
|
56
|
+
)
|
|
57
|
+
external
|
|
58
|
+
pure
|
|
59
|
+
override
|
|
60
|
+
returns (JBRuleset memory, uint256, uint256, JBCashOutHookSpecification[] memory)
|
|
61
|
+
{}
|
|
62
|
+
function previewPayFor(
|
|
63
|
+
uint256,
|
|
64
|
+
address,
|
|
65
|
+
uint256,
|
|
66
|
+
address,
|
|
67
|
+
bytes calldata
|
|
68
|
+
)
|
|
69
|
+
external
|
|
70
|
+
pure
|
|
71
|
+
override
|
|
72
|
+
returns (JBRuleset memory, uint256, uint256, JBPayHookSpecification[] memory)
|
|
73
|
+
{}
|
|
44
74
|
function addAccountingContextsFor(uint256, JBAccountingContext[] calldata) external override {}
|
|
45
75
|
function addToBalanceOf(
|
|
46
76
|
uint256,
|
|
@@ -75,6 +105,23 @@ contract MockSurplusTerminal is ERC165, IJBTerminal {
|
|
|
75
105
|
{
|
|
76
106
|
return 0;
|
|
77
107
|
}
|
|
108
|
+
|
|
109
|
+
function cashOutTokensOf(
|
|
110
|
+
address,
|
|
111
|
+
uint256,
|
|
112
|
+
uint256,
|
|
113
|
+
address,
|
|
114
|
+
uint256,
|
|
115
|
+
address payable,
|
|
116
|
+
bytes calldata
|
|
117
|
+
)
|
|
118
|
+
external
|
|
119
|
+
pure
|
|
120
|
+
override
|
|
121
|
+
returns (uint256)
|
|
122
|
+
{
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
78
125
|
}
|
|
79
126
|
|
|
80
127
|
/// @notice Fuzz tests for the JBSurplus library.
|