@bananapus/core-v6 0.0.23 → 0.0.25
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 +16 -3
- package/ARCHITECTURE.md +28 -9
- package/AUDIT_INSTRUCTIONS.md +102 -17
- package/CHANGE_LOG.md +18 -8
- package/README.md +58 -2
- package/RISKS.md +13 -20
- package/SKILLS.md +158 -11
- package/STYLE_GUIDE.md +11 -6
- package/USER_JOURNEYS.md +53 -16
- package/foundry.toml +1 -1
- package/package.json +2 -2
- package/script/Deploy.s.sol +2 -2
- package/script/DeployPeriphery.s.sol +2 -5
- package/script/helpers/CoreDeploymentLib.sol +2 -2
- package/src/JBChainlinkV3PriceFeed.sol +1 -1
- package/src/JBChainlinkV3SequencerPriceFeed.sol +1 -1
- package/src/JBController.sol +14 -7
- package/src/JBDeadline.sol +1 -1
- package/src/JBDirectory.sol +1 -1
- package/src/JBERC20.sol +6 -2
- package/src/JBFeelessAddresses.sol +1 -1
- package/src/JBFundAccessLimits.sol +1 -1
- package/src/JBMultiTerminal.sol +53 -4
- package/src/JBPermissions.sol +6 -2
- package/src/JBPrices.sol +1 -1
- package/src/JBProjects.sol +1 -1
- package/src/JBRulesets.sol +1 -1
- package/src/JBSplits.sol +1 -1
- package/src/JBTerminalStore.sol +64 -65
- package/src/JBTokens.sol +5 -1
- package/src/interfaces/IJBController.sol +7 -1
- package/src/libraries/JBPayoutSplitGroupLib.sol +1 -1
- package/src/periphery/JBDeadline1Day.sol +1 -1
- package/src/periphery/JBDeadline3Days.sol +1 -1
- package/src/periphery/JBDeadline3Hours.sol +1 -1
- package/src/periphery/JBDeadline7Days.sol +1 -1
- package/src/periphery/JBMatchingPriceFeed.sol +1 -1
- package/test/TestAccessToFunds.sol +4 -4
- package/test/TestFeeFreeCashOutBypass.sol +332 -0
- package/test/TestJBERC20Inheritance.sol +1 -1
- package/test/TestMetadataOffsetOverflow.sol +1 -1
- package/test/TestMetadataParserLib.sol +1 -1
- package/test/TestMultiTerminalSurplus.sol +1 -1
- package/test/TestMultiTokenSurplus.sol +1 -1
- package/test/TestMultipleAccessLimits.sol +4 -4
- package/test/TestPermit2DataHook.t.sol +1 -1
- package/test/TestPermit2Terminal.sol +1 -1
- package/test/TestTerminalPreviewParity.sol +1 -1
- package/test/audit/CashOutReenterPay.t.sol +496 -0
- package/test/audit/FeeFreeSurplusLifecycle.t.sol +392 -0
- package/test/audit/FeeFreeSurplusStale.t.sol +242 -0
- package/test/audit/USDTVoidReturnCompat.t.sol +519 -0
- package/test/fork/TestChainlinkPriceFeedFork.sol +1 -1
- package/test/fork/TestSequencerPriceFeedFork.sol +1 -1
- package/test/fork/TestTerminalPreviewParityFork.sol +1 -1
- package/test/helpers/JBTest.sol +1 -1
- package/test/helpers/MetadataResolverHelper.sol +1 -1
- package/test/mock/MockERC20.sol +1 -1
- package/test/mock/MockMaliciousBeneficiary.sol +1 -1
- package/test/mock/MockMaliciousSplitHook.sol +1 -1
- package/test/mock/MockPriceFeed.sol +1 -1
- package/test/mock/MockUSDT.sol +80 -0
- package/test/regression/HoldFeesCashOutReserved.t.sol +2 -2
- package/test/units/static/JBChainlinkV3PriceFeed/TestPriceFeed.sol +1 -1
- package/test/units/static/JBController/JBControllerSetup.sol +1 -1
- package/test/units/static/JBController/TestBurnTokensOf.sol +1 -1
- package/test/units/static/JBController/TestClaimTokensFor.sol +1 -1
- package/test/units/static/JBController/TestDeployErc20For.sol +1 -1
- package/test/units/static/JBController/TestLaunchProjectFor.sol +1 -1
- package/test/units/static/JBController/TestLaunchRulesetsFor.sol +1 -1
- package/test/units/static/JBController/TestMigrateController.sol +1 -1
- package/test/units/static/JBController/TestMintTokensOfUnits.sol +1 -1
- package/test/units/static/JBController/TestOmnichainRulesetOperator.sol +324 -0
- package/test/units/static/JBController/TestPayReservedTokenToTerminal.sol +1 -1
- package/test/units/static/JBController/TestPreviewMintOf.sol +1 -1
- package/test/units/static/JBController/TestReceiveMigrationFrom.sol +1 -1
- package/test/units/static/JBController/TestRulesetViews.sol +1 -1
- package/test/units/static/JBController/TestSendReservedTokensToSplitsOf.sol +1 -1
- package/test/units/static/JBController/TestSetSplitGroupsOf.sol +1 -1
- package/test/units/static/JBController/TestSetTokenFor.sol +1 -1
- package/test/units/static/JBController/TestSetUriOf.sol +1 -1
- package/test/units/static/JBController/TestTransferCreditsFrom.sol +1 -1
- package/test/units/static/JBDeadline/TestDeadlineFuzz.sol +1 -1
- package/test/units/static/JBDirectory/JBDirectorySetup.sol +1 -1
- package/test/units/static/JBDirectory/TestPrimaryTerminalOf.sol +1 -1
- package/test/units/static/JBDirectory/TestSetControllerOf.sol +1 -1
- package/test/units/static/JBDirectory/TestSetControllerOfMigrationOrder.sol +1 -1
- package/test/units/static/JBDirectory/TestSetPrimaryTerminalOf.sol +1 -1
- package/test/units/static/JBDirectory/TestSetTerminalsOf.sol +1 -1
- package/test/units/static/JBERC20/JBERC20Setup.sol +11 -4
- package/test/units/static/JBERC20/SigUtils.sol +1 -1
- package/test/units/static/JBERC20/TestInitialize.sol +8 -1
- package/test/units/static/JBERC20/TestName.sol +1 -1
- package/test/units/static/JBERC20/TestNonces.sol +1 -1
- package/test/units/static/JBERC20/TestSymbol.sol +1 -1
- package/test/units/static/JBFeelessAdresses/JBFeelessSetup.sol +1 -1
- package/test/units/static/JBFeelessAdresses/TestInterfaces.sol +1 -1
- package/test/units/static/JBFeelessAdresses/TestSetFeelessAddress.sol +1 -1
- package/test/units/static/JBFees/TestFeesFuzz.sol +1 -1
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimals.sol +1 -1
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimalsFuzz.sol +1 -1
- package/test/units/static/JBFundAccessLimits/JBFundAccessSetup.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestFundAccessLimitsEdge.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitOf.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitsOf.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestSetFundAccessLimitsFor.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowanceOf.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowancesOf.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestGetDataFor.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestMetadataResolverEdgeCases.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestMetadataResolverFuzz.sol +1 -1
- package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestPay.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +1 -1
- package/test/units/static/JBPermissions/JBPermissionsSetup.sol +1 -1
- package/test/units/static/JBPermissions/TestHasPermission.sol +1 -1
- package/test/units/static/JBPermissions/TestHasPermissions.sol +1 -1
- package/test/units/static/JBPermissions/TestSetPermissionsFor.sol +1 -1
- package/test/units/static/JBPrices/JBPricesSetup.sol +1 -1
- package/test/units/static/JBPrices/TestAddPriceFeedFor.sol +1 -1
- package/test/units/static/JBPrices/TestPricePerUnitOf.sol +1 -1
- package/test/units/static/JBPrices/TestPrices.sol +1 -1
- package/test/units/static/JBProjects/JBProjectsSetup.sol +1 -1
- package/test/units/static/JBProjects/TestCreateFor.sol +1 -1
- package/test/units/static/JBProjects/TestInitialProject.sol +1 -1
- package/test/units/static/JBProjects/TestInterfaces.sol +1 -1
- package/test/units/static/JBProjects/TestSetResolver.sol +1 -1
- package/test/units/static/JBProjects/TestTokenUri.sol +1 -1
- package/test/units/static/JBRulesetMetadataResolver/TestSetCashOutTaxRateTo.sol +1 -1
- package/test/units/static/JBRulesets/JBRulesetsSetup.sol +1 -1
- package/test/units/static/JBRulesets/TestCurrentApprovalStatusForLatestRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestCurrentOf.sol +1 -1
- package/test/units/static/JBRulesets/TestGetRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestLatestQueuedRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestRulesets.sol +1 -1
- package/test/units/static/JBRulesets/TestRulesetsOf.sol +1 -1
- package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestUpdateRulesetWeightCache.sol +1 -1
- package/test/units/static/JBSplits/JBSplitsSetup.sol +1 -1
- package/test/units/static/JBSplits/TestSelfManagedSplitGroups.sol +1 -1
- package/test/units/static/JBSplits/TestSetSplitGroupsOf.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsLockedEdge.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsOf.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsPacking.sol +1 -1
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +1 -1
- package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +1 -1
- package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +1 -1
- package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordTerminalMigration.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +1 -1
- package/test/units/static/JBTokens/JBTokensSetup.sol +1 -1
- package/test/units/static/JBTokens/TestBurnFrom.sol +1 -1
- package/test/units/static/JBTokens/TestClaimTokensFor.sol +1 -1
- package/test/units/static/JBTokens/TestDeployERC20ForUnits.sol +1 -1
- package/test/units/static/JBTokens/TestMintFor.sol +1 -1
- package/test/units/static/JBTokens/TestSetTokenFor.sol +1 -1
- package/test/units/static/JBTokens/TestTotalBalanceOf.sol +1 -1
- package/test/units/static/JBTokens/TestTotalSupplyOf.sol +1 -1
- package/test/units/static/JBTokens/TestTransferCreditsFrom.sol +1 -1
|
@@ -387,8 +387,8 @@ contract HoldFeesCashOutReserved_Local is TestBaseWorkflow {
|
|
|
387
387
|
surplus: surplus, cashOutCount: payerTokens, totalSupply: circulatingSupply, cashOutTaxRate: 5000
|
|
388
388
|
});
|
|
389
389
|
|
|
390
|
-
// With pending reserves, the cashout value is lower (known behavior
|
|
391
|
-
assertLt(reclaimWithReserves, reclaimWithoutReserves, "Pending reserves reduce cashout value (expected
|
|
390
|
+
// With pending reserves, the cashout value is lower (known behavior).
|
|
391
|
+
assertLt(reclaimWithReserves, reclaimWithoutReserves, "Pending reserves reduce cashout value (expected)");
|
|
392
392
|
|
|
393
393
|
// Actually perform the cashout and confirm it matches the with-reserves calculation (net of fee).
|
|
394
394
|
// cashOutTokensOf returns the NET reclaim after the 2.5% fee is deducted.
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {JBController} from "../../../../src/JBController.sol";
|
|
5
|
+
import {JBPermissioned} from "../../../../src/abstract/JBPermissioned.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 {IJBPermissions} from "../../../../src/interfaces/IJBPermissions.sol";
|
|
10
|
+
import {IJBRulesetApprovalHook} from "../../../../src/interfaces/IJBRulesetApprovalHook.sol";
|
|
11
|
+
import {IJBRulesets} from "../../../../src/interfaces/IJBRulesets.sol";
|
|
12
|
+
import {IJBPrices} from "../../../../src/interfaces/IJBPrices.sol";
|
|
13
|
+
import {IJBProjects} from "../../../../src/interfaces/IJBProjects.sol";
|
|
14
|
+
import {IJBSplits} from "../../../../src/interfaces/IJBSplits.sol";
|
|
15
|
+
import {IJBTokens} from "../../../../src/interfaces/IJBTokens.sol";
|
|
16
|
+
import {JBConstants} from "../../../../src/libraries/JBConstants.sol";
|
|
17
|
+
import {JBRulesetMetadataResolver} from "../../../../src/libraries/JBRulesetMetadataResolver.sol";
|
|
18
|
+
import {JBCurrencyAmount} from "../../../../src/structs/JBCurrencyAmount.sol";
|
|
19
|
+
import {JBFundAccessLimitGroup} from "../../../../src/structs/JBFundAccessLimitGroup.sol";
|
|
20
|
+
import {JBRuleset} from "../../../../src/structs/JBRuleset.sol";
|
|
21
|
+
import {JBRulesetConfig} from "../../../../src/structs/JBRulesetConfig.sol";
|
|
22
|
+
import {JBRulesetMetadata} from "../../../../src/structs/JBRulesetMetadata.sol";
|
|
23
|
+
import {JBSplitGroup} from "../../../../src/structs/JBSplitGroup.sol";
|
|
24
|
+
import {JBTerminalConfig} from "../../../../src/structs/JBTerminalConfig.sol";
|
|
25
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
26
|
+
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
27
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
28
|
+
import {JBTest} from "../../../helpers/JBTest.sol";
|
|
29
|
+
|
|
30
|
+
/// @notice Tests the OMNICHAIN_RULESET_OPERATOR trust boundary — verifying the operator can bypass
|
|
31
|
+
/// LAUNCH_RULESETS, SET_TERMINALS, and QUEUE_RULESETS permission checks via alsoGrantAccessIf.
|
|
32
|
+
contract TestOmnichainRulesetOperator_Local is JBTest {
|
|
33
|
+
IJBController public _controller;
|
|
34
|
+
|
|
35
|
+
// Mocks
|
|
36
|
+
IJBPermissions public permissions = IJBPermissions(makeAddr("permissions"));
|
|
37
|
+
IJBDirectory public directory = IJBDirectory(makeAddr("directory"));
|
|
38
|
+
IJBRulesets public rulesets = IJBRulesets(makeAddr("rulesets"));
|
|
39
|
+
IJBSplits public splits = IJBSplits(makeAddr("splits"));
|
|
40
|
+
IJBFundAccessLimits public fundAccessLimits = IJBFundAccessLimits(makeAddr("limits"));
|
|
41
|
+
|
|
42
|
+
// Use makeAddr for projects so we can mock ERC721 calls
|
|
43
|
+
address public projectsAddr = makeAddr("projects");
|
|
44
|
+
|
|
45
|
+
address public operator = makeAddr("omnichainOperator");
|
|
46
|
+
address public projectOwner = makeAddr("projectOwner");
|
|
47
|
+
address public randomCaller = makeAddr("randomCaller");
|
|
48
|
+
uint256 public projectId = 1;
|
|
49
|
+
|
|
50
|
+
function setUp() public {
|
|
51
|
+
// Deploy controller with a non-zero OMNICHAIN_RULESET_OPERATOR
|
|
52
|
+
_controller = new JBController(
|
|
53
|
+
directory,
|
|
54
|
+
fundAccessLimits,
|
|
55
|
+
permissions,
|
|
56
|
+
IJBPrices(makeAddr("prices")),
|
|
57
|
+
IJBProjects(projectsAddr),
|
|
58
|
+
rulesets,
|
|
59
|
+
splits,
|
|
60
|
+
IJBTokens(makeAddr("tokens")),
|
|
61
|
+
operator,
|
|
62
|
+
makeAddr("forwarder")
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Helpers
|
|
67
|
+
// ──────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
function _genRulesetConfig() internal pure returns (JBTerminalConfig[] memory, JBRulesetConfig[] memory) {
|
|
70
|
+
JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](0);
|
|
71
|
+
JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
|
|
72
|
+
|
|
73
|
+
JBRulesetMetadata memory metadata = JBRulesetMetadata({
|
|
74
|
+
reservedPercent: 0,
|
|
75
|
+
cashOutTaxRate: 0,
|
|
76
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
77
|
+
pausePay: false,
|
|
78
|
+
pauseCreditTransfers: false,
|
|
79
|
+
allowOwnerMinting: false,
|
|
80
|
+
allowSetCustomToken: false,
|
|
81
|
+
allowTerminalMigration: false,
|
|
82
|
+
allowSetTerminals: false,
|
|
83
|
+
ownerMustSendPayouts: false,
|
|
84
|
+
allowSetController: false,
|
|
85
|
+
allowAddAccountingContext: true,
|
|
86
|
+
allowAddPriceFeed: false,
|
|
87
|
+
holdFees: false,
|
|
88
|
+
useTotalSurplusForCashOuts: false,
|
|
89
|
+
useDataHookForPay: false,
|
|
90
|
+
useDataHookForCashOut: false,
|
|
91
|
+
dataHook: address(0),
|
|
92
|
+
metadata: 0
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
JBFundAccessLimitGroup[] memory limitGroups = new JBFundAccessLimitGroup[](1);
|
|
96
|
+
JBCurrencyAmount[] memory payoutLimits = new JBCurrencyAmount[](1);
|
|
97
|
+
payoutLimits[0] = JBCurrencyAmount({amount: 0, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))});
|
|
98
|
+
JBCurrencyAmount[] memory surplusAllowances = new JBCurrencyAmount[](1);
|
|
99
|
+
surplusAllowances[0] = JBCurrencyAmount({amount: 0, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))});
|
|
100
|
+
limitGroups[0] = JBFundAccessLimitGroup({
|
|
101
|
+
terminal: address(0),
|
|
102
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
103
|
+
payoutLimits: payoutLimits,
|
|
104
|
+
surplusAllowances: surplusAllowances
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
rulesetConfigs[0].mustStartAtOrAfter = 0;
|
|
108
|
+
rulesetConfigs[0].duration = 0;
|
|
109
|
+
rulesetConfigs[0].weight = 0;
|
|
110
|
+
rulesetConfigs[0].weightCutPercent = 0;
|
|
111
|
+
rulesetConfigs[0].approvalHook = IJBRulesetApprovalHook(address(0));
|
|
112
|
+
rulesetConfigs[0].metadata = metadata;
|
|
113
|
+
rulesetConfigs[0].splitGroups = new JBSplitGroup[](0);
|
|
114
|
+
rulesetConfigs[0].fundAccessLimitGroups = limitGroups;
|
|
115
|
+
|
|
116
|
+
return (terminalConfigs, rulesetConfigs);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// @dev Mock the full call chain for a successful launchRulesetsFor.
|
|
120
|
+
function _mockLaunchSuccess(JBRulesetConfig[] memory rulesetConfigs) internal {
|
|
121
|
+
uint48 ts = uint48(block.timestamp);
|
|
122
|
+
|
|
123
|
+
// ownerOf
|
|
124
|
+
vm.mockCall(projectsAddr, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(projectOwner));
|
|
125
|
+
|
|
126
|
+
// No existing rulesets
|
|
127
|
+
vm.mockCall(address(rulesets), abi.encodeCall(IJBRulesets.latestRulesetIdOf, (projectId)), abi.encode(0));
|
|
128
|
+
|
|
129
|
+
// setControllerOf
|
|
130
|
+
vm.mockCall(
|
|
131
|
+
address(directory),
|
|
132
|
+
abi.encodeCall(IJBDirectory.setControllerOf, (projectId, IERC165(address(_controller)))),
|
|
133
|
+
""
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// queueFor
|
|
137
|
+
JBRuleset memory ruleset = JBRuleset({
|
|
138
|
+
cycleNumber: 1,
|
|
139
|
+
id: ts,
|
|
140
|
+
basedOnId: 0,
|
|
141
|
+
start: ts,
|
|
142
|
+
duration: 0,
|
|
143
|
+
weight: 0,
|
|
144
|
+
weightCutPercent: 0,
|
|
145
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
146
|
+
metadata: 0
|
|
147
|
+
});
|
|
148
|
+
vm.mockCall(
|
|
149
|
+
address(rulesets),
|
|
150
|
+
abi.encodeCall(
|
|
151
|
+
IJBRulesets.queueFor,
|
|
152
|
+
(
|
|
153
|
+
projectId,
|
|
154
|
+
0,
|
|
155
|
+
0,
|
|
156
|
+
0,
|
|
157
|
+
rulesetConfigs[0].approvalHook,
|
|
158
|
+
JBRulesetMetadataResolver.packRulesetMetadata(rulesetConfigs[0].metadata),
|
|
159
|
+
0
|
|
160
|
+
)
|
|
161
|
+
),
|
|
162
|
+
abi.encode(ruleset)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// setSplitGroupsOf
|
|
166
|
+
vm.mockCall(
|
|
167
|
+
address(splits),
|
|
168
|
+
abi.encodeCall(IJBSplits.setSplitGroupsOf, (projectId, ts, rulesetConfigs[0].splitGroups)),
|
|
169
|
+
""
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// setFundAccessLimitsFor
|
|
173
|
+
vm.mockCall(
|
|
174
|
+
address(fundAccessLimits),
|
|
175
|
+
abi.encodeCall(
|
|
176
|
+
IJBFundAccessLimits.setFundAccessLimitsFor, (projectId, ts, rulesetConfigs[0].fundAccessLimitGroups)
|
|
177
|
+
),
|
|
178
|
+
""
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// @dev Mock the full call chain for a successful queueRulesetsOf.
|
|
183
|
+
function _mockQueueSuccess(JBRulesetConfig[] memory rulesetConfigs) internal {
|
|
184
|
+
uint48 ts = uint48(block.timestamp);
|
|
185
|
+
|
|
186
|
+
// ownerOf
|
|
187
|
+
vm.mockCall(projectsAddr, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(projectOwner));
|
|
188
|
+
|
|
189
|
+
// queueFor
|
|
190
|
+
JBRuleset memory ruleset = JBRuleset({
|
|
191
|
+
cycleNumber: 2,
|
|
192
|
+
id: ts,
|
|
193
|
+
basedOnId: 1,
|
|
194
|
+
start: ts,
|
|
195
|
+
duration: 0,
|
|
196
|
+
weight: 0,
|
|
197
|
+
weightCutPercent: 0,
|
|
198
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
199
|
+
metadata: 0
|
|
200
|
+
});
|
|
201
|
+
vm.mockCall(
|
|
202
|
+
address(rulesets),
|
|
203
|
+
abi.encodeCall(
|
|
204
|
+
IJBRulesets.queueFor,
|
|
205
|
+
(
|
|
206
|
+
projectId,
|
|
207
|
+
0,
|
|
208
|
+
0,
|
|
209
|
+
0,
|
|
210
|
+
rulesetConfigs[0].approvalHook,
|
|
211
|
+
JBRulesetMetadataResolver.packRulesetMetadata(rulesetConfigs[0].metadata),
|
|
212
|
+
0
|
|
213
|
+
)
|
|
214
|
+
),
|
|
215
|
+
abi.encode(ruleset)
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// setSplitGroupsOf
|
|
219
|
+
vm.mockCall(
|
|
220
|
+
address(splits),
|
|
221
|
+
abi.encodeCall(IJBSplits.setSplitGroupsOf, (projectId, ts, rulesetConfigs[0].splitGroups)),
|
|
222
|
+
""
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// setFundAccessLimitsFor
|
|
226
|
+
vm.mockCall(
|
|
227
|
+
address(fundAccessLimits),
|
|
228
|
+
abi.encodeCall(
|
|
229
|
+
IJBFundAccessLimits.setFundAccessLimitsFor, (projectId, ts, rulesetConfigs[0].fundAccessLimitGroups)
|
|
230
|
+
),
|
|
231
|
+
""
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Tests
|
|
236
|
+
// ────────────────────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
function test_operatorCanLaunchRulesetsWithoutPermission() external {
|
|
239
|
+
(JBTerminalConfig[] memory terminalConfigs, JBRulesetConfig[] memory rulesetConfigs) = _genRulesetConfig();
|
|
240
|
+
_mockLaunchSuccess(rulesetConfigs);
|
|
241
|
+
|
|
242
|
+
// Operator calls launchRulesetsFor without owning the project or having any permission grants.
|
|
243
|
+
// The alsoGrantAccessIf bypass should allow this.
|
|
244
|
+
vm.prank(operator);
|
|
245
|
+
_controller.launchRulesetsFor(projectId, rulesetConfigs, terminalConfigs, "");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function test_operatorCanQueueRulesetsWithoutPermission() external {
|
|
249
|
+
(, JBRulesetConfig[] memory rulesetConfigs) = _genRulesetConfig();
|
|
250
|
+
_mockQueueSuccess(rulesetConfigs);
|
|
251
|
+
|
|
252
|
+
// Operator calls queueRulesetsOf without owning the project or having any permission grants.
|
|
253
|
+
vm.prank(operator);
|
|
254
|
+
_controller.queueRulesetsOf(projectId, rulesetConfigs, "");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function test_nonOperatorCannotBypassPermissions() external {
|
|
258
|
+
(JBTerminalConfig[] memory terminalConfigs, JBRulesetConfig[] memory rulesetConfigs) = _genRulesetConfig();
|
|
259
|
+
|
|
260
|
+
// Mock ownerOf — randomCaller is not the owner
|
|
261
|
+
vm.mockCall(projectsAddr, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(projectOwner));
|
|
262
|
+
|
|
263
|
+
// Mock permission check — randomCaller has no permissions
|
|
264
|
+
vm.mockCall(
|
|
265
|
+
address(permissions),
|
|
266
|
+
abi.encodeCall(
|
|
267
|
+
IJBPermissions.hasPermission,
|
|
268
|
+
(randomCaller, projectOwner, projectId, JBPermissionIds.LAUNCH_RULESETS, true, true)
|
|
269
|
+
),
|
|
270
|
+
abi.encode(false)
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Should revert for launchRulesetsFor
|
|
274
|
+
vm.prank(randomCaller);
|
|
275
|
+
vm.expectRevert(
|
|
276
|
+
abi.encodeWithSelector(
|
|
277
|
+
JBPermissioned.JBPermissioned_Unauthorized.selector,
|
|
278
|
+
projectOwner,
|
|
279
|
+
randomCaller,
|
|
280
|
+
projectId,
|
|
281
|
+
JBPermissionIds.LAUNCH_RULESETS
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
_controller.launchRulesetsFor(projectId, rulesetConfigs, terminalConfigs, "");
|
|
285
|
+
|
|
286
|
+
// Mock permission check for QUEUE_RULESETS — randomCaller has no permissions
|
|
287
|
+
vm.mockCall(
|
|
288
|
+
address(permissions),
|
|
289
|
+
abi.encodeCall(
|
|
290
|
+
IJBPermissions.hasPermission,
|
|
291
|
+
(randomCaller, projectOwner, projectId, JBPermissionIds.QUEUE_RULESETS, true, true)
|
|
292
|
+
),
|
|
293
|
+
abi.encode(false)
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// Should revert for queueRulesetsOf
|
|
297
|
+
vm.prank(randomCaller);
|
|
298
|
+
vm.expectRevert(
|
|
299
|
+
abi.encodeWithSelector(
|
|
300
|
+
JBPermissioned.JBPermissioned_Unauthorized.selector,
|
|
301
|
+
projectOwner,
|
|
302
|
+
randomCaller,
|
|
303
|
+
projectId,
|
|
304
|
+
JBPermissionIds.QUEUE_RULESETS
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
_controller.queueRulesetsOf(projectId, rulesetConfigs, "");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function test_operatorCannotLaunchIfRulesetsExist() external {
|
|
311
|
+
(JBTerminalConfig[] memory terminalConfigs, JBRulesetConfig[] memory rulesetConfigs) = _genRulesetConfig();
|
|
312
|
+
|
|
313
|
+
// Mock ownerOf
|
|
314
|
+
vm.mockCall(projectsAddr, abi.encodeCall(IERC721.ownerOf, (projectId)), abi.encode(projectOwner));
|
|
315
|
+
|
|
316
|
+
// Project already has rulesets
|
|
317
|
+
vm.mockCall(address(rulesets), abi.encodeCall(IJBRulesets.latestRulesetIdOf, (projectId)), abi.encode(1));
|
|
318
|
+
|
|
319
|
+
// Even the operator cannot launch if rulesets already exist
|
|
320
|
+
vm.prank(operator);
|
|
321
|
+
vm.expectRevert(abi.encodeWithSelector(JBController.JBController_RulesetsAlreadyLaunched.selector, projectId));
|
|
322
|
+
_controller.launchRulesetsFor(projectId, rulesetConfigs, terminalConfigs, "");
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
|
+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
4
5
|
import {JBERC20} from "../../../../src/JBERC20.sol";
|
|
5
6
|
import {IJBToken} from "../../../../src/interfaces/IJBToken.sol";
|
|
6
7
|
import {JBTest} from "../../../helpers/JBTest.sol";
|
|
@@ -12,11 +13,17 @@ Tests relative to this contract will be dependent on mock calls/emits and stdSto
|
|
|
12
13
|
contract JBERC20Setup is JBTest {
|
|
13
14
|
address _owner = makeAddr("owner");
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
+
// Implementation (constructor sets _name = "invalid", cannot be initialized)
|
|
17
|
+
IJBToken public _implementation;
|
|
18
|
+
|
|
19
|
+
// Target Contract (clone with empty _name, can be initialized)
|
|
16
20
|
IJBToken public _erc20;
|
|
17
21
|
|
|
18
22
|
function erc20Setup() public virtual {
|
|
19
|
-
//
|
|
20
|
-
|
|
23
|
+
// Deploy the implementation
|
|
24
|
+
_implementation = new JBERC20();
|
|
25
|
+
|
|
26
|
+
// Clone it — clones start with empty storage, so initialize() works
|
|
27
|
+
_erc20 = IJBToken(Clones.clone(address(_implementation)));
|
|
21
28
|
}
|
|
22
29
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
|
+
import {JBERC20} from "../../../../src/JBERC20.sol";
|
|
5
6
|
import {JBERC20Setup} from "./JBERC20Setup.sol";
|
|
6
7
|
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
7
8
|
|
|
@@ -13,6 +14,12 @@ contract TestInitialize_Local is JBERC20Setup {
|
|
|
13
14
|
super.erc20Setup();
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
function test_ImplementationCannotBeInitialized() external {
|
|
18
|
+
// The implementation has _name = "invalid" set in constructor, so initialize() must revert.
|
|
19
|
+
vm.expectRevert(JBERC20.JBERC20_AlreadyInitialized.selector);
|
|
20
|
+
_implementation.initialize(_name, _symbol, _owner);
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
function test_WhenANameIsAlreadySet() external {
|
|
17
24
|
// it will revert
|
|
18
25
|
|