@bananapus/core-v6 0.0.34 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ADMINISTRATION.md CHANGED
@@ -1,377 +1,103 @@
1
1
  # Administration
2
2
 
3
- Admin privileges and their scope in nana-core-v6.
4
-
5
3
  ## At A Glance
6
4
 
7
5
  | 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.
6
+ | --- | --- |
7
+ | Scope | Core Juicebox V6 control plane: directory, controller, terminals, permissions, prices, and global protocol switches |
8
+ | Control posture | Mixed protocol-owner, project-owner, delegated-operator, controller, and terminal control |
9
+ | Highest-risk actions | Controller migration, terminal migration, token binding, price-feed installation, and broad permission grants |
10
+ | Recovery posture | Project-local mistakes may be fixable if rulesets permit; immutable infra mistakes usually require replacement layers and migration |
21
11
 
22
- ## One-Way Or High-Risk Actions
12
+ ## Purpose
23
13
 
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.
14
+ `nana-core-v6` is the largest control plane in the stack. It combines protocol-owned contracts, project-local ownership, delegated operators through `JBPermissions`, and ruleset flags that selectively permit or forbid changes. This file is about who can still change project behavior once the core is live.
28
15
 
29
- ## Recovery Notes
16
+ ## Control Model
30
17
 
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.
18
+ - Protocol-wide `Ownable` surfaces exist on `JBDirectory`, `JBProjects`, `JBPrices`, and `JBFeelessAddresses`.
19
+ - Project-local control runs through the project NFT owner in `JBProjects`.
20
+ - Fine-grained operator delegation runs through `JBPermissions`.
21
+ - Controllers and terminals are privileged system callers once the directory points to them.
22
+ - Current ruleset flags can further allow or deny certain owner or operator actions.
33
23
 
34
24
  ## Roles
35
25
 
