@bananapus/ownable-v6 0.0.22 → 0.0.23

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/foundry.toml CHANGED
@@ -14,7 +14,7 @@ depth = 100
14
14
  fail_on_revert = false
15
15
 
16
16
  [lint]
17
- exclude_lints = ["pascal-case-struct", "mixed-case-variable"]
17
+ exclude_lints = ["mixed-case-variable", "pascal-case-struct"]
18
18
  [fmt]
19
19
  number_underscore = "thousands"
20
20
  multiline_func_header = "all"
package/package.json CHANGED
@@ -1,18 +1,27 @@
1
1
  {
2
2
  "name": "@bananapus/ownable-v6",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Bananapus/nana-ownable-v6"
8
8
  },
9
+ "files": [
10
+ "CHANGELOG.md",
11
+ "foundry.lock",
12
+ "foundry.toml",
13
+ "references/",
14
+ "remappings.txt",
15
+ "src/",
16
+ "test/mocks/"
17
+ ],
9
18
  "engines": {
10
19
  "node": ">=20.0.0"
11
20
  },
12
21
  "dependencies": {
13
- "@bananapus/core-v6": "^0.0.36",
14
- "@bananapus/permission-ids-v6": "^0.0.19",
15
- "@openzeppelin/contracts": "^5.6.1"
22
+ "@bananapus/core-v6": "0.0.38",
23
+ "@bananapus/permission-ids-v6": "0.0.22",
24
+ "@openzeppelin/contracts": "5.6.1"
16
25
  },
