@bananapus/core-v6 0.0.38 → 0.0.40

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 (56) hide show
  1. package/foundry.toml +1 -1
  2. package/package.json +1 -1
  3. package/src/JBChainlinkV3PriceFeed.sol +4 -1
  4. package/src/JBChainlinkV3SequencerPriceFeed.sol +4 -2
  5. package/src/JBController.sol +67 -58
  6. package/src/JBDeadline.sol +4 -4
  7. package/src/JBDirectory.sol +38 -36
  8. package/src/JBERC20.sol +10 -9
  9. package/src/JBFeelessAddresses.sol +6 -3
  10. package/src/JBFundAccessLimits.sol +26 -22
  11. package/src/JBMultiTerminal.sol +128 -125
  12. package/src/JBPermissions.sol +35 -38
  13. package/src/JBPrices.sol +25 -20
  14. package/src/JBProjects.sol +6 -3
  15. package/src/JBRulesets.sol +45 -42
  16. package/src/JBSplits.sol +19 -17
  17. package/src/JBTerminalStore.sol +57 -50
  18. package/src/JBTokens.sol +42 -32
  19. package/src/abstract/JBControlled.sol +3 -1
  20. package/src/abstract/JBPermissioned.sol +3 -1
  21. package/src/enums/JBApprovalStatus.sol +7 -1
  22. package/src/interfaces/IJBCashOutTerminal.sol +5 -5
  23. package/src/interfaces/IJBController.sol +13 -12
  24. package/src/interfaces/IJBDirectory.sol +3 -1
  25. package/src/interfaces/IJBMigratable.sol +5 -5
  26. package/src/interfaces/IJBMultiTerminal.sol +3 -2
  27. package/src/interfaces/IJBPayoutTerminal.sol +3 -3
  28. package/src/interfaces/IJBPermissions.sol +6 -5
  29. package/src/interfaces/IJBPermitTerminal.sol +1 -1
  30. package/src/interfaces/IJBPrices.sol +7 -5
  31. package/src/interfaces/IJBRulesets.sol +2 -1
  32. package/src/interfaces/IJBSplits.sol +2 -1
  33. package/src/interfaces/IJBTerminal.sol +13 -11
  34. package/src/interfaces/IJBTerminalStore.sol +23 -21
  35. package/src/interfaces/IJBTokens.sol +8 -7
  36. package/src/libraries/JBCashOuts.sol +8 -3
  37. package/src/libraries/JBConstants.sol +12 -3
  38. package/src/libraries/JBCurrencyIds.sol +2 -0
  39. package/src/libraries/JBFees.sol +5 -1
  40. package/src/libraries/JBFixedPointNumber.sol +2 -0
  41. package/src/libraries/JBPayoutSplitGroupLib.sol +10 -7
  42. package/src/libraries/JBRulesetMetadataResolver.sol +4 -0
  43. package/src/libraries/JBSplitGroupIds.sol +2 -1
  44. package/src/libraries/JBSurplus.sol +4 -2
  45. package/src/periphery/JBMatchingPriceFeed.sol +2 -0
  46. package/src/structs/JBAccountingContext.sol +7 -4
  47. package/src/structs/JBAfterCashOutRecordedContext.sol +8 -8
  48. package/src/structs/JBAfterPayRecordedContext.sol +5 -5
  49. package/src/structs/JBBeforeCashOutRecordedContext.sol +7 -7
  50. package/src/structs/JBBeforePayRecordedContext.sol +5 -5
  51. package/src/structs/JBFundAccessLimitGroup.sol +10 -17
  52. package/src/structs/JBPermissionsData.sol +3 -3
  53. package/src/structs/JBRuleset.sol +18 -26
  54. package/src/structs/JBRulesetConfig.sol +13 -25
  55. package/src/structs/JBRulesetMetadata.sol +25 -32
  56. package/src/structs/JBSplitHookContext.sol +2 -2
package/foundry.toml CHANGED
@@ -17,7 +17,7 @@ fail_on_revert = false
17
17
  ethereum = "${RPC_ETHEREUM_MAINNET}"
18
18
 
19
19
  [lint]
20
- exclude_lints = ["block-timestamp", "mixed-case-variable", "pascal-case-struct"]
20
+ exclude_lints = ["mixed-case-variable", "pascal-case-struct"]
21
21
  [fmt]
22
22
  number_underscore = "thousands"
23
23
  multiline_func_header = "all"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/core-v6",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -6,7 +6,9 @@ import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interf
6
6
  import {IJBPriceFeed} from "./interfaces/IJBPriceFeed.sol";
7
7
  import {JBFixedPointNumber} from "./libraries/JBFixedPointNumber.sol";
8
8
 