36
- ### Project Owner
37
-
38
- - **How assigned:** Holds the ERC-721 NFT minted by `JBProjects.createFor()`. Transferable via standard ERC-721 transfer.
39
- - **Scope:** Controls a single project. The owner's address is `PROJECTS.ownerOf(projectId)`. Operators can be granted subsets of the owner's permissions via `JBPermissions`.
40
-
41
- ### Controller
42
-
43
- - **How assigned:** Set via `JBDirectory.setControllerOf()`. The controller is stored as `controllerOf[projectId]` in JBDirectory. Typically a `JBController` instance.
44
- - **Scope:** Manages a single project's rulesets, tokens, splits, and fund access limits. Contracts gated by `onlyControllerOf(projectId)` (from `JBControlled`) only accept calls from the address registered as the project's controller in the directory.
45
-
46
- ### Terminal
47
-
48
- - **How assigned:** Set via `JBDirectory.setTerminalsOf()`. Stored in the directory's `_terminalsOf[projectId]` array.
49
- - **Scope:** Manages fund inflows and outflows for a single project. Terminal identity is verified via `DIRECTORY.isTerminalOf()`. Terminals interact with `JBTerminalStore` using `msg.sender`-scoped bookkeeping -- each terminal can only modify its own balances.
50
-
51
- ### Operator
52
-
53
- - **How assigned:** Granted permissions by any address via `JBPermissions.setPermissionsFor()`. Permissions are stored as a packed `uint256` bitmap per `(operator, account, projectId)` tuple.
54
- - **Scope:** Can execute specific functions on behalf of the account that granted them permissions, scoped to a specific project ID (or all projects if `projectId = 0` wildcard is used).
55
-
56
- ### ROOT Permission Holder
57
-
58
- - **How assigned:** Granted the ROOT permission (ID 1) by an account via `JBPermissions`. ROOT on a specific project grants all permissions for that project. ROOT cannot be granted on the wildcard project ID (0) to prevent unlimited cross-project access.
59
- - **Scope:** ROOT holders for a project can do anything the project owner can do for that project. ROOT holders can also grant non-ROOT permissions to other operators for the same project but cannot grant ROOT to others or set wildcard permissions.
60
-
61
- ### Directory Owner
62
-
63
- - **How assigned:** Set at JBDirectory deployment via the Ownable constructor. Transferable via `Ownable.transferOwnership()`.
64
- - **Scope:** Protocol-wide. Controls which addresses are allowed to set a project's first controller (`isAllowedToSetFirstController`).
65
-
66
- ### JBProjects Owner
67
-
68
- - **How assigned:** Set at JBProjects deployment via the Ownable constructor. Transferable via `Ownable.transferOwnership()`.
69
- - **Scope:** Protocol-wide. Can set the `tokenUriResolver` that resolves project NFT metadata URIs.
70
-
71
- ### JBPrices Owner
72
-
73
- - **How assigned:** Set at JBPrices deployment via the Ownable constructor. Transferable via `Ownable.transferOwnership()`.
74
- - **Scope:** Protocol-wide. Can add default price feeds (project ID 0) that apply to all projects as fallback.
75
-
76
- ### JBFeelessAddresses Owner
77
-
78
- - **How assigned:** Set at JBFeelessAddresses deployment via the Ownable constructor. Transferable via `Ownable.transferOwnership()`.
79
- - **Scope:** Protocol-wide. Controls which addresses are exempt from protocol fees.
80
-
81
- ### JBERC20 Access Control
82
-
83
- - **How assigned:** The `TOKENS` reference is set to the `JBTokens` contract address during `initialize()`. `PERMISSIONS` and `PROJECTS` are constructor immutables inherited from `JBPermissioned` and the implementation contract respectively.
84
- - **Scope:** Single token contract. Only the `JBTokens` contract (via the `onlyTokens` modifier) can mint, burn, and update token metadata. Project owners or operators with the `SIGN_FOR_ERC20` permission can authorize ERC-1271 signatures.
85
-
86
- ### Omnichain Ruleset Operator
87
-
88
- - **How assigned:** Immutable address set in the `JBController` constructor as `OMNICHAIN_RULESET_OPERATOR`.
89
- - **Scope:** Can call `launchRulesetsFor` and `queueRulesetsOf` on any project, bypassing owner permission checks. This allows the omnichain deployer to synchronize rulesets across chains.
90
-
91
- ### Data Hook
92
-
93
- - **How assigned:** Specified in a ruleset's metadata as the `dataHook` address. Active only during the ruleset it is configured for.
94
- - **Scope:** Can mint tokens via `JBController.mintTokensOf()` when the data hook reports `hasMintPermissionFor()` returns true. Also controls pay/cashout hook specifications returned from `JBTerminalStore`.
95
-
96
- ### Fee Beneficiary (Project ID 1)
97
-
98
- - **How assigned:** Hardcoded as `_FEE_BENEFICIARY_PROJECT_ID = 1` in JBMultiTerminal.
99
- - **Scope:** Receives all protocol fees (2.5%) from payouts, surplus allowance usage, and cash outs with non-zero tax rates. This is the first project created during deployment.
100
-
101
- ## Privileged Functions
102
-
103
- ### JBPermissions
104
-
105
- | Function | Required Role | Permission ID | Scope | What It Does |
106
- |----------|--------------|---------------|-------|-------------|
107
- | `setPermissionsFor` | Account owner, or ROOT operator for that account+project | ROOT (1) | Per account + project | Sets the permission bitmap for an operator. ROOT operators can set non-ROOT permissions for the same project but cannot grant ROOT or set wildcard project permissions. |
108
-
109
- ### JBProjects
110
-
111
- | Function | Required Role | Permission ID | Scope | What It Does |
112
- |----------|--------------|---------------|-------|-------------|
113
- | `setTokenUriResolver` | Contract owner | N/A (onlyOwner) | Protocol-wide | Sets the contract that resolves token URIs for all project NFTs. |
114
- | `createFor` | Anyone | N/A | N/A | Creates a new project NFT. No permission required -- anyone can create a project for any owner. |
115
-
116
- ### JBDirectory
26
+ | Role | How Assigned | Scope | Notes |
27
+ | --- | --- | --- | --- |
28
+ | Project owner | `JBProjects.ownerOf(projectId)` | Per project | Root human control surface for a project |
29
+ | Project operator | `JBPermissions` grant | Per project or wildcard | Can be narrow or dangerously broad |
30
+ | Controller | `JBDirectory.controllerOf(projectId)` | Per project | Manages rulesets, token setup, splits, and fund-access config |
31
+ | Terminal | `JBDirectory` terminal set | Per project | Can move funds through `JBTerminalStore` and terminal entrypoints |
32
+ | Protocol owner | `Ownable(owner)` on protocol-wide contracts | Global | Different contracts have different owners |
33
+ | Omnichain ruleset operator | `JBController` constructor immutable | Global or broad | Bypasses some ordinary owner paths for synchronized ruleset flows |
117
34
 
