@bananapus/core-v6 0.0.30 → 0.0.32

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 (49) hide show
  1. package/ADMINISTRATION.md +43 -13
  2. package/ARCHITECTURE.md +62 -137
  3. package/AUDIT_INSTRUCTIONS.md +149 -428
  4. package/CHANGELOG.md +73 -0
  5. package/README.md +90 -201
  6. package/RISKS.md +27 -12
  7. package/SKILLS.md +31 -441
  8. package/STYLE_GUIDE.md +52 -19
  9. package/USER_JOURNEYS.md +76 -627
  10. package/package.json +1 -2
  11. package/references/entrypoints.md +160 -0
  12. package/references/types-errors-events.md +297 -0
  13. package/script/Deploy.s.sol +7 -2
  14. package/script/DeployPeriphery.s.sol +51 -4
  15. package/src/JBController.sol +45 -17
  16. package/src/JBDirectory.sol +26 -13
  17. package/src/JBFundAccessLimits.sol +28 -7
  18. package/src/JBMultiTerminal.sol +180 -86
  19. package/src/JBPermissions.sol +17 -17
  20. package/src/JBRulesets.sol +82 -23
  21. package/src/JBSplits.sol +31 -12
  22. package/src/JBTerminalStore.sol +137 -53
  23. package/src/JBTokens.sol +5 -2
  24. package/src/abstract/JBControlled.sol +10 -3
  25. package/src/abstract/JBPermissioned.sol +1 -1
  26. package/src/interfaces/IJBRulesetDataHook.sol +5 -4
  27. package/src/libraries/JBCashOuts.sol +1 -1
  28. package/src/libraries/JBConstants.sol +1 -1
  29. package/src/libraries/JBCurrencyIds.sol +1 -1
  30. package/src/libraries/JBFees.sol +1 -1
  31. package/src/libraries/JBFixedPointNumber.sol +1 -1
  32. package/src/libraries/JBMetadataResolver.sol +5 -2
  33. package/src/libraries/JBPayoutSplitGroupLib.sol +7 -2
  34. package/src/libraries/JBRulesetMetadataResolver.sol +1 -1
  35. package/src/libraries/JBSplitGroupIds.sol +1 -1
  36. package/src/libraries/JBSurplus.sol +5 -2
  37. package/src/structs/JBSplit.sol +4 -1
  38. package/test/TestForwardedTokenConsumption.sol +419 -0
  39. package/test/audit/CrossTerminalSurplusSpoof.t.sol +140 -0
  40. package/test/audit/CycledSurplusAllowanceReset.t.sol +184 -0
  41. package/test/units/static/JBController/TestPreviewMintOf.sol +5 -4
  42. package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +15 -12
  43. package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +6 -0
  44. package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +3 -0
  45. package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +3 -0
  46. package/test/units/static/JBMultiTerminal/TestPay.sol +7 -15
  47. package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +1 -1
  48. package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +1 -1
  49. package/CHANGE_LOG.md +0 -479
package/ADMINISTRATION.md CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  Admin privileges and their scope in nana-core-v6.
4
4
 
5
+ ## At A Glance
6
+
7
+ | Item | Details |
8
+ |------|---------|
9
+ | Scope | Core Juicebox V6 protocol control plane: projects, permissions, directory, controller, terminals, tokens, prices, splits, and fund access limits. |
10
+ | Operators | Protocol-level contract owners, project owners, delegated operators, controllers, terminals, and ruleset-selected hooks. |
11
+ | Highest-risk actions | Adding immutable price feeds, changing controllers or terminals, deploying or setting a project's ERC-20, and granting broad operator or ROOT permissions. |
12
+ | Recovery posture | Bad immutable choices usually require deploying new infrastructure and migrating project configuration rather than "editing" the existing contracts. |
13
+
14
+ ## Routine Operations
15
+
16
+ - Whitelist or remove first-controller setters on `JBDirectory` only when rolling out a new controller implementation.
17
+ - Add price feeds only after confirming the exact project scope and pair, because feeds cannot be replaced once written.
18
+ - Manage project operators through `JBPermissions` with the narrowest possible project scope and permission bitmap.
19
+ - Reconfigure controllers, terminals, splits, fund access limits, and rulesets only when the current ruleset flags permit the change.
20
+ - Use protocol-owner functions on `JBFeelessAddresses`, `JBProjects`, and `JBPrices` sparingly because they affect many projects at once.
21
+
22
+ ## One-Way Or High-Risk Actions
23
+
24
+ - `JBPrices.addPriceFeedFor` is immutable for a given project/currency pair. A bad feed entry cannot be edited in place.
25
+ - `JBController.deployERC20For` and `JBTokens.setTokenFor` are effectively one-time token-binding decisions for a project.
26
+ - The fee beneficiary is hardcoded to project `1` inside `JBMultiTerminal`; changing that requires new terminal deployments.
27
+ - Over-broad ROOT or wildcard permissions can hand an operator more power than intended across a project's full control surface.
28
+
29
+ ## Recovery Notes
30
+
31
+ - If an immutable contract reference or price feed is wrong, the normal recovery path is redeploying the affected contract layer and migrating projects to the new controller or terminal stack.
32
+ - If a project's controller or terminal setup is wrong but the ruleset still allows migration, recover with `JBDirectory` updates before deploying replacement infrastructure.
33
+
5
34
  ## Roles