9
- /// @notice An `IJBPriceFeed` implementation that reports prices from a Chainlink `AggregatorV3Interface`.
9
+ /// @notice A price feed adapter that reads from a Chainlink V3 aggregator. Reverts if the price is stale (older than
10
+ /// `THRESHOLD` seconds), negative, or from an incomplete round — protecting against serving outdated oracle data.
11
+ /// Used by `JBPrices` to convert between currencies (e.g. ETH/USD).
10
12
  contract JBChainlinkV3PriceFeed is IJBPriceFeed {
11
13
  // A library that provides utility for fixed point numbers.
12
14
  using JBFixedPointNumber for uint256;
@@ -60,6 +62,7 @@ contract JBChainlinkV3PriceFeed is IJBPriceFeed {
60
62
  if (answeredInRound < roundId) revert JBChainlinkV3PriceFeed_IncompleteRound();
61
63
 
62
64
  // Make sure the price's update threshold is met.
65
+ // forge-lint: disable-next-line(block-timestamp)
63
66
  if (block.timestamp > THRESHOLD + updatedAt) {
64
67
  revert JBChainlinkV3PriceFeed_StalePrice(block.timestamp, THRESHOLD, updatedAt);
65
68
  }
@@ -6,8 +6,9 @@ import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interf
6
6
 
7
7
  import {JBChainlinkV3PriceFeed} from "./JBChainlinkV3PriceFeed.sol";
8
8
 
9
- /// @notice An `IJBPriceFeed` implementation that reports prices from a Chainlink `AggregatorV3Interface` from
10
- /// optimistic sequencers.
9
+ /// @notice Extends `JBChainlinkV3PriceFeed` with L2 sequencer uptime checks (for Optimism, Arbitrum, etc.). Reverts if
10
+ /// the sequencer is down or has not been back online for at least `GRACE_PERIOD_TIME` seconds — preventing stale
11
+ /// prices from to use immediately after an outage.
11
12
  contract JBChainlinkV3SequencerPriceFeed is JBChainlinkV3PriceFeed {
12
13
  //*********************************************************************//
13
14
  // --------------------------- custom errors ------------------------- //
@@ -64,6 +65,7 @@ contract JBChainlinkV3SequencerPriceFeed is JBChainlinkV3PriceFeed {
64
65
  if (startedAt == 0) revert JBChainlinkV3SequencerPriceFeed_InvalidRound();
65
66
 
66
67
  // Revert if sequencer has too recently restarted or is currently down.
68
+ // forge-lint: disable-next-line(block-timestamp)
67
69
  if (block.timestamp <= GRACE_PERIOD_TIME + startedAt || answer != 0) {
68
70
  revert JBChainlinkV3SequencerPriceFeed_SequencerDownOrRestarting(
69
71
  block.timestamp, GRACE_PERIOD_TIME, startedAt
@@ -41,8 +41,13 @@ import {JBSplitGroup} from "./structs/JBSplitGroup.sol";
41
41
  import {JBSplitHookContext} from "./structs/JBSplitHookContext.sol";
42
42
  import {JBTerminalConfig} from "./structs/JBTerminalConfig.sol";
43
43
 
44
- /// @notice `JBController` coordinates rulesets and project tokens, and is the entry point for most operations related
45
- /// to rulesets and project tokens.
44
+ /// @notice The orchestrator for every Juicebox project's lifecycle. Use the controller to launch a project, queue new
45
+ /// rulesets (funding cycles), mint or burn tokens, deploy an ERC-20, distribute reserved tokens, and manage
46
+ /// permissions. The controller coordinates between the terminal (money), rulesets (rules), tokens (issuance), and
47
+ /// splits (distribution).
48
+ /// @dev Supports ERC-2771 meta-transactions. Implements `IJBMigratable` for controller-to-controller migration.
49
+ /// An omnichain deployer address is trusted to launch and queue rulesets on behalf of any project for cross-chain
50
+ /// coordination.
46
51
  contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigratable {
47
52
  // A library that parses packed ruleset metadata into a friendlier format.
48
53
  using JBRulesetMetadataResolver for JBRuleset;
@@ -166,11 +171,12 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
166
171
  // ---------------------- external transactions ---------------------- //
167
172
  //*********************************************************************//
168
173
 
169
- /// @notice Add a price feed for a project.
170
- /// @dev Can only be called by the project's owner or an address with the owner's permission to `ADD_PRICE_FEED`.
174
+ /// @notice Registers a price feed so the project can use a new currency for payout limits or surplus allowances.
175
+ /// @dev Can only be called by the project's owner or an operator with `ADD_PRICE_FEED` permission. The current
176
+ /// ruleset must have `allowAddPriceFeed` enabled.
171
177
  /// @param projectId The ID of the project to add the feed for.
172
178
  /// @param pricingCurrency The currency the feed's output price is in terms of.
173
- /// @param unitCurrency The currency being priced by the feed.
179
+ /// @param unitCurrency The currency the feed prices.
174
180
  /// @param feed The address of the price feed to add.
175
181
  function addPriceFeedFor(
176
182
  uint256 projectId,
@@ -198,7 +204,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
198
204
 
199
205
  /// @notice Called after this controller has been set as the project's controller in the directory.
200
206
  /// @dev Can only be called by the directory.
201
- /// @param from The controller being migrated from.
207
+ /// @param from The controller to migrate from.
202
208
  /// @param projectId The ID of the project that migrated to this controller.
203
209
  function afterReceiveMigrationFrom(IERC165 from, uint256 projectId) external view override {
204
210
  from; // Suppress unused variable warning.
@@ -208,9 +214,9 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
208
214
  if (_msgSender() != address(DIRECTORY)) revert JBController_OnlyDirectory(_msgSender(), DIRECTORY);
209
215
  }
210
216
 
211
- /// @notice Prepares this controller to receive a project being migrated from another controller.
217
+ /// @notice Prepares this controller to receive a project to migrate from another controller.
212
218
  /// @dev This controller should not be the project's controller yet.
213
- /// @param from The controller being migrated from.
219
+ /// @param from The controller to migrate from.
214
220
  /// @param projectId The ID of the project that will migrate to this controller.
215
221
  function beforeReceiveMigrationFrom(IERC165 from, uint256 projectId) external override {
216
222
  // Keep a reference to the sender.
@@ -234,11 +240,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
234
240
  }
235
241
  }
236
242
 
237
- /// @notice Burns a project's tokens or credits from the specific holder's balance.
238
- /// @dev Can only be called by the holder, an address with the holder's permission to `BURN_TOKENS`, or a project's
239
- /// terminal.
240
- /// @param holder The address whose tokens are being burned.
241
- /// @param projectId The ID of the project whose tokens are being burned.
243
+ /// @notice Burns a holder's project tokens (or credits), permanently removing them from supply. Used by terminals
244
+ /// during cash outs, or directly by holders who want to burn voluntarily.
245
+ /// @dev Can only be called by the holder, an operator with `BURN_TOKENS` permission, or a project terminal.
246
+ /// @param holder The address to burn tokens for.
247
+ /// @param projectId The ID of the project to burn tokens for.
242
248
  /// @param tokenCount The number of tokens to burn.
243
249
  /// @param memo A memo to pass along to the emitted event.
244
250
  function burnTokensOf(
@@ -269,10 +275,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
269
275
  TOKENS.burnFrom({holder: holder, projectId: projectId, count: tokenCount});
270
276
  }
271
277
 
272
- /// @notice Redeem credits to claim tokens into a `beneficiary`'s account.
273
- /// @dev Can only be called by the credit holder or an address with the holder's permission to `CLAIM_TOKENS`.
278
+ /// @notice Converts internal credits into the project's ERC-20 token, transferring them to the beneficiary's
279
+ /// wallet. Credits and ERC-20 tokens are interchangeable this just makes them transferable/tradeable.
280
+ /// @dev Can only be called by the credit holder or an operator with `CLAIM_TOKENS` permission.
274
281
  /// @param holder The address to redeem credits from.
275
- /// @param projectId The ID of the project whose tokens are being claimed.
282
+ /// @param projectId The ID of the project to claim tokens for.
276
283
  /// @param tokenCount The number of tokens to claim.
277
284
  /// @param beneficiary The account the claimed tokens will go to.
278
285
  function claimTokensFor(
@@ -290,9 +297,10 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
290
297
  TOKENS.claimTokensFor({holder: holder, projectId: projectId, count: tokenCount, beneficiary: beneficiary});
291
298
  }
292
299
 
293
- /// @notice Deploys an ERC-20 token for a project. It will be used when claiming tokens (with credits).
294
- /// @dev Deploys the project's ERC-20 contract.
295
- /// @dev Can only be called by the project's owner or an address with the owner's permission to `DEPLOY_ERC20`.
300
+ /// @notice Deploys a new ERC-20 token for a project. Once deployed, holders can claim their credits as this token.
301
+ /// Includes ERC20Votes (governance) and ERC20Permit (gasless approvals).
302
+ /// @dev Can only be called by the project's owner or an operator with `DEPLOY_ERC20` permission.
303
+ /// @dev Each project can only have one ERC-20 deployed — calling this again after deployment will revert.
296
304
  /// @param projectId The ID of the project to deploy the ERC-20 for.
297
305
  /// @param name The ERC-20's name.
298
306
  /// @param symbol The ERC-20's symbol.
@@ -330,9 +338,9 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
330
338
  /// terminal.
331
339
  /// @dev Can only be called by this controller.
332
340
  /// @param terminal The terminal to pay.
333
- /// @param projectId The ID of the project being paid.
334
- /// @param token The token being paid with.
335
- /// @param splitTokenCount The number of tokens being paid.
341
+ /// @param projectId The ID of the project to pay.
342
+ /// @param token The token to pay with.
343
+ /// @param splitTokenCount The number of tokens to pay.
336
344
  /// @param beneficiary The payment's beneficiary.
337
345
  /// @param metadata The pay metadata sent to the terminal.
338
346
  function executePayReservedTokenToTerminal(
@@ -368,10 +376,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
368
376
  }
369
377
  }
370
378
 
371
- /// @notice Creates a project.
372
- /// @dev This will mint the project's ERC-721 to the `owner`'s address, queue the specified rulesets, and set up the
373
- /// specified splits and terminals. Each operation within this transaction can be done in sequence separately.
374
- /// @dev Anyone can deploy a project to any `owner`'s address.
379
+ /// @notice Creates a new Juicebox project in one transaction — mints the project NFT, queues initial rulesets,
380
+ /// and
381
+ /// configures terminals. This is the primary entry point for launching a project.
382
+ /// @dev Anyone can call this on behalf of any owner. Each sub-operation (mint, queue, configure) can also be done
383
+ /// individually if needed.
375
384
  /// @param owner The project's owner. The project ERC-721 will be minted to this address.
376
385
  /// @param projectUri The project's metadata URI. This is typically an IPFS hash, optionally with the `ipfs://`
377
386
  /// prefix. This can be updated by the project's owner.
@@ -414,10 +423,10 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
414
423
  });
415
424
  }
416
425
 
417
- /// @notice Queue a project's initial rulesets and set up terminals for it. Projects which already have rulesets
418
- /// should use `queueRulesetsOf(...)`.
419
- /// @dev Each operation within this transaction can be done in sequence separately.
420
- /// @dev Can only be called by the project's owner or an address with the owner's permission to `LAUNCH_RULESETS`.
426
+ /// @notice Queues the first rulesets for an existing project and configures its terminals. For projects that
427
+ /// already have active rulesets, use `queueRulesetsOf(...)` instead.
428
+ /// @dev Can only be called by the project's owner or an operator with `LAUNCH_RULESETS` permission.
429
+ /// @dev Each sub-operation can also be done individually if needed.
421
430
  /// @param projectId The ID of the project to launch rulesets for.
422
431
  /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
423
432
  /// @param rulesetConfigurations The rulesets to queue.
@@ -508,13 +517,11 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
508
517
  if (pendingReservedTokenBalance != 0) revert JBController_PendingReservedTokens(pendingReservedTokenBalance);
509
518
  }
510
519
 
511
- /// @notice Add new project tokens or credits to the specified beneficiary's balance. Optionally, reserve a portion
512
- /// according to the ruleset's reserved percent.
513
- /// @dev Can only be called by the project's owner, an address with the owner's permission to `MINT_TOKENS`, one of
514
- /// the project's terminals, or the project's data hook.
515
- /// @dev If the ruleset's metadata has `allowOwnerMinting` set to `false`, this function can only be called by the
516
- /// project's terminals or data hook.
517
- /// @param projectId The ID of the project whose tokens are being minted.
520
+ /// @notice Mints new project tokens to a beneficiary. Optionally reserves a portion according to the ruleset's
521
+ /// reserved percent (which accumulates until `sendReservedTokensToSplitsOf` is called).
522
+ /// @dev Can be called by the project owner, an operator with `MINT_TOKENS` permission, a project terminal, or the
523
+ /// data hook. If `allowOwnerMinting` is false in the current ruleset, only terminals and the data hook can mint.
524
+ /// @param projectId The ID of the project to mint tokens for.
518
525
  /// @param tokenCount The number of tokens to mint, including any reserved tokens.
519
526
  /// @param beneficiary The address which will receive the (non-reserved) tokens.
520
527
  /// @param memo A memo to pass along to the emitted event.
@@ -594,9 +601,9 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
594
601
  }
595
602
  }