17
26
  "scripts": {
18
27
  "test": "forge test",
package/ADMINISTRATION.md DELETED
@@ -1,79 +0,0 @@
1
- # Administration
2
-
3
- ## At A Glance
4
-
5
- | Item | Details |
6
- | --- | --- |
7
- | Scope | Ownership resolution primitive used by downstream repos |
8
- | Control posture | Primitive only; control depends on the inheriting contract |
9
- | Highest-risk actions | Transferring ownership to the wrong address or project and assuming delegated operators survive transfer |
10
- | Recovery posture | Recovery depends on the inheriting contract and the still-recognized current owner |
11
-
12
- ## Purpose
13
-
14
- `nana-ownable-v6` does not add a new admin surface by itself. It defines how ownership is resolved for other repos. The important question is how a contract's `owner()` is determined and how delegated permission IDs behave across ownership transfers.
15
-
16
- ## Control Model
17
-
18
- - ownership can be address-based or project-based
19
- - delegated operator checks run through `JBPermissions`
20
- - transfer and renounce semantics are part of the primitive
21
- - delegated permission resets on ownership transfer
22
-
23
- ## Roles
24
-
25
- | Role | How Assigned | Scope | Notes |
26
- | --- | --- | --- | --- |
27
- | Direct owner | Stored owner address | Per contract | Standard `Ownable`-like control |
28
- | Project owner | Holder of the referenced project NFT | Per contract | Dynamic ownership resolution |
29
- | Delegated operator | `JBPermissions` grant with the configured permission ID | Per contract and project | Only if the inheriting contract enables it |
30
-
31
- ## Privileged Surfaces
32
-
33
- The meaningful control surfaces are inherited by downstream contracts:
34
-
35
- - `setPermissionId(...)`
36
- - `transferOwnership(...)`
37
- - `transferOwnershipToProject(...)`
38
- - `renounceOwnership()`
39
- - `onlyOwner` checks that resolve either the direct owner or the current project NFT holder
40
-
41
- ## Immutable And One-Way
42
-
43
- - project ownership changes dynamically with project NFT transfers
44
- - delegated permission ID resets on ownership transfer
45
- - renouncing ownership is final unless the inheriting contract adds a separate recovery path
46
-
47
- ## Operational Notes
48
-
49
- - treat project-based ownership as live routing, not a snapshot
50
- - do not assume an operator permission survives ownership transfer
51
- - treat `setPermissionId(...)` as a real authority change because it rewires which delegated permission bit counts as owner access
52
- - review the inheriting contract, not just this primitive, to understand the full admin surface
53
-
54
- ## Machine Notes
55
-
56
- - do not conclude authority from this repo alone; follow the inheriting contract's `onlyOwner` surfaces
57
- - treat ownership transfer as potentially changing both the owner identity and the usable delegated permission ID
58
- - if the current permission ID is undocumented, inspect `jbOwner.permissionId` before reasoning about delegated owner access
59
- - if a downstream repo uses project-based ownership, re-evaluate owner resolution after every project NFT transfer
60
-
61
- ## Recovery
62
-
63
- - this primitive has no protocol-wide recovery surface
64
- - if ownership was transferred to the wrong project or address, recovery depends on the inheriting contract still recognizing the current owner
65
-
66
- ## Admin Boundaries
67
-
68
- - this repo does not create a new permission namespace
69
- - it cannot make an inheriting contract safer than that contract's own privileged functions
70
- - it cannot preserve delegated operators across ownership transfer by default
71
-
72
- ## Source Map
73
-
74
- - `src/JBOwnable.sol`
75
- - `src/JBOwnableOverrides.sol`
76
- - `src/structs/JBOwner.sol`
77
- - `test/OwnableInvariantTests.sol`
78
- - `test/OwnableEdgeCases.t.sol`
79
- - `test/regression/BurnLockProtection.t.sol`
package/ARCHITECTURE.md DELETED
@@ -1,92 +0,0 @@
1
- # Architecture
2
-
3
- ## Purpose
4
-
5
- `nana-ownable-v6` adapts `Ownable` to the Juicebox model. A contract can be owned by an address or by a Juicebox project NFT, and delegated operators can satisfy `onlyOwner` through `JBPermissions`.
6
-
7
- ## System Overview
8
-
9
- This repo is an ownership primitive, not a policy layer. `JBOwnable` gives downstream repos a familiar inheritance surface. `JBOwnableOverrides` implements dynamic owner resolution, ownership transfer, renounce behavior, and delegated permission checks.
10
-
11
- Ownership can follow the current holder of a Juicebox project NFT instead of staying fixed to one address.
12
-
13
- ## Core Invariants
14
-
15
- - project-owned contracts must resolve the owner dynamically from the current project NFT holder
16
- - the delegated permission ID resets on ownership transfer
17
- - pointing ownership at an unminted project can temporarily lock the contract until that project exists
18
- - an invalid or otherwise unresolvable project NFT effectively renounces ownership
19
- - this repo should stay a drop-in primitive, not grow product-specific access rules
20
-
21
- ## Modules
22
-
23
- | Module | Responsibility | Notes |
24
- | --- | --- | --- |
25
- | `JBOwnable` | Familiar `onlyOwner` inheritance target | Concrete surface |
26
- | `JBOwnableOverrides` | Resolution, transfer, renounce, and delegated-permission logic | Core behavior |
27
- | `JBOwner` | Packed owner state | Shared struct |
28
- | `IJBOwnable` | Public interface and events | Integration surface |
29
-
30
- ## Trust Boundaries
31
-
32
- - ownership resolution depends on `JBProjects` and `JBPermissions` from `nana-core-v6`
33
- - this repo does not create a new permission namespace
34
- - inheriting contracts may add policy on top, but the resolution semantics here are infrastructure-level
35
-
36
- ## Critical Flows
37
-
38
- ### Owner Check
39
-
40
- ```text
41
- onlyOwner modifier
42
- -> load packed owner state
43
- -> if project-owned, resolve the current project NFT holder
44
- -> otherwise use the stored owner address
45
- -> accept either the resolved owner or an operator with the configured JB permission
46
- ```
47
-
48
- ## Accounting Model
49
-
50
- No treasury accounting lives here. The important state is ownership resolution data and delegated permission ID.
51
-
52
- ## Security Model
53
-
54
- - ownership resolution edge cases matter more than surface API shape
55
- - permission delegation is simple but security-sensitive because it composes with a global permission registry
56
- - unresolvable project ownership is intentionally fail-closed
57
-
58
- ## Safe Change Guide
59
-
60
- - be conservative with transfer and renounce semantics
61
- - if event emission or transfer behavior changes, inspect deployer wrappers and inheriting repos
62
- - if project-based ownership semantics change, re-check unminted-project and unresolvable-project behavior explicitly
63
- - do not make delegated permission IDs sticky across ownership transfers
64
-
65
- ## Canonical Checks
66
-
67
- - baseline address-owner and project-owner behavior:
68
- `test/Ownable.t.sol`
69
- - transfer, renounce, and hostile-call edge cases:
70
- `test/OwnableEdgeCases.t.sol`
71
- `test/OwnableAttacks.t.sol`
72
- - unminted-project and burn-lock safety:
73
- `test/CodexUnmintedProjectHijack.t.sol`
74
- `test/regression/BurnLockProtection.t.sol`
75
- - ownership-state invariants:
76
- `test/OwnableInvariantTests.sol`
77
-
78
- ## Source Map
79
-
80
- - `src/JBOwnable.sol`
81
- - `src/JBOwnableOverrides.sol`
82
- - `src/structs/JBOwner.sol`
83
- - `src/interfaces/IJBOwnable.sol`
84
- - `test/Ownable.t.sol`
85
- - `test/OwnableEdgeCases.t.sol`
86
- - `test/OwnableAttacks.t.sol`
87
- - `test/CodexUnmintedProjectHijack.t.sol`
88
- - `test/regression/BurnLockProtection.t.sol`
89
- - `test/regression/ZeroAddressValidation.t.sol`
90
- - `test/OwnableInvariantTests.sol`
91
- - `references/runtime.md`
92
- - `references/operations.md`
@@ -1,69 +0,0 @@
1
- # Audit Instructions
2
-
3
- This repo provides ownership helpers that can follow Juicebox project NFTs instead of a fixed EOA. It is a small repo with outsized privilege impact.
4
-
5
- ## Audit Objective
6
-
7
- Find issues that:
8
-
9
- - let unauthorized actors satisfy owner checks
10
- - break ownership updates when a project NFT moves, burns, or locks
11
- - let override logic produce a different owner than the project system intends
12
- - leave dependent repos with stale or permanently wrong ownership views
13
-
14
- ## Scope
15
-
16
- In scope:
17
-
18
- - `src/JBOwnable.sol`
19
- - `src/JBOwnableOverrides.sol`
20
- - `src/interfaces/`
21
- - `src/structs/`
22
-
23
- ## Start Here
24
-
25
- 1. `src/JBOwnable.sol`
26
- 2. `src/JBOwnableOverrides.sol`
27
-
28
- ## Security Model
29
-
30
- These contracts abstract "owner" as a project-based identity. Downstream repos use them to:
31
-
32
- - treat a Juicebox project owner as contract owner
33
- - apply per-project override rules
34
- - keep admin power aligned with project NFT ownership instead of a static address
35
-
36
- ## Roles And Privileges
37
-
38
- | Role | Powers | How constrained |
39
- |------|--------|-----------------|
40
- | Project NFT owner | Become the effective contract owner | Should update automatically with NFT transfers |
41
- | Override authority | Set alternative owner resolution where allowed | Must not outrank project ownership unexpectedly |
42
-
43
- ## Integration Assumptions
44
-
45
- | Dependency | Assumption | What breaks if wrong |
46
- |------------|------------|----------------------|
47
- | Juicebox project ownership | NFT ownership reflects intended authority | Downstream admin checks drift from reality |
48
-
49
- ## Critical Invariants
50
-
51
- 1. Owner resolution is correct.
52
- For any supported mode, `owner()` and owner checks must resolve to the intended authority and no one else.
53
- 2. Burn and lock behavior is safe.
54
- If project ownership is intentionally burned or locked, the helper must not accidentally reopen control or brick valid admin paths.
55
- 3. Override precedence is coherent.
56
- Overrides must not silently supersede project ownership in cases the design does not permit.
57
-
58
- ## Attack Surfaces
59
-
60
- - owner resolution after project NFT transfer
61
- - zero-address, burn, and lock states
62
- - override configuration and precedence
63
- - downstream assumptions that cache owner state instead of re-reading it
64
-
65
- ## Verification
66
-
67
- - `npm install`
68
- - `forge build`
69
- - `forge test`
package/RISKS.md DELETED
@@ -1,51 +0,0 @@
1
- # Juicebox Ownable Risk Register
2
-
3
- This file covers the ownership-model risks in `JBOwnable`: dynamic ownership through project NFTs, delegated owner authority, and mismatches with standard `Ownable` expectations.
4
-
5
- ## How To Use This File
6
-
7
- - Read `Priority risks` first. Most failures here come from authority-model mistakes, not arithmetic bugs.
8
- - Use the later sections to understand what changes when ownership follows a project instead of a fixed address.
9
- - Treat `Invariants to verify` as the minimum proof that owner resolution stays coherent.
10
-
11
- ## Priority Risks
12
-
13
- | Priority | Risk | Why it matters | Primary controls |
14
- |----------|------|----------------|------------------|
15
- | P1 | Misunderstanding dynamic owner resolution | Ownership can move when the project NFT moves or when permissions change, which breaks static `Ownable` assumptions. | Clear docs, careful integration review, and explicit tests around transfer paths. |
16
- | P1 | Over-broad delegated owner permissions | `JBPermissions` can broaden who effectively acts as owner. Bad configuration expands blast radius quickly. | Permission hygiene and explicit review of delegated grants. |
17
- | P2 | Tooling assumptions about standard `Ownable` | Some tooling assumes `owner()` maps to one address with no external permission system behind it. | Integration testing and clear documentation of the semantic differences. |
18
-
19
- ## 1. Trust Assumptions
20
-
21
- - **`JBPermissions` works correctly.** A bug there affects every `JBOwnable` contract that relies on delegated owner access.
22
- - **`JBProjects` ownership is the source of truth.** When a contract is project-owned, whoever holds the project NFT has owner access.
23
- - **Delegated permission means owner-equivalent access.** Anyone granted the configured `permissionId` through `JBPermissions` can satisfy owner checks for the scoped contract.
24
- - **Deployment inputs are intentional.** If `initialProjectIdOwner != 0`, deployers must understand whether that project already exists.
25
-
26
- ## 2. Known Risks
27
-
28
- - **Project NFT transfer changes contract ownership.** Anyone who acquires the project NFT gains owner access to contracts using that project-owned mode.
29
- - **Two ownership modes can confuse integrations.** Setting both `newOwner` and `projectId` is disallowed, but integrators still need to check which mode is active.
30
- - **`renounceOwnership` is final.** Once called, `owner()` resolves to `address(0)` and owner-gated functions stop working permanently unless a downstream contract adds its own recovery path.
31
- - **Constructor pre-binding can intentionally lock the contract.** If a deployer points ownership at a future project ID, `owner()` resolves to `address(0)` until that project exists.
32
- - **`PROJECTS == address(0)` breaks project-owned mode.** The constructor defends against this, but wrappers should still treat it as a high-signal deployment surface.
33
- - **Unminted project ID ownership.** Contracts using `JBOwnableOverrides` can be configured with an `initialProjectIdOwner` that references a project ID not yet minted. The first account to mint that sequential project ID will become the effective owner of the contract. Deployers must ensure the referenced project ID is already minted, or deploy the ownable contract and the project in the same transaction to prevent front-running.
34
-
35
- ## 3. Accepted Behaviors
36
-
37
- - **Permission ID resets on transfer.** `permissionId` resets to `0` on ownership transfer so old delegated operators do not automatically retain power.
38
- - **`permissionId = 0` means direct-owner-only mode.** This is a valid configuration, not an error state.
39
- - **Invalid project ownership resolves fail-closed.** If `ownerOf` cannot resolve, the contract is effectively renounced until ownership becomes readable again.
40
- - **`transferOwnershipToProject` rejects non-existent projects.** The function checks existence at transfer time.
41
- - **Constructor pre-binding to a future project ID is supported.** This is useful in controlled deployment flows, but dangerous if the deployer does not control mint sequencing.
42
- - **Transfer events can temporarily show `address(0)`.** When ownership points to an unminted future project, the transfer event shows `address(0)` until ownership can resolve dynamically.
43
-
44
- ## 4. Invariants To Verify
45
-
46
- - ownership is always exactly one of: direct address or project NFT holder
47
- - `_checkOwner()` reverts for all callers when the owner resolves to `address(0)`
48
- - `permissionId` resets to `0` on every ownership transfer
49
- - after `renounceOwnership()`, `jbOwner()` returns `(address(0), 0, 0)` and no address can pass `_checkOwner()`
50
- - `transferOwnershipToProject(projectId)` reverts for all `projectId > PROJECTS.count()` at call time
51
- - `initialProjectIdOwner != 0` with `PROJECTS == address(0)` always reverts during construction
package/SKILLS.md DELETED
@@ -1,41 +0,0 @@
1
- # Juicebox Ownable
2
-
3
- ## Use This File For
4
-
5
- - Use this file when the task involves project-based ownership, delegated `onlyOwner` permissions, or ownership that should follow a Juicebox project NFT instead of a fixed wallet.
6
- - Start here, then decide whether the question is about owner resolution, permission delegation, or ownership transfer semantics.
7
-
8
- ## Read This Next
9
-
10
- | If you need... | Open this next |
11
- |---|---|
12
- | Repo overview and ownership model | [`README.md`](./README.md), [`ARCHITECTURE.md`](./ARCHITECTURE.md) |
13
- | Concrete contract | [`src/JBOwnable.sol`](./src/JBOwnable.sol) |
14
- | Resolution and permission logic | [`src/JBOwnableOverrides.sol`](./src/JBOwnableOverrides.sol), [`src/interfaces/`](./src/interfaces/), [`src/structs/`](./src/structs/) |
15
- | Runtime and migration assumptions | [`references/runtime.md`](./references/runtime.md), [`references/operations.md`](./references/operations.md) |
16
- | Edge, attack, and invariant coverage | [`test/Ownable.t.sol`](./test/Ownable.t.sol), [`test/OwnableEdgeCases.t.sol`](./test/OwnableEdgeCases.t.sol), [`test/OwnableAttacks.t.sol`](./test/OwnableAttacks.t.sol), [`test/OwnableInvariantTests.sol`](./test/OwnableInvariantTests.sol), [`test/CodexUnmintedProjectHijack.t.sol`](./test/CodexUnmintedProjectHijack.t.sol) |
17
-
18
- ## Repo Map
19
-
20
- | Area | Where to look |
21
- |---|---|
22
- | Main contracts | [`src/`](./src/) |
23
- | Types | [`src/interfaces/`](./src/interfaces/), [`src/structs/`](./src/structs/) |
24
- | Tests | [`test/`](./test/) |
25
-
26
- ## Purpose
27
-
28
- Ownership adapter for contracts that should follow Juicebox project ownership instead of a fixed address, with optional delegated permission IDs on top of the familiar `Ownable` pattern.
29
-
30
- ## Reference Files
31
-
32
- - Open [`references/runtime.md`](./references/runtime.md) when you need owner resolution, transfer semantics, or delegated access behavior.
33
- - Open [`references/operations.md`](./references/operations.md) when you need migration pitfalls, test breadcrumbs, or the common stale assumptions around permission resets.
34
-
35
- ## Working Rules
36
-
37
- - Start in [`src/JBOwnableOverrides.sol`](./src/JBOwnableOverrides.sol) when the question is about who the effective owner is or why `onlyOwner` passed or failed.
38
- - Treat ownership transfer and delegated permission resets as security-sensitive.
39
- - Project-based ownership can intentionally become unusable if it points at an unminted or invalid project.
40
- - Unminted or unexpectedly transferred project NFTs can change the effective owner surface. Check the project lifecycle, not just this adapter.
41
- - When a bug looks like project ownership itself, confirm whether the real source is upstream in `nana-core-v6` rather than this adapter.