6
35
 
7
36
  ### Project Owner
@@ -90,14 +119,14 @@ Admin privileges and their scope in nana-core-v6.
90
119
  |----------|--------------|---------------|-------|-------------|
91
120
  | `setControllerOf` | Project owner or operator, OR an `isAllowedToSetFirstController` address (for first controller only) | SET_CONTROLLER (14) | Per project | Sets or migrates a project's controller. Also requires the current ruleset's `allowSetController` flag to be true (unless setting the first controller). Triggers migration lifecycle hooks on both old and new controllers. |
92
121
  | `setIsAllowedToSetFirstController` | Contract owner | N/A (onlyOwner) | Protocol-wide | Adds or removes an address from the allowlist of addresses that can set a project's first controller. |
93
- | `setPrimaryTerminalOf` | Project owner or operator | SET_PRIMARY_TERMINAL (16) | Per project | Sets which terminal is the default for a given token. Adds the terminal to the project if not already present. |
122
+ | `setPrimaryTerminalOf` | Project owner or operator | SET_PRIMARY_TERMINAL (17), plus ADD_TERMINALS (16) if the terminal is not already configured | Per project | Sets which terminal is the default for a given token. Adds the terminal to the project if not already present. |
94
123
  | `setTerminalsOf` | Project owner, operator, or the project's controller | SET_TERMINALS (15) | Per project | Replaces the entire list of terminals for a project. If the caller is not the controller, the ruleset must have `allowSetTerminals` enabled. |
95
124
 
96
125
  ### JBController
97
126
 
98
127
  | Function | Required Role | Permission ID | Scope | What It Does |
99
128
  |----------|--------------|---------------|-------|-------------|
100
- | `addPriceFeedFor` | Project owner or operator | ADD_PRICE_FEED (19) | Per project | Adds a price feed for a project. Requires the ruleset's `allowAddPriceFeed` flag. Price feeds are immutable once set. |
129
+ | `addPriceFeedFor` | Project owner or operator | ADD_PRICE_FEED (20) | Per project | Adds a price feed for a project. Requires the ruleset's `allowAddPriceFeed` flag. Price feeds are immutable once set. |
101
130
  | `burnTokensOf` | Token holder, operator with BURN_TOKENS, or a project terminal | BURN_TOKENS (11) | Per project | Burns tokens or credits from a holder's balance. Terminals can burn without explicit permission (for cash outs). |
102
131
  | `claimTokensFor` | Credit holder or operator | CLAIM_TOKENS (12) | Per project | Redeems internal credits for ERC-20 tokens. |
103
132
  | `deployERC20For` | Project owner or operator | DEPLOY_ERC20 (8) | Per project | Deploys a new ERC-20 token contract for the project. Can only be called once per project. |
@@ -106,9 +135,9 @@ Admin privileges and their scope in nana-core-v6.
106
135
  | `mintTokensOf` | Project owner, operator, terminal, or data hook (with mint permission) | MINT_TOKENS (10) | Per project | Mints new tokens. If the caller is not a terminal or data hook, the ruleset must have `allowOwnerMinting` enabled. |
107
136
  | `queueRulesetsOf` | Project owner, operator, or OMNICHAIN_RULESET_OPERATOR | QUEUE_RULESETS (2) | Per project | Queues new rulesets at the end of the project's ruleset queue. |
