@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.
Files changed (436) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/SKILLS.md +151 -0
  4. package/docs/book.css +13 -0
  5. package/docs/book.toml +12 -0
  6. package/docs/solidity.min.js +74 -0
  7. package/docs/src/README.md +703 -0
  8. package/docs/src/SUMMARY.md +94 -0
  9. package/docs/src/src/JBChainlinkV3PriceFeed.sol/contract.JBChainlinkV3PriceFeed.md +83 -0
  10. package/docs/src/src/JBChainlinkV3SequencerPriceFeed.sol/contract.JBChainlinkV3SequencerPriceFeed.md +88 -0
  11. package/docs/src/src/JBController.sol/contract.JBController.md +1121 -0
  12. package/docs/src/src/JBDeadline.sol/contract.JBDeadline.md +84 -0
  13. package/docs/src/src/JBDirectory.sol/contract.JBDirectory.md +294 -0
  14. package/docs/src/src/JBERC20.sol/contract.JBERC20.md +190 -0
  15. package/docs/src/src/JBFeelessAddresses.sol/contract.JBFeelessAddresses.md +80 -0
  16. package/docs/src/src/JBFundAccessLimits.sol/contract.JBFundAccessLimits.md +253 -0
  17. package/docs/src/src/JBMultiTerminal.sol/contract.JBMultiTerminal.md +1472 -0
  18. package/docs/src/src/JBPermissions.sol/contract.JBPermissions.md +199 -0
  19. package/docs/src/src/JBPrices.sol/contract.JBPrices.md +154 -0
  20. package/docs/src/src/JBProjects.sol/contract.JBProjects.md +131 -0
  21. package/docs/src/src/JBRulesets.sol/contract.JBRulesets.md +677 -0
  22. package/docs/src/src/JBSplits.sol/contract.JBSplits.md +237 -0
  23. package/docs/src/src/JBTerminalStore.sol/contract.JBTerminalStore.md +591 -0
  24. package/docs/src/src/JBTokens.sol/contract.JBTokens.md +353 -0
  25. package/docs/src/src/README.md +25 -0
  26. package/docs/src/src/abstract/JBControlled.sol/abstract.JBControlled.md +64 -0
  27. package/docs/src/src/abstract/JBPermissioned.sol/abstract.JBPermissioned.md +84 -0
  28. package/docs/src/src/abstract/README.md +5 -0
  29. package/docs/src/src/enums/JBApprovalStatus.sol/enum.JBApprovalStatus.md +17 -0
  30. package/docs/src/src/enums/README.md +4 -0
  31. package/docs/src/src/interfaces/IJBCashOutHook.sol/interface.IJBCashOutHook.md +29 -0
  32. package/docs/src/src/interfaces/IJBCashOutTerminal.sol/interface.IJBCashOutTerminal.md +57 -0
  33. package/docs/src/src/interfaces/IJBControlled.sol/interface.IJBControlled.md +12 -0
  34. package/docs/src/src/interfaces/IJBController.sol/interface.IJBController.md +334 -0
  35. package/docs/src/src/interfaces/IJBDirectory.sol/interface.IJBDirectory.md +108 -0
  36. package/docs/src/src/interfaces/IJBDirectoryAccessControl.sol/interface.IJBDirectoryAccessControl.md +19 -0
  37. package/docs/src/src/interfaces/IJBFeeTerminal.sol/interface.IJBFeeTerminal.md +91 -0
  38. package/docs/src/src/interfaces/IJBFeelessAddresses.sol/interface.IJBFeelessAddresses.md +26 -0
  39. package/docs/src/src/interfaces/IJBFundAccessLimits.sol/interface.IJBFundAccessLimits.md +88 -0
  40. package/docs/src/src/interfaces/IJBMigratable.sol/interface.IJBMigratable.md +29 -0
  41. package/docs/src/src/interfaces/IJBMultiTerminal.sol/interface.IJBMultiTerminal.md +50 -0
  42. package/docs/src/src/interfaces/IJBPayHook.sol/interface.IJBPayHook.md +28 -0
  43. package/docs/src/src/interfaces/IJBPayoutTerminal.sol/interface.IJBPayoutTerminal.md +105 -0
  44. package/docs/src/src/interfaces/IJBPermissioned.sol/interface.IJBPermissioned.md +12 -0
  45. package/docs/src/src/interfaces/IJBPermissions.sol/interface.IJBPermissions.md +74 -0
  46. package/docs/src/src/interfaces/IJBPermitTerminal.sol/interface.IJBPermitTerminal.md +15 -0
  47. package/docs/src/src/interfaces/IJBPriceFeed.sol/interface.IJBPriceFeed.md +12 -0
  48. package/docs/src/src/interfaces/IJBPrices.sol/interface.IJBPrices.md +74 -0
  49. package/docs/src/src/interfaces/IJBProjectUriRegistry.sol/interface.IJBProjectUriRegistry.md +19 -0
  50. package/docs/src/src/interfaces/IJBProjects.sol/interface.IJBProjects.md +49 -0
  51. package/docs/src/src/interfaces/IJBRulesetApprovalHook.sol/interface.IJBRulesetApprovalHook.md +35 -0
  52. package/docs/src/src/interfaces/IJBRulesetDataHook.sol/interface.IJBRulesetDataHook.md +97 -0
  53. package/docs/src/src/interfaces/IJBRulesets.sol/interface.IJBRulesets.md +165 -0
  54. package/docs/src/src/interfaces/IJBSplitHook.sol/interface.IJBSplitHook.md +31 -0
  55. package/docs/src/src/interfaces/IJBSplits.sol/interface.IJBSplits.md +35 -0
  56. package/docs/src/src/interfaces/IJBTerminal.sol/interface.IJBTerminal.md +141 -0
  57. package/docs/src/src/interfaces/IJBTerminalStore.sol/interface.IJBTerminalStore.md +198 -0
  58. package/docs/src/src/interfaces/IJBToken.sol/interface.IJBToken.md +54 -0
  59. package/docs/src/src/interfaces/IJBTokenUriResolver.sol/interface.IJBTokenUriResolver.md +12 -0
  60. package/docs/src/src/interfaces/IJBTokens.sol/interface.IJBTokens.md +151 -0
  61. package/docs/src/src/interfaces/README.md +33 -0
  62. package/docs/src/src/libraries/JBCashOuts.sol/library.JBCashOuts.md +40 -0
  63. package/docs/src/src/libraries/JBConstants.sol/library.JBConstants.md +52 -0
  64. package/docs/src/src/libraries/JBCurrencyIds.sol/library.JBCurrencyIds.md +19 -0
  65. package/docs/src/src/libraries/JBFees.sol/library.JBFees.md +52 -0
  66. package/docs/src/src/libraries/JBFixedPointNumber.sol/library.JBFixedPointNumber.md +12 -0
  67. package/docs/src/src/libraries/JBMetadataResolver.sol/library.JBMetadataResolver.md +242 -0
  68. package/docs/src/src/libraries/JBRulesetMetadataResolver.sol/library.JBRulesetMetadataResolver.md +180 -0
  69. package/docs/src/src/libraries/JBSplitGroupIds.sol/library.JBSplitGroupIds.md +14 -0
  70. package/docs/src/src/libraries/JBSurplus.sol/library.JBSurplus.md +44 -0
  71. package/docs/src/src/libraries/README.md +12 -0
  72. package/docs/src/src/periphery/JBDeadline1Day.sol/contract.JBDeadline1Day.md +15 -0
  73. package/docs/src/src/periphery/JBDeadline3Days.sol/contract.JBDeadline3Days.md +15 -0
  74. package/docs/src/src/periphery/JBDeadline3Hours.sol/contract.JBDeadline3Hours.md +15 -0
  75. package/docs/src/src/periphery/JBDeadline7Days.sol/contract.JBDeadline7Days.md +15 -0
  76. package/docs/src/src/periphery/JBMatchingPriceFeed.sol/contract.JBMatchingPriceFeed.md +22 -0
  77. package/docs/src/src/periphery/README.md +8 -0
  78. package/docs/src/src/structs/JBAccountingContext.sol/struct.JBAccountingContext.md +20 -0
  79. package/docs/src/src/structs/JBAfterCashOutRecordedContext.sol/struct.JBAfterCashOutRecordedContext.md +43 -0
  80. package/docs/src/src/structs/JBAfterPayRecordedContext.sol/struct.JBAfterPayRecordedContext.md +42 -0
  81. package/docs/src/src/structs/JBBeforeCashOutRecordedContext.sol/struct.JBBeforeCashOutRecordedContext.md +45 -0
  82. package/docs/src/src/structs/JBBeforePayRecordedContext.sol/struct.JBBeforePayRecordedContext.md +41 -0
  83. package/docs/src/src/structs/JBCashOutHookSpecification.sol/struct.JBCashOutHookSpecification.md +22 -0
  84. package/docs/src/src/structs/JBCurrencyAmount.sol/struct.JBCurrencyAmount.md +17 -0
  85. package/docs/src/src/structs/JBFee.sol/struct.JBFee.md +20 -0
  86. package/docs/src/src/structs/JBFundAccessLimitGroup.sol/struct.JBFundAccessLimitGroup.md +39 -0
  87. package/docs/src/src/structs/JBPayHookSpecification.sol/struct.JBPayHookSpecification.md +22 -0
  88. package/docs/src/src/structs/JBPermissionsData.sol/struct.JBPermissionsData.md +21 -0
  89. package/docs/src/src/structs/JBRuleset.sol/struct.JBRuleset.md +55 -0
  90. package/docs/src/src/structs/JBRulesetConfig.sol/struct.JBRulesetConfig.md +51 -0
  91. package/docs/src/src/structs/JBRulesetMetadata.sol/struct.JBRulesetMetadata.md +79 -0
  92. package/docs/src/src/structs/JBRulesetWeightCache.sol/struct.JBRulesetWeightCache.md +16 -0
  93. package/docs/src/src/structs/JBRulesetWithMetadata.sol/struct.JBRulesetWithMetadata.md +16 -0
  94. package/docs/src/src/structs/JBSingleAllowance.sol/struct.JBSingleAllowance.md +26 -0
  95. package/docs/src/src/structs/JBSplit.sol/struct.JBSplit.md +49 -0
  96. package/docs/src/src/structs/JBSplitGroup.sol/struct.JBSplitGroup.md +17 -0
  97. package/docs/src/src/structs/JBSplitHookContext.sol/struct.JBSplitHookContext.md +29 -0
  98. package/docs/src/src/structs/JBTerminalConfig.sol/struct.JBTerminalConfig.md +16 -0
  99. package/docs/src/src/structs/JBTokenAmount.sol/struct.JBTokenAmount.md +23 -0
  100. package/docs/src/src/structs/README.md +25 -0
  101. package/foundry.lock +11 -0
  102. package/foundry.toml +41 -0
  103. package/package.json +38 -0
  104. package/remappings.txt +1 -0
  105. package/script/Deploy.s.sol +111 -0
  106. package/script/DeployPeriphery.s.sol +287 -0
  107. package/script/helpers/CoreDeploymentLib.sol +121 -0
  108. package/slither-ci.config.json +10 -0
  109. package/sphinx.lock +507 -0
  110. package/src/JBChainlinkV3PriceFeed.sol +77 -0
  111. package/src/JBChainlinkV3SequencerPriceFeed.sol +75 -0
  112. package/src/JBController.sol +1186 -0
  113. package/src/JBDeadline.sol +73 -0
  114. package/src/JBDirectory.sol +343 -0
  115. package/src/JBERC20.sol +131 -0
  116. package/src/JBFeelessAddresses.sol +54 -0
  117. package/src/JBFundAccessLimits.sol +308 -0
  118. package/src/JBMultiTerminal.sol +2024 -0
  119. package/src/JBPermissions.sol +252 -0
  120. package/src/JBPrices.sol +227 -0
  121. package/src/JBProjects.sol +126 -0
  122. package/src/JBRulesets.sol +1093 -0
  123. package/src/JBSplits.sol +324 -0
  124. package/src/JBTerminalStore.sol +908 -0
  125. package/src/JBTokens.sol +376 -0
  126. package/src/abstract/JBControlled.sol +48 -0
  127. package/src/abstract/JBPermissioned.sol +77 -0
  128. package/src/enums/JBApprovalStatus.sol +12 -0
  129. package/src/interfaces/IJBCashOutHook.sol +15 -0
  130. package/src/interfaces/IJBCashOutTerminal.sol +51 -0
  131. package/src/interfaces/IJBControlled.sol +10 -0
  132. package/src/interfaces/IJBController.sol +280 -0
  133. package/src/interfaces/IJBDirectory.sol +69 -0
  134. package/src/interfaces/IJBDirectoryAccessControl.sol +15 -0
  135. package/src/interfaces/IJBFeeTerminal.sol +61 -0
  136. package/src/interfaces/IJBFeelessAddresses.sol +17 -0
  137. package/src/interfaces/IJBFundAccessLimits.sol +94 -0
  138. package/src/interfaces/IJBMigratable.sol +24 -0
  139. package/src/interfaces/IJBMultiTerminal.sol +36 -0
  140. package/src/interfaces/IJBPayHook.sol +14 -0
  141. package/src/interfaces/IJBPayoutTerminal.sol +92 -0
  142. package/src/interfaces/IJBPermissioned.sol +10 -0
  143. package/src/interfaces/IJBPermissions.sol +71 -0
  144. package/src/interfaces/IJBPermitTerminal.sol +14 -0
  145. package/src/interfaces/IJBPriceFeed.sol +10 -0
  146. package/src/interfaces/IJBPrices.sol +65 -0
  147. package/src/interfaces/IJBProjectUriRegistry.sol +15 -0
  148. package/src/interfaces/IJBProjects.sol +27 -0
  149. package/src/interfaces/IJBRulesetApprovalHook.sol +21 -0
  150. package/src/interfaces/IJBRulesetDataHook.sol +56 -0
  151. package/src/interfaces/IJBRulesets.sol +151 -0
  152. package/src/interfaces/IJBSplitHook.sol +16 -0
  153. package/src/interfaces/IJBSplits.sol +28 -0
  154. package/src/interfaces/IJBTerminal.sol +120 -0
  155. package/src/interfaces/IJBTerminalStore.sol +225 -0
  156. package/src/interfaces/IJBToken.sol +39 -0
  157. package/src/interfaces/IJBTokenUriResolver.sol +10 -0
  158. package/src/interfaces/IJBTokens.sol +113 -0
  159. package/src/libraries/JBCashOuts.sol +120 -0
  160. package/src/libraries/JBConstants.sol +14 -0
  161. package/src/libraries/JBCurrencyIds.sol +7 -0
  162. package/src/libraries/JBFees.sol +28 -0
  163. package/src/libraries/JBFixedPointNumber.sol +12 -0
  164. package/src/libraries/JBMetadataResolver.sol +306 -0
  165. package/src/libraries/JBRulesetMetadataResolver.sol +160 -0
  166. package/src/libraries/JBSplitGroupIds.sol +7 -0
  167. package/src/libraries/JBSurplus.sol +40 -0
  168. package/src/periphery/JBDeadline1Day.sol +8 -0
  169. package/src/periphery/JBDeadline3Days.sol +8 -0
  170. package/src/periphery/JBDeadline3Hours.sol +8 -0
  171. package/src/periphery/JBDeadline7Days.sol +8 -0
  172. package/src/periphery/JBMatchingPriceFeed.sol +13 -0
  173. package/src/structs/JBAccountingContext.sol +12 -0
  174. package/src/structs/JBAfterCashOutRecordedContext.sol +30 -0
  175. package/src/structs/JBAfterPayRecordedContext.sol +29 -0
  176. package/src/structs/JBBeforeCashOutRecordedContext.sol +31 -0
  177. package/src/structs/JBBeforePayRecordedContext.sol +28 -0
  178. package/src/structs/JBCashOutHookSpecification.sol +15 -0
  179. package/src/structs/JBCurrencyAmount.sol +10 -0
  180. package/src/structs/JBFee.sol +12 -0
  181. package/src/structs/JBFundAccessLimitGroup.sol +28 -0
  182. package/src/structs/JBPayHookSpecification.sol +15 -0
  183. package/src/structs/JBPermissionsData.sol +13 -0
  184. package/src/structs/JBRuleset.sol +42 -0
  185. package/src/structs/JBRulesetConfig.sol +43 -0
  186. package/src/structs/JBRulesetMetadata.sol +56 -0
  187. package/src/structs/JBRulesetWeightCache.sol +9 -0
  188. package/src/structs/JBRulesetWithMetadata.sol +12 -0
  189. package/src/structs/JBSingleAllowance.sol +16 -0
  190. package/src/structs/JBSplit.sol +37 -0
  191. package/src/structs/JBSplitGroup.sol +12 -0
  192. package/src/structs/JBSplitHookContext.sol +20 -0
  193. package/src/structs/JBTerminalConfig.sol +12 -0
  194. package/src/structs/JBTokenAmount.sol +14 -0
  195. package/test/AuditExploits.t.sol +2710 -0
  196. package/test/ComprehensiveInvariant.t.sol +298 -0
  197. package/test/EconomicSimulation.t.sol +340 -0
  198. package/test/EntryPointPermutations.t.sol +671 -0
  199. package/test/FlashLoanAttacks.t.sol +792 -0
  200. package/test/PermissionEscalation.t.sol +679 -0
  201. package/test/RulesetTransitions.t.sol +699 -0
  202. package/test/SplitLoopTests.t.sol +731 -0
  203. package/test/TestAccessToFunds.sol +2644 -0
  204. package/test/TestCashOut.sol +185 -0
  205. package/test/TestCashOutCountFor.sol +272 -0
  206. package/test/TestCashOutHooks.sol +317 -0
  207. package/test/TestCashOutTimingEdge.sol +229 -0
  208. package/test/TestDurationUnderflow.sol +220 -0
  209. package/test/TestFeeProcessingFailure.sol +208 -0
  210. package/test/TestFees.sol +604 -0
  211. package/test/TestInterfaceSupport.sol +62 -0
  212. package/test/TestJBERC20Inheritance.sol +91 -0
  213. package/test/TestLaunchProject.sol +176 -0
  214. package/test/TestMetaTx.sol +203 -0
  215. package/test/TestMetadataParserLib.sol +438 -0
  216. package/test/TestMigrationHeldFees.sol +249 -0
  217. package/test/TestMintTokensOf.sol +172 -0
  218. package/test/TestMultiTokenSurplus.sol +206 -0
  219. package/test/TestMultipleAccessLimits.sol +642 -0
  220. package/test/TestPayBurnRedeemFlow.sol +180 -0
  221. package/test/TestPayHooks.sol +190 -0
  222. package/test/TestPermissions.sol +305 -0
  223. package/test/TestPermissionsEdge.sol +286 -0
  224. package/test/TestPermit2Terminal.sol +339 -0
  225. package/test/TestRulesetQueueing.sol +1001 -0
  226. package/test/TestRulesetQueuingStress.sol +778 -0
  227. package/test/TestRulesetWeightCaching.sol +177 -0
  228. package/test/TestSplits.sol +369 -0
  229. package/test/TestTerminalMigration.sol +167 -0
  230. package/test/TestTokenFlow.sol +174 -0
  231. package/test/WeirdTokenTests.t.sol +764 -0
  232. package/test/formal/BondingCurveProperties.t.sol +411 -0
  233. package/test/formal/FeeProperties.t.sol +246 -0
  234. package/test/helpers/JBTest.sol +129 -0
  235. package/test/helpers/MetadataResolverHelper.sol +116 -0
  236. package/test/helpers/TestBaseWorkflow.sol +317 -0
  237. package/test/invariants/Phase3DeepInvariant.t.sol +404 -0
  238. package/test/invariants/RulesetsInvariant.t.sol +115 -0
  239. package/test/invariants/TerminalStoreInvariant.t.sol +220 -0
  240. package/test/invariants/TokensInvariant.t.sol +184 -0
  241. package/test/invariants/handlers/ComprehensiveHandler.sol +285 -0
  242. package/test/invariants/handlers/EconomicHandler.sol +347 -0
  243. package/test/invariants/handlers/Phase3Handler.sol +414 -0
  244. package/test/invariants/handlers/RulesetsHandler.sol +111 -0
  245. package/test/invariants/handlers/TerminalStoreHandler.sol +146 -0
  246. package/test/invariants/handlers/TokensHandler.sol +127 -0
  247. package/test/mock/ERC2771ForwarderMock.sol +37 -0
  248. package/test/mock/MockERC20.sol +18 -0
  249. package/test/mock/MockMaliciousBeneficiary.sol +67 -0
  250. package/test/mock/MockMaliciousSplitHook.sol +42 -0
  251. package/test/mock/MockPriceFeed.sol +20 -0
  252. package/test/trees/JBController/burnTokensOf.tree +9 -0
  253. package/test/trees/JBController/claimTokensFor.tree +5 -0
  254. package/test/trees/JBController/deployERC20For.tree +5 -0
  255. package/test/trees/JBController/getRulesetOf.tree +5 -0
  256. package/test/trees/JBController/launchProjectFor.tree +12 -0
  257. package/test/trees/JBController/launchRulesetsFor.tree +8 -0
  258. package/test/trees/JBController/migrateController.tree +12 -0
  259. package/test/trees/JBController/mintTokensOf.tree +12 -0
  260. package/test/trees/JBController/payReservedTokenToTerminal.tree +8 -0
  261. package/test/trees/JBController/receiveMigrationFrom.tree +4 -0
  262. package/test/trees/JBController/sendReservedTokensToSplitsOf.tree +12 -0
  263. package/test/trees/JBController/setMetadataOf.tree +5 -0
  264. package/test/trees/JBController/setSplitGroupsOf.tree +5 -0
  265. package/test/trees/JBController/setTokenFor.tree +5 -0
  266. package/test/trees/JBController/transferCreditsFrom.tree +8 -0
  267. package/test/trees/JBDirectory/primaryTerminalOf.tree +8 -0
  268. package/test/trees/JBDirectory/setControllerOf.tree +11 -0
  269. package/test/trees/JBDirectory/setPrimaryTerminalOf.tree +15 -0
  270. package/test/trees/JBDirectory/setTerminalsOf.tree +11 -0
  271. package/test/trees/JBERC20/initialize.tree +7 -0
  272. package/test/trees/JBERC20/name.tree +5 -0
  273. package/test/trees/JBERC20/nonces.tree +5 -0
  274. package/test/trees/JBERC20/symbol.tree +5 -0
  275. package/test/trees/JBFeelessAddresses/setFeelessAddress.tree +5 -0
  276. package/test/trees/JBFeelessAddresses/supportsInterface.tree +5 -0
  277. package/test/trees/JBFundAccessLimits/payoutLimitOf.tree +5 -0
  278. package/test/trees/JBFundAccessLimits/payoutLimitsOf.tree +8 -0
  279. package/test/trees/JBFundAccessLimits/setFundAccessLimitsFor.tree +18 -0
  280. package/test/trees/JBFundAccessLimits/surplusAllowanceOf.tree +5 -0
  281. package/test/trees/JBFundAccessLimits/surplusAllowancesOf.tree +8 -0
  282. package/test/trees/JBMetadataResolver/getDataFor.tree +8 -0
  283. package/test/trees/JBMultiTerminal/accountingContextsOf.tree +5 -0
  284. package/test/trees/JBMultiTerminal/addAccountingContextsFor.tree +10 -0
  285. package/test/trees/JBMultiTerminal/addToBalanceOf.tree +23 -0
  286. package/test/trees/JBMultiTerminal/cashOutTokensOf.tree +23 -0
  287. package/test/trees/JBMultiTerminal/executePayout.tree +32 -0
  288. package/test/trees/JBMultiTerminal/executeProcessFee.tree +14 -0
  289. package/test/trees/JBMultiTerminal/migrateBalanceOf.tree +12 -0
  290. package/test/trees/JBMultiTerminal/pay.tree +23 -0
  291. package/test/trees/JBMultiTerminal/processHeldFeesOf.tree +8 -0
  292. package/test/trees/JBMultiTerminal/sendPayoutsOf.tree +34 -0
  293. package/test/trees/JBMultiTerminal/useAllowanceOf.tree +16 -0
  294. package/test/trees/JBPermissions/hasPermission.tree +8 -0
  295. package/test/trees/JBPermissions/hasPermissions.tree +8 -0
  296. package/test/trees/JBPermissions/setPermissionsFor.tree +5 -0
  297. package/test/trees/JBPrices/addPriceFeedFor.tree +14 -0
  298. package/test/trees/JBPrices/pricePerUnitOf.tree +11 -0
  299. package/test/trees/JBProjects/createFor.tree +11 -0
  300. package/test/trees/JBProjects/setTokenUriResolver.tree +5 -0
  301. package/test/trees/JBProjects/supportsInterface.tree +9 -0
  302. package/test/trees/JBProjects/tokenURI.tree +5 -0
  303. package/test/trees/JBRulesets/currentApprovalStatusForLatestRulesetOf.tree +8 -0
  304. package/test/trees/JBRulesets/currentOf.tree +12 -0
  305. package/test/trees/JBRulesets/getRulesetOf.tree +5 -0
  306. package/test/trees/JBRulesets/latestQueuedRulesetOf.tree +10 -0
  307. package/test/trees/JBRulesets/rulesetsOf.tree +11 -0
  308. package/test/trees/JBRulesets/upcomingRulesetOf.tree +20 -0
  309. package/test/trees/JBRulesets/updateRulesetWeightCache.tree +5 -0
  310. package/test/trees/JBSplits/setSplitGroupsOf.tree +17 -0
  311. package/test/trees/JBSplits/splitsOf.tree +5 -0
  312. package/test/trees/JBTerminalStore/currentReclaimableSurplusOf.tree +16 -0
  313. package/test/trees/JBTerminalStore/currentSurplusOf.tree +25 -0
  314. package/test/trees/JBTerminalStore/currentTotalSurplusOf.tree +5 -0
  315. package/test/trees/JBTerminalStore/recordCashOutsFor.tree +16 -0
  316. package/test/trees/JBTerminalStore/recordPaymentFrom.tree +14 -0
  317. package/test/trees/JBTerminalStore/recordPayoutFor.tree +10 -0
  318. package/test/trees/JBTerminalStore/recordTerminalMigration.tree +5 -0
  319. package/test/trees/JBTerminalStore/recordUsedAllowanceOf.tree +10 -0
  320. package/test/trees/JBTokens/burnFrom.tree +10 -0
  321. package/test/trees/JBTokens/claimTokensFor.tree +10 -0
  322. package/test/trees/JBTokens/deployERC20For.tree +12 -0
  323. package/test/trees/JBTokens/mintFor.tree +10 -0
  324. package/test/trees/JBTokens/setTokenFor.tree +11 -0
  325. package/test/trees/JBTokens/totalBalanceOf.tree +5 -0
  326. package/test/trees/JBTokens/totalSupplyOf.tree +5 -0
  327. package/test/trees/JBTokens/transferCreditsFrom.tree +8 -0
  328. package/test/trees/mintTokensOf.tree +12 -0
  329. package/test/units/static/JBChainlinkV3PriceFeed/TestPriceFeed.sol +220 -0
  330. package/test/units/static/JBController/JBControllerSetup.sol +40 -0
  331. package/test/units/static/JBController/TestBurnTokensOf.sol +107 -0
  332. package/test/units/static/JBController/TestClaimTokensFor.sol +60 -0
  333. package/test/units/static/JBController/TestDeployErc20For.sol +80 -0
  334. package/test/units/static/JBController/TestLaunchProjectFor.sol +282 -0
  335. package/test/units/static/JBController/TestLaunchRulesetsFor.sol +322 -0
  336. package/test/units/static/JBController/TestMigrateController.sol +148 -0
  337. package/test/units/static/JBController/TestMintTokensOfUnits.sol +102 -0
  338. package/test/units/static/JBController/TestPayReservedTokenToTerminal.sol +71 -0
  339. package/test/units/static/JBController/TestReceiveMigrationFrom.sol +95 -0
  340. package/test/units/static/JBController/TestRulesetViews.sol +219 -0
  341. package/test/units/static/JBController/TestSendReservedTokensToSplitsOf.sol +595 -0
  342. package/test/units/static/JBController/TestSetSplitGroupsOf.sol +63 -0
  343. package/test/units/static/JBController/TestSetTokenFor.sol +227 -0
  344. package/test/units/static/JBController/TestSetUriOf.sol +53 -0
  345. package/test/units/static/JBController/TestTransferCreditsFrom.sol +159 -0
  346. package/test/units/static/JBDeadline/TestDeadlineFuzz.sol +194 -0
  347. package/test/units/static/JBDirectory/JBDirectorySetup.sol +22 -0
  348. package/test/units/static/JBDirectory/TestPrimaryTerminalOf.sol +122 -0
  349. package/test/units/static/JBDirectory/TestSetControllerOf.sol +173 -0
  350. package/test/units/static/JBDirectory/TestSetControllerOfMigrationOrder.sol +98 -0
  351. package/test/units/static/JBDirectory/TestSetPrimaryTerminalOf.sol +169 -0
  352. package/test/units/static/JBDirectory/TestSetTerminalsOf.sol +128 -0
  353. package/test/units/static/JBERC20/JBERC20Setup.sol +20 -0
  354. package/test/units/static/JBERC20/SigUtils.sol +34 -0
  355. package/test/units/static/JBERC20/TestInitialize.sol +54 -0
  356. package/test/units/static/JBERC20/TestName.sol +30 -0
  357. package/test/units/static/JBERC20/TestNonces.sol +59 -0
  358. package/test/units/static/JBERC20/TestSymbol.sol +31 -0
  359. package/test/units/static/JBFeelessAdresses/JBFeelessSetup.sol +20 -0
  360. package/test/units/static/JBFeelessAdresses/TestInterfaces.sol +29 -0
  361. package/test/units/static/JBFeelessAdresses/TestSetFeelessAddress.sol +35 -0
  362. package/test/units/static/JBFees/TestFeesFuzz.sol +78 -0
  363. package/test/units/static/JBFixedPointNumber/TestAdjustDecimals.sol +16 -0
  364. package/test/units/static/JBFixedPointNumber/TestAdjustDecimalsFuzz.sol +71 -0
  365. package/test/units/static/JBFundAccessLimits/JBFundAccessSetup.sol +21 -0
  366. package/test/units/static/JBFundAccessLimits/TestFundAccessLimitsEdge.sol +159 -0
  367. package/test/units/static/JBFundAccessLimits/TestPayoutLimitOf.sol +56 -0
  368. package/test/units/static/JBFundAccessLimits/TestPayoutLimitsOf.sol +94 -0
  369. package/test/units/static/JBFundAccessLimits/TestSetFundAccessLimitsFor.sol +182 -0
  370. package/test/units/static/JBFundAccessLimits/TestSurplusAllowanceOf.sol +61 -0
  371. package/test/units/static/JBFundAccessLimits/TestSurplusAllowancesOf.sol +96 -0
  372. package/test/units/static/JBMetadataResolver/TestGetDataFor.sol +89 -0
  373. package/test/units/static/JBMetadataResolver/TestMetadataResolverFuzz.sol +227 -0
  374. package/test/units/static/JBMetadataResolver/TestMetadataResolverM20M21.sol +245 -0
  375. package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +39 -0
  376. package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +65 -0
  377. package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +313 -0
  378. package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +432 -0
  379. package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +478 -0
  380. package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +577 -0
  381. package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +176 -0
  382. package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +190 -0
  383. package/test/units/static/JBMultiTerminal/TestPay.sol +514 -0
  384. package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +29 -0
  385. package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +243 -0
  386. package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +310 -0
  387. package/test/units/static/JBPermissions/JBPermissionsSetup.sol +18 -0
  388. package/test/units/static/JBPermissions/TestHasPermission.sol +50 -0
  389. package/test/units/static/JBPermissions/TestHasPermissions.sol +93 -0
  390. package/test/units/static/JBPermissions/TestSetPermissionsFor.sol +62 -0
  391. package/test/units/static/JBPrices/JBPricesSetup.sol +26 -0
  392. package/test/units/static/JBPrices/TestAddPriceFeedFor.sol +102 -0
  393. package/test/units/static/JBPrices/TestPricePerUnitOf.sol +129 -0
  394. package/test/units/static/JBPrices/TestPrices.sol +262 -0
  395. package/test/units/static/JBProjects/JBProjectsSetup.sol +20 -0
  396. package/test/units/static/JBProjects/TestCreateFor.sol +69 -0
  397. package/test/units/static/JBProjects/TestInitialProject.sol +19 -0
  398. package/test/units/static/JBProjects/TestInterfaces.sol +27 -0
  399. package/test/units/static/JBProjects/TestSetResolver.sol +36 -0
  400. package/test/units/static/JBProjects/TestTokenUri.sol +38 -0
  401. package/test/units/static/JBRulesetMetadataResolver/TestSetCashOutTaxRateTo.sol +99 -0
  402. package/test/units/static/JBRulesets/JBRulesetsSetup.sol +21 -0
  403. package/test/units/static/JBRulesets/TestCurrentApprovalStatusForLatestRulesetOf.sol +257 -0
  404. package/test/units/static/JBRulesets/TestCurrentOf.sol +231 -0
  405. package/test/units/static/JBRulesets/TestGetRulesetOf.sol +94 -0
  406. package/test/units/static/JBRulesets/TestLatestQueuedRulesetOf.sol +252 -0
  407. package/test/units/static/JBRulesets/TestRulesets.sol +617 -0
  408. package/test/units/static/JBRulesets/TestRulesetsOf.sol +37 -0
  409. package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +526 -0
  410. package/test/units/static/JBRulesets/TestUpdateRulesetWeightCache.sol +91 -0
  411. package/test/units/static/JBSplits/JBSplitsSetup.sol +23 -0
  412. package/test/units/static/JBSplits/TestSelfManagedSplitGroups.sol +502 -0
  413. package/test/units/static/JBSplits/TestSetSplitGroupsOf.sol +370 -0
  414. package/test/units/static/JBSplits/TestSplitsLockedEdge.sol +262 -0
  415. package/test/units/static/JBSplits/TestSplitsOf.sol +24 -0
  416. package/test/units/static/JBSplits/TestSplitsPacking.sol +33 -0
  417. package/test/units/static/JBSurplus/TestSurplusFuzz.sol +125 -0
  418. package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +23 -0
  419. package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +434 -0
  420. package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +428 -0
  421. package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +65 -0
  422. package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +479 -0
  423. package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +508 -0
  424. package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +257 -0
  425. package/test/units/static/JBTerminalStore/TestRecordTerminalMigration.sol +131 -0
  426. package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +390 -0
  427. package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +187 -0
  428. package/test/units/static/JBTokens/JBTokensSetup.sol +23 -0
  429. package/test/units/static/JBTokens/TestBurnFrom.sol +104 -0
  430. package/test/units/static/JBTokens/TestClaimTokensFor.sol +107 -0
  431. package/test/units/static/JBTokens/TestDeployERC20ForUnits.sol +89 -0
  432. package/test/units/static/JBTokens/TestMintFor.sol +97 -0
  433. package/test/units/static/JBTokens/TestSetTokenFor.sol +95 -0
  434. package/test/units/static/JBTokens/TestTotalBalanceOf.sol +65 -0
  435. package/test/units/static/JBTokens/TestTotalSupplyOf.sol +56 -0
  436. package/test/units/static/JBTokens/TestTransferCreditsFrom.sol +54 -0