596
603
 
597
- /// @notice Add one or more rulesets to the end of a project's ruleset queue. Rulesets take effect after the
598
- /// previous ruleset in the queue ends, and only if they are approved by the previous ruleset's approval hook.
599
- /// @dev Can only be called by the project's owner or an address with the owner's permission to `QUEUE_RULESETS`.
604
+ /// @notice Queues new rulesets to take effect after the current one ends. Each queued ruleset must be approved by
605
+ /// the previous ruleset's approval hook (if one is set) before it can take effect.
606
+ /// @dev Can only be called by the project's owner or an operator with `QUEUE_RULESETS` permission.
600
607
  /// @param projectId The ID of the project to queue rulesets for.
601
608
  /// @param rulesetConfigurations The rulesets to queue.
602
609
  /// @param memo A memo to pass along to the emitted event.
@@ -628,17 +635,18 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
628
635
  emit QueueRulesets({rulesetId: rulesetId, projectId: projectId, memo: memo, caller: _msgSender()});
629
636
  }
630
637
 
631
- /// @notice Sends a project's pending reserved tokens to its reserved token splits.
632
- /// @dev If the project has no reserved token splits, or if they don't add up to 100%, leftover tokens are sent to
633
- /// the project's owner.
638
+ /// @notice Mints and distributes all pending reserved tokens to the project's reserved token split recipients.
639
+ /// Anyone can call this it's permissionless.
640
+ /// @dev If splits don't add up to 100%, the remainder goes to the project owner.
634
641
  /// @param projectId The ID of the project to send reserved tokens for.