108
137
  | `sendReservedTokensToSplitsOf` | Anyone | N/A | Per project | Distributes accumulated reserved tokens to the project's reserved token split group. No permission required -- anyone can trigger this. |
109
- | `setSplitGroupsOf` | Project owner or operator | SET_SPLIT_GROUPS (18) | Per project | Sets split groups for a project. Must preserve any currently locked splits. |
138
+ | `setSplitGroupsOf` | Project owner or operator | SET_SPLIT_GROUPS (19) | Per project | Sets split groups for a project. Must preserve any currently locked splits. |
110
139
  | `setTokenFor` | Project owner or operator | SET_TOKEN (9) | Per project | Assigns an external ERC-20 token to the project. Requires the ruleset's `allowSetCustomToken` flag. Can only be called once (before any token is set). |
111
- | `setTokenMetadataOf` | Project owner or operator | SET_TOKEN_METADATA (21) | Per project | Sets the name and symbol of a project's ERC-20 token. The project must have a token deployed. |
140
+ | `setTokenMetadataOf` | Project owner or operator | SET_TOKEN_METADATA (22) | Per project | Sets the name and symbol of a project's ERC-20 token. The project must have a token deployed. |
112
141
  | `setUriOf` | Project owner or operator | SET_PROJECT_URI (7) | Per project | Updates the project's metadata URI. |
113
142
  | `transferCreditsFrom` | Credit holder or operator | TRANSFER_CREDITS (13) | Per project | Transfers internal token credits between addresses. Requires the ruleset's `pauseCreditTransfers` flag to be false. |
114
143
  | `migrate` | JBDirectory only | N/A (msg.sender == DIRECTORY) | Per project | Called by the directory during controller migration. Reverts if there are pending reserved tokens. |
@@ -121,11 +150,11 @@ Admin privileges and their scope in nana-core-v6.
121
150
 
122
151
  | Function | Required Role | Permission ID | Scope | What It Does |
123
152
  |----------|--------------|---------------|-------|-------------|
124
- | `addAccountingContextsFor` | Project owner, operator, or the project's controller | ADD_ACCOUNTING_CONTEXTS (20) | Per project | Adds tokens that the terminal will accept for a project. Requires the ruleset's `allowAddAccountingContext` flag (if a ruleset exists). |
153
+ | `addAccountingContextsFor` | Project owner, operator, or the project's controller | ADD_ACCOUNTING_CONTEXTS (21) | Per project | Adds tokens that the terminal will accept for a project. Requires the ruleset's `allowAddAccountingContext` flag (if a ruleset exists). |
125
154
  | `cashOutTokensOf` | Token holder or operator | CASH_OUT_TOKENS (4) | Per project | Cashes out project tokens for a share of the project's surplus. Fees are charged unless the beneficiary is feeless or the cash out tax rate is zero. |
126
155
  | `migrateBalanceOf` | Project owner or operator | MIGRATE_TERMINAL (6) | Per project | Migrates a project's balance from this terminal to another. The destination terminal must accept the same token. The ruleset must have `allowTerminalMigration` enabled (checked in JBTerminalStore). The standard 2.5% protocol fee is charged when migrating to a non-feeless terminal. |
127
156
  | `sendPayoutsOf` | Anyone (unless `ownerMustSendPayouts` is set) | SEND_PAYOUTS (5) if `ownerMustSendPayouts` | Per project | Sends payouts to the project's payout split group up to the payout limit. Anyone can call unless the ruleset has `ownerMustSendPayouts` enabled, which requires the project owner or an operator with SEND_PAYOUTS permission. |
128
- | `useAllowanceOf` | Project owner or operator | USE_ALLOWANCE (17) | Per project | Withdraws funds from the project's surplus up to the surplus allowance. Fees are charged unless the owner or beneficiary is feeless. |
157
+ | `useAllowanceOf` | Project owner or operator | USE_ALLOWANCE (18) | Per project | Withdraws funds from the project's surplus up to the surplus allowance. Fees are charged unless the owner or beneficiary is feeless. |
129
158
  | `pay` | Anyone | N/A | N/A | Pays a project with tokens. No permission required. |
130
159
  | `addToBalanceOf` | Anyone | N/A | N/A | Adds funds to a project's balance without minting tokens. Can optionally return held fees. No permission required. |