118
- | Function | Required Role | Permission ID | Scope | What It Does |
119
- |----------|--------------|---------------|-------|-------------|
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. |
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. |
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. |
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. |
35
+ ## Privileged Surfaces
124
36
 
125
- ### JBController
37
+ High-value admin functions include:
126
38
 
127
- | Function | Required Role | Permission ID | Scope | What It Does |
128
- |----------|--------------|---------------|-------|-------------|
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. |
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). |
131
- | `claimTokensFor` | Credit holder or operator | CLAIM_TOKENS (12) | Per project | Redeems internal credits for ERC-20 tokens. |
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. |
133
- | `launchProjectFor` | Anyone | N/A | N/A | Creates a new project, sets this controller, configures terminals, and queues initial rulesets. No permission required. |
134
- | `launchRulesetsFor` | Project owner, operator, or OMNICHAIN_RULESET_OPERATOR | LAUNCH_RULESETS (3) + SET_TERMINALS (15) | Per project | Queues initial rulesets and configures terminals for an existing project that has no rulesets yet. Also sets this contract as the project's controller. |
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. |
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. |
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. |
138
- | `setSplitGroupsOf` | Project owner or operator | SET_SPLIT_GROUPS (19) | Per project | Sets split groups for a project. Must preserve any currently locked splits. |
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). |
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. |
141
- | `setUriOf` | Project owner or operator | SET_PROJECT_URI (7) | Per project | Updates the project's metadata URI. |
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. |
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. |
144
- | `beforeReceiveMigrationFrom` | JBDirectory only | N/A (msg.sender == DIRECTORY) | Per project | Called before migration to prepare the new controller. Copies metadata URI and distributes pending reserved tokens from the old controller. |
145
- | `afterReceiveMigrationFrom` | JBDirectory only | N/A (msg.sender == DIRECTORY) | Per project | Called after migration completes. Currently a no-op. |
146
- | `executePayReservedTokenToTerminal` | Self only | N/A (msg.sender == address(this)) | Internal | Pays a terminal with reserved tokens. Called internally via try-catch during reserved token distribution. |
147
- | `previewMintOf` | Anyone | N/A (view) | Per project | Simulates a mint under the current ruleset and returns the beneficiary and reserved token counts. No state modification. |
39
+ - `JBDirectory.setControllerOf(...)`, `setTerminalsOf(...)`, `setPrimaryTerminalOf(...)`
40
+ - `JBController.queueRulesetsOf(...)`, `launchRulesetsFor(...)`, `setSplitGroupsOf(...)`, `deployERC20For(...)`, `setTokenFor(...)`, `setUriOf(...)`, `addPriceFeedFor(...)`
41
+ - `JBMultiTerminal.useAllowanceOf(...)`, `migrateBalanceOf(...)`, `cashOutTokensOf(...)` when permission-gated by the holder or delegated authority
42
+ - `JBPermissions.setPermissionsFor(...)`
43
+ - `JBPrices.addPriceFeedFor(...)` for protocol defaults or project-local feeds
44
+ - `JBFeelessAddresses.setFeelessAddress(...)`
45
+ - `JBProjects.setTokenUriResolver(...)`
148
46
 
149
- ### JBMultiTerminal
47
+ The most important practical distinction is:
150
48
 