@@ -0,0 +1,1093 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import {mulDiv} from "@prb/math/src/Common.sol";
5
+
6
+ import {JBControlled} from "./abstract/JBControlled.sol";
7
+ import {JBApprovalStatus} from "./enums/JBApprovalStatus.sol";
8
+ import {IJBDirectory} from "./interfaces/IJBDirectory.sol";
9
+ import {IJBRulesetApprovalHook} from "./interfaces/IJBRulesetApprovalHook.sol";
10
+ import {IJBRulesets} from "./interfaces/IJBRulesets.sol";
11
+ import {JBConstants} from "./libraries/JBConstants.sol";
12
+ import {JBRuleset} from "./structs/JBRuleset.sol";
13
+ import {JBRulesetWeightCache} from "./structs/JBRulesetWeightCache.sol";
14
+
15
+ /// @notice Manages rulesets and queuing.
16
+ /// @dev Rulesets dictate how a project behaves for a period of time. To learn more about their functionality, see the
17
+ /// `JBRuleset` data structure.
18
+ /// @dev Throughout this contract, `rulesetId` is an identifier for each ruleset. The `rulesetId` is the unix timestamp
19
+ /// when the ruleset was initialized.
20
+ /// @dev `approvable` means a ruleset which may or may not be approved.
21
+ contract JBRulesets is JBControlled, IJBRulesets {
22
+ //*********************************************************************//
23
+ // --------------------------- custom errors ------------------------- //
24
+ //*********************************************************************//
25
+
26
+ error JBRulesets_InvalidRulesetApprovalHook(IJBRulesetApprovalHook hook);
27
+ error JBRulesets_InvalidRulesetDuration(uint256 duration, uint256 limit);
28
+ error JBRulesets_InvalidRulesetEndTime(uint256 timestamp, uint256 limit);
29
+ error JBRulesets_InvalidWeight(uint256 weight, uint256 limit);
30
+ error JBRulesets_InvalidWeightCutPercent(uint256 percent);
31
+ error JBRulesets_WeightCacheRequired(uint256 projectId);
32
+
33
+ //*********************************************************************//
34
+ // ------------------------- internal constants ----------------------- //
35
+ //*********************************************************************//
36
+
37
+ /// @notice The maximum number of weight cut iterations allowed per call.
38
+ /// If more cycles are needed, callers must populate the cache via updateRulesetWeightCache() first.
39
+ uint256 internal constant _WEIGHT_CUT_MULTIPLE_CACHE_LOOKUP_THRESHOLD = 20_000;
40
+
41
+ //*********************************************************************//
42
+ // --------------------- public stored properties -------------------- //
43
+ //*********************************************************************//
44
+
45
+ /// @notice The ID of the ruleset with the latest start time for a specific project, whether the ruleset has been
46
+ /// approved or not.
47
+ /// @dev If a project has multiple rulesets queued, the `latestRulesetIdOf` will be the last one. This is the
48
+ /// "changeable" cycle.
49
+ /// @custom:param projectId The ID of the project to get the latest ruleset ID of.
50
+ /// @return latestRulesetIdOf The `rulesetId` of the project's latest ruleset.
51
+ mapping(uint256 projectId => uint256) public override latestRulesetIdOf;
52
+
53
+ //*********************************************************************//
54
+ // --------------------- internal stored properties ------------------- //
55
+ //*********************************************************************//
56
+
57
+ /// @notice The metadata for each ruleset, packed into one storage slot.
58
+ /// @custom:param projectId The ID of the project to get metadata of.
59
+ /// @custom:param rulesetId The ID of the ruleset to get metadata of.
60
+ mapping(uint256 projectId => mapping(uint256 rulesetId => uint256)) internal _metadataOf;
61
+
62
+ /// @notice The mechanism-added properties to manage and schedule each ruleset, packed into one storage slot.
63
+ /// @custom:param projectId The ID of the project to get the intrinsic properties of.
64
+ /// @custom:param rulesetId The ID of the ruleset to get the intrinsic properties of.
65
+ mapping(uint256 projectId => mapping(uint256 rulesetId => uint256)) internal _packedIntrinsicPropertiesOf;
66
+
67
+ /// @notice The user-defined properties of each ruleset, packed into one storage slot.
68
+ /// @custom:param projectId The ID of the project to get the user-defined properties of.
69
+ /// @custom:param rulesetId The ID of the ruleset to get the user-defined properties of.
70
+ mapping(uint256 projectId => mapping(uint256 rulesetId => uint256)) internal _packedUserPropertiesOf;
71
+
72
+ /// @notice Cached weight values to derive rulesets from.
73
+ /// @custom:param projectId The ID of the project to which the cache applies.
74
+ /// @custom:param rulesetId The ID of the ruleset to which the cache applies.
75
+ mapping(uint256 projectId => mapping(uint256 rulesetId => JBRulesetWeightCache)) internal _weightCacheOf;
76
+
77
+ //*********************************************************************//
78
+ // -------------------------- constructor ---------------------------- //
79
+ //*********************************************************************//
80
+
81
+ /// @param directory A contract storing directories of terminals and controllers for each project.
82
+ // solhint-disable-next-line no-empty-blocks
83
+ constructor(IJBDirectory directory) JBControlled(directory) {}
84
+
85
+ //*********************************************************************//
86
+ // ------------------------- external views -------------------------- //
87
+ //*********************************************************************//
88
+
89
+ /// @notice Get an array of a project's rulesets up to a maximum array size, sorted from latest to earliest.
90
+ /// @param projectId The ID of the project to get the rulesets of.
91
+ /// @param startingId The ID of the ruleset to begin with. This will be the latest ruleset in the result. If 0 is
92
+ /// passed, the project's latest ruleset will be used.
93
+ /// @param size The maximum number of rulesets to return.
94
+ /// @return rulesets The rulesets as an array of `JBRuleset` structs.
95
+ function allOf(
96
+ uint256 projectId,
97
+ uint256 startingId,
98
+ uint256 size
99
+ )
100
+ external
101
+ view
102
+ override
103
+ returns (JBRuleset[] memory rulesets)
104
+ {
105
+ // If no starting ID was provided, set it to the latest ruleset's ID.
106
+ if (startingId == 0) startingId = latestRulesetIdOf[projectId];
107
+
108
+ // Keep a reference to the number of rulesets being returned.
109
+ uint256 count = 0;
110
+
111
+ // Keep a reference to the starting ruleset.
112
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: startingId});
113
+
114
+ // First, count the number of rulesets to include in the result by iterating backwards from the starting
115
+ // ruleset.
116
+ while (ruleset.id != 0 && count < size) {
117
+ // Increment the counter.
118
+ count++;
119
+
120
+ // Iterate to the ruleset it was based on.
121
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
122
+ }
123
+
124
+ // Keep a reference to the array of rulesets that'll be populated.
125
+ rulesets = new JBRuleset[](count);
126
+
127
+ // Return an empty array if there are no rulesets to return.
128
+ if (count == 0) {
129
+ return rulesets;
130
+ }
131
+
132
+ // Reset the ruleset being iterated on to the starting ruleset.
133
+ ruleset = _getStructFor({projectId: projectId, rulesetId: startingId});
134
+
135
+ // Set the counter.
136
+ uint256 i;
137
+
138
+ // Populate the array of rulesets to return.
139
+ while (i < count) {
140
+ // Add the ruleset to the array.
141
+ rulesets[i++] = ruleset;
142
+
143
+ // Get the ruleset it was based on if needed.
144
+ if (i != count) ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
145
+ }
146
+ }
147
+
148
+ /// @notice The current approval status of a given project's latest ruleset.
149
+ /// @param projectId The ID of the project to check the approval status of.
150
+ /// @return The project's current approval status.
151
+ function currentApprovalStatusForLatestRulesetOf(uint256 projectId)
152
+ external
153
+ view
154
+ override
155
+ returns (JBApprovalStatus)
156
+ {
157
+ // Get a reference to the latest ruleset ID.
158
+ uint256 rulesetId = latestRulesetIdOf[projectId];
159
+
160
+ // Resolve the struct for the latest ruleset.
161
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
162
+
163
+ return _approvalStatusOf({projectId: projectId, ruleset: ruleset});
164
+ }
165
+
166
+ /// @notice The ruleset that is currently active for the specified project.
167
+ /// @dev If a current ruleset of the project is not found, returns an empty ruleset with all properties set to 0.
168
+ /// @param projectId The ID of the project to get the current ruleset of.
169
+ /// @return ruleset The project's current ruleset.
170
+ function currentOf(uint256 projectId) external view override returns (JBRuleset memory ruleset) {
171
+ // If the project does not have a ruleset, return an empty struct.
172
+ // slither-disable-next-line incorrect-equality
173
+ if (latestRulesetIdOf[projectId] == 0) return _getStructFor({projectId: 0, rulesetId: 0});
174
+
175
+ // Get a reference to the currently approvable ruleset's ID.
176
+ uint256 rulesetId = _currentlyApprovableRulesetIdOf(projectId);
177
+
178
+ // If a currently approvable ruleset exists...
179
+ if (rulesetId != 0) {
180
+ // Resolve the struct for the currently approvable ruleset.
181
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
182
+
183
+ // Get a reference to the approval status.
184
+ JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
185
+
186
+ // Check to see if this ruleset's approval hook is approved if it exists.
187
+ // If so, return it.
188
+ // slither-disable-next-line incorrect-equality
189
+ if (approvalStatus == JBApprovalStatus.Approved || approvalStatus == JBApprovalStatus.Empty) {
190
+ return ruleset;
191
+ }
192
+
193
+ // If it hasn't been approved, set the ruleset configuration to be the configuration of the ruleset that
194
+ // it's based on,
195
+ // which carries the last approved configuration.
196
+ rulesetId = ruleset.basedOnId;
197
+
198
+ // Keep a reference to its ruleset.
199
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
200
+ } else {
201
+ // No upcoming ruleset found that is currently approvable,
202
+ // so use the latest ruleset ID.
203
+ rulesetId = latestRulesetIdOf[projectId];
204
+
205
+ // Get the struct for the latest ID.
206
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
207
+
208
+ // Get a reference to the approval status.
209
+ JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
210
+
211
+ // While the ruleset has a approval hook that isn't approved or if it hasn't yet started, get a reference to
212
+ // the ruleset that the latest is based on, which has the latest approved configuration.
213
+ while (
214
+ (approvalStatus != JBApprovalStatus.Approved && approvalStatus != JBApprovalStatus.Empty)
215
+ || block.timestamp < ruleset.start
216
+ ) {
217
+ rulesetId = ruleset.basedOnId;
218
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
219
+ approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
220
+ }
221
+ }
222
+
223
+ // If the base has no duration, it's still the current one.
224
+ // slither-disable-next-line incorrect-equality
225
+ if (ruleset.duration == 0) return ruleset;
226
+
227
+ // Return a simulation of the current ruleset.
228
+ return _simulateCycledRulesetBasedOn({projectId: projectId, baseRuleset: ruleset, allowMidRuleset: true});
229
+ }
230
+
231
+ /// @notice Get the ruleset struct for a given `rulesetId` and `projectId`.
232
+ /// @param projectId The ID of the project to which the ruleset belongs.
233
+ /// @param rulesetId The ID of the ruleset to get the struct of.
234
+ /// @return ruleset The ruleset struct.
235
+ function getRulesetOf(
236
+ uint256 projectId,
237
+ uint256 rulesetId
238
+ )
239
+ external
240
+ view
241
+ override
242
+ returns (JBRuleset memory ruleset)
243
+ {
244
+ return _getStructFor({projectId: projectId, rulesetId: rulesetId});
245
+ }
246
+
247
+ /// @notice The latest ruleset queued for a project. Returns the ruleset's struct and its current approval status.
248
+ /// @dev Returns struct and status for the ruleset initialized furthest in the future (at the end of the ruleset
249
+ /// queue).
250
+ /// @param projectId The ID of the project to get the latest queued ruleset of.
251
+ /// @return ruleset The project's latest queued ruleset's struct.
252
+ /// @return approvalStatus The approval hook's status for the ruleset.
253
+ function latestQueuedOf(uint256 projectId)
254
+ external
255
+ view
256
+ override
257
+ returns (JBRuleset memory ruleset, JBApprovalStatus approvalStatus)
258
+ {
259
+ // Get a reference to the latest ruleset's ID.
260
+ uint256 rulesetId = latestRulesetIdOf[projectId];
261
+
262
+ // Resolve the struct for the latest ruleset.
263
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
264
+
265
+ // Resolve the approval status.
266
+ approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
267
+ }
268
+
269
+ /// @notice The ruleset that's up next for a project.
270
+ /// @dev If an upcoming ruleset is not found for the project, returns an empty ruleset with all properties set to 0.
271
+ /// @param projectId The ID of the project to get the upcoming ruleset of.
272
+ /// @return ruleset The struct for the project's upcoming ruleset.
273
+ function upcomingOf(uint256 projectId) external view override returns (JBRuleset memory ruleset) {
274
+ // If the project does not have a latest ruleset, return an empty struct.
275
+ // slither-disable-next-line incorrect-equality
276
+ if (latestRulesetIdOf[projectId] == 0) return _getStructFor({projectId: 0, rulesetId: 0});
277
+
278
+ // Get a reference to the upcoming approvable ruleset's ID.
279
+ uint256 upcomingApprovableRulesetId = _upcomingApprovableRulesetIdOf(projectId);
280
+
281
+ // Keep a reference to its approval status.
282
+ JBApprovalStatus approvalStatus;
283
+
284
+ // If an upcoming approvable ruleset has been queued, and it's approval status is Approved or ApprovalExpected,
285
+ // return its ruleset struct
286
+ if (upcomingApprovableRulesetId != 0) {
287
+ ruleset = _getStructFor({projectId: projectId, rulesetId: upcomingApprovableRulesetId});
288
+
289
+ // Get a reference to the approval status.
290
+ approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
291
+
292
+ // If the approval hook is empty, expects approval, or has approved the ruleset, return it.
293
+ if (
294
+ // slither-disable-next-line incorrect-equality
295
+ approvalStatus == JBApprovalStatus.Approved || approvalStatus == JBApprovalStatus.ApprovalExpected
296
+ || approvalStatus == JBApprovalStatus.Empty
297
+ ) return ruleset;
298
+
299
+ // Resolve the ruleset for the ruleset the upcoming approvable ruleset was based on.
300
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
301
+ } else {
302
+ // Resolve the ruleset for the latest queued ruleset.
303
+ ruleset = _getStructFor({projectId: projectId, rulesetId: latestRulesetIdOf[projectId]});
304
+
305
+ // If the latest ruleset starts in the future, it must start in the distant future
306
+ // Since its not the upcoming approvable ruleset. In this case, base the upcoming ruleset on the base
307
+ // ruleset.
308
+ while (ruleset.start > block.timestamp) {
309
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
310
+ }
311
+ }
312
+
313
+ // There's no queued if the current has a duration of 0.
314
+ // slither-disable-next-line incorrect-equality
315
+ if (ruleset.duration == 0) return _getStructFor({projectId: 0, rulesetId: 0});
316
+
317
+ // Get a reference to the approval status.
318
+ approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: ruleset});
319
+
320
+ // Check to see if this ruleset's approval hook hasn't failed.
321
+ // If so, return a ruleset based on it.
322
+ // slither-disable-next-line incorrect-equality
323
+ if (approvalStatus == JBApprovalStatus.Approved || approvalStatus == JBApprovalStatus.Empty) {
324
+ return _simulateCycledRulesetBasedOn({projectId: projectId, baseRuleset: ruleset, allowMidRuleset: false});
325
+ }
326
+
327
+ // Get the ruleset of its base ruleset, which carries the last approved configuration.
328
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
329
+
330
+ // There's no queued if the base, which must still be the current, has a duration of 0.
331
+ // slither-disable-next-line incorrect-equality
332
+ if (ruleset.duration == 0) return _getStructFor({projectId: 0, rulesetId: 0});
333
+
334
+ // Return a simulated cycled ruleset.
335
+ return _simulateCycledRulesetBasedOn({projectId: projectId, baseRuleset: ruleset, allowMidRuleset: false});
336
+ }
337
+
338
+ //*********************************************************************//
339
+ // --------------------------- public views -------------------------- //
340
+ //*********************************************************************//
341
+
342
+ /// @notice The cycle number of the next ruleset given the specified ruleset.
343
+ /// @dev Each time a ruleset starts, whether it was queued or cycled over, the cycle number is incremented by 1.
344
+ /// @param baseRulesetCycleNumber The cycle number of the base ruleset.
345
+ /// @param baseRulesetStart The start time of the base ruleset.
346
+ /// @param baseRulesetDuration The duration of the base ruleset.
347
+ /// @param start The start time of the ruleset to derive a cycle number for.
348
+ /// @return The ruleset's cycle number.
349
+ function deriveCycleNumberFrom(
350
+ uint256 baseRulesetCycleNumber,
351
+ uint256 baseRulesetStart,
352
+ uint256 baseRulesetDuration,
353
+ uint256 start
354
+ )
355
+ public
356
+ pure
357
+ returns (uint256)
358
+ {
359
+ // A subsequent ruleset to one with a duration of 0 should be the next number.
360
+ // slither-disable-next-line incorrect-equality
361
+ if (baseRulesetDuration == 0) {
362
+ return baseRulesetCycleNumber + 1;
363
+ }
364
+
365
+ // The difference between the start of the base ruleset and the proposed start.
366
+ uint256 startDistance = start - baseRulesetStart;
367
+
368
+ // Find the number of base rulesets that fit in the start distance.
369
+ return baseRulesetCycleNumber + (startDistance / baseRulesetDuration);
370
+ }
371
+
372
+ /// @notice The date that is the nearest multiple of the base ruleset's duration from the start of the next cycle.
373
+ /// @param baseRulesetStart The start time of the base ruleset.
374
+ /// @param baseRulesetDuration The duration of the base ruleset.
375
+ /// @param mustStartAtOrAfter The earliest time the next ruleset can start. The ruleset cannot start before this
376
+ /// timestamp.
377
+ /// @return start The next start time.
378
+ function deriveStartFrom(
379
+ uint256 baseRulesetStart,
380
+ uint256 baseRulesetDuration,
381
+ uint256 mustStartAtOrAfter
382
+ )
383
+ public
384
+ pure
385
+ returns (uint256 start)
386
+ {
387
+ // A subsequent ruleset to one with a duration of 0 should start as soon as possible.
388
+ // slither-disable-next-line incorrect-equality
389
+ if (baseRulesetDuration == 0) return mustStartAtOrAfter;
390
+
391
+ // The time when the ruleset immediately after the specified ruleset starts.
392
+ uint256 nextImmediateStart = baseRulesetStart + baseRulesetDuration;
393
+
394
+ // If the next immediate start is now or in the future, return it.
395
+ if (nextImmediateStart >= mustStartAtOrAfter) {
396
+ return nextImmediateStart;
397
+ }
398
+
399
+ // The amount of seconds since the `mustStartAtOrAfter` time which results in a start time that might satisfy
400
+ // the specified limits.
401
+ // slither-disable-next-line weak-prng
402
+ uint256 timeFromImmediateStartMultiple = (mustStartAtOrAfter - nextImmediateStart) % baseRulesetDuration;
403
+
404
+ // A reference to the first possible start timestamp.
405
+ start = mustStartAtOrAfter - timeFromImmediateStartMultiple;
406
+
407
+ // Add increments of duration as necessary to satisfy the threshold.
408
+ while (mustStartAtOrAfter > start) {
409
+ start += baseRulesetDuration;
410
+ }
411
+ }
412
+
413
+ /// @notice The accumulated weight change since the specified ruleset.
414
+ /// @param projectId The ID of the project to which the ruleset weights apply.
415
+ /// @param baseRulesetStart The start time of the base ruleset.
416
+ /// @param baseRulesetDuration The duration of the base ruleset.
417
+ /// @param baseRulesetWeight The weight of the base ruleset.
418
+ /// @param baseRulesetWeightCutPercent The weight cut percent of the base ruleset.
419
+ /// @param baseRulesetCacheId The ID of the ruleset to base the calculation on (the previous ruleset).
420
+ /// @param start The start time of the ruleset to derive a weight for.
421
+ /// @return weight The derived weight, as a fixed point number with 18 decimals.
422
+ function deriveWeightFrom(
423
+ uint256 projectId,
424
+ uint256 baseRulesetStart,
425
+ uint256 baseRulesetDuration,
426
+ uint256 baseRulesetWeight,
427
+ uint256 baseRulesetWeightCutPercent,
428
+ uint256 baseRulesetCacheId,
429
+ uint256 start
430
+ )
431
+ public
432
+ view
433
+ returns (uint256 weight)
434
+ {
435
+ // A subsequent ruleset to one with a duration of 0 should have the next possible weight.
436
+ // slither-disable-next-line incorrect-equality
437
+ if (baseRulesetDuration == 0) {
438
+ return mulDiv(
439
+ baseRulesetWeight,
440
+ JBConstants.MAX_WEIGHT_CUT_PERCENT - baseRulesetWeightCutPercent,
441
+ JBConstants.MAX_WEIGHT_CUT_PERCENT
442
+ );
443
+ }
444
+
445
+ // The weight should be based off the base ruleset's weight.
446
+ weight = baseRulesetWeight;
447
+
448
+ // If the weight cut percent is 0, the weight doesn't change.
449
+ // slither-disable-next-line incorrect-equality
450
+ if (baseRulesetWeightCutPercent == 0) return weight;
451
+
452
+ // The difference between the start of the base ruleset and the proposed start.
453
+ uint256 startDistance = start - baseRulesetStart;
454
+
455
+ // Apply the base ruleset's weight cut percent for each ruleset that has passed.
456
+ uint256 weightCutMultiple;
457
+ unchecked {
458
+ weightCutMultiple = startDistance / baseRulesetDuration; // Non-null duration is excluded above
459
+ }
460
+
461
+ // Check the cache if needed.
462
+ if (baseRulesetCacheId > 0 && weightCutMultiple > _WEIGHT_CUT_MULTIPLE_CACHE_LOOKUP_THRESHOLD) {
463
+ // Get a cached weight for the rulesetId.
464
+ JBRulesetWeightCache memory cache = _weightCacheOf[projectId][baseRulesetCacheId];
465
+
466
+ // If a cached value is available, use it.
467
+ if (cache.weightCutMultiple > 0) {
468
+ // Set the starting weight to be the cached value.
469
+ weight = cache.weight;
470
+
471
+ // Set the weight cut multiple to be the difference between the cached value and the total weight cut
472
+ // multiple that should be applied.
473
+ weightCutMultiple -= cache.weightCutMultiple;
474
+ }
475
+ }
476
+
477
+ // If too many iterations remain after cache lookup, require the cache to be populated first.
478
+ // This prevents gas exhaustion for short-duration rulesets with large cycle counts.
479
+ if (weightCutMultiple > _WEIGHT_CUT_MULTIPLE_CACHE_LOOKUP_THRESHOLD) {
480
+ revert JBRulesets_WeightCacheRequired(projectId);
481
+ }
482
+
483
+ for (uint256 i; i < weightCutMultiple; i++) {
484
+ // The number of times to apply the weight cut percent.
485
+ // Base the new weight on the specified ruleset's weight.
486
+ weight = mulDiv(
487
+ weight,
488
+ JBConstants.MAX_WEIGHT_CUT_PERCENT - baseRulesetWeightCutPercent,
489
+ JBConstants.MAX_WEIGHT_CUT_PERCENT
490
+ );
491
+
492
+ // The calculation doesn't need to continue if the weight is 0.
493
+ if (weight == 0) break;
494
+ }
495
+ }
496
+
497
+ //*********************************************************************//
498
+ // -------------------------- internal views ------------------------- //
499
+ //*********************************************************************//
500
+
501
+ /// @notice The approval status of a given ruleset for a given project ID.
502
+ /// @param projectId The ID of the project the ruleset belongs to.
503
+ /// @param ruleset The ruleset to get the approval status of.
504
+ /// @return The approval status of the project.
505
+ function _approvalStatusOf(uint256 projectId, JBRuleset memory ruleset) internal view returns (JBApprovalStatus) {
506
+ // If there is no ruleset ID to check the approval hook of, the approval hook is empty.
507
+ // slither-disable-next-line incorrect-equality
508
+ if (ruleset.basedOnId == 0) return JBApprovalStatus.Empty;
509
+
510
+ // Get the struct of the ruleset with the approval hook.
511
+ JBRuleset memory approvalHookRuleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
512
+
513
+ // If there is no approval hook, it's considered empty.
514
+ if (approvalHookRuleset.approvalHook == IJBRulesetApprovalHook(address(0))) {
515
+ return JBApprovalStatus.Empty;
516
+ }
517
+
518
+ // Return the approval hook's approval status.
519
+ // Wrap in try/catch to prevent a reverting approval hook from permanently freezing the project.
520
+ // Note: A malicious hook that consumes all gas (e.g. infinite loop) could still DoS via gas exhaustion.
521
+ // This is accepted risk since the project owner chose their own approval hook.
522
+ // slither-disable-next-line calls-loop
523
+ try approvalHookRuleset.approvalHook.approvalStatusOf({projectId: projectId, ruleset: ruleset}) returns (
524
+ JBApprovalStatus status
525
+ ) {
526
+ return status;
527
+ } catch {
528
+ return JBApprovalStatus.Failed;
529
+ }
530
+ }
531
+
532
+ /// @notice The ID of the ruleset which has started and hasn't expired yet, whether or not it has been approved, for
533
+ /// a given project. If approved, this is the active ruleset.
534
+ /// @dev A value of 0 is returned if no ruleset was found.
535
+ /// @dev Assumes the project has a latest ruleset.
536
+ /// @param projectId The ID of the project to check for a currently approvable ruleset.
537
+ /// @return The ID of a currently approvable ruleset if one exists, or 0 if one doesn't exist.
538
+ function _currentlyApprovableRulesetIdOf(uint256 projectId) internal view returns (uint256) {
539
+ // Get a reference to the project's latest ruleset.
540
+ uint256 rulesetId = latestRulesetIdOf[projectId];
541
+
542
+ // Get the struct for the latest ruleset.
543
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
544
+
545
+ // Loop through all most recently queued rulesets until an approvable one is found, or we've proven one can't
546
+ // exist.
547
+ do {
548
+ // If the latest ruleset is expired, return an empty ruleset.
549
+ // A ruleset with a duration of 0 cannot expire.
550
+ if (ruleset.duration != 0 && block.timestamp >= ruleset.start + ruleset.duration) {
551
+ return 0;
552
+ }
553
+
554
+ // Return the ruleset's `rulesetId` if it has started.
555
+ if (block.timestamp >= ruleset.start) {
556
+ return ruleset.id;
557
+ }
558
+
559
+ ruleset = _getStructFor({projectId: projectId, rulesetId: ruleset.basedOnId});
560
+ } while (ruleset.cycleNumber != 0);
561
+
562
+ return 0;
563
+ }
564
+
565
+ /// @notice Unpack a ruleset's packed stored values into an easy-to-work-with ruleset struct.
566
+ /// @param projectId The ID of the project the ruleset belongs to.
567
+ /// @param rulesetId The ID of the ruleset to get the full struct for.
568
+ /// @return ruleset A ruleset struct.
569
+ function _getStructFor(uint256 projectId, uint256 rulesetId) internal view returns (JBRuleset memory ruleset) {
570
+ // Return an empty ruleset if the specified `rulesetId` is 0.
571
+ // slither-disable-next-line incorrect-equality
572
+ if (rulesetId == 0) return ruleset;
573
+
574
+ ruleset.id = uint48(rulesetId);
575
+
576
+ uint256 packedIntrinsicProperties = _packedIntrinsicPropertiesOf[projectId][rulesetId];
577
+
578
+ // `weight` in bits 0-111 bits.
579
+ ruleset.weight = uint112(packedIntrinsicProperties);
580
+ // `basedOnId` in bits 112-159 bits.
581
+ ruleset.basedOnId = uint48(packedIntrinsicProperties >> 112);
582
+ // `start` in bits 160-207 bits.
583
+ ruleset.start = uint48(packedIntrinsicProperties >> 160);
584
+ // `cycleNumber` in bits 208-255 bits.
585
+ ruleset.cycleNumber = uint48(packedIntrinsicProperties >> 208);
586
+
587
+ uint256 packedUserProperties = _packedUserPropertiesOf[projectId][rulesetId];
588
+
589
+ // approval hook in bits 0-159 bits.
590
+ ruleset.approvalHook = IJBRulesetApprovalHook(address(uint160(packedUserProperties)));
591
+ // `duration` in bits 160-191 bits.
592
+ ruleset.duration = uint32(packedUserProperties >> 160);
593
+ // weight cut percent in bits 192-223 bits.
594
+ ruleset.weightCutPercent = uint32(packedUserProperties >> 192);
595
+
596
+ ruleset.metadata = _metadataOf[projectId][rulesetId];
597
+ }
598
+
599
+ /// @notice A simulated view of the ruleset that would be created if the provided one cycled over (if the project
600
+ /// doesn't queue a new ruleset).
601
+ /// @dev Returns an empty ruleset if a ruleset can't be simulated based on the provided one.
602
+ /// @dev Assumes a simulated ruleset will never be based on a ruleset with a duration of 0.
603
+ /// @param projectId The ID of the project of the ruleset.
604
+ /// @param baseRuleset The ruleset that the simulated ruleset should be based on.
605
+ /// @param allowMidRuleset A flag indicating if the simulated ruleset is allowed to already be mid ruleset.
606
+ /// @return A simulated ruleset struct: the next ruleset by default. This will be overwritten if a new ruleset is
607
+ /// queued for the project.
608
+ function _simulateCycledRulesetBasedOn(
609
+ uint256 projectId,
610
+ JBRuleset memory baseRuleset,
611
+ bool allowMidRuleset
612
+ )
613
+ internal
614
+ view
615
+ returns (JBRuleset memory)
616
+ {
617
+ // Get the distance from the current time to the start of the next possible ruleset.
618
+ // If the simulated ruleset must not yet have started, the start time of the simulated ruleset must be in the
619
+ // future.
620
+ uint256 mustStartAtOrAfter = !allowMidRuleset
621
+ ? block.timestamp + 1
622
+ : baseRuleset.duration >= block.timestamp ? 1 : block.timestamp - baseRuleset.duration + 1;
623
+
624
+ // Calculate what the start time should be.
625
+ uint256 start = deriveStartFrom({
626
+ baseRulesetStart: baseRuleset.start,
627
+ baseRulesetDuration: baseRuleset.duration,
628
+ mustStartAtOrAfter: mustStartAtOrAfter
629
+ });
630
+
631
+ // Calculate what the cycle number should be.
632
+ uint256 rulesetCycleNumber = deriveCycleNumberFrom({
633
+ baseRulesetCycleNumber: baseRuleset.cycleNumber,
634
+ baseRulesetStart: baseRuleset.start,
635
+ baseRulesetDuration: baseRuleset.duration,
636
+ start: start
637
+ });
638
+
639
+ return JBRuleset({
640
+ cycleNumber: uint48(rulesetCycleNumber),
641
+ id: baseRuleset.id,
642
+ basedOnId: baseRuleset.basedOnId,
643
+ start: uint48(start),
644
+ duration: baseRuleset.duration,
645
+ weight: uint112(
646
+ deriveWeightFrom({
647
+ projectId: projectId,
648
+ baseRulesetStart: baseRuleset.start,
649
+ baseRulesetDuration: baseRuleset.duration,
650
+ baseRulesetWeight: baseRuleset.weight,
651
+ baseRulesetWeightCutPercent: baseRuleset.weightCutPercent,
652
+ baseRulesetCacheId: baseRuleset.id,
653
+ start: start
654
+ })
655
+ ),
656
+ weightCutPercent: baseRuleset.weightCutPercent,
657
+ approvalHook: baseRuleset.approvalHook,
658
+ metadata: baseRuleset.metadata
659
+ });
660
+ }
661
+
662
+ /// @notice The ruleset up next for a project, if one exists, whether or not that ruleset has been approved.
663
+ /// @dev A value of 0 is returned if no ruleset was found.
664
+ /// @dev Assumes the project has a `latestRulesetIdOf` value.
665
+ /// @param projectId The ID of the project to check for an upcoming approvable ruleset.
666
+ /// @return rulesetId The `rulesetId` of the upcoming approvable ruleset if one exists, or 0 if one doesn't exist.
667
+ function _upcomingApprovableRulesetIdOf(uint256 projectId) internal view returns (uint256 rulesetId) {
668
+ // Get a reference to the ID of the project's latest ruleset.
669
+ rulesetId = latestRulesetIdOf[projectId];
670
+
671
+ // Get the struct for the latest ruleset.
672
+ JBRuleset memory ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
673
+
674
+ // There is no upcoming ruleset if the latest ruleset has already started.
675
+ // slither-disable-next-line incorrect-equality
676
+ if (block.timestamp >= ruleset.start) return 0;
677
+
678
+ // If this is the first ruleset, it is queued.
679
+ // slither-disable-next-line incorrect-equality
680
+ if (ruleset.cycleNumber == 1) return rulesetId;
681
+
682
+ // Get a reference to the ID of the ruleset the latest ruleset was based on.
683
+ uint256 basedOnId = ruleset.basedOnId;
684
+
685
+ // Get the necessary properties for the base ruleset.
686
+ JBRuleset memory baseRuleset;
687
+
688
+ // Find the base ruleset that is not still queued.
689
+ while (true) {
690
+ baseRuleset = _getStructFor({projectId: projectId, rulesetId: basedOnId});
691
+
692
+ // If the base ruleset starts in the future,
693
+ if (block.timestamp < baseRuleset.start) {
694
+ // Set the `rulesetId` to the one found.
695
+ rulesetId = baseRuleset.id;
696
+ // Check the ruleset it was based on in the next iteration.
697
+ basedOnId = baseRuleset.basedOnId;
698
+ } else {
699
+ // Break out of the loop when a base ruleset which has already started is found.
700
+ break;
701
+ }
702
+ }
703
+
704
+ // Get the ruleset struct for the ID found.
705
+ ruleset = _getStructFor({projectId: projectId, rulesetId: rulesetId});
706
+
707
+ // If the latest ruleset doesn't start until after another base ruleset return 0.
708
+ if (baseRuleset.duration != 0 && block.timestamp < ruleset.start - baseRuleset.duration) {
709
+ return 0;
710
+ }
711
+ }
712
+
713
+ //*********************************************************************//
714
+ // ---------------------- external transactions ---------------------- //
715
+ //*********************************************************************//
716
+
717
+ /// @notice Queues the upcoming approvable ruleset for the specified project.
718
+ /// @dev Only a project's current controller can queue its rulesets.
719
+ /// @param projectId The ID of the project to queue the ruleset for.
720
+ /// @param duration The number of seconds the ruleset lasts for, after which a new ruleset starts.
721
+ /// - A `duration` of 0 means this ruleset will remain active until the project owner queues a new ruleset. That new
722
+ /// ruleset will start immediately.
723
+ /// - A ruleset with a non-zero `duration` applies until the duration ends – any newly queued rulesets will be
724
+ /// *queued* to take effect afterwards.
725
+ /// - If a duration ends and no new rulesets are queued, the ruleset rolls over to a new ruleset with the same rules
726
+ /// (except for a new `start` timestamp and a cut `weight`).
727
+ /// @param weight A fixed point number with 18 decimals that contracts can use to base arbitrary calculations on.
728
+ /// Payment terminals generally use this to determine how many tokens should be minted when the project is paid.
729
+ /// @param weightCutPercent A fraction (out of `JBConstants.MAX_WEIGHT_CUT_PERCENT`) to reduce the next ruleset's
730
+ /// `weight`
731
+ /// by.
732
+ /// - If a ruleset specifies a non-zero `weight`, the `weightCutPercent` does not apply.
733
+ /// - If the `weightCutPercent` is 0, the `weight` stays the same.
734
+ /// - If the `weightCutPercent` is 10% of `JBConstants.MAX_WEIGHT_CUT_PERCENT`, next ruleset's `weight` will be 90%
735
+ /// of the
736
+ /// current
737
+ /// one.
738
+ /// @param approvalHook A contract which dictates whether a proposed ruleset should be accepted or rejected. It can
739
+ /// be used to constrain a project owner's ability to change ruleset parameters over time.
740
+ /// @param metadata Arbitrary extra data to associate with this ruleset. This metadata is not used by `JBRulesets`.
741
+ /// @param mustStartAtOrAfter The earliest time the ruleset can start. The ruleset cannot start before this
742
+ /// timestamp.
743
+ /// @return The struct of the new ruleset.
744
+ function queueFor(
745
+ uint256 projectId,
746
+ uint256 duration,
747
+ uint256 weight,
748
+ uint256 weightCutPercent,
749
+ IJBRulesetApprovalHook approvalHook,
750
+ uint256 metadata,
751
+ uint256 mustStartAtOrAfter
752
+ )
753
+ external
754
+ override
755
+ onlyControllerOf(projectId)
756
+ returns (JBRuleset memory)
757
+ {
758
+ // Duration must fit in a uint32.
759
+ if (duration > type(uint32).max) revert JBRulesets_InvalidRulesetDuration(duration, type(uint32).max);
760
+
761
+ // Weight cut percent must be less than or equal to 100%.
762
+ if (weightCutPercent > JBConstants.MAX_WEIGHT_CUT_PERCENT) {
763
+ revert JBRulesets_InvalidWeightCutPercent(weightCutPercent);
764
+ }
765
+
766
+ // Weight must fit into a uint112.
767
+ if (weight > type(uint112).max) revert JBRulesets_InvalidWeight(weight, type(uint112).max);
768
+
769
+ // If the start date is not set, set it to be the current timestamp.
770
+ if (mustStartAtOrAfter == 0) {
771
+ mustStartAtOrAfter = block.timestamp;
772
+ }
773
+
774
+ // Make sure the min start date fits in a uint48, and that the start date of the following ruleset will also fit
775
+ // within the max.
776
+ if (mustStartAtOrAfter + duration > type(uint48).max) {
777
+ revert JBRulesets_InvalidRulesetEndTime(mustStartAtOrAfter + duration, type(uint48).max);
778
+ }
779
+
780
+ // Approval hook should be a valid contract, supporting the correct interface
781
+ if (approvalHook != IJBRulesetApprovalHook(address(0))) {
782
+ // Revert if there isn't a contract at the address
783
+ if (address(approvalHook).code.length == 0) revert JBRulesets_InvalidRulesetApprovalHook(approvalHook);
784
+
785
+ // Make sure the approval hook supports the expected interface.
786
+ try approvalHook.supportsInterface(type(IJBRulesetApprovalHook).interfaceId) returns (bool doesSupport) {
787
+ if (!doesSupport) revert JBRulesets_InvalidRulesetApprovalHook(approvalHook); // Contract exists at the
788
+ // address but
789
+ // with the
790
+ // wrong interface
791
+ } catch {
792
+ revert JBRulesets_InvalidRulesetApprovalHook(approvalHook); // No ERC165 support
793
+ }
794
+ }
795
+
796
+ // Get a reference to the latest ruleset's ID.
797
+ uint256 latestId = latestRulesetIdOf[projectId];
798
+
799
+ // The new rulesetId timestamp is now, or an increment from now if the current timestamp is taken.
800
+ uint256 rulesetId = latestId >= block.timestamp ? latestId + 1 : block.timestamp;
801
+
802
+ // Set up the ruleset by configuring intrinsic properties.
803
+ _configureIntrinsicPropertiesFor({
804
+ projectId: projectId, rulesetId: rulesetId, weight: weight, mustStartAtOrAfter: mustStartAtOrAfter
805
+ });
806
+
807
+ // Efficiently stores the ruleset's user-defined properties.
808
+ // If all user config properties are zero, no need to store anything as the default value will have the same
809
+ // outcome.
810
+ if (approvalHook != IJBRulesetApprovalHook(address(0)) || duration > 0 || weightCutPercent > 0) {
811
+ // approval hook in bits 0-159 bytes.
812
+ uint256 packed = uint160(address(approvalHook));
813
+
814
+ // duration in bits 160-191 bytes.
815
+ packed |= duration << 160;
816
+
817
+ // weightCutPercent in bits 192-223 bytes.
818
+ packed |= weightCutPercent << 192;
819
+
820
+ // Set in storage.
821
+ _packedUserPropertiesOf[projectId][rulesetId] = packed;
822
+ }
823
+
824
+ // Set the metadata if needed.
825
+ if (metadata > 0) _metadataOf[projectId][rulesetId] = metadata;
826
+
827
+ emit RulesetQueued({
828
+ rulesetId: rulesetId,
829
+ projectId: projectId,
830
+ duration: duration,
831
+ weight: weight,
832
+ weightCutPercent: weightCutPercent,
833
+ approvalHook: approvalHook,
834
+ metadata: metadata,
835
+ mustStartAtOrAfter: mustStartAtOrAfter,
836
+ caller: msg.sender
837
+ });
838
+
839
+ // Return the struct for the new ruleset's ID.
840
+ return _getStructFor({projectId: projectId, rulesetId: rulesetId});
841
+ }
842
+
843
+ /// @notice Cache the value of the ruleset weight.
844
+ /// @param projectId The ID of the project having its ruleset weight cached.
845
+ function updateRulesetWeightCache(uint256 projectId) external override {
846
+ // Keep a reference to the struct for the latest queued ruleset.
847
+ // The cached value will be based on this struct.
848
+ JBRuleset memory latestQueuedRuleset =
849
+ _getStructFor({projectId: projectId, rulesetId: latestRulesetIdOf[projectId]});
850
+
851
+ // Nothing to cache if the latest ruleset doesn't have a duration or a weight cut percent.
852
+ // slither-disable-next-line incorrect-equality
853
+ if (latestQueuedRuleset.duration == 0 || latestQueuedRuleset.weightCutPercent == 0) return;
854
+
855
+ // Get a reference to the current cache.
856
+ JBRulesetWeightCache storage cache = _weightCacheOf[projectId][latestQueuedRuleset.id];
857
+
858
+ // Determine the largest start timestamp the cache can be filled to.
859
+ // Cap the advance to the cache lookup threshold per call to stay within the iteration limit in
860
+ // deriveWeightFrom.
861
+ // Multiple calls are needed to advance the cache for large cycle gaps.
862
+ uint256 maxStart = latestQueuedRuleset.start
863
+ + (cache.weightCutMultiple + _WEIGHT_CUT_MULTIPLE_CACHE_LOOKUP_THRESHOLD) * latestQueuedRuleset.duration;
864
+
865
+ // Determine the start timestamp to derive a weight from for the cache.
866
+ uint256 start = block.timestamp < maxStart ? block.timestamp : maxStart;
867
+
868
+ // The difference between the start of the latest queued ruleset and the start of the ruleset we're caching the
869
+ // weight of.
870
+ uint256 startDistance = start - latestQueuedRuleset.start;
871
+
872
+ // Calculate the weight cut multiple.
873
+ uint168 weightCutMultiple;
874
+ unchecked {
875
+ weightCutMultiple = uint168(startDistance / latestQueuedRuleset.duration);
876
+ }
877
+
878
+ // Store the new values.
879
+ cache.weight = uint112(
880
+ deriveWeightFrom({
881
+ projectId: projectId,
882
+ baseRulesetStart: latestQueuedRuleset.start,
883
+ baseRulesetDuration: latestQueuedRuleset.duration,
884
+ baseRulesetWeight: latestQueuedRuleset.weight,
885
+ baseRulesetWeightCutPercent: latestQueuedRuleset.weightCutPercent,
886
+ baseRulesetCacheId: latestQueuedRuleset.id,
887
+ start: start
888
+ })
889
+ );
890
+ cache.weightCutMultiple = weightCutMultiple;
891
+
892
+ emit WeightCacheUpdated({
893
+ projectId: projectId, weight: cache.weight, weightCutMultiple: weightCutMultiple, caller: msg.sender
894
+ });
895
+ }
896
+
897
+ //*********************************************************************//
898
+ // ------------------------ internal functions ----------------------- //
899
+ //*********************************************************************//
900
+
901
+ /// @notice Updates the latest ruleset for this project if it exists. If there is no ruleset, initializes one.
902
+ /// @param projectId The ID of the project to update the latest ruleset for.
903
+ /// @param rulesetId The timestamp of when the ruleset was queued.
904
+ /// @param weight The weight to store in the queued ruleset.
905
+ /// @param mustStartAtOrAfter The earliest time the ruleset can start. The ruleset cannot start before this
906
+ /// timestamp.
907
+ function _configureIntrinsicPropertiesFor(
908
+ uint256 projectId,
909
+ uint256 rulesetId,
910
+ uint256 weight,
911
+ uint256 mustStartAtOrAfter
912
+ )
913
+ internal
914
+ {
915
+ // Keep a reference to the project's latest ruleset's ID.
916
+ uint256 latestId = latestRulesetIdOf[projectId];
917
+
918
+ // If the project doesn't have a ruleset yet, initialize one.
919
+ // slither-disable-next-line incorrect-equality
920
+ if (latestId == 0) {
921
+ // Use an empty ruleset as the base.
922
+ return _initializeRulesetFor({
923
+ projectId: projectId,
924
+ baseRuleset: _getStructFor({projectId: 0, rulesetId: 0}),
925
+ rulesetId: rulesetId,
926
+ mustStartAtOrAfter: mustStartAtOrAfter,
927
+ weight: weight
928
+ });
929
+ }
930
+
931
+ // Get a reference to the latest ruleset's struct.
932
+ JBRuleset memory baseRuleset = _getStructFor({projectId: projectId, rulesetId: latestId});
933
+
934
+ // Get a reference to the approval status.
935
+ JBApprovalStatus approvalStatus = _approvalStatusOf({projectId: projectId, ruleset: baseRuleset});
936
+
937
+ // If the base ruleset has started but wasn't approved if a approval hook exists
938
+ // OR it hasn't started but is currently approved
939
+ // OR it hasn't started but it is likely to be approved and takes place before the proposed one,
940
+ // set the struct to be the ruleset it's based on, which carries the latest approved ruleset.
941
+ if (
942
+ (block.timestamp >= baseRuleset.start
943
+ && approvalStatus != JBApprovalStatus.Approved
944
+ && approvalStatus != JBApprovalStatus.Empty)
945
+ || (block.timestamp < baseRuleset.start
946
+ && mustStartAtOrAfter < baseRuleset.start + baseRuleset.duration
947
+ && approvalStatus != JBApprovalStatus.Approved)
948
+ || (block.timestamp < baseRuleset.start
949
+ && mustStartAtOrAfter >= baseRuleset.start + baseRuleset.duration
950
+ && approvalStatus != JBApprovalStatus.Approved
951
+ && approvalStatus != JBApprovalStatus.ApprovalExpected
952
+ && approvalStatus != JBApprovalStatus.Empty)
953
+ ) {
954
+ baseRuleset = _getStructFor({projectId: projectId, rulesetId: baseRuleset.basedOnId});
955
+ }
956
+
957
+ // Make sure the ruleset starts after the base ruleset.
958
+ if (baseRuleset.start > mustStartAtOrAfter) mustStartAtOrAfter = baseRuleset.start;
959
+
960
+ // The time when the duration of the base ruleset's approval hook has finished.
961
+ // If the provided ruleset has no approval hook, return 0 (no constraint on start time).
962
+ uint256 timestampAfterApprovalHook;
963
+ if (baseRuleset.approvalHook != IJBRulesetApprovalHook(address(0))) {
964
+ try baseRuleset.approvalHook.DURATION() returns (uint256 duration) {
965
+ timestampAfterApprovalHook = rulesetId + duration;
966
+ } catch {
967
+ // If DURATION() reverts, treat as no approval hook constraint.
968
+ timestampAfterApprovalHook = 0;
969
+ }
970
+ }
971
+
972
+ _initializeRulesetFor({
973
+ projectId: projectId,
974
+ baseRuleset: baseRuleset,
975
+ rulesetId: rulesetId,
976
+ // Can only start after the approval hook.
977
+ mustStartAtOrAfter: timestampAfterApprovalHook > mustStartAtOrAfter
978
+ ? timestampAfterApprovalHook
979
+ : mustStartAtOrAfter,
980
+ weight: weight
981
+ });
982
+ }
983
+
984
+ /// @notice Initializes a ruleset with the specified properties.
985
+ /// @param projectId The ID of the project to initialize the ruleset for.
986
+ /// @param baseRuleset The ruleset struct to base the newly initialized one on.
987
+ /// @param rulesetId The `rulesetId` for the ruleset being initialized.
988
+ /// @param mustStartAtOrAfter The earliest time the ruleset can start. The ruleset cannot start before this
989
+ /// timestamp.
990
+ /// @param weight The weight to give the newly initialized ruleset.
991
+ function _initializeRulesetFor(
992
+ uint256 projectId,
993
+ JBRuleset memory baseRuleset,
994
+ uint256 rulesetId,
995
+ uint256 mustStartAtOrAfter,
996
+ uint256 weight
997
+ )
998
+ internal
999
+ {
1000
+ // If there is no base, initialize a first ruleset.
1001
+ // slither-disable-next-line incorrect-equality
1002
+ if (baseRuleset.cycleNumber == 0) {
1003
+ // Set fresh intrinsic properties.
1004
+ _packAndStoreIntrinsicPropertiesOf({
1005
+ rulesetId: rulesetId,
1006
+ projectId: projectId,
1007
+ rulesetCycleNumber: 1,
1008
+ weight: weight,
1009
+ basedOnId: baseRuleset.id,
1010
+ start: mustStartAtOrAfter
1011
+ });
1012
+ } else {
1013
+ // Derive the correct next start time from the base.
1014
+ uint256 start = deriveStartFrom({
1015
+ baseRulesetStart: baseRuleset.start,
1016
+ baseRulesetDuration: baseRuleset.duration,
1017
+ mustStartAtOrAfter: mustStartAtOrAfter
1018
+ });
1019
+
1020
+ // A weight of 1 is a special case that represents inheriting the cut weight of the previous
1021
+ // ruleset.
1022
+ weight = weight == 1
1023
+ ? deriveWeightFrom({
1024
+ projectId: projectId,
1025
+ baseRulesetStart: baseRuleset.start,
1026
+ baseRulesetDuration: baseRuleset.duration,
1027
+ baseRulesetWeight: baseRuleset.weight,
1028
+ baseRulesetWeightCutPercent: baseRuleset.weightCutPercent,
1029
+ baseRulesetCacheId: baseRuleset.id,
1030
+ start: start
1031
+ })
1032
+ : weight;
1033
+
1034
+ // Derive the correct ruleset cycle number.
1035
+ uint256 rulesetCycleNumber = deriveCycleNumberFrom({
1036
+ baseRulesetCycleNumber: baseRuleset.cycleNumber,
1037
+ baseRulesetStart: baseRuleset.start,
1038
+ baseRulesetDuration: baseRuleset.duration,
1039
+ start: start
1040
+ });
1041
+
1042
+ // Update the intrinsic properties.
1043
+ _packAndStoreIntrinsicPropertiesOf({
1044
+ rulesetId: rulesetId,
1045
+ projectId: projectId,
1046
+ rulesetCycleNumber: rulesetCycleNumber,
1047
+ weight: weight,
1048
+ basedOnId: baseRuleset.id,
1049
+ start: start
1050
+ });
1051
+ }
1052
+
1053
+ // Set the project's latest ruleset configuration.
1054
+ latestRulesetIdOf[projectId] = rulesetId;
1055
+
1056
+ emit RulesetInitialized({
1057
+ rulesetId: rulesetId, projectId: projectId, basedOnId: baseRuleset.id, caller: msg.sender
1058
+ });
1059
+ }
1060
+
1061
+ /// @notice Efficiently stores the provided intrinsic properties of a ruleset.
1062
+ /// @param rulesetId The `rulesetId` of the ruleset to pack and store for.
1063
+ /// @param projectId The ID of the project the ruleset belongs to.
1064
+ /// @param rulesetCycleNumber The cycle number of the ruleset.
1065
+ /// @param weight The weight of the ruleset.
1066
+ /// @param basedOnId The `rulesetId` of the ruleset this ruleset was based on.
1067
+ /// @param start The start time of this ruleset.
1068
+ function _packAndStoreIntrinsicPropertiesOf(
1069
+ uint256 rulesetId,
1070
+ uint256 projectId,
1071
+ uint256 rulesetCycleNumber,
1072
+ uint256 weight,
1073
+ uint256 basedOnId,
1074
+ uint256 start
1075
+ )
1076
+ internal
1077
+ {
1078
+ // `weight` in bits 0-111.
1079
+ uint256 packed = weight;
1080
+
1081
+ // `basedOnId` in bits 112-159.
1082
+ packed |= basedOnId << 112;
1083
+
1084
+ // `start` in bits 160-207.
1085
+ packed |= start << 160;
1086
+
1087
+ // cycle number in bits 208-255.
1088
+ packed |= rulesetCycleNumber << 208;
1089
+
1090
+ // Store the packed value.
1091
+ _packedIntrinsicPropertiesOf[projectId][rulesetId] = packed;
1092
+ }
1093
+ }