131
160
  | `processHeldFeesOf` | Anyone | N/A | Per project | Processes held fees that have passed their 28-day unlock period. No permission required. |
@@ -242,12 +271,13 @@ JBPermissions implements a 256-bit packed permission bitmap system:
242
271
  | 13 | TRANSFER_CREDITS | `JBController.transferCreditsFrom` |
243
272
  | 14 | SET_CONTROLLER | `JBDirectory.setControllerOf` |
244
273
  | 15 | SET_TERMINALS | `JBDirectory.setTerminalsOf` |
245
- | 16 | SET_PRIMARY_TERMINAL | `JBDirectory.setPrimaryTerminalOf` |
246
- | 17 | USE_ALLOWANCE | `JBMultiTerminal.useAllowanceOf` |
247
- | 18 | SET_SPLIT_GROUPS | `JBController.setSplitGroupsOf` |
248
- | 19 | ADD_PRICE_FEED | `JBController.addPriceFeedFor` |
249
- | 20 | ADD_ACCOUNTING_CONTEXTS | `JBMultiTerminal.addAccountingContextsFor` |
250
- | 21 | SET_TOKEN_METADATA | `JBController.setTokenMetadataOf` |
274
+ | 16 | ADD_TERMINALS | `JBDirectory.setPrimaryTerminalOf` when it must add a new terminal first |
275
+ | 17 | SET_PRIMARY_TERMINAL | `JBDirectory.setPrimaryTerminalOf` |
276
+ | 18 | USE_ALLOWANCE | `JBMultiTerminal.useAllowanceOf` |
277
+ | 19 | SET_SPLIT_GROUPS | `JBController.setSplitGroupsOf` |
278
+ | 20 | ADD_PRICE_FEED | `JBController.addPriceFeedFor` |
279
+ | 21 | ADD_ACCOUNTING_CONTEXTS | `JBMultiTerminal.addAccountingContextsFor` |
280
+ | 22 | SET_TOKEN_METADATA | `JBController.setTokenMetadataOf` |
251
281
 
252
282
  ## Immutable Configuration
253
283
 
@@ -338,7 +368,7 @@ What admins CANNOT do:
338
368
  - **No admin can change the fee beneficiary.** Project ID 1 is hardcoded as the fee recipient. This cannot be changed.
339
369
  - **Price feeds cannot be replaced.** Once a price feed is set for a currency pair (in either direction), it is permanent for that project ID. Recovery requires deploying a new JBPrices contract.
340
370
  - **Token assignments are one-way.** Once a project's ERC-20 token is set (via `deployERC20For` or `setTokenFor`), it cannot be changed to a different token.
341
- - **Locked splits cannot be removed.** Splits with a `lockedUntil` timestamp in the future must be preserved (with the same or extended lock) when updating split groups.
371
+ - **Locked splits cannot be removed from the same split table.** Splits with a `lockedUntil` timestamp in the future must be preserved (with the same or extended lock) when updating that split group. This does not automatically freeze successor rulesets; operators that queue new rulesets must consciously carry forward any still-locked economic promises they intend to preserve.
342
372
  - **Operators cannot escalate to ROOT via ROOT.** A ROOT operator can set permissions for other operators on the same project but cannot grant ROOT to them or set permissions on the wildcard project ID.
343
373
  - **The directory owner cannot set controllers directly.** The directory owner can only manage the `isAllowedToSetFirstController` allowlist. They cannot set or change a project's controller.
344
374
  - **Ruleset approval hooks can block changes.** If a ruleset specifies an approval hook, queued rulesets must be approved by that hook before they take effect. A rejected ruleset falls back to the previous approved ruleset's cycling behavior.
package/ARCHITECTURE.md CHANGED
@@ -1,159 +1,84 @@
1
- # nana-core-v6 — Architecture
1
+ # Architecture
2
2
 
3
3
  ## Purpose
4
4
 
5
- Core protocol for Juicebox V6. Provides programmable treasuries with configurable governance, bonding-curve cash outs, split-based payouts, and a compositional hook system.
5
+ `nana-core-v6` is the protocol root. It owns project identity, permissions, rulesets, token supply, treasury balances, payout limits, fee logic, and the hook interfaces that every extension repo composes with.
6
6
 
7
- ## Contract Map
7
+ If a change affects accounting, supply, fee behavior, terminal routing, or permission semantics, this is the repo that defines the source of truth.
8
8
 