151
- | Function | Required Role | Permission ID | Scope | What It Does |
152
- |----------|--------------|---------------|-------|-------------|
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). |
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. |
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. |
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. |
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. |
158
- | `pay` | Anyone | N/A | N/A | Pays a project with tokens. No permission required. |
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. |
160
- | `processHeldFeesOf` | Anyone | N/A | Per project | Processes held fees that have passed their 28-day unlock period. No permission required. |
161
- | `executePayout` | Self only | N/A (msg.sender == address(this)) | Internal | Executes a single payout to a split. Called internally via try-catch during payout distribution. |
162
- | `executeProcessFee` | Self only | N/A (msg.sender == address(this)) | Internal | Processes a fee payment to the fee beneficiary project. Called internally via try-catch. |
163
- | `executeTransferTo` | Self only | N/A (msg.sender == address(this)) | Internal | Transfers tokens to an address. Called internally via try-catch during payout leftover distribution. |
164
- | `previewPayFor` | Anyone | N/A (view) | Per project | Simulates a payment and returns the ruleset, beneficiary token count, reserved token count, and hook specifications. No state modification. |
165
- | `previewCashOutFrom` | Anyone | N/A (view) | Per project | Simulates a cash out and returns the ruleset, reclaim amount, cash out tax rate, and hook specifications. No state modification. |
49
+ - protocol owners change global infrastructure or defaults
50
+ - project owners and their operators change project configuration
51
+ - controllers and terminals are trusted system actors once the directory points to them
166
52
 
167
- ### JBTokens
53
+ ## Immutable And One-Way
168
54
 
169
- | Function | Required Role | Permission ID | Scope | What It Does |
170
- |----------|--------------|---------------|-------|-------------|
171
- | `burnFrom` | Project's controller | N/A (onlyControllerOf) | Per project | Burns tokens and/or credits from a holder. Credits are burned first, then ERC-20 tokens. |
172
- | `claimTokensFor` | Project's controller | N/A (onlyControllerOf) | Per project | Converts credits to ERC-20 tokens for a holder. |
173
- | `deployERC20For` | Project's controller | N/A (onlyControllerOf) | Per project | Deploys a cloned JBERC20 token for the project. |
174
- | `mintFor` | Project's controller | N/A (onlyControllerOf) | Per project | Mints new tokens (ERC-20 if deployed) or credits for a holder. |
175
- | `setTokenFor` | Project's controller | N/A (onlyControllerOf) | Per project | Sets an external ERC-20 token for the project. Cannot be changed once set. |
176
- | `setTokenMetadataFor` | Project's controller | N/A (onlyControllerOf) | Per project | Sets the name and symbol of a project's token. |
177
- | `transferCreditsFrom` | Project's controller | N/A (onlyControllerOf) | Per project | Transfers credits between addresses. |
55
+ - Default or project-specific price feeds are write-once for a given pair.
56
+ - ERC-20 token binding decisions for a project are effectively one-time.
57
+ - The fee beneficiary project ID inside `JBMultiTerminal` is hardcoded.
58
+ - Constructor immutables on controller, directory, terminal, store, prices, and tokens are not patchable.
178
59
 
179
- ### JBSplits
60
+ ## Operational Notes
180
61
 
181
- | Function | Required Role | Permission ID | Scope | What It Does |
182
- |----------|--------------|---------------|-------|-------------|
183
- | `setSplitGroupsOf` | Project's controller, OR the address whose lower 160 bits match the group ID AND the upper 96 bits are non-zero (for self-namespaced splits) | N/A (onlyControllerOf or msg.sender namespace) | Per project | Sets split groups for a project/ruleset. Must preserve any currently locked splits. Percentage total per group must not exceed 100%. GroupIds with zero upper 96 bits (e.g., `uint256(uint160(tokenAddress))`) are protocol-reserved for terminal payout groups and always require controller authorization. |
62
+ - Use narrow project-scoped permissions instead of wildcard or ROOT permissions whenever possible.
63
+ - Validate whether the active ruleset allows the change before assuming the owner or operator can perform it.
64
+ - Treat controller migration, terminal migration, token deployment, and price-feed installation as control-plane changes with large blast radius.
65
+ - Read both the permission check and the current ruleset flags before concluding an action is allowed.
66
+ - Keep an eye on fee-route and payout-path failure semantics: some failures are intentionally caught so funds stay recoverable instead of being permanently trapped.
184
67
 
185
- ### JBFundAccessLimits
68
+ ## Machine Notes
186
69
 
