@bananapus/core-v6 0.0.1
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/LICENSE +21 -0
- package/README.md +112 -0
- package/SKILLS.md +151 -0
- package/docs/book.css +13 -0
- package/docs/book.toml +12 -0
- package/docs/solidity.min.js +74 -0
- package/docs/src/README.md +703 -0
- package/docs/src/SUMMARY.md +94 -0
- package/docs/src/src/JBChainlinkV3PriceFeed.sol/contract.JBChainlinkV3PriceFeed.md +83 -0
- package/docs/src/src/JBChainlinkV3SequencerPriceFeed.sol/contract.JBChainlinkV3SequencerPriceFeed.md +88 -0
- package/docs/src/src/JBController.sol/contract.JBController.md +1121 -0
- package/docs/src/src/JBDeadline.sol/contract.JBDeadline.md +84 -0
- package/docs/src/src/JBDirectory.sol/contract.JBDirectory.md +294 -0
- package/docs/src/src/JBERC20.sol/contract.JBERC20.md +190 -0
- package/docs/src/src/JBFeelessAddresses.sol/contract.JBFeelessAddresses.md +80 -0
- package/docs/src/src/JBFundAccessLimits.sol/contract.JBFundAccessLimits.md +253 -0
- package/docs/src/src/JBMultiTerminal.sol/contract.JBMultiTerminal.md +1472 -0
- package/docs/src/src/JBPermissions.sol/contract.JBPermissions.md +199 -0
- package/docs/src/src/JBPrices.sol/contract.JBPrices.md +154 -0
- package/docs/src/src/JBProjects.sol/contract.JBProjects.md +131 -0
- package/docs/src/src/JBRulesets.sol/contract.JBRulesets.md +677 -0
- package/docs/src/src/JBSplits.sol/contract.JBSplits.md +237 -0
- package/docs/src/src/JBTerminalStore.sol/contract.JBTerminalStore.md +591 -0
- package/docs/src/src/JBTokens.sol/contract.JBTokens.md +353 -0
- package/docs/src/src/README.md +25 -0
- package/docs/src/src/abstract/JBControlled.sol/abstract.JBControlled.md +64 -0
- package/docs/src/src/abstract/JBPermissioned.sol/abstract.JBPermissioned.md +84 -0
- package/docs/src/src/abstract/README.md +5 -0
- package/docs/src/src/enums/JBApprovalStatus.sol/enum.JBApprovalStatus.md +17 -0
- package/docs/src/src/enums/README.md +4 -0
- package/docs/src/src/interfaces/IJBCashOutHook.sol/interface.IJBCashOutHook.md +29 -0
- package/docs/src/src/interfaces/IJBCashOutTerminal.sol/interface.IJBCashOutTerminal.md +57 -0
- package/docs/src/src/interfaces/IJBControlled.sol/interface.IJBControlled.md +12 -0
- package/docs/src/src/interfaces/IJBController.sol/interface.IJBController.md +334 -0
- package/docs/src/src/interfaces/IJBDirectory.sol/interface.IJBDirectory.md +108 -0
- package/docs/src/src/interfaces/IJBDirectoryAccessControl.sol/interface.IJBDirectoryAccessControl.md +19 -0
- package/docs/src/src/interfaces/IJBFeeTerminal.sol/interface.IJBFeeTerminal.md +91 -0
- package/docs/src/src/interfaces/IJBFeelessAddresses.sol/interface.IJBFeelessAddresses.md +26 -0
- package/docs/src/src/interfaces/IJBFundAccessLimits.sol/interface.IJBFundAccessLimits.md +88 -0
- package/docs/src/src/interfaces/IJBMigratable.sol/interface.IJBMigratable.md +29 -0
- package/docs/src/src/interfaces/IJBMultiTerminal.sol/interface.IJBMultiTerminal.md +50 -0
- package/docs/src/src/interfaces/IJBPayHook.sol/interface.IJBPayHook.md +28 -0
- package/docs/src/src/interfaces/IJBPayoutTerminal.sol/interface.IJBPayoutTerminal.md +105 -0
- package/docs/src/src/interfaces/IJBPermissioned.sol/interface.IJBPermissioned.md +12 -0
- package/docs/src/src/interfaces/IJBPermissions.sol/interface.IJBPermissions.md +74 -0
- package/docs/src/src/interfaces/IJBPermitTerminal.sol/interface.IJBPermitTerminal.md +15 -0
- package/docs/src/src/interfaces/IJBPriceFeed.sol/interface.IJBPriceFeed.md +12 -0
- package/docs/src/src/interfaces/IJBPrices.sol/interface.IJBPrices.md +74 -0
- package/docs/src/src/interfaces/IJBProjectUriRegistry.sol/interface.IJBProjectUriRegistry.md +19 -0
- package/docs/src/src/interfaces/IJBProjects.sol/interface.IJBProjects.md +49 -0
- package/docs/src/src/interfaces/IJBRulesetApprovalHook.sol/interface.IJBRulesetApprovalHook.md +35 -0
- package/docs/src/src/interfaces/IJBRulesetDataHook.sol/interface.IJBRulesetDataHook.md +97 -0
- package/docs/src/src/interfaces/IJBRulesets.sol/interface.IJBRulesets.md +165 -0
- package/docs/src/src/interfaces/IJBSplitHook.sol/interface.IJBSplitHook.md +31 -0
- package/docs/src/src/interfaces/IJBSplits.sol/interface.IJBSplits.md +35 -0
- package/docs/src/src/interfaces/IJBTerminal.sol/interface.IJBTerminal.md +141 -0
- package/docs/src/src/interfaces/IJBTerminalStore.sol/interface.IJBTerminalStore.md +198 -0
- package/docs/src/src/interfaces/IJBToken.sol/interface.IJBToken.md +54 -0
- package/docs/src/src/interfaces/IJBTokenUriResolver.sol/interface.IJBTokenUriResolver.md +12 -0
- package/docs/src/src/interfaces/IJBTokens.sol/interface.IJBTokens.md +151 -0
- package/docs/src/src/interfaces/README.md +33 -0
- package/docs/src/src/libraries/JBCashOuts.sol/library.JBCashOuts.md +40 -0
- package/docs/src/src/libraries/JBConstants.sol/library.JBConstants.md +52 -0
- package/docs/src/src/libraries/JBCurrencyIds.sol/library.JBCurrencyIds.md +19 -0
- package/docs/src/src/libraries/JBFees.sol/library.JBFees.md +52 -0
- package/docs/src/src/libraries/JBFixedPointNumber.sol/library.JBFixedPointNumber.md +12 -0
- package/docs/src/src/libraries/JBMetadataResolver.sol/library.JBMetadataResolver.md +242 -0
- package/docs/src/src/libraries/JBRulesetMetadataResolver.sol/library.JBRulesetMetadataResolver.md +180 -0
- package/docs/src/src/libraries/JBSplitGroupIds.sol/library.JBSplitGroupIds.md +14 -0
- package/docs/src/src/libraries/JBSurplus.sol/library.JBSurplus.md +44 -0
- package/docs/src/src/libraries/README.md +12 -0
- package/docs/src/src/periphery/JBDeadline1Day.sol/contract.JBDeadline1Day.md +15 -0
- package/docs/src/src/periphery/JBDeadline3Days.sol/contract.JBDeadline3Days.md +15 -0
- package/docs/src/src/periphery/JBDeadline3Hours.sol/contract.JBDeadline3Hours.md +15 -0
- package/docs/src/src/periphery/JBDeadline7Days.sol/contract.JBDeadline7Days.md +15 -0
- package/docs/src/src/periphery/JBMatchingPriceFeed.sol/contract.JBMatchingPriceFeed.md +22 -0
- package/docs/src/src/periphery/README.md +8 -0
- package/docs/src/src/structs/JBAccountingContext.sol/struct.JBAccountingContext.md +20 -0
- package/docs/src/src/structs/JBAfterCashOutRecordedContext.sol/struct.JBAfterCashOutRecordedContext.md +43 -0
- package/docs/src/src/structs/JBAfterPayRecordedContext.sol/struct.JBAfterPayRecordedContext.md +42 -0
- package/docs/src/src/structs/JBBeforeCashOutRecordedContext.sol/struct.JBBeforeCashOutRecordedContext.md +45 -0
- package/docs/src/src/structs/JBBeforePayRecordedContext.sol/struct.JBBeforePayRecordedContext.md +41 -0
- package/docs/src/src/structs/JBCashOutHookSpecification.sol/struct.JBCashOutHookSpecification.md +22 -0
- package/docs/src/src/structs/JBCurrencyAmount.sol/struct.JBCurrencyAmount.md +17 -0
- package/docs/src/src/structs/JBFee.sol/struct.JBFee.md +20 -0
- package/docs/src/src/structs/JBFundAccessLimitGroup.sol/struct.JBFundAccessLimitGroup.md +39 -0
- package/docs/src/src/structs/JBPayHookSpecification.sol/struct.JBPayHookSpecification.md +22 -0
- package/docs/src/src/structs/JBPermissionsData.sol/struct.JBPermissionsData.md +21 -0
- package/docs/src/src/structs/JBRuleset.sol/struct.JBRuleset.md +55 -0
- package/docs/src/src/structs/JBRulesetConfig.sol/struct.JBRulesetConfig.md +51 -0
- package/docs/src/src/structs/JBRulesetMetadata.sol/struct.JBRulesetMetadata.md +79 -0
- package/docs/src/src/structs/JBRulesetWeightCache.sol/struct.JBRulesetWeightCache.md +16 -0
- package/docs/src/src/structs/JBRulesetWithMetadata.sol/struct.JBRulesetWithMetadata.md +16 -0
- package/docs/src/src/structs/JBSingleAllowance.sol/struct.JBSingleAllowance.md +26 -0
- package/docs/src/src/structs/JBSplit.sol/struct.JBSplit.md +49 -0
- package/docs/src/src/structs/JBSplitGroup.sol/struct.JBSplitGroup.md +17 -0
- package/docs/src/src/structs/JBSplitHookContext.sol/struct.JBSplitHookContext.md +29 -0
- package/docs/src/src/structs/JBTerminalConfig.sol/struct.JBTerminalConfig.md +16 -0
- package/docs/src/src/structs/JBTokenAmount.sol/struct.JBTokenAmount.md +23 -0
- package/docs/src/src/structs/README.md +25 -0
- package/foundry.lock +11 -0
- package/foundry.toml +41 -0
- package/package.json +38 -0
- package/remappings.txt +1 -0
- package/script/Deploy.s.sol +111 -0
- package/script/DeployPeriphery.s.sol +287 -0
- package/script/helpers/CoreDeploymentLib.sol +121 -0
- package/slither-ci.config.json +10 -0
- package/sphinx.lock +507 -0
- package/src/JBChainlinkV3PriceFeed.sol +77 -0
- package/src/JBChainlinkV3SequencerPriceFeed.sol +75 -0
- package/src/JBController.sol +1186 -0
- package/src/JBDeadline.sol +73 -0
- package/src/JBDirectory.sol +343 -0
- package/src/JBERC20.sol +131 -0
- package/src/JBFeelessAddresses.sol +54 -0
- package/src/JBFundAccessLimits.sol +308 -0
- package/src/JBMultiTerminal.sol +2024 -0
- package/src/JBPermissions.sol +252 -0
- package/src/JBPrices.sol +227 -0
- package/src/JBProjects.sol +126 -0
- package/src/JBRulesets.sol +1093 -0
- package/src/JBSplits.sol +324 -0
- package/src/JBTerminalStore.sol +908 -0
- package/src/JBTokens.sol +376 -0
- package/src/abstract/JBControlled.sol +48 -0
- package/src/abstract/JBPermissioned.sol +77 -0
- package/src/enums/JBApprovalStatus.sol +12 -0
- package/src/interfaces/IJBCashOutHook.sol +15 -0
- package/src/interfaces/IJBCashOutTerminal.sol +51 -0
- package/src/interfaces/IJBControlled.sol +10 -0
- package/src/interfaces/IJBController.sol +280 -0
- package/src/interfaces/IJBDirectory.sol +69 -0
- package/src/interfaces/IJBDirectoryAccessControl.sol +15 -0
- package/src/interfaces/IJBFeeTerminal.sol +61 -0
- package/src/interfaces/IJBFeelessAddresses.sol +17 -0
- package/src/interfaces/IJBFundAccessLimits.sol +94 -0
- package/src/interfaces/IJBMigratable.sol +24 -0
- package/src/interfaces/IJBMultiTerminal.sol +36 -0
- package/src/interfaces/IJBPayHook.sol +14 -0
- package/src/interfaces/IJBPayoutTerminal.sol +92 -0
- package/src/interfaces/IJBPermissioned.sol +10 -0
- package/src/interfaces/IJBPermissions.sol +71 -0
- package/src/interfaces/IJBPermitTerminal.sol +14 -0
- package/src/interfaces/IJBPriceFeed.sol +10 -0
- package/src/interfaces/IJBPrices.sol +65 -0
- package/src/interfaces/IJBProjectUriRegistry.sol +15 -0
- package/src/interfaces/IJBProjects.sol +27 -0
- package/src/interfaces/IJBRulesetApprovalHook.sol +21 -0
- package/src/interfaces/IJBRulesetDataHook.sol +56 -0
- package/src/interfaces/IJBRulesets.sol +151 -0
- package/src/interfaces/IJBSplitHook.sol +16 -0
- package/src/interfaces/IJBSplits.sol +28 -0
- package/src/interfaces/IJBTerminal.sol +120 -0
- package/src/interfaces/IJBTerminalStore.sol +225 -0
- package/src/interfaces/IJBToken.sol +39 -0
- package/src/interfaces/IJBTokenUriResolver.sol +10 -0
- package/src/interfaces/IJBTokens.sol +113 -0
- package/src/libraries/JBCashOuts.sol +120 -0
- package/src/libraries/JBConstants.sol +14 -0
- package/src/libraries/JBCurrencyIds.sol +7 -0
- package/src/libraries/JBFees.sol +28 -0
- package/src/libraries/JBFixedPointNumber.sol +12 -0
- package/src/libraries/JBMetadataResolver.sol +306 -0
- package/src/libraries/JBRulesetMetadataResolver.sol +160 -0
- package/src/libraries/JBSplitGroupIds.sol +7 -0
- package/src/libraries/JBSurplus.sol +40 -0
- package/src/periphery/JBDeadline1Day.sol +8 -0
- package/src/periphery/JBDeadline3Days.sol +8 -0
- package/src/periphery/JBDeadline3Hours.sol +8 -0
- package/src/periphery/JBDeadline7Days.sol +8 -0
- package/src/periphery/JBMatchingPriceFeed.sol +13 -0
- package/src/structs/JBAccountingContext.sol +12 -0
- package/src/structs/JBAfterCashOutRecordedContext.sol +30 -0
- package/src/structs/JBAfterPayRecordedContext.sol +29 -0
- package/src/structs/JBBeforeCashOutRecordedContext.sol +31 -0
- package/src/structs/JBBeforePayRecordedContext.sol +28 -0
- package/src/structs/JBCashOutHookSpecification.sol +15 -0
- package/src/structs/JBCurrencyAmount.sol +10 -0
- package/src/structs/JBFee.sol +12 -0
- package/src/structs/JBFundAccessLimitGroup.sol +28 -0
- package/src/structs/JBPayHookSpecification.sol +15 -0
- package/src/structs/JBPermissionsData.sol +13 -0
- package/src/structs/JBRuleset.sol +42 -0
- package/src/structs/JBRulesetConfig.sol +43 -0
- package/src/structs/JBRulesetMetadata.sol +56 -0
- package/src/structs/JBRulesetWeightCache.sol +9 -0
- package/src/structs/JBRulesetWithMetadata.sol +12 -0
- package/src/structs/JBSingleAllowance.sol +16 -0
- package/src/structs/JBSplit.sol +37 -0
- package/src/structs/JBSplitGroup.sol +12 -0
- package/src/structs/JBSplitHookContext.sol +20 -0
- package/src/structs/JBTerminalConfig.sol +12 -0
- package/src/structs/JBTokenAmount.sol +14 -0
- package/test/AuditExploits.t.sol +2710 -0
- package/test/ComprehensiveInvariant.t.sol +298 -0
- package/test/EconomicSimulation.t.sol +340 -0
- package/test/EntryPointPermutations.t.sol +671 -0
- package/test/FlashLoanAttacks.t.sol +792 -0
- package/test/PermissionEscalation.t.sol +679 -0
- package/test/RulesetTransitions.t.sol +699 -0
- package/test/SplitLoopTests.t.sol +731 -0
- package/test/TestAccessToFunds.sol +2644 -0
- package/test/TestCashOut.sol +185 -0
- package/test/TestCashOutCountFor.sol +272 -0
- package/test/TestCashOutHooks.sol +317 -0
- package/test/TestCashOutTimingEdge.sol +229 -0
- package/test/TestDurationUnderflow.sol +220 -0
- package/test/TestFeeProcessingFailure.sol +208 -0
- package/test/TestFees.sol +604 -0
- package/test/TestInterfaceSupport.sol +62 -0
- package/test/TestJBERC20Inheritance.sol +91 -0
- package/test/TestLaunchProject.sol +176 -0
- package/test/TestMetaTx.sol +203 -0
- package/test/TestMetadataParserLib.sol +438 -0
- package/test/TestMigrationHeldFees.sol +249 -0
- package/test/TestMintTokensOf.sol +172 -0
- package/test/TestMultiTokenSurplus.sol +206 -0
- package/test/TestMultipleAccessLimits.sol +642 -0
- package/test/TestPayBurnRedeemFlow.sol +180 -0
- package/test/TestPayHooks.sol +190 -0
- package/test/TestPermissions.sol +305 -0
- package/test/TestPermissionsEdge.sol +286 -0
- package/test/TestPermit2Terminal.sol +339 -0
- package/test/TestRulesetQueueing.sol +1001 -0
- package/test/TestRulesetQueuingStress.sol +778 -0
- package/test/TestRulesetWeightCaching.sol +177 -0
- package/test/TestSplits.sol +369 -0
- package/test/TestTerminalMigration.sol +167 -0
- package/test/TestTokenFlow.sol +174 -0
- package/test/WeirdTokenTests.t.sol +764 -0
- package/test/formal/BondingCurveProperties.t.sol +411 -0
- package/test/formal/FeeProperties.t.sol +246 -0
- package/test/helpers/JBTest.sol +129 -0
- package/test/helpers/MetadataResolverHelper.sol +116 -0
- package/test/helpers/TestBaseWorkflow.sol +317 -0
- package/test/invariants/Phase3DeepInvariant.t.sol +404 -0
- package/test/invariants/RulesetsInvariant.t.sol +115 -0
- package/test/invariants/TerminalStoreInvariant.t.sol +220 -0
- package/test/invariants/TokensInvariant.t.sol +184 -0
- package/test/invariants/handlers/ComprehensiveHandler.sol +285 -0
- package/test/invariants/handlers/EconomicHandler.sol +347 -0
- package/test/invariants/handlers/Phase3Handler.sol +414 -0
- package/test/invariants/handlers/RulesetsHandler.sol +111 -0
- package/test/invariants/handlers/TerminalStoreHandler.sol +146 -0
- package/test/invariants/handlers/TokensHandler.sol +127 -0
- package/test/mock/ERC2771ForwarderMock.sol +37 -0
- package/test/mock/MockERC20.sol +18 -0
- package/test/mock/MockMaliciousBeneficiary.sol +67 -0
- package/test/mock/MockMaliciousSplitHook.sol +42 -0
- package/test/mock/MockPriceFeed.sol +20 -0
- package/test/trees/JBController/burnTokensOf.tree +9 -0
- package/test/trees/JBController/claimTokensFor.tree +5 -0
- package/test/trees/JBController/deployERC20For.tree +5 -0
- package/test/trees/JBController/getRulesetOf.tree +5 -0
- package/test/trees/JBController/launchProjectFor.tree +12 -0
- package/test/trees/JBController/launchRulesetsFor.tree +8 -0
- package/test/trees/JBController/migrateController.tree +12 -0
- package/test/trees/JBController/mintTokensOf.tree +12 -0
- package/test/trees/JBController/payReservedTokenToTerminal.tree +8 -0
- package/test/trees/JBController/receiveMigrationFrom.tree +4 -0
- package/test/trees/JBController/sendReservedTokensToSplitsOf.tree +12 -0
- package/test/trees/JBController/setMetadataOf.tree +5 -0
- package/test/trees/JBController/setSplitGroupsOf.tree +5 -0
- package/test/trees/JBController/setTokenFor.tree +5 -0
- package/test/trees/JBController/transferCreditsFrom.tree +8 -0
- package/test/trees/JBDirectory/primaryTerminalOf.tree +8 -0
- package/test/trees/JBDirectory/setControllerOf.tree +11 -0
- package/test/trees/JBDirectory/setPrimaryTerminalOf.tree +15 -0
- package/test/trees/JBDirectory/setTerminalsOf.tree +11 -0
- package/test/trees/JBERC20/initialize.tree +7 -0
- package/test/trees/JBERC20/name.tree +5 -0
- package/test/trees/JBERC20/nonces.tree +5 -0
- package/test/trees/JBERC20/symbol.tree +5 -0
- package/test/trees/JBFeelessAddresses/setFeelessAddress.tree +5 -0
- package/test/trees/JBFeelessAddresses/supportsInterface.tree +5 -0
- package/test/trees/JBFundAccessLimits/payoutLimitOf.tree +5 -0
- package/test/trees/JBFundAccessLimits/payoutLimitsOf.tree +8 -0
- package/test/trees/JBFundAccessLimits/setFundAccessLimitsFor.tree +18 -0
- package/test/trees/JBFundAccessLimits/surplusAllowanceOf.tree +5 -0
- package/test/trees/JBFundAccessLimits/surplusAllowancesOf.tree +8 -0
- package/test/trees/JBMetadataResolver/getDataFor.tree +8 -0
- package/test/trees/JBMultiTerminal/accountingContextsOf.tree +5 -0
- package/test/trees/JBMultiTerminal/addAccountingContextsFor.tree +10 -0
- package/test/trees/JBMultiTerminal/addToBalanceOf.tree +23 -0
- package/test/trees/JBMultiTerminal/cashOutTokensOf.tree +23 -0
- package/test/trees/JBMultiTerminal/executePayout.tree +32 -0
- package/test/trees/JBMultiTerminal/executeProcessFee.tree +14 -0
- package/test/trees/JBMultiTerminal/migrateBalanceOf.tree +12 -0
- package/test/trees/JBMultiTerminal/pay.tree +23 -0
- package/test/trees/JBMultiTerminal/processHeldFeesOf.tree +8 -0
- package/test/trees/JBMultiTerminal/sendPayoutsOf.tree +34 -0
- package/test/trees/JBMultiTerminal/useAllowanceOf.tree +16 -0
- package/test/trees/JBPermissions/hasPermission.tree +8 -0
- package/test/trees/JBPermissions/hasPermissions.tree +8 -0
- package/test/trees/JBPermissions/setPermissionsFor.tree +5 -0
- package/test/trees/JBPrices/addPriceFeedFor.tree +14 -0
- package/test/trees/JBPrices/pricePerUnitOf.tree +11 -0
- package/test/trees/JBProjects/createFor.tree +11 -0
- package/test/trees/JBProjects/setTokenUriResolver.tree +5 -0
- package/test/trees/JBProjects/supportsInterface.tree +9 -0
- package/test/trees/JBProjects/tokenURI.tree +5 -0
- package/test/trees/JBRulesets/currentApprovalStatusForLatestRulesetOf.tree +8 -0
- package/test/trees/JBRulesets/currentOf.tree +12 -0
- package/test/trees/JBRulesets/getRulesetOf.tree +5 -0
- package/test/trees/JBRulesets/latestQueuedRulesetOf.tree +10 -0
- package/test/trees/JBRulesets/rulesetsOf.tree +11 -0
- package/test/trees/JBRulesets/upcomingRulesetOf.tree +20 -0
- package/test/trees/JBRulesets/updateRulesetWeightCache.tree +5 -0
- package/test/trees/JBSplits/setSplitGroupsOf.tree +17 -0
- package/test/trees/JBSplits/splitsOf.tree +5 -0
- package/test/trees/JBTerminalStore/currentReclaimableSurplusOf.tree +16 -0
- package/test/trees/JBTerminalStore/currentSurplusOf.tree +25 -0
- package/test/trees/JBTerminalStore/currentTotalSurplusOf.tree +5 -0
- package/test/trees/JBTerminalStore/recordCashOutsFor.tree +16 -0
- package/test/trees/JBTerminalStore/recordPaymentFrom.tree +14 -0
- package/test/trees/JBTerminalStore/recordPayoutFor.tree +10 -0
- package/test/trees/JBTerminalStore/recordTerminalMigration.tree +5 -0
- package/test/trees/JBTerminalStore/recordUsedAllowanceOf.tree +10 -0
- package/test/trees/JBTokens/burnFrom.tree +10 -0
- package/test/trees/JBTokens/claimTokensFor.tree +10 -0
- package/test/trees/JBTokens/deployERC20For.tree +12 -0
- package/test/trees/JBTokens/mintFor.tree +10 -0
- package/test/trees/JBTokens/setTokenFor.tree +11 -0
- package/test/trees/JBTokens/totalBalanceOf.tree +5 -0
- package/test/trees/JBTokens/totalSupplyOf.tree +5 -0
- package/test/trees/JBTokens/transferCreditsFrom.tree +8 -0
- package/test/trees/mintTokensOf.tree +12 -0
- package/test/units/static/JBChainlinkV3PriceFeed/TestPriceFeed.sol +220 -0
- package/test/units/static/JBController/JBControllerSetup.sol +40 -0
- package/test/units/static/JBController/TestBurnTokensOf.sol +107 -0
- package/test/units/static/JBController/TestClaimTokensFor.sol +60 -0
- package/test/units/static/JBController/TestDeployErc20For.sol +80 -0
- package/test/units/static/JBController/TestLaunchProjectFor.sol +282 -0
- package/test/units/static/JBController/TestLaunchRulesetsFor.sol +322 -0
- package/test/units/static/JBController/TestMigrateController.sol +148 -0
- package/test/units/static/JBController/TestMintTokensOfUnits.sol +102 -0
- package/test/units/static/JBController/TestPayReservedTokenToTerminal.sol +71 -0
- package/test/units/static/JBController/TestReceiveMigrationFrom.sol +95 -0
- package/test/units/static/JBController/TestRulesetViews.sol +219 -0
- package/test/units/static/JBController/TestSendReservedTokensToSplitsOf.sol +595 -0
- package/test/units/static/JBController/TestSetSplitGroupsOf.sol +63 -0
- package/test/units/static/JBController/TestSetTokenFor.sol +227 -0
- package/test/units/static/JBController/TestSetUriOf.sol +53 -0
- package/test/units/static/JBController/TestTransferCreditsFrom.sol +159 -0
- package/test/units/static/JBDeadline/TestDeadlineFuzz.sol +194 -0
- package/test/units/static/JBDirectory/JBDirectorySetup.sol +22 -0
- package/test/units/static/JBDirectory/TestPrimaryTerminalOf.sol +122 -0
- package/test/units/static/JBDirectory/TestSetControllerOf.sol +173 -0
- package/test/units/static/JBDirectory/TestSetControllerOfMigrationOrder.sol +98 -0
- package/test/units/static/JBDirectory/TestSetPrimaryTerminalOf.sol +169 -0
- package/test/units/static/JBDirectory/TestSetTerminalsOf.sol +128 -0
- package/test/units/static/JBERC20/JBERC20Setup.sol +20 -0
- package/test/units/static/JBERC20/SigUtils.sol +34 -0
- package/test/units/static/JBERC20/TestInitialize.sol +54 -0
- package/test/units/static/JBERC20/TestName.sol +30 -0
- package/test/units/static/JBERC20/TestNonces.sol +59 -0
- package/test/units/static/JBERC20/TestSymbol.sol +31 -0
- package/test/units/static/JBFeelessAdresses/JBFeelessSetup.sol +20 -0
- package/test/units/static/JBFeelessAdresses/TestInterfaces.sol +29 -0
- package/test/units/static/JBFeelessAdresses/TestSetFeelessAddress.sol +35 -0
- package/test/units/static/JBFees/TestFeesFuzz.sol +78 -0
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimals.sol +16 -0
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimalsFuzz.sol +71 -0
- package/test/units/static/JBFundAccessLimits/JBFundAccessSetup.sol +21 -0
- package/test/units/static/JBFundAccessLimits/TestFundAccessLimitsEdge.sol +159 -0
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitOf.sol +56 -0
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitsOf.sol +94 -0
- package/test/units/static/JBFundAccessLimits/TestSetFundAccessLimitsFor.sol +182 -0
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowanceOf.sol +61 -0
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowancesOf.sol +96 -0
- package/test/units/static/JBMetadataResolver/TestGetDataFor.sol +89 -0
- package/test/units/static/JBMetadataResolver/TestMetadataResolverFuzz.sol +227 -0
- package/test/units/static/JBMetadataResolver/TestMetadataResolverM20M21.sol +245 -0
- package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +39 -0
- package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +65 -0
- package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +313 -0
- package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +432 -0
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +478 -0
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +577 -0
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +176 -0
- package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +190 -0
- package/test/units/static/JBMultiTerminal/TestPay.sol +514 -0
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +29 -0
- package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +243 -0
- package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +310 -0
- package/test/units/static/JBPermissions/JBPermissionsSetup.sol +18 -0
- package/test/units/static/JBPermissions/TestHasPermission.sol +50 -0
- package/test/units/static/JBPermissions/TestHasPermissions.sol +93 -0
- package/test/units/static/JBPermissions/TestSetPermissionsFor.sol +62 -0
- package/test/units/static/JBPrices/JBPricesSetup.sol +26 -0
- package/test/units/static/JBPrices/TestAddPriceFeedFor.sol +102 -0
- package/test/units/static/JBPrices/TestPricePerUnitOf.sol +129 -0
- package/test/units/static/JBPrices/TestPrices.sol +262 -0
- package/test/units/static/JBProjects/JBProjectsSetup.sol +20 -0
- package/test/units/static/JBProjects/TestCreateFor.sol +69 -0
- package/test/units/static/JBProjects/TestInitialProject.sol +19 -0
- package/test/units/static/JBProjects/TestInterfaces.sol +27 -0
- package/test/units/static/JBProjects/TestSetResolver.sol +36 -0
- package/test/units/static/JBProjects/TestTokenUri.sol +38 -0
- package/test/units/static/JBRulesetMetadataResolver/TestSetCashOutTaxRateTo.sol +99 -0
- package/test/units/static/JBRulesets/JBRulesetsSetup.sol +21 -0
- package/test/units/static/JBRulesets/TestCurrentApprovalStatusForLatestRulesetOf.sol +257 -0
- package/test/units/static/JBRulesets/TestCurrentOf.sol +231 -0
- package/test/units/static/JBRulesets/TestGetRulesetOf.sol +94 -0
- package/test/units/static/JBRulesets/TestLatestQueuedRulesetOf.sol +252 -0
- package/test/units/static/JBRulesets/TestRulesets.sol +617 -0
- package/test/units/static/JBRulesets/TestRulesetsOf.sol +37 -0
- package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +526 -0
- package/test/units/static/JBRulesets/TestUpdateRulesetWeightCache.sol +91 -0
- package/test/units/static/JBSplits/JBSplitsSetup.sol +23 -0
- package/test/units/static/JBSplits/TestSelfManagedSplitGroups.sol +502 -0
- package/test/units/static/JBSplits/TestSetSplitGroupsOf.sol +370 -0
- package/test/units/static/JBSplits/TestSplitsLockedEdge.sol +262 -0
- package/test/units/static/JBSplits/TestSplitsOf.sol +24 -0
- package/test/units/static/JBSplits/TestSplitsPacking.sol +33 -0
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +125 -0
- package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +23 -0
- package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +434 -0
- package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +428 -0
- package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +65 -0
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +479 -0
- package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +508 -0
- package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +257 -0
- package/test/units/static/JBTerminalStore/TestRecordTerminalMigration.sol +131 -0
- package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +390 -0
- package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +187 -0
- package/test/units/static/JBTokens/JBTokensSetup.sol +23 -0
- package/test/units/static/JBTokens/TestBurnFrom.sol +104 -0
- package/test/units/static/JBTokens/TestClaimTokensFor.sol +107 -0
- package/test/units/static/JBTokens/TestDeployERC20ForUnits.sol +89 -0
- package/test/units/static/JBTokens/TestMintFor.sol +97 -0
- package/test/units/static/JBTokens/TestSetTokenFor.sol +95 -0
- package/test/units/static/JBTokens/TestTotalBalanceOf.sol +65 -0
- package/test/units/static/JBTokens/TestTotalSupplyOf.sol +56 -0
- package/test/units/static/JBTokens/TestTransferCreditsFrom.sol +54 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.6;
|
|
3
|
+
|
|
4
|
+
import /* {*} from */ "./helpers/TestBaseWorkflow.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice Mock approval hook that always returns a configurable status.
|
|
7
|
+
contract MockApprovalHookConfigurable is IJBRulesetApprovalHook {
|
|
8
|
+
JBApprovalStatus public immutable STATUS;
|
|
9
|
+
uint256 public immutable override DURATION;
|
|
10
|
+
|
|
11
|
+
constructor(JBApprovalStatus status, uint256 duration) {
|
|
12
|
+
STATUS = status;
|
|
13
|
+
DURATION = duration;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function approvalStatusOf(uint256, JBRuleset memory) external view override returns (JBApprovalStatus) {
|
|
17
|
+
return STATUS;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
|
|
21
|
+
return interfaceId == type(IJBRulesetApprovalHook).interfaceId || interfaceId == type(IERC165).interfaceId;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// @notice Mock approval hook that always reverts (for DoS testing — H-3 confirmation).
|
|
26
|
+
contract RevertingApprovalHook is IJBRulesetApprovalHook {
|
|
27
|
+
uint256 public constant override DURATION = 3 days;
|
|
28
|
+
|
|
29
|
+
function approvalStatusOf(uint256, JBRuleset memory) external pure override returns (JBApprovalStatus) {
|
|
30
|
+
revert("HOOK_REVERTED");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
|
|
34
|
+
return interfaceId == type(IJBRulesetApprovalHook).interfaceId || interfaceId == type(IERC165).interfaceId;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// @notice Stress tests for JBRulesets queuing logic under wild circumstances.
|
|
39
|
+
/// Tests duration transitions, approval hook edge cases, rapid-fire queuing,
|
|
40
|
+
/// weight decay to extremes, rollover behavior, start-time alignment, and
|
|
41
|
+
/// complex multi-queue scenarios.
|
|
42
|
+
contract TestRulesetQueuingStress_Local is TestBaseWorkflow {
|
|
43
|
+
IJBController private _controller;
|
|
44
|
+
IJBRulesets private _rulesets;
|
|
45
|
+
JBRulesetMetadata private _metadata;
|
|
46
|
+
JBDeadline private _deadline3Day;
|
|
47
|
+
|
|
48
|
+
uint32 constant FOURTEEN_DAYS = 14 days;
|
|
49
|
+
uint32 constant SEVEN_DAYS = 7 days;
|
|
50
|
+
uint112 constant INITIAL_WEIGHT = 1000e18;
|
|
51
|
+
|
|
52
|
+
function setUp() public override {
|
|
53
|
+
super.setUp();
|
|
54
|
+
_controller = jbController();
|
|
55
|
+
_rulesets = jbRulesets();
|
|
56
|
+
_deadline3Day = new JBDeadline(3 days);
|
|
57
|
+
|
|
58
|
+
_metadata = JBRulesetMetadata({
|
|
59
|
+
reservedPercent: 0,
|
|
60
|
+
cashOutTaxRate: 0,
|
|
61
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
62
|
+
pausePay: false,
|
|
63
|
+
pauseCreditTransfers: false,
|
|
64
|
+
allowOwnerMinting: false,
|
|
65
|
+
allowSetCustomToken: false,
|
|
66
|
+
allowTerminalMigration: false,
|
|
67
|
+
allowSetTerminals: false,
|
|
68
|
+
ownerMustSendPayouts: false,
|
|
69
|
+
allowSetController: false,
|
|
70
|
+
allowAddAccountingContext: false,
|
|
71
|
+
allowAddPriceFeed: false,
|
|
72
|
+
holdFees: false,
|
|
73
|
+
useTotalSurplusForCashOuts: false,
|
|
74
|
+
useDataHookForPay: false,
|
|
75
|
+
useDataHookForCashOut: false,
|
|
76
|
+
dataHook: address(0),
|
|
77
|
+
metadata: 0
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// @dev Launch a project with a single ruleset.
|
|
82
|
+
function _launchProject(
|
|
83
|
+
uint32 duration,
|
|
84
|
+
uint112 weight,
|
|
85
|
+
uint32 weightCutPercent,
|
|
86
|
+
IJBRulesetApprovalHook approvalHook
|
|
87
|
+
)
|
|
88
|
+
internal
|
|
89
|
+
returns (uint256)
|
|
90
|
+
{
|
|
91
|
+
JBRulesetConfig[] memory config = new JBRulesetConfig[](1);
|
|
92
|
+
config[0].mustStartAtOrAfter = 0;
|
|
93
|
+
config[0].duration = duration;
|
|
94
|
+
config[0].weight = weight;
|
|
95
|
+
config[0].weightCutPercent = weightCutPercent;
|
|
96
|
+
config[0].approvalHook = approvalHook;
|
|
97
|
+
config[0].metadata = _metadata;
|
|
98
|
+
config[0].splitGroups = new JBSplitGroup[](0);
|
|
99
|
+
config[0].fundAccessLimitGroups = new JBFundAccessLimitGroup[](0);
|
|
100
|
+
|
|
101
|
+
return _controller.launchProjectFor({
|
|
102
|
+
owner: multisig(),
|
|
103
|
+
projectUri: "stress",
|
|
104
|
+
rulesetConfigurations: config,
|
|
105
|
+
terminalConfigurations: new JBTerminalConfig[](0),
|
|
106
|
+
memo: ""
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// @dev Launch a project with start in the future.
|
|
111
|
+
function _launchProjectFutureStart(uint48 startAt, uint32 duration, uint112 weight) internal returns (uint256) {
|
|
112
|
+
JBRulesetConfig[] memory config = new JBRulesetConfig[](1);
|
|
113
|
+
config[0].mustStartAtOrAfter = startAt;
|
|
114
|
+
config[0].duration = duration;
|
|
115
|
+
config[0].weight = weight;
|
|
116
|
+
config[0].weightCutPercent = 0;
|
|
117
|
+
config[0].approvalHook = IJBRulesetApprovalHook(address(0));
|
|
118
|
+
config[0].metadata = _metadata;
|
|
119
|
+
config[0].splitGroups = new JBSplitGroup[](0);
|
|
120
|
+
config[0].fundAccessLimitGroups = new JBFundAccessLimitGroup[](0);
|
|
121
|
+
|
|
122
|
+
return _controller.launchProjectFor({
|
|
123
|
+
owner: multisig(),
|
|
124
|
+
projectUri: "future",
|
|
125
|
+
rulesetConfigurations: config,
|
|
126
|
+
terminalConfigurations: new JBTerminalConfig[](0),
|
|
127
|
+
memo: ""
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// @dev Queue a ruleset for a project.
|
|
132
|
+
function _queueRuleset(
|
|
133
|
+
uint256 projectId,
|
|
134
|
+
uint48 mustStartAtOrAfter,
|
|
135
|
+
uint32 duration,
|
|
136
|
+
uint112 weight,
|
|
137
|
+
uint32 weightCutPercent,
|
|
138
|
+
IJBRulesetApprovalHook approvalHook
|
|
139
|
+
)
|
|
140
|
+
internal
|
|
141
|
+
{
|
|
142
|
+
JBRulesetConfig[] memory config = new JBRulesetConfig[](1);
|
|
143
|
+
config[0].mustStartAtOrAfter = mustStartAtOrAfter;
|
|
144
|
+
config[0].duration = duration;
|
|
145
|
+
config[0].weight = weight;
|
|
146
|
+
config[0].weightCutPercent = weightCutPercent;
|
|
147
|
+
config[0].approvalHook = approvalHook;
|
|
148
|
+
config[0].metadata = _metadata;
|
|
149
|
+
config[0].splitGroups = new JBSplitGroup[](0);
|
|
150
|
+
config[0].fundAccessLimitGroups = new JBFundAccessLimitGroup[](0);
|
|
151
|
+
|
|
152
|
+
vm.prank(multisig());
|
|
153
|
+
_controller.queueRulesetsOf(projectId, config, "");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ───────────────────── DURATION CYCLING EDGE CASES
|
|
157
|
+
// ─────────────────────
|
|
158
|
+
|
|
159
|
+
/// @notice Duration=0 means new queued rulesets start immediately.
|
|
160
|
+
function test_durationZero_toNonZero_transition() external {
|
|
161
|
+
uint256 pid = _launchProject(0, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
162
|
+
|
|
163
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
164
|
+
assertEq(current.duration, 0, "Initial should have duration=0");
|
|
165
|
+
assertEq(current.cycleNumber, 1);
|
|
166
|
+
|
|
167
|
+
// Queue with duration=7 days. Since current has duration=0, new one starts immediately.
|
|
168
|
+
_queueRuleset(pid, 0, SEVEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(0)));
|
|
169
|
+
|
|
170
|
+
current = _rulesets.currentOf(pid);
|
|
171
|
+
assertEq(current.weight, INITIAL_WEIGHT * 2, "Duration=0 -> new ruleset immediately current");
|
|
172
|
+
assertEq(current.duration, SEVEN_DAYS, "New duration should be 7 days");
|
|
173
|
+
assertEq(current.cycleNumber, 2, "Should be cycle 2");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// @notice Transitioning from duration>0 to duration=0 stops cycling.
|
|
177
|
+
function test_durationNonZero_toZero_transition() external {
|
|
178
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
179
|
+
|
|
180
|
+
_queueRuleset(pid, 0, 0, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(0)));
|
|
181
|
+
|
|
182
|
+
// Before current ends, original is still active.
|
|
183
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
184
|
+
assertEq(current.weight, INITIAL_WEIGHT, "Original still active");
|
|
185
|
+
|
|
186
|
+
// Warp past current duration.
|
|
187
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
188
|
+
|
|
189
|
+
current = _rulesets.currentOf(pid);
|
|
190
|
+
assertEq(current.weight, INITIAL_WEIGHT * 2, "Duration=0 ruleset now current");
|
|
191
|
+
assertEq(current.duration, 0, "New ruleset has duration=0");
|
|
192
|
+
|
|
193
|
+
// With duration=0, no upcoming.
|
|
194
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
195
|
+
assertEq(upcoming.cycleNumber, 0, "No upcoming when current has duration=0");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/// @notice Very short duration (1 second) should cycle correctly over many periods.
|
|
199
|
+
function test_veryShortDuration_manyCycles() external {
|
|
200
|
+
uint256 pid = _launchProject(1, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
201
|
+
|
|
202
|
+
vm.warp(block.timestamp + 1000);
|
|
203
|
+
|
|
204
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
205
|
+
assertEq(current.cycleNumber, 1001, "Should have cycled 1000 times");
|
|
206
|
+
assertEq(current.weight, INITIAL_WEIGHT, "No cut -> weight unchanged");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/// @notice Very short duration with weight cut: decay accumulates per cycle.
|
|
210
|
+
function test_veryShortDuration_withWeightCut() external {
|
|
211
|
+
uint32 tenPercentCut = uint32(JBConstants.MAX_WEIGHT_CUT_PERCENT / 10);
|
|
212
|
+
uint256 pid = _launchProject(1, INITIAL_WEIGHT, tenPercentCut, IJBRulesetApprovalHook(address(0)));
|
|
213
|
+
|
|
214
|
+
// 10 seconds -> 10 weight cuts.
|
|
215
|
+
vm.warp(block.timestamp + 10);
|
|
216
|
+
|
|
217
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
218
|
+
assertEq(current.cycleNumber, 11, "Cycle 11 after 10 seconds");
|
|
219
|
+
assertLt(current.weight, INITIAL_WEIGHT, "Weight should decrease");
|
|
220
|
+
assertGt(current.weight, 0, "Weight should not be zero after only 10 cuts");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/// @notice Mid-cycle queuing: new ruleset starts at next duration boundary.
|
|
224
|
+
function test_midCycleQueuing_snapsToNextBoundary() external {
|
|
225
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
226
|
+
uint256 originalStart = block.timestamp;
|
|
227
|
+
|
|
228
|
+
// Warp to mid-cycle (day 3 of 7).
|
|
229
|
+
vm.warp(block.timestamp + 3 days);
|
|
230
|
+
|
|
231
|
+
_queueRuleset(pid, 0, SEVEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(0)));
|
|
232
|
+
|
|
233
|
+
// Current should still be the original (we're mid-cycle).
|
|
234
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
235
|
+
assertEq(current.weight, INITIAL_WEIGHT, "Original still current mid-cycle");
|
|
236
|
+
|
|
237
|
+
// Upcoming should start at the next boundary (T+7d).
|
|
238
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
239
|
+
assertEq(upcoming.start, originalStart + SEVEN_DAYS, "Should start at next boundary");
|
|
240
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT * 2, "Queued weight should match");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ───────────────────── APPROVAL HOOK STRESS
|
|
244
|
+
// ─────────────────────
|
|
245
|
+
|
|
246
|
+
/// @notice Queue well before deadline -> approved.
|
|
247
|
+
function test_approvalHook_queueBeforeDeadline_approved() external {
|
|
248
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, _deadline3Day);
|
|
249
|
+
|
|
250
|
+
// Queue immediately: start ≈ T+14d, rulesetId ≈ T. Gap = 14d > 3d -> Approved.
|
|
251
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 2, 0, _deadline3Day);
|
|
252
|
+
|
|
253
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
254
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT * 2, "Queued should be upcoming");
|
|
255
|
+
|
|
256
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
257
|
+
|
|
258
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
259
|
+
assertEq(current.weight, INITIAL_WEIGHT * 2, "Approved ruleset should be current");
|
|
260
|
+
assertEq(current.cycleNumber, 2);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/// @notice Queue too late (past deadline) -> failed -> original rolls over.
|
|
264
|
+
function test_approvalHook_queueTooLate_failsAndRollsOver() external {
|
|
265
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, _deadline3Day);
|
|
266
|
+
|
|
267
|
+
// Warp to day 12 of 14 (only 2 days left, less than 3-day deadline).
|
|
268
|
+
vm.warp(block.timestamp + 12 days);
|
|
269
|
+
|
|
270
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 5, 0, _deadline3Day);
|
|
271
|
+
|
|
272
|
+
// Warp past cycle 1 end.
|
|
273
|
+
vm.warp(block.timestamp + 3 days);
|
|
274
|
+
|
|
275
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
276
|
+
assertEq(current.weight, INITIAL_WEIGHT, "Failed approval -> original rolls over");
|
|
277
|
+
assertEq(current.cycleNumber, 2, "Rolled over to cycle 2");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// @notice Chain of always-failed approvals: original always rolls over.
|
|
281
|
+
function test_approvalHook_chainOfFailures_originalPersists() external {
|
|
282
|
+
MockApprovalHookConfigurable alwaysFail = new MockApprovalHookConfigurable(JBApprovalStatus.Failed, 3 days);
|
|
283
|
+
|
|
284
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(alwaysFail)));
|
|
285
|
+
|
|
286
|
+
// Queue 3 rulesets — all will fail.
|
|
287
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(alwaysFail)));
|
|
288
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 3, 0, IJBRulesetApprovalHook(address(alwaysFail)));
|
|
289
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 4, 0, IJBRulesetApprovalHook(address(alwaysFail)));
|
|
290
|
+
|
|
291
|
+
// Warp far past all possible starts.
|
|
292
|
+
vm.warp(block.timestamp + 100 days);
|
|
293
|
+
|
|
294
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
295
|
+
assertEq(current.weight, INITIAL_WEIGHT, "All failed -> original persists");
|
|
296
|
+
assertGt(current.cycleNumber, 1, "Should have rolled over multiple cycles");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/// @notice H-3 FIX VERIFICATION: Reverting approval hook no longer causes DoS.
|
|
300
|
+
/// The try/catch in _approvalStatusOf catches the revert and returns Failed status,
|
|
301
|
+
/// so currentOf succeeds and falls back to the previous ruleset.
|
|
302
|
+
function test_approvalHook_revert_causesDoS_H3() external {
|
|
303
|
+
RevertingApprovalHook revertHook = new RevertingApprovalHook();
|
|
304
|
+
|
|
305
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(revertHook)));
|
|
306
|
+
|
|
307
|
+
// Queue a new ruleset.
|
|
308
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(revertHook)));
|
|
309
|
+
|
|
310
|
+
// Warp past current cycle so the queued one is checked.
|
|
311
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
312
|
+
|
|
313
|
+
// H-3 FIX: The reverting hook is caught by try/catch, treated as Failed.
|
|
314
|
+
// currentOf succeeds and falls back to the original ruleset (weight not doubled).
|
|
315
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
316
|
+
assertGt(current.id, 0, "currentOf should succeed, not revert");
|
|
317
|
+
assertEq(current.weight, INITIAL_WEIGHT, "Should fall back to original ruleset weight");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/// @notice Approval status transitions from ApprovalExpected to Approved.
|
|
321
|
+
function test_approvalHook_statusTransition_expectedToApproved() external {
|
|
322
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, _deadline3Day);
|
|
323
|
+
|
|
324
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 2, 0, _deadline3Day);
|
|
325
|
+
|
|
326
|
+
// Immediately after queuing: ApprovalExpected (deadline not yet passed).
|
|
327
|
+
(, JBApprovalStatus status) = _rulesets.latestQueuedOf(pid);
|
|
328
|
+
assertEq(
|
|
329
|
+
uint256(status), uint256(JBApprovalStatus.ApprovalExpected), "Should be ApprovalExpected immediately"
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
// Warp past the deadline threshold: need block.timestamp + 3d >= ruleset.start.
|
|
333
|
+
// Ruleset starts at ~T+14d, so we need to be at T+11d or later.
|
|
334
|
+
vm.warp(block.timestamp + 11 days);
|
|
335
|
+
|
|
336
|
+
(, status) = _rulesets.latestQueuedOf(pid);
|
|
337
|
+
assertEq(uint256(status), uint256(JBApprovalStatus.Approved), "Should be Approved after deadline");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ───────────────────── RAPID-FIRE QUEUING
|
|
341
|
+
// ─────────────────────
|
|
342
|
+
|
|
343
|
+
/// @notice 5 rulesets queued in the same block: rulesetIds increment, last one wins.
|
|
344
|
+
function test_sameBlock_fiveQueues_lastOneWins() external {
|
|
345
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
346
|
+
uint256 initialRulesetId = block.timestamp;
|
|
347
|
+
|
|
348
|
+
for (uint256 i = 1; i <= 5; i++) {
|
|
349
|
+
_queueRuleset(
|
|
350
|
+
pid,
|
|
351
|
+
0,
|
|
352
|
+
FOURTEEN_DAYS,
|
|
353
|
+
uint112(INITIAL_WEIGHT + uint112(i) * 100e18),
|
|
354
|
+
0,
|
|
355
|
+
IJBRulesetApprovalHook(address(0))
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Latest should be the 5th (timestamp + 5).
|
|
360
|
+
uint256 latestId = _rulesets.latestRulesetIdOf(pid);
|
|
361
|
+
assertEq(latestId, initialRulesetId + 5, "5th queued should be latest");
|
|
362
|
+
|
|
363
|
+
// Warp past current cycle.
|
|
364
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
365
|
+
|
|
366
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
367
|
+
assertEq(current.weight, INITIAL_WEIGHT + 500e18, "Last queued should be current");
|
|
368
|
+
assertEq(current.cycleNumber, 2);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/// @notice Override a queued ruleset before it starts: replacement takes effect.
|
|
372
|
+
function test_overrideQueued_replacementTakesEffect() external {
|
|
373
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
374
|
+
|
|
375
|
+
// Queue A.
|
|
376
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(0)));
|
|
377
|
+
|
|
378
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
379
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT * 2, "A should be upcoming");
|
|
380
|
+
|
|
381
|
+
// Queue B — overrides A.
|
|
382
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 3, 0, IJBRulesetApprovalHook(address(0)));
|
|
383
|
+
|
|
384
|
+
upcoming = _rulesets.upcomingOf(pid);
|
|
385
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT * 3, "B should override A");
|
|
386
|
+
|
|
387
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
388
|
+
|
|
389
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
390
|
+
assertEq(current.weight, INITIAL_WEIGHT * 3, "B should be current, not A");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ───────────────────── WEIGHT DECAY STRESS
|
|
394
|
+
// ─────────────────────
|
|
395
|
+
|
|
396
|
+
/// @notice 50% weight cut over 100 cycles: weight decays to zero.
|
|
397
|
+
function test_weightDecay_fiftyPercent_100cycles_toZero() external {
|
|
398
|
+
uint32 fiftyPercentCut = uint32(JBConstants.MAX_WEIGHT_CUT_PERCENT / 2);
|
|
399
|
+
uint256 pid = _launchProject(
|
|
400
|
+
SEVEN_DAYS, INITIAL_WEIGHT, fiftyPercentCut, IJBRulesetApprovalHook(address(0))
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
// 100 cycles (700 days). 1000e18 * (0.5^100) ≈ 7.9e-13 -> truncated to 0.
|
|
404
|
+
vm.warp(block.timestamp + 700 days);
|
|
405
|
+
|
|
406
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
407
|
+
assertEq(current.weight, 0, "Weight should decay to 0 after 100 x 50% cuts");
|
|
408
|
+
assertEq(current.cycleNumber, 101);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/// @notice 100% weight cut: weight goes to zero after one cycle.
|
|
412
|
+
function test_weightCut_100percent_zeroAfterOneCycle() external {
|
|
413
|
+
uint32 fullCut = uint32(JBConstants.MAX_WEIGHT_CUT_PERCENT);
|
|
414
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, fullCut, IJBRulesetApprovalHook(address(0)));
|
|
415
|
+
|
|
416
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
417
|
+
|
|
418
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
419
|
+
assertEq(current.weight, 0, "100% cut -> zero weight after one cycle");
|
|
420
|
+
assertEq(current.cycleNumber, 2);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/// @notice 0% weight cut: weight unchanged indefinitely.
|
|
424
|
+
function test_weightCut_zeroPercent_unchangedForever() external {
|
|
425
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
426
|
+
|
|
427
|
+
// 1000 cycles.
|
|
428
|
+
vm.warp(block.timestamp + 7000 days);
|
|
429
|
+
|
|
430
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
431
|
+
assertEq(current.weight, INITIAL_WEIGHT, "0% cut -> weight unchanged");
|
|
432
|
+
assertEq(current.cycleNumber, 1001);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/// @notice weight=1 is a special case that inherits the derived (cut) weight.
|
|
436
|
+
function test_weight_inheritSpecialCase_weight1() external {
|
|
437
|
+
uint32 tenPercentCut = uint32(JBConstants.MAX_WEIGHT_CUT_PERCENT / 10);
|
|
438
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, tenPercentCut, IJBRulesetApprovalHook(address(0)));
|
|
439
|
+
|
|
440
|
+
// Queue with weight=1 -> inherits derived weight (one cut applied).
|
|
441
|
+
_queueRuleset(pid, 0, SEVEN_DAYS, 1, tenPercentCut, IJBRulesetApprovalHook(address(0)));
|
|
442
|
+
|
|
443
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
444
|
+
|
|
445
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
446
|
+
uint256 expectedWeight = (uint256(INITIAL_WEIGHT) * (JBConstants.MAX_WEIGHT_CUT_PERCENT - tenPercentCut))
|
|
447
|
+
/ JBConstants.MAX_WEIGHT_CUT_PERCENT;
|
|
448
|
+
assertEq(current.weight, expectedWeight, "weight=1 should inherit derived weight (one 10% cut)");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ───────────────────── ROLLOVER BEHAVIOR
|
|
452
|
+
// ─────────────────────
|
|
453
|
+
|
|
454
|
+
/// @notice Rollover preserves all rules and applies weight cut per cycle.
|
|
455
|
+
function test_rollover_manyCycles_preservesRules() external {
|
|
456
|
+
uint32 onePercentCut = uint32(JBConstants.MAX_WEIGHT_CUT_PERCENT / 100);
|
|
457
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, onePercentCut, IJBRulesetApprovalHook(address(0)));
|
|
458
|
+
|
|
459
|
+
// 100 cycles (700 days).
|
|
460
|
+
vm.warp(block.timestamp + 700 days);
|
|
461
|
+
|
|
462
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
463
|
+
assertEq(current.cycleNumber, 101, "Cycle 101 after 100 rollovers");
|
|
464
|
+
assertEq(current.duration, SEVEN_DAYS, "Duration preserved");
|
|
465
|
+
assertEq(current.weightCutPercent, onePercentCut, "Weight cut preserved");
|
|
466
|
+
assertLt(current.weight, INITIAL_WEIGHT, "Weight decreased");
|
|
467
|
+
assertGt(current.weight, 0, "Weight not zero after only 1% cuts");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/// @notice After many rollovers, a newly queued ruleset takes effect at the correct boundary.
|
|
471
|
+
function test_rollover_thenQueuedTakesEffect() external {
|
|
472
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
473
|
+
|
|
474
|
+
// Roll over 5 cycles.
|
|
475
|
+
vm.warp(block.timestamp + 5 * uint256(SEVEN_DAYS));
|
|
476
|
+
|
|
477
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
478
|
+
assertEq(current.cycleNumber, 6, "Cycle 6 after 5 rollovers");
|
|
479
|
+
|
|
480
|
+
// Queue new ruleset.
|
|
481
|
+
_queueRuleset(pid, 0, SEVEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(0)));
|
|
482
|
+
|
|
483
|
+
// Warp to next boundary.
|
|
484
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
485
|
+
|
|
486
|
+
current = _rulesets.currentOf(pid);
|
|
487
|
+
assertEq(current.weight, INITIAL_WEIGHT * 2, "Queued ruleset now current");
|
|
488
|
+
assertEq(current.cycleNumber, 7, "Cycle number continues from rollover");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/// @notice After failed approval, original rolls over (not the failed one).
|
|
492
|
+
function test_rollover_afterFailedApproval_originalPersists() external {
|
|
493
|
+
MockApprovalHookConfigurable alwaysFail = new MockApprovalHookConfigurable(JBApprovalStatus.Failed, 3 days);
|
|
494
|
+
|
|
495
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(alwaysFail)));
|
|
496
|
+
|
|
497
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 5, 0, IJBRulesetApprovalHook(address(alwaysFail)));
|
|
498
|
+
|
|
499
|
+
// Warp past 3 cycles.
|
|
500
|
+
vm.warp(block.timestamp + 3 * uint256(FOURTEEN_DAYS));
|
|
501
|
+
|
|
502
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
503
|
+
assertEq(current.weight, INITIAL_WEIGHT, "Original rolls over after failed approval");
|
|
504
|
+
assertGt(current.cycleNumber, 1, "Rolled over past cycle 1");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ───────────────────── START TIME EDGE CASES
|
|
508
|
+
// ─────────────────────
|
|
509
|
+
|
|
510
|
+
/// @notice mustStartAtOrAfter in distant future: current doesn't change until then.
|
|
511
|
+
function test_distantFuture_start_currentUnchanged() external {
|
|
512
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
513
|
+
|
|
514
|
+
// Queue with start 365 days from now.
|
|
515
|
+
_queueRuleset(
|
|
516
|
+
pid,
|
|
517
|
+
uint48(block.timestamp + 365 days),
|
|
518
|
+
SEVEN_DAYS,
|
|
519
|
+
INITIAL_WEIGHT * 2,
|
|
520
|
+
0,
|
|
521
|
+
IJBRulesetApprovalHook(address(0))
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
// Current should still be original.
|
|
525
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
526
|
+
assertEq(current.weight, INITIAL_WEIGHT, "Current unchanged until distant future");
|
|
527
|
+
|
|
528
|
+
// Upcoming should be the rolled-over original (not the distant future one).
|
|
529
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
530
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT, "Upcoming is rolled-over, not distant future");
|
|
531
|
+
|
|
532
|
+
// Now warp close to 365 days.
|
|
533
|
+
vm.warp(block.timestamp + 365 days);
|
|
534
|
+
|
|
535
|
+
// The distant future ruleset should now be upcoming.
|
|
536
|
+
upcoming = _rulesets.upcomingOf(pid);
|
|
537
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT * 2, "Distant future ruleset now upcoming");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/// @notice deriveStartFrom at exact duration boundary: starts at that boundary.
|
|
541
|
+
function test_deriveStartFrom_exactBoundary() external {
|
|
542
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
543
|
+
uint256 originalStart = block.timestamp;
|
|
544
|
+
|
|
545
|
+
_queueRuleset(
|
|
546
|
+
pid,
|
|
547
|
+
uint48(originalStart + SEVEN_DAYS),
|
|
548
|
+
SEVEN_DAYS,
|
|
549
|
+
INITIAL_WEIGHT * 2,
|
|
550
|
+
0,
|
|
551
|
+
IJBRulesetApprovalHook(address(0))
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
555
|
+
assertEq(upcoming.start, originalStart + SEVEN_DAYS, "Should start exactly at boundary");
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/// @notice deriveStartFrom one second after boundary: snaps to next boundary.
|
|
559
|
+
function test_deriveStartFrom_oneSecondAfterBoundary_snapsToNext() external {
|
|
560
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
561
|
+
uint256 originalStart = block.timestamp;
|
|
562
|
+
|
|
563
|
+
// 1 second after first boundary -> should snap to second boundary.
|
|
564
|
+
_queueRuleset(
|
|
565
|
+
pid,
|
|
566
|
+
uint48(originalStart + SEVEN_DAYS + 1),
|
|
567
|
+
SEVEN_DAYS,
|
|
568
|
+
INITIAL_WEIGHT * 2,
|
|
569
|
+
0,
|
|
570
|
+
IJBRulesetApprovalHook(address(0))
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
// At T=0, upcomingOf returns the simulated rolled-over cycle (T+7d) since the
|
|
574
|
+
// queued ruleset (T+14d) is more than one duration away. Warp to T+7d first.
|
|
575
|
+
vm.warp(originalStart + SEVEN_DAYS);
|
|
576
|
+
|
|
577
|
+
JBRuleset memory upcoming = _rulesets.upcomingOf(pid);
|
|
578
|
+
assertEq(upcoming.start, originalStart + 2 * uint256(SEVEN_DAYS), "Should snap to next boundary");
|
|
579
|
+
assertEq(upcoming.weight, INITIAL_WEIGHT * 2, "Should be the queued ruleset");
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/// @notice currentOf returns empty when the only ruleset hasn't started yet.
|
|
583
|
+
function test_currentOf_onlyRulesetNotStarted_returnsEmpty() external {
|
|
584
|
+
uint256 pid = _launchProjectFutureStart(uint48(block.timestamp + 30 days), SEVEN_DAYS, INITIAL_WEIGHT);
|
|
585
|
+
|
|
586
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
587
|
+
assertEq(current.cycleNumber, 0, "No current when only ruleset hasn't started");
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ───────────────────── COMPLEX MULTI-QUEUE SCENARIOS
|
|
591
|
+
// ─────────────────────
|
|
592
|
+
|
|
593
|
+
/// @notice 10 sequential rulesets: each queued, time advanced, verified as current.
|
|
594
|
+
function test_tenSequentialRulesets() external {
|
|
595
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
596
|
+
|
|
597
|
+
for (uint256 i = 1; i <= 10; i++) {
|
|
598
|
+
_queueRuleset(
|
|
599
|
+
pid,
|
|
600
|
+
0,
|
|
601
|
+
SEVEN_DAYS,
|
|
602
|
+
uint112(INITIAL_WEIGHT + uint112(i) * 100e18),
|
|
603
|
+
0,
|
|
604
|
+
IJBRulesetApprovalHook(address(0))
|
|
605
|
+
);
|
|
606
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
607
|
+
|
|
608
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
609
|
+
assertEq(
|
|
610
|
+
current.weight,
|
|
611
|
+
INITIAL_WEIGHT + uint112(i) * 100e18,
|
|
612
|
+
string.concat("Cycle ", vm.toString(i + 1), " weight mismatch")
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
JBRuleset memory finalRuleset = _rulesets.currentOf(pid);
|
|
617
|
+
assertEq(finalRuleset.cycleNumber, 11, "Should be cycle 11");
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/// @notice Interleaved approve/fail: approved ruleset persists through failed attempts.
|
|
621
|
+
function test_interleavedApproveAndFail() external {
|
|
622
|
+
uint256 pid = _launchProject(FOURTEEN_DAYS, INITIAL_WEIGHT, 0, _deadline3Day);
|
|
623
|
+
|
|
624
|
+
// Queue first — queued at T, starts at ~T+14d. Gap = 14d > 3d -> Approved.
|
|
625
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, uint112(2000e18), 0, _deadline3Day);
|
|
626
|
+
|
|
627
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
628
|
+
|
|
629
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
630
|
+
assertEq(current.weight, 2000e18, "First approved ruleset is current");
|
|
631
|
+
|
|
632
|
+
// Queue second too late (only 2 days before end).
|
|
633
|
+
vm.warp(block.timestamp + 12 days);
|
|
634
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, uint112(3000e18), 0, _deadline3Day);
|
|
635
|
+
|
|
636
|
+
vm.warp(block.timestamp + 3 days);
|
|
637
|
+
|
|
638
|
+
current = _rulesets.currentOf(pid);
|
|
639
|
+
assertEq(current.weight, 2000e18, "Failed attempt -> last approved rolls over");
|
|
640
|
+
assertEq(current.cycleNumber, 3, "Cycle 3");
|
|
641
|
+
|
|
642
|
+
// Queue third with enough time (queued at start of cycle 3, >3 days before end).
|
|
643
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, uint112(4000e18), 0, _deadline3Day);
|
|
644
|
+
|
|
645
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
646
|
+
|
|
647
|
+
current = _rulesets.currentOf(pid);
|
|
648
|
+
assertEq(current.weight, 4000e18, "Third queued approved and current");
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/// @notice Duration change mid-queue: start with 7d, queue 14d, then queue 1d.
|
|
652
|
+
function test_multipleQueuedDurationChanges() external {
|
|
653
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
654
|
+
|
|
655
|
+
// Queue 14-day ruleset.
|
|
656
|
+
_queueRuleset(pid, 0, FOURTEEN_DAYS, INITIAL_WEIGHT * 2, 0, IJBRulesetApprovalHook(address(0)));
|
|
657
|
+
|
|
658
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
659
|
+
|
|
660
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
661
|
+
assertEq(current.weight, INITIAL_WEIGHT * 2, "14-day ruleset now current");
|
|
662
|
+
assertEq(current.duration, FOURTEEN_DAYS);
|
|
663
|
+
|
|
664
|
+
// Queue 1-day ruleset.
|
|
665
|
+
_queueRuleset(pid, 0, 1 days, INITIAL_WEIGHT * 3, 0, IJBRulesetApprovalHook(address(0)));
|
|
666
|
+
|
|
667
|
+
vm.warp(block.timestamp + FOURTEEN_DAYS);
|
|
668
|
+
|
|
669
|
+
current = _rulesets.currentOf(pid);
|
|
670
|
+
assertEq(current.weight, INITIAL_WEIGHT * 3, "1-day ruleset now current");
|
|
671
|
+
assertEq(current.duration, 1 days);
|
|
672
|
+
|
|
673
|
+
// Verify rapid cycling with the 1-day ruleset.
|
|
674
|
+
vm.warp(block.timestamp + 5 days);
|
|
675
|
+
current = _rulesets.currentOf(pid);
|
|
676
|
+
assertEq(current.cycleNumber, current.cycleNumber, "Should have cycled 5 more times");
|
|
677
|
+
assertGt(current.cycleNumber, 3, "Cycle number should advance with 1-day duration");
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ───────────────────── FUZZ: CYCLE NUMBER CONSISTENCY
|
|
681
|
+
// ─────────────────────
|
|
682
|
+
|
|
683
|
+
/// @notice Fuzz: cycle number always equals elapsed cycles + 1.
|
|
684
|
+
function testFuzz_cycleNumber_consistency(uint16 numCycles) external {
|
|
685
|
+
numCycles = uint16(bound(numCycles, 1, 500));
|
|
686
|
+
|
|
687
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
688
|
+
|
|
689
|
+
vm.warp(block.timestamp + uint256(numCycles) * uint256(SEVEN_DAYS));
|
|
690
|
+
|
|
691
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
692
|
+
assertEq(current.cycleNumber, uint256(numCycles) + 1, "Cycle number should be elapsed + 1");
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/// @notice Fuzz: weight decay is monotonically non-increasing.
|
|
696
|
+
function testFuzz_weightDecay_monotonic(uint8 numCycles, uint32 cutPercent) external {
|
|
697
|
+
numCycles = uint8(bound(numCycles, 1, 50));
|
|
698
|
+
cutPercent = uint32(bound(cutPercent, 1, JBConstants.MAX_WEIGHT_CUT_PERCENT));
|
|
699
|
+
|
|
700
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, cutPercent, IJBRulesetApprovalHook(address(0)));
|
|
701
|
+
|
|
702
|
+
uint256 prevWeight = INITIAL_WEIGHT;
|
|
703
|
+
for (uint256 i = 1; i <= numCycles; i++) {
|
|
704
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
705
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
706
|
+
assertLe(current.weight, prevWeight, "Weight should never increase");
|
|
707
|
+
prevWeight = current.weight;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/// @notice Fuzz: deriveStartFrom always returns a value >= mustStartAtOrAfter and aligned to duration.
|
|
712
|
+
function testFuzz_deriveStartFrom_alignment(uint48 baseStart, uint32 duration, uint48 mustStartAfter) external {
|
|
713
|
+
// Bound to reasonable values.
|
|
714
|
+
baseStart = uint48(bound(baseStart, 1, type(uint48).max / 2));
|
|
715
|
+
duration = uint32(bound(duration, 1, type(uint32).max / 2));
|
|
716
|
+
mustStartAfter = uint48(bound(mustStartAfter, baseStart, baseStart + 100 * uint256(duration)));
|
|
717
|
+
|
|
718
|
+
uint256 start = _rulesets.deriveStartFrom(baseStart, duration, mustStartAfter);
|
|
719
|
+
|
|
720
|
+
// Must be >= mustStartAtOrAfter.
|
|
721
|
+
assertGe(start, mustStartAfter, "Start should be >= mustStartAtOrAfter");
|
|
722
|
+
|
|
723
|
+
// Must be aligned to duration boundaries from baseStart.
|
|
724
|
+
assertEq((start - baseStart) % duration, 0, "Start should be aligned to duration from baseStart");
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// ───────────────────── EDGE: ZERO WEIGHT AFTER DECAY
|
|
728
|
+
// ─────────────────────
|
|
729
|
+
|
|
730
|
+
/// @notice After weight decays to zero, currentOf still works and returns weight=0.
|
|
731
|
+
function test_zeroWeight_currentOfStillWorks() external {
|
|
732
|
+
uint32 fullCut = uint32(JBConstants.MAX_WEIGHT_CUT_PERCENT);
|
|
733
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, fullCut, IJBRulesetApprovalHook(address(0)));
|
|
734
|
+
|
|
735
|
+
// Warp 100 cycles past zero.
|
|
736
|
+
vm.warp(block.timestamp + 700 days);
|
|
737
|
+
|
|
738
|
+
JBRuleset memory current = _rulesets.currentOf(pid);
|
|
739
|
+
assertEq(current.weight, 0, "Weight is zero");
|
|
740
|
+
assertEq(current.cycleNumber, 101, "Still cycling correctly");
|
|
741
|
+
|
|
742
|
+
// Queue a new ruleset with explicit weight.
|
|
743
|
+
_queueRuleset(pid, 0, SEVEN_DAYS, 500e18, 0, IJBRulesetApprovalHook(address(0)));
|
|
744
|
+
|
|
745
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
746
|
+
|
|
747
|
+
current = _rulesets.currentOf(pid);
|
|
748
|
+
assertEq(current.weight, 500e18, "New weight should override zero");
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/// @notice allOf returns the correct chain of rulesets after many queues.
|
|
752
|
+
function test_allOf_chainIntegrity_afterManyQueues() external {
|
|
753
|
+
uint256 pid = _launchProject(SEVEN_DAYS, INITIAL_WEIGHT, 0, IJBRulesetApprovalHook(address(0)));
|
|
754
|
+
|
|
755
|
+
// Queue 4 more rulesets.
|
|
756
|
+
for (uint256 i = 1; i <= 4; i++) {
|
|
757
|
+
_queueRuleset(
|
|
758
|
+
pid,
|
|
759
|
+
0,
|
|
760
|
+
SEVEN_DAYS,
|
|
761
|
+
uint112(INITIAL_WEIGHT + uint112(i) * 100e18),
|
|
762
|
+
0,
|
|
763
|
+
IJBRulesetApprovalHook(address(0))
|
|
764
|
+
);
|
|
765
|
+
vm.warp(block.timestamp + SEVEN_DAYS);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Get all rulesets (5 total).
|
|
769
|
+
JBRuleset[] memory all = _rulesets.allOf(pid, 0, 10);
|
|
770
|
+
assertEq(all.length, 5, "Should have 5 rulesets in chain");
|
|
771
|
+
|
|
772
|
+
// Verify descending order (latest first).
|
|
773
|
+
for (uint256 i = 0; i < all.length - 1; i++) {
|
|
774
|
+
assertGt(all[i].id, all[i + 1].id, "Should be in descending ID order");
|
|
775
|
+
assertEq(all[i].basedOnId, all[i + 1].id, "basedOnId should link to previous");
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|