9
- ```
10
- src/
11
- ├── JBMultiTerminal.sol — Multi-token payment terminal (pay, cash out, payouts, fees)
12
- ├── JBController.sol — Orchestrator (project lifecycle, rulesets, token minting, reserved tokens)
13
- ├── JBTerminalStore.sol — Bookkeeping (balances, payout limits, surplus, bonding curve math)
14
- ├── JBRulesets.sol — Ruleset lifecycle (linked-list, weight decay, approval hooks)
15
- ├── JBDirectory.sol — Routes projects to terminals and controllers
16
- ├── JBTokens.sol — Dual token system (internal credits + ERC-20)
17
- ├── JBSplits.sol — Packed split storage with lock enforcement
18
- ├── JBFundAccessLimits.sol — Payout limits and surplus allowances
19
- ├── JBPrices.sol — Price feeds with project-specific + default fallback
20
- ├── JBPermissions.sol — 256-bit packed permission system
21
- ├── JBProjects.sol — ERC-721 project ownership
22
- ├── JBERC20.sol — Cloneable ERC20Votes+Permit token
23
- ├── JBFeelessAddresses.sol — Fee-exempt address registry
24
- ├── JBDeadline.sol — Approval hook requiring minimum delay
25
- ├── JBChainlinkV3PriceFeed.sol — Chainlink v3 price feed with staleness + answeredInRound check
26
- ├── JBChainlinkV3SequencerPriceFeed.sol — L2 sequencer-aware price feed (answer != 0 = down)
27
- ├── abstract/
28
- │ ├── JBPermissioned.sol — Base for permission-checked contracts
29
- │ └── JBControlled.sol — Base for controller-gated contracts
30
- ├── enums/
31
- │ └── JBApprovalStatus.sol — Approval hook status enum
32
- ├── periphery/
33
- │ ├── JBDeadline1Day.sol — 1-day approval hook
34
- │ ├── JBDeadline3Days.sol — 3-day approval hook
35
- │ ├── JBDeadline3Hours.sol — 3-hour approval hook
36
- │ ├── JBDeadline7Days.sol — 7-day approval hook
37
- │ └── JBMatchingPriceFeed.sol — 1:1 price feed
38
- ├── interfaces/ — 30 interface files (IJBController, IJBTerminal, etc.)
39
- ├── structs/ — 22 struct files (JBRuleset, JBSplit, etc.)
40
- └── libraries/
41
- ├── JBCashOuts.sol — Bonding curve math
42
- ├── JBConstants.sol — Protocol constants
43
- ├── JBCurrencyIds.sol — Currency ID constants (ETH, USD)
44
- ├── JBFees.sol — Fee calculation (forward/backward)
45
- ├── JBFixedPointNumber.sol — Decimal adjustment
46
- ├── JBMetadataResolver.sol — Variable-length key-value metadata
47
- ├── JBPayoutSplitGroupLib.sol — Payout split group helpers
48
- ├── JBRulesetMetadataResolver.sol — Bit-packed metadata (256 bits)
49
- ├── JBSplitGroupIds.sol — Split group ID constants
50
- └── JBSurplus.sol — Cross-terminal surplus calculation
51
- ```
9
+ ## Boundaries
52
10
 
53
- ## Key Data Flows
11
+ - The core owns balance and supply transitions.
12
+ - Extensions may adjust economics through hooks, but they should not invent competing ledgers.
13
+ - The core deliberately avoids app-specific behaviors like NFT composition, DEX routing strategy, or bridge-specific message transport.
54
14
 
55
- ### Payment Flow
15
+ ## Main Components
56
16
 
57
- ```
58
- User -> JBMultiTerminal.pay()
59
- -> JBTerminalStore.recordPaymentFrom()
60
- -> Read current ruleset
61
- -> [Optional] Data hook overrides weight
62
- -> Calculate token count from weight
63
- -> Update balance
64
- -> JBController.mintTokensOf()
65
- -> Calculate reserved tokens
66
- -> Mint beneficiary tokens
67
- -> Accumulate pendingReservedTokenBalanceOf
68
- -> [Optional] Pay hooks execute
69
- ```
17
+ | Component | Responsibility |
18
+ | --- | --- |
19
+ | `JBMultiTerminal` | Entry point for payments, cash outs, payouts, balance additions, and fee handling |
20
+ | `JBTerminalStore` | Bookkeeping and preview math for payment, payout, allowance, and cash-out paths |
21
+ | `JBController` | Project launch, ruleset queueing, token minting and burning, split-group updates |
22
+ | `JBDirectory` | Maps projects to controllers and terminals |
23
+ | `JBRulesets` | Time-ordered ruleset lifecycle and approval-hook integration |
24
+ | `JBTokens`, `JBERC20`, `JBProjects` | Token and project identity surfaces |
25
+ | `JBSplits`, `JBFundAccessLimits`, `JBPrices`, `JBPermissions` | Shared state for distribution, limits, price conversion, and authorization |
70
26
 