187
- | Function | Required Role | Permission ID | Scope | What It Does |
188
- |----------|--------------|---------------|-------|-------------|
189
- | `setFundAccessLimitsFor` | Project's controller | N/A (onlyControllerOf) | Per project | Sets payout limits and surplus allowances for a project's ruleset. Limits must be in strictly increasing currency order. |
70
+ - Do not infer authority from project ownership alone; many paths also depend on the active ruleset and permission bitmap.
71
+ - Treat `JBDirectory`, `JBController`, `JBMultiTerminal`, `JBPermissions`, `JBPrices`, `JBFeelessAddresses`, and `JBProjects` as the minimum source-of-truth set for control-plane crawling.
72
+ - If a controller, terminal, or price-feed action is not backed by the exact current directory entry, stop and resolve the mismatch first.
73
+ - If a permission is not named explicitly in the call path, inspect the contract check before assuming delegated authority exists.
74
+ - If a fee route or split payout failed, inspect whether the core intentionally restored project balance or left a retry path before calling it a permanent loss.
190
75
 
191
- ### JBRulesets
76
+ ## Recovery
192
77
 
193
- | Function | Required Role | Permission ID | Scope | What It Does |
194
- |----------|--------------|---------------|-------|-------------|
195
- | `queueFor` | Project's controller | N/A (onlyControllerOf) | Per project | Queues a new ruleset with specified duration, weight, weight cut percent, approval hook, and metadata. |
196
- | `updateRulesetWeightCache` | Anyone | N/A | Per project | Updates the cached weight for a ruleset to avoid excessive iteration. No permission required -- anyone can call this maintenance function. |
197
-
198
- ### JBPrices
199
-
200
- | Function | Required Role | Permission ID | Scope | What It Does |
201
- |----------|--------------|---------------|-------|-------------|
202
- | `addPriceFeedFor` | Contract owner (for project ID 0 defaults) or project's controller (for project-specific feeds) | N/A (onlyOwner or onlyControllerOf) | Protocol-wide or per project | Adds an immutable price feed for a currency pair. Default feeds (project 0) apply as fallback for all projects. |
203
-
204
- ### JBFeelessAddresses
205
-
206
- | Function | Required Role | Permission ID | Scope | What It Does |
207
- |----------|--------------|---------------|-------|-------------|
208
- | `setFeelessAddress` | Contract owner | N/A (onlyOwner) | Protocol-wide | Marks an address as feeless or not. Feeless addresses do not incur the 2.5% protocol fee on payouts, surplus allowance usage, or cash outs. |
209
-
210
- ### JBERC20
211
-
212
- | Function | Required Role | Permission ID | Scope | What It Does |
213
- |----------|--------------|---------------|-------|-------------|
214
- | `mint` | JBTokens contract | N/A (onlyTokens) | Per token | Mints new tokens to an address. |
215
- | `burn` | JBTokens contract | N/A (onlyTokens) | Per token | Burns tokens from an address. |
216
- | `initialize` | Anyone (once) | N/A | Per token | Initializes the token name, symbol, and JBTokens contract reference. Can only be called once. |
217
- | `setMetadata` | JBTokens contract | N/A (onlyTokens) | Per token | Updates the token's name and symbol. |
218
- | `isValidSignature` | Anyone (view) | `SIGN_FOR_ERC20` | Per token | Validates ERC-1271 signatures for project owner or permitted operators. |
219
-
220
- ### JBTerminalStore
221
-
222
- JBTerminalStore has no explicit access control modifiers. Instead, it uses `msg.sender`-scoped storage -- each terminal can only read and write its own balance slots. The functions are designed to be called by terminal contracts:
223
-
224
- | Function | Implicit Caller | What It Does |
225
- |----------|----------------|-------------|
226
- | `recordAddedBalanceFor` | Any address (terminal) | Increments the caller's recorded balance for a project/token. |
227
- | `recordCashOutFor` | Any address (terminal) | Records a cash out, calculating reclaim amounts via bonding curve. Decrements the caller's balance. |
228
- | `recordPaymentFrom` | Any address (terminal) | Records a payment, calculating token issuance via weight. Increments the caller's balance. |
229
- | `recordPayoutFor` | Any address (terminal) | Records a payout against payout limits. Decrements the caller's balance. |
230
- | `recordTerminalMigration` | Any address (terminal) | Records a full balance migration. Requires the ruleset's `allowTerminalMigration` flag. Zeros out the caller's balance. |
231
- | `recordUsedAllowanceOf` | Any address (terminal) | Records surplus allowance usage. Decrements the caller's balance. |
232
-
233
- ## Deployment Dependencies
234
-
235
- Contracts must be deployed in dependency order. The constructor dependency graph:
236
-
237
- 1. **No dependencies:** JBPermissions, JBProjects, JBERC20 (implementation), JBFeelessAddresses
238
- 2. **Depends on (1):** JBDirectory (← JBPermissions, JBProjects)
239
- 3. **Depends on (2):** JBRulesets, JBSplits, JBFundAccessLimits, JBTokens (← JBDirectory); JBPrices (← JBDirectory, JBPermissions, JBProjects)
240
- 4. **Depends on (3):** JBTerminalStore (← JBDirectory, JBPrices, JBRulesets)
241
- 5. **Depends on (1-4):** JBController (← JBDirectory, JBFundAccessLimits, JBPermissions, JBPrices, JBProjects, JBRulesets, JBSplits, JBTokens + omnichainRulesetOperator address)
242
- 6. **Depends on (1-4):** JBMultiTerminal (← JBTerminalStore, JBFeelessAddresses, JBPermissions, JBProjects, JBSplits, JBTokens, Permit2)
243
-
244
- After deployment, `JBDirectory.setIsAllowedToSetFirstController()` must be called to authorize the controller. The first project (ID 1, the fee project) is created automatically in the `JBProjects` constructor.
245
-
246
- ## Permission System
247
-
248
- JBPermissions implements a 256-bit packed permission bitmap system:
249
-
250
- - **256 permission slots**: Each bit in a `uint256` represents one permission. Bit 0 is reserved (cannot be set). Bits 1-255 are available for permission IDs.
251
- - **ROOT permission (ID 1)**: Acts as a superuser for the granted project. A ROOT operator passes all permission checks for that project via `includeRoot: true`.
252
- - **Wildcard project ID (0)**: Granting a permission on project ID 0 makes it apply to all projects for that account. ROOT cannot be granted on the wildcard project ID to prevent unlimited cross-project access.
253
- - **Operator delegation**: Any address can grant any other address specific permissions. The `_requirePermissionFrom` check passes if `msg.sender == account` OR if the sender has the required permission (with ROOT fallback and wildcard fallback).
254
- - **Override pattern**: `_requirePermissionAllowingOverrideFrom` adds an `alsoGrantAccessIf` boolean that bypasses the permission check entirely when true. This is used to allow terminals, controllers, and data hooks to call privileged functions without explicit operator permissions.
255
-
256
- ### Permission IDs Used in nana-core-v6
257
-
258
- | ID | Name | Function It Gates |
259
- |----|------|-------------------|
260
- | 1 | ROOT | All permissions. Also allows setting non-ROOT permissions for others on the same project. |
261
- | 2 | QUEUE_RULESETS | `JBController.queueRulesetsOf` |
262
- | 3 | LAUNCH_RULESETS | `JBController.launchRulesetsFor` |
263
- | 4 | CASH_OUT_TOKENS | `JBMultiTerminal.cashOutTokensOf` |
264
- | 5 | SEND_PAYOUTS | `JBMultiTerminal.sendPayoutsOf` (only when `ownerMustSendPayouts` is set) |
265
- | 6 | MIGRATE_TERMINAL | `JBMultiTerminal.migrateBalanceOf` |
266
- | 7 | SET_PROJECT_URI | `JBController.setUriOf` |
267
- | 8 | DEPLOY_ERC20 | `JBController.deployERC20For` |
268
- | 9 | SET_TOKEN | `JBController.setTokenFor` |
269
- | 10 | MINT_TOKENS | `JBController.mintTokensOf` |
270
- | 11 | BURN_TOKENS | `JBController.burnTokensOf` |
271
- | 12 | CLAIM_TOKENS | `JBController.claimTokensFor` |
272
- | 13 | TRANSFER_CREDITS | `JBController.transferCreditsFrom` |
273
- | 14 | SET_CONTROLLER | `JBDirectory.setControllerOf` |
274
- | 15 | SET_TERMINALS | `JBDirectory.setTerminalsOf` |
275
- | 16 | ADD_TERMINALS | `JBDirectory.setPrimaryTerminalOf` when it must add a new terminal first |
276
- | 17 | SET_PRIMARY_TERMINAL | `JBDirectory.setPrimaryTerminalOf` |
277
- | 18 | USE_ALLOWANCE | `JBMultiTerminal.useAllowanceOf` |
278
- | 19 | SET_SPLIT_GROUPS | `JBController.setSplitGroupsOf` |
279
- | 20 | ADD_PRICE_FEED | `JBController.addPriceFeedFor` |
280
- | 21 | ADD_ACCOUNTING_CONTEXTS | `JBMultiTerminal.addAccountingContextsFor` |
281
- | 22 | SET_TOKEN_METADATA | `JBController.setTokenMetadataOf` |
282
-
283
- ## Immutable Configuration
284
-
285
- The following values are set at deploy time and cannot be changed:
286
-
287
- ### JBController
288
- - `DIRECTORY` -- the directory contract
289
- - `FUND_ACCESS_LIMITS` -- the fund access limits contract
290
- - `PERMISSIONS` -- the permissions contract (from JBPermissioned)
291
- - `PRICES` -- the prices contract
292
- - `PROJECTS` -- the projects NFT contract
293
- - `RULESETS` -- the rulesets contract
294
- - `SPLITS` -- the splits contract
295
- - `TOKENS` -- the tokens contract
296
- - `OMNICHAIN_RULESET_OPERATOR` -- the address allowed to launch/queue rulesets for any project
297
-
298
- ### JBMultiTerminal
299
- - `DIRECTORY` -- derived from STORE.DIRECTORY()
300
- - `FEELESS_ADDRESSES` -- the feeless addresses registry
301
- - `FEE` -- hardcoded at 25 (2.5% of MAX_FEE=1000)
302
- - `PERMISSIONS` -- the permissions contract
303
- - `PERMIT2` -- the Uniswap Permit2 contract
304
- - `PROJECTS` -- the projects NFT contract
305
- - `SPLITS` -- the splits contract
306
- - `STORE` -- the terminal store contract
307
- - `TOKENS` -- the tokens contract
308
- - `_FEE_BENEFICIARY_PROJECT_ID` -- hardcoded as 1
309
- - `_FEE_HOLDING_SECONDS` -- hardcoded as 2,419,200 (28 days)
310
-
311
- ### JBDirectory
312
- - `PERMISSIONS` -- the permissions contract
313
- - `PROJECTS` -- the projects NFT contract
314
-
315
- ### JBPrices
316
- - `DIRECTORY` -- the directory contract
317
- - `PERMISSIONS` -- the permissions contract
318
- - `PROJECTS` -- the projects NFT contract
319
- - Price feeds are immutable once set (cannot be replaced or removed)
320
-
321
- ### JBTokens
322
- - `DIRECTORY` -- the directory contract
323
- - `TOKEN` -- the JBERC20 implementation used for cloning
324
- - Project token assignments are immutable once set (cannot be changed to a different token)
325
-
326
- ### JBRulesets
327
- - `DIRECTORY` -- the directory contract
328
-
329
- ### JBSplits
330
- - `DIRECTORY` -- the directory contract
331
-
332
- ### JBFundAccessLimits
333
- - `DIRECTORY` -- the directory contract
334
-
335
- ### JBTerminalStore
336
- - `DIRECTORY` -- the directory contract
337
- - `PRICES` -- the prices contract
338
- - `RULESETS` -- the rulesets contract
339
-
340
- ### JBPermissions
341
- - Trusted forwarder for ERC-2771 meta-transactions
342
-
343
- ## Ruleset Flags That Gate Admin Actions
344
-
345
- Several admin functions are further gated by boolean flags in the active ruleset's metadata. These flags are set when the ruleset is queued and cannot be changed until a new ruleset takes effect:
346
-
347
- | Flag | What It Gates |
348
- |------|--------------|
349
- | `allowOwnerMinting` | Whether the project owner/operator can call `mintTokensOf`. Terminals and data hooks can always mint regardless. |
350
- | `allowSetCustomToken` | Whether `setTokenFor` can be called. |
351
- | `allowTerminalMigration` | Whether `migrateBalanceOf` / `recordTerminalMigration` can proceed. |
352
- | `allowSetTerminals` | Whether `setTerminalsOf` can be called by non-controller callers. |
353
- | `allowSetController` | Whether `setControllerOf` can be called (checked via `IJBDirectoryAccessControl`). |
354
- | `allowAddAccountingContext` | Whether `addAccountingContextsFor` can add new token contexts. |
355
- | `allowAddPriceFeed` | Whether `addPriceFeedFor` can add new price feeds. |
356
- | `ownerMustSendPayouts` | Whether `sendPayoutsOf` requires SEND_PAYOUTS permission (otherwise anyone can call it). |
357
- | `pausePay` | Whether payments are paused (checked in JBTerminalStore). |
358
- | `pauseCreditTransfers` | Whether credit transfers are paused. |
359
- | `holdFees` | Whether fees are held (deferred for 28 days) instead of being processed immediately. |
78
+ - Wrong immutable infra usually means a new controller, terminal, store, or price layer and then migration.
79
+ - Wrong project-local config can often be corrected if the current ruleset still permits the change.
80
+ - Wrong wildcard permissions are fixable only by updating the permission bitmap; they are dangerous mainly because of what can happen before revocation.
81
+ - Some fee-route and payout-route failures are recoverable in place because the core prefers restoring balance and preserving retry paths over trapping funds.
360
82
 