635
642
  /// @return The amount of reserved tokens minted and sent.
636
643
  function sendReservedTokensToSplitsOf(uint256 projectId) external override returns (uint256) {
637
644
  return _sendReservedTokensToSplitsOf(projectId);
638
645
  }
639
646
 
640
- /// @notice Sets a project's split groups. The new split groups must include any current splits which are locked.
641
- /// @dev Can only be called by the project's owner or an address with the owner's permission to `SET_SPLIT_GROUPS`.
647
+ /// @notice Configures how a project distributes payouts and reserved tokens. Locked splits from the current
648
+ /// configuration must be preserved in the new split groups.
649
+ /// @dev Can only be called by the project's owner or an operator with `SET_SPLIT_GROUPS` permission.
642
650
  /// @param projectId The ID of the project to set the split groups of.
643
651
  /// @param rulesetId The ID of the ruleset the split groups should be active in. Use a `rulesetId` of 0 to set the
644
652
  /// default split groups, which are used when a ruleset has no splits set. If there are no default splits and no
@@ -686,7 +694,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
686
694
  /// @notice Sets the name and symbol of a project's token.
687
695
  /// @dev Can only be called by the project's owner or an address with the owner's permission to
688
696
  /// `SET_TOKEN_METADATA`.
689
- /// @param projectId The ID of the project whose token is being updated.
697
+ /// @param projectId The ID of the project to update the token for.
690
698
  /// @param name The new name.