71
- ### Cash Out Flow
27
+ ## Runtime Model
72
28
 
73
- ```
74
- Holder -> JBMultiTerminal.cashOutTokensOf()
75
- -> JBTerminalStore.recordCashOutFor()
76
- -> Calculate surplus (all terminals, converted via JBPrices)
77
- -> Get totalSupply (including pending reserved)
78
- -> [Optional] Data hook overrides parameters
79
- -> JBCashOuts.cashOutFrom() — bonding curve
80
- -> Deduct balance
81
- -> JBController.burnTokensOf()
82
- -> Deduct fee from reclaim amount (if applicable), transfer remainder to beneficiary
83
- -> [Optional] Cash out hooks execute
84
- -> Send accumulated fees to project #1
85
- Fees apply when cashOutTaxRate > 0 (on full reclaim)
86
- OR when cashOutTaxRate == 0 and project has unconsumed _feeFreeSurplusOf (on that portion only)
87
- _feeFreeSurplusOf lifecycle:
88
- - Incremented on fee-free intra-terminal payouts
89
- - Capped at remaining balance after any outflow (payouts, useAllowanceOf, non-zero-tax/feeless cashouts) — non-fee-free funds leave first
90
- - Consumed (decremented by feeable amount) during zero-tax cashouts
91
- - Cleared to zero on terminal migration (migrateBalanceOf)
92
- ```
29
+ ### Payment
93
30
 
94
- ### Preview Flow
31
+ ```text
32
+ terminal receives funds
33
+ -> terminal store reads the current ruleset and optional data hooks
34
+ -> store computes weight-based minting results
35
+ -> controller mints beneficiary tokens and accrues reserved tokens
36
+ -> pay hooks execute only after settlement
37
+ ```
95
38
 
96
- Every core user action has a `view` counterpart that simulates the operation without modifying state. These compose the same internal computation paths as their non-preview counterparts and include data hook effects.
39
+ ### Cash Out
97
40
 
98
- ```
99
- Caller -> JBMultiTerminal.previewPayFor(projectId, token, amount, beneficiary, metadata)
100
- -> JBTerminalStore.previewPayFrom()
101
- -> Read current ruleset, apply data hook
102
- -> Calculate token count from weight
103
- -> JBController.previewMintOf()
104
- -> Split token count into beneficiary + reserved portions
105
- -> Returns (ruleset, beneficiaryTokenCount, reservedTokenCount, hookSpecifications)
106
-
107
- Caller -> JBMultiTerminal.previewCashOutFrom(holder, projectId, cashOutCount, tokenToReclaim, beneficiary, metadata)
108
- -> JBTerminalStore.previewCashOutFrom()
109
- -> Calculate surplus, get totalSupply, apply data hook
110
- -> JBCashOuts.cashOutFrom() — bonding curve
111
- -> Returns (ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications)
112
-
113
- Caller -> JBController.previewMintOf(projectId, tokenCount, useReservedPercent)
114
- -> Read current ruleset
115
- -> Returns (beneficiaryTokenCount, reservedTokenCount)
41
+ ```text
42
+ holder requests redemption
43
+ -> terminal store computes reclaim amount from surplus, supply, and tax settings
44
+ -> optional data hooks can adjust the calculation inputs
45
+ -> controller burns tokens
46
+ -> terminal pays the reclaim amount and routes protocol fees
47
+ -> cash-out hooks execute only after settlement
116
48
  ```
117
49
 
118
- ### Payout Flow
50
+ ### Payouts And Allowances
119
51
 
52
+ ```text
53
+ authorized caller
54
+ -> consumes payout limits or surplus allowances
55
+ -> funds move to splits, projects, hooks, or direct recipients
120
56
  ```
121
- Owner -> JBMultiTerminal.sendPayoutsOf()
122
- -> JBTerminalStore.recordPayoutFor()
123
- -> Deduct balance, check payout limits
124
- -> Distribute to splits (JBSplits)
125
- -> Split to project -> pay project's terminal
126
- -> Split to address -> direct transfer
127
- -> Split to hook -> IJBSplitHook.processSplitWith()
128
- -> Take fees on non-feeless payouts
129
- ```
130
57
 