361
83
  ## Admin Boundaries
362
84
 
363
- What admins CANNOT do:
364
-
365
- - **Project owners cannot access other projects' funds.** All permission checks are scoped to a specific `projectId`. A project owner's permissions only apply to their own project.
366
- - **Controllers cannot bypass terminal accounting.** JBTerminalStore uses `msg.sender`-scoped balances. A controller cannot directly manipulate a terminal's recorded balances -- only the terminal itself can update its own balance slots.
367
- - **Terminals cannot mint tokens without controller involvement.** Token minting goes through the controller's `mintTokensOf`, which enforces ruleset rules (reserved percent, `allowOwnerMinting`).
368
- - **No single admin can change the fee rate.** The fee (2.5%) is hardcoded as a constant in JBMultiTerminal. Changing it requires deploying a new terminal contract.
369
- - **No admin can change the fee beneficiary.** Project ID 1 is hardcoded as the fee recipient. This cannot be changed.
370
- - **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.
371
- - **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.
372
- - **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.
373
- - **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.
374
- - **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.
375
- - **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.
376
- - **Reserved tokens must be distributed before controller migration.** The `migrate` function reverts if `pendingReservedTokenBalanceOf[projectId] > 0`, preventing loss of reserved tokens during migration.
377
- - **The JBTerminalStore has no admin functions.** It has no owner, no permission checks, and no special roles. Access is controlled entirely by the msg.sender-scoped storage pattern -- callers can only affect their own balance slots.
85
+ - Protocol owners cannot directly rewrite project economics without going through the contracts and ruleset constraints that enforce those changes.
86
+ - Project owners cannot bypass immutable constructor references or rewrite existing price-feed entries.
87
+ - Controllers and terminals only have the authority the directory and core contracts give them; they do not get arbitrary global power.
88
+ - Nobody can change the hardcoded fee beneficiary or retroactively patch immutable deployment mistakes in place.
89
+
90
+ ## Source Map
91
+
92
+ - `src/JBDirectory.sol`
93
+ - `src/JBController.sol`
94
+ - `src/JBMultiTerminal.sol`
95
+ - `src/JBPermissions.sol`
96
+ - `src/JBPrices.sol`
97
+ - `src/JBFeelessAddresses.sol`
98
+ - `src/JBProjects.sol`
99
+ - `test/units/static/JBController/`
100
+ - `test/units/static/JBDirectory/`
101
+ - `test/units/static/JBMultiTerminal/`
102
+ - `test/units/static/JBPermissions/`
103
+ - `test/units/static/JBPrices/`