691
699
  /// @param symbol The new symbol.
692
700
  function setTokenMetadataOf(uint256 projectId, string calldata name, string calldata symbol) external override {
@@ -716,10 +724,12 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
716
724
  emit SetUri({projectId: projectId, uri: uri, caller: _msgSender()});
717
725
  }
718
726
 
719
- /// @notice Allows a credit holder to transfer credits to another address.
720
- /// @dev Can only be called by the credit holder or an address with the holder's permission to `TRANSFER_CREDITS`.
727
+ /// @notice Transfers internal credits (unclaimed tokens) from one address to another. Credits function like tokens
728
+ /// but live inside the protocol rather than as an ERC-20.
729
+ /// @dev Can only be called by the credit holder or an operator with `TRANSFER_CREDITS` permission. The current
730
+ /// ruleset must not have credit transfers paused.
721
731
  /// @param holder The address to transfer credits from.
722
- /// @param projectId The ID of the project whose credits are being transferred.
732
+ /// @param projectId The ID of the project to transfer credits for.
723
733
  /// @param recipient The address to transfer credits to.
724
734
  /// @param creditCount The number of credits to transfer.
725
735
  function transferCreditsFrom(
@@ -747,8 +757,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
747
757
  // ------------------------- external views -------------------------- //
748
758
  //*********************************************************************//
749
759
 
750
- /// @notice Get an array of a project's rulesets (with metadata) up to a maximum array size, sorted from latest to
751
- /// earliest.
760
+ /// @notice Returns a paginated history of a project's rulesets (with decoded metadata), sorted newest-first.
752
761
  /// @param projectId The ID of the project to get the rulesets of.
753
762
  /// @param startingId The ID of the ruleset to begin with. This will be the latest ruleset in the result. If the
754
763
  /// `startingId` is 0, passed, the project's latest ruleset will be used.
@@ -786,7 +795,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
786
795
  }
787
796
  }
788
797
 
789
- /// @notice A project's currently active ruleset and its metadata.
798
+ /// @notice Returns the ruleset currently governing a project, along with its decoded metadata.
790
799
  /// @param projectId The ID of the project to get the current ruleset of.
791
800
  /// @return ruleset The current ruleset's struct.
792
801
  /// @return metadata The current ruleset's metadata.
@@ -835,7 +844,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
835
844
  }
836
845
 
837
846
  /// @notice Previews how many beneficiary and reserved tokens `mintTokensOf(...)` would produce.
838
- /// @param projectId The ID of the project whose tokens are being minted.
847
+ /// @param projectId The ID of the project to mint tokens for.
839
848
  /// @param tokenCount The number of tokens to mint, including any reserved tokens.
840
849
  /// @param useReservedPercent Whether to apply the ruleset's reserved percent.
841
850
  /// @return beneficiaryTokenCount The number of tokens that would be minted for the beneficiary.
@@ -862,21 +871,21 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
862
871
  });
863
872
  }
864
873
 
865
- /// @notice Check whether the project's controller can currently be set.
874
+ /// @notice Whether the project's current ruleset allows migrating to a different controller.
866
875
  /// @param projectId The ID of the project to check.
867
876
  /// @return A `bool` which is true if the project allows controllers to be set.
868
877
  function setControllerAllowed(uint256 projectId) external view returns (bool) {
869
878
  return _currentRulesetOf(projectId).expandMetadata().allowSetController;
870
879
  }
871
880
 
872
- /// @notice Check whether the project's terminals can currently be set.
881
+ /// @notice Whether the project's current ruleset allows changing its terminals.
873
882
  /// @param projectId The ID of the project to check.
874
883
  /// @return A `bool` which is true if the project allows terminals to be set.
875
884
  function setTerminalsAllowed(uint256 projectId) external view returns (bool) {
876
885
  return _currentRulesetOf(projectId).expandMetadata().allowSetTerminals;
877
886
  }
878
887
 
879
- /// @notice Gets the a project token's total supply, including pending reserved tokens.
888
+ /// @notice Returns the project's total token supply including tokens that have been reserved but not yet minted.
880
889
  /// @param projectId The ID of the project to get the total token supply of.
881
890
  /// @return The total supply of the project's token, including pending reserved tokens.
882
891
  function totalTokenSupplyWithReservedTokensOf(uint256 projectId) external view override returns (uint256) {
@@ -884,7 +893,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
884
893
  return TOKENS.totalSupplyOf(projectId) + pendingReservedTokenBalanceOf[projectId];
885
894
  }
886
895
 
887
- /// @notice A project's next ruleset along with its metadata.
896
+ /// @notice Returns the ruleset that will take effect after the current one ends, along with its decoded metadata.
888
897
  /// @dev If an upcoming ruleset isn't found, returns an empty ruleset with all properties set to 0.
889
898
  /// @param projectId The ID of the project to get the next ruleset of.
890
899
  /// @return ruleset The upcoming ruleset's struct.
@@ -951,7 +960,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
951
960
 
952
961
  /// @notice Queues one or more rulesets and stores information pertinent to the configuration.
953
962
  /// @param projectId The ID of the project to queue rulesets for.
954
- /// @param rulesetConfigurations The rulesets being queued.
963
+ /// @param rulesetConfigurations The rulesets to queue.
955
964
  /// @return rulesetId The ID of the last ruleset that was successfully queued.