131
- ## Extension Points
58
+ ## Critical Invariants
59
+
60
+ - Preview functions must remain behaviorally aligned with state-changing functions.
61
+ - Data hooks run before settlement and may alter the economics; pay and cash-out hooks run after settlement. That phase boundary is intentional.
62
+ - Reserved tokens and pending reserves affect supply-sensitive math even before distribution.
63
+ - Terminal balances, fee accounting, and surplus calculations must agree. Any drift here contaminates every product repo.
64
+ - Rulesets are time-ordered and approval-aware; deployment wrappers depend on predictable ID progression and activation semantics.
65
+ - Permission checks are not a UI concern. They are part of protocol state transition validity.
132
66
 
133
- | Extension Point | Interface | Called By |
134
- |----------------|-----------|-----------|
135
- | Data Hook (pay) | `IJBRulesetDataHook.beforePayRecordedWith` | JBTerminalStore |
136
- | Data Hook (cashout) | `IJBRulesetDataHook.beforeCashOutRecordedWith` | JBTerminalStore |
137
- | Pay Hook | `IJBPayHook.afterPayRecordedWith` | JBMultiTerminal |
138
- | Cash Out Hook | `IJBCashOutHook.afterCashOutRecordedWith` | JBMultiTerminal |
139
- | Split Hook | `IJBSplitHook.processSplitWith` | JBMultiTerminal, JBController (try-catch; reverts emit `SplitHookReverted`) |
140
- | Approval Hook | `IJBRulesetApprovalHook.approvalStatusOf` | JBRulesets |
67
+ ## Where Complexity Lives
68
+
69
+ - `JBMultiTerminal`, `JBTerminalStore`, and `JBController` form one accounting pipeline and are easiest to misunderstand when read separately.
70
+ - Preview paths deliberately mirror state-changing paths; keeping them aligned is a permanent maintenance burden.
71
+ - Fee-free surplus, held fees, and payout/allowance interactions create edge cases that are small in code size but large in blast radius.
141
72
 
142
73
  ## Dependencies
143
74
 
144
- - `@bananapus/permission-ids-v6` Permission ID constants
145
- - `@openzeppelin/contracts` — ERC-721, ERC-20, ERC2771, Clones
146
- - `@prb/math` — Fixed-point math (mulDiv)
147
- - `@chainlink/contracts` Price feed interfaces
148
- - `@uniswap/permit2` — Permit2 token approvals
149
-
150
- ## Key Constants
151
-
152
- - FEE = 25 (2.5%) defined on JBMultiTerminal, MAX_FEE = 1000 defined in JBConstants
153
- - MAX_RESERVED_PERCENT = 10,000 (basis points)
154
- - MAX_CASH_OUT_TAX_RATE = 10,000
155
- - MAX_WEIGHT_CUT_PERCENT = 1,000,000,000 (9 decimals)
156
- - SPLITS_TOTAL_PERCENT = 1,000,000,000
157
- - NATIVE_TOKEN = 0x000000000000000000000000000000000000EEEe
158
- - Fee holding: 28 days (2,419,200 seconds)
159
- - Fee beneficiary: project ID 1
75
+ - `nana-permission-ids-v6` for the shared permission namespace
76
+ - OpenZeppelin, PRBMath, Chainlink, and Permit2 for standards and math utilities
77
+
78
+ ## Safe Change Guide
79
+
80
+ - Start any nontrivial change by tracing both the preview path and the state-changing path.
81
+ - Read downstream hook repos before changing hook interfaces or metadata expectations.
82
+ - Keep fee logic, balance logic, and surplus logic in sync; "small" tweaks here are almost always ecosystem-wide changes.
83
+ - When adding permissions or changing who is checked against a permission, update ecosystem docs and downstream assumptions immediately.
84
+ - If a change seems local inside core, assume it is not until proven otherwise.