956
965
  function _queueRulesets(
957
966
  uint256 projectId,
@@ -7,10 +7,9 @@ import {JBApprovalStatus} from "./enums/JBApprovalStatus.sol";
7
7
  import {IJBRulesetApprovalHook} from "./interfaces/IJBRulesetApprovalHook.sol";
8
8
  import {JBRuleset} from "./structs/JBRuleset.sol";
9
9
 
10
- /// @notice `JBDeadline` is a ruleset approval hook which rejects rulesets if they are not queued at least `duration`
11
- /// seconds before the current ruleset ends. In other words, rulesets must be queued before the deadline to take effect.
12
- /// @dev Project rulesets are stored in a queue. Rulesets take effect after the previous ruleset in the queue ends, and
13
- /// only if they are approved by the previous ruleset's approval hook.
10
+ /// @notice A ruleset approval hook that enforces a queuing deadline. If a new ruleset is not queued at least `DURATION`
11
+ /// seconds before the current ruleset ends, it is rejected and the existing rules continue. This gives token holders
12
+ /// a guaranteed notice period before any project configuration changes take effect.
14
13
  /// @dev If `DURATION` is set longer than the ruleset's cycle duration, no queued ruleset can ever satisfy the deadline
15
14
  /// and the current ruleset will effectively be locked in perpetuity. Choose a `DURATION` shorter than the shortest
16
15
  /// cycle it will govern.
@@ -60,6 +59,7 @@ contract JBDeadline is IJBRulesetApprovalHook {
60
59
  // If we've already passed the deadline, the ruleset is `Approved`.
61
60
  return (ruleset.start - ruleset.id < DURATION)
62
61
  ? JBApprovalStatus.Failed
62
+ // forge-lint: disable-next-line(block-timestamp)
63
63
  : (block.timestamp + DURATION < ruleset.start)
64
64
  ? JBApprovalStatus.ApprovalExpected
65
65
  : JBApprovalStatus.Approved;
@@ -13,9 +13,11 @@ import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
13
13
  import {IJBProjects} from "./interfaces/IJBProjects.sol";
14
14
  import {IJBTerminal} from "./interfaces/IJBTerminal.sol";
15
15
 
16
- /// @notice `JBDirectory` tracks the terminals and the controller used by each project.
17
- /// @dev Tracks which `IJBTerminal`s each project is currently accepting funds through, and which `IJBController` is
18
- /// managing each project's tokens and rulesets.
16
+ /// @notice The routing table for the protocol. Every project registers which terminals accept its payments and which
17
+ /// controller manages its rulesets and tokens. Frontends and other contracts use the directory to discover where to
18
+ /// send funds for a given project and token.
19
+ /// @dev Also manages controller migration — when a project upgrades its controller, the directory orchestrates the
20
+ /// handoff including `beforeReceiveMigrationFrom`, `migrate`, and `afterReceiveMigrationFrom` lifecycle hooks.
19
21
  contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
20
22
  //*********************************************************************//
21
23
  // --------------------------- custom errors ------------------------- //
@@ -83,13 +85,15 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
83
85
  // ---------------------- external transactions ---------------------- //
84
86
  //*********************************************************************//
85
87
 
86
- /// @notice Set a project's controller. Controllers manage how terminals interact with tokens and rulesets.
88
+ /// @notice Assign a new controller to a project. The controller dictates how the project's terminals interact with
89
+ /// its tokens and rulesets. If the project already has a controller, this triggers a full migration lifecycle
90
+ /// (`beforeReceiveMigrationFrom` → `migrate` → state update → `afterReceiveMigrationFrom`).
87
91
  /// @dev Can only be called if:
88
- /// - The ruleset's metadata has `allowSetController` enabled, and the message's sender is the project's owner or an
89
- /// address with the owner's permission to `SET_CONTROLLER`.
90
- /// - OR the message's sender is the project's current controller.
91
- /// - OR an address which `isAllowedToSetFirstController` is setting a project's first controller.
92
- /// @param projectId The ID of the project whose controller is being set.
92
+ /// - The ruleset's metadata has `allowSetController` enabled, and the caller is the project's owner or has
93
+ /// `SET_CONTROLLER` permission.
94
+ /// - OR the caller is the project's current controller.
95
+ /// - OR the caller `isAllowedToSetFirstController` and the project has no controller yet.
96
+ /// @param projectId The ID of the project to set the controller for.
93
97
  /// @param controller The address of the controller to set.
94
98
  function setControllerOf(uint256 projectId, IERC165 controller) external override {
95
99
  // Keep a reference to the current controller.
@@ -149,15 +153,13 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
149
153
  }
150
154
  }
151
155
 
152
- /// @notice Add or remove an address/contract from a list of trusted addresses which are allowed to set a first
153
- /// controller for projects.
154
- /// @dev Only this contract's owner can call this function.
155
- /// @dev These addresses are vetted controllers as well as contracts designed to launch new projects.
156
- /// @dev A project can set its own controller without being on this list.
157
- /// @dev If you would like to add an address/contract to this list, please reach out to this contract's owner.
158
- /// @param addr The address to allow or not allow.
159
- /// @param flag Whether the address is allowed to set first controllers for projects. Use `true` to allow and
160
- /// `false` to not allow.
156
+ /// @notice Allow or disallow an address to set the first controller for new projects. Typically used to whitelist
157
+ /// deployer contracts (like `JBController`) that set a controller during `launchProjectFor`.
158
+ /// @dev Only this contract's owner can call this function. These addresses are vetted controllers and project
159
+ /// launchers. A project owner can always set their own controller this list only governs *first* controller
160
+ /// assignment by third parties.
161
+ /// @param addr The address to allow or disallow.
162
+ /// @param flag Whether the address is allowed to set first controllers. `true` to allow, `false` to revoke.
161
163
  function setIsAllowedToSetFirstController(address addr, bool flag) external override onlyOwner {
162
164
  // Set the flag in the allowlist.
163
165
  isAllowedToSetFirstController[addr] = flag;
@@ -165,14 +167,14 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
165
167
  emit SetIsAllowedToSetFirstController({addr: addr, isAllowed: flag, caller: msg.sender});
166
168
  }
167
169
 
168
- /// @notice Set a project's primary terminal for a token.
169
- /// @dev The primary terminal for a token is where payments in that token are routed to by default.
170
- /// @dev This is useful in cases where a project has multiple terminals which accept the same token.
171
- /// @dev Can only be called by the project's owner, or an address with the owner's permission to
172
- /// `SET_PRIMARY_TERMINAL`.
173
- /// @param projectId The ID of the project whose primary terminal is being set.
170
+ /// @notice Designate which terminal should receive payments by default when someone pays a project in a specific
171
+ /// token. Useful when a project has multiple terminals that accept the same token.
172
+ /// @dev Can only be called by the project's owner or an address with `SET_PRIMARY_TERMINAL` permission. The
173
+ /// terminal must accept the token for this project. If the terminal isn't already in the project's terminal list,
174
+ /// it will be added automatically (requires `ADD_TERMINALS` permission).
175
+ /// @param projectId The ID of the project to set the primary terminal for.
174
176
  /// @param token The token to set the primary terminal for.
175
- /// @param terminal The terminal being set as the primary terminal.
177
+ /// @param terminal The terminal to set as the primary terminal.
176
178
  function setPrimaryTerminalOf(uint256 projectId, address token, IJBTerminal terminal) external override {
177
179
  // Enforce permissions.
178
180
  _requirePermissionFrom({
@@ -203,11 +205,11 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
203
205
  emit SetPrimaryTerminal({projectId: projectId, token: token, terminal: terminal, caller: msg.sender});
204
206
  }
205
207
 
206
- /// @notice Set a project's terminals.
207
- /// @dev Can only be called by the project's owner, an address with the owner's permission to `SET_TERMINALS`, or
208
- /// the project's controller.
209
- /// @dev Unless the caller is the project's controller, the project's ruleset must allow setting terminals.
210
- /// @param projectId The ID of the project whose terminals are being set.
208
+ /// @notice Replace a project's entire terminal list. Terminals are the contracts that accept payments and process
209
+ /// cash outs for a project. This overwrites the existing list.
210
+ /// @dev Can only be called by the project's owner, an address with `SET_TERMINALS` permission, or the project's
211
+ /// controller. Unless the caller is the controller, the ruleset must have `allowSetTerminals` enabled.
212
+ /// @param projectId The ID of the project to set terminals for.
211
213
  /// @param terminals An array of terminal addresses to set for the project.
212
214
  function setTerminalsOf(uint256 projectId, IJBTerminal[] calldata terminals) external override {
213
215
  // Cache the controller to avoid redundant storage reads.
@@ -254,10 +256,9 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
254
256
  // ------------------------- external views -------------------------- //
255
257
  //*********************************************************************//
256
258
 
257
- /// @notice The primary terminal that a project uses for the specified token.
258
- /// @dev Returns the first terminal that accepts the token if the project hasn't explicitly set a primary terminal
259
- /// for it.
260
- /// @dev Returns the zero address if no terminal accepts the token.
259
+ /// @notice Look up the terminal where payments in a given token should be sent for a project. Returns the
260
+ /// explicitly-set primary terminal, or falls back to the first terminal in the project's list that accepts the
261
+ /// token. Returns the zero address if no terminal accepts the token.
261
262
  /// @param projectId The ID of the project to get the primary terminal of.
262
263
  /// @param token The token that the terminal accepts.
263
264
  /// @return The primary terminal's address.
@@ -298,7 +299,8 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
298
299
  return IJBTerminal(address(0));
299
300
  }
300
301
 
301
- /// @notice The specified project's terminals.
302
+ /// @notice Get all terminals registered for a project. Terminals are the contracts that hold a project's funds and
303
+ /// process payments and cash outs on its behalf.
302
304
  /// @param projectId The ID of the project to get the terminals of.
303
305
  /// @return An array of the project's terminal addresses.
304
306
  function terminalsOf(uint256 projectId) external view override returns (IJBTerminal[] memory) {
@@ -309,7 +311,7 @@ contract JBDirectory is JBPermissioned, Ownable, IJBDirectory {
309
311
  // -------------------------- public views --------------------------- //
310
312
  //*********************************************************************//
311
313
 
312
- /// @notice Check if a project uses a specific terminal.
314
+ /// @notice Check whether a specific terminal is in a project's registered terminal list.
313
315
  /// @param projectId The ID of the project to check.
314
316
  /// @param terminal The terminal to check for.
315
317
  /// @return A flag indicating whether the project uses the terminal.
package/src/JBERC20.sol CHANGED
@@ -15,10 +15,11 @@ import {IJBProjects} from "./interfaces/IJBProjects.sol";
15
15
  import {IJBToken} from "./interfaces/IJBToken.sol";
16
16
  import {IJBTokens} from "./interfaces/IJBTokens.sol";
17
17
 
18
- /// @notice An ERC-20 token that can be used by a project in `JBTokens` and `JBController`.
19
- /// @dev By default, a project uses "credits" to track balances. Once a project sets their `IJBToken` using
20
- /// `JBController.deployERC20For(...)` or `JBController.setTokenFor(...)`, credits can be redeemed to claim tokens.
21
- /// @dev `JBController.deployERC20For(...)` deploys a `JBERC20` contract and sets it as the project's token.
18
+ /// @notice The ERC-20 token implementation used by Juicebox projects. Includes ERC20Votes (governance delegation) and
19
+ /// ERC20Permit (gasless approvals). Deployed as a minimal clone via `JBController.deployERC20For` once deployed,
20
+ /// holders can claim their internal credits into this transferable token.
21
+ /// @dev Only `JBTokens` can mint and burn. The project owner (via `SET_TOKEN_METADATA` permission) can rename the
22
+ /// token. Supports ERC-1271 signature validation for smart-contract wallets.
22
23
  contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken {
23
24
  //*********************************************************************//
24
25
  // --------------------------- custom errors ------------------------- //
@@ -126,7 +127,7 @@ contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken
126
127
  /// @notice Validates a signature on behalf of this token contract (ERC-1271).
127
128
  /// @dev Allows the project owner or an operator with `SIGN_FOR_ERC20` permission to sign messages on behalf of
128
129
  /// this token. Useful for Etherscan contract verification and other off-chain signature flows.
129
- /// @param hash The hash of the data being signed.
130
+ /// @param hash The hash of the data to sign.
130
131
  /// @param signature The signature to validate.
131
132
  /// @return magicValue `0x1626ba7e` if the signature is valid, `0xffffffff` otherwise.
132
133
  function isValidSignature(bytes32 hash, bytes memory signature) external view override returns (bytes4 magicValue) {
@@ -169,17 +170,17 @@ contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken
169
170
  return super.decimals();
170
171
  }
171
172
 
172
- /// @notice The token's name.
173
+ /// @notice The token's name, set during initialization.
173
174
  function name() public view virtual override returns (string memory) {
174
175
  return _name;
175
176
  }
176
177
 
177
- /// @notice Required override.
178
+ /// @notice The current nonce for a given account, used for permit signatures.
178
179
  function nonces(address owner) public view virtual override(ERC20Permit, Nonces) returns (uint256) {
179
180
  return super.nonces(owner);
180
181
  }
181
182
 
182
- /// @notice The token's symbol.
183
+ /// @notice The token's ticker symbol, set during initialization.
183
184
  function symbol() public view virtual override returns (string memory) {
184
185
  return _symbol;
185
186
  }
@@ -194,7 +195,7 @@ contract JBERC20 is ERC20Votes, ERC20Permit, JBPermissioned, IERC1271, IJBToken
194
195
  // ----------------------- public transactions ----------------------- //
195
196
  //*********************************************************************//
196
197
 
197
- /// @notice Initializes the token.
198
+ /// @notice Initialize a new project token with the given name, symbol, and owner.
198
199
  /// @param name_ The token's name.
199
200
  /// @param symbol_ The token's symbol.
200
201
  /// @param tokens The JBTokens contract that manages this token.
@@ -6,7 +6,9 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
6
6
 
7
7
  import {IJBFeelessAddresses} from "./interfaces/IJBFeelessAddresses.sol";
8
8
 
9
- /// @notice Stores a list of addresses that shouldn't incur fees when sending or receiving payments.
9
+ /// @notice A registry of addresses exempt from the protocol's 2.5% fee. Feeless addresses don't incur fees on
10
+ /// payouts they receive, surplus allowance they use, or cash outs where they are the beneficiary. Managed by the
11
+ /// contract owner (typically the protocol multisig).
10
12
  contract JBFeelessAddresses is Ownable, IJBFeelessAddresses, IERC165 {
11
13
  //*********************************************************************//
12
14
  // --------------------- public stored properties -------------------- //
@@ -30,8 +32,9 @@ contract JBFeelessAddresses is Ownable, IJBFeelessAddresses, IERC165 {
30
32
  // ---------------------- external transactions ---------------------- //
31
33
  //*********************************************************************//
32
34
 
33
- /// @notice Sets whether an address is feeless.
34
- /// @dev Can only be called by this contract's owner.
35
+ /// @notice Add or remove an address from the fee-exempt list. Feeless addresses don't pay the 2.5% protocol fee
36
+ /// on payouts received, surplus allowance used, or cash outs where they're the beneficiary.
37
+ /// @dev Can only be called by this contract's owner (typically the protocol multisig).
35
38
  /// @param addr The address to set as feeless or not feeless.
36
39
  /// @param flag Whether the address should be feeless (`true`) or not feeless (`false`).
37
40
  function setFeelessAddress(address addr, bool flag) external virtual override onlyOwner {