@bananapus/ownable-v6 0.0.23 → 0.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/ownable-v6",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
package/src/JBOwnable.sol CHANGED
@@ -7,16 +7,11 @@ import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.s
7
7
 
8
8
  import {JBOwnableOverrides} from "./JBOwnableOverrides.sol";
9
9
 
10
- /// @notice A function restricted by `JBOwnable` can only be called by a Juicebox project's owner, a specified owner
11
- /// address (if set), or addresses with permission from the owner.
12
- /// @dev A function with the `onlyOwner` modifier from `JBOwnable` can only be called by addresses with owner access
13
- /// based on a `JBOwner` struct:
14
- /// 1. If `JBOwner.projectId` isn't zero, the address holding the `JBProjects` NFT with the `JBOwner.projectId` ID is
15
- /// the owner.
16
- /// 2. If `JBOwner.projectId` is set to `0`, the `JBOwner.owner` address is the owner.
17
- /// 3. The owner can give other addresses access with `JBPermissions.setPermissionsFor(...)`, using the
18
- /// `JBOwner.permissionId` permission.
19
- /// @dev To use `onlyOwner`, inherit this contract and apply the modifier to a function.
10
+ /// @notice Juicebox-aware ownership for any contract. Inherit this and apply the `onlyOwner` modifier to restrict
11
+ /// functions to the project owner, a fixed address, or anyone the owner has granted permission to via `JBPermissions`.
12
+ /// @dev Ownership resolves dynamically: if `JBOwner.projectId` is set, the holder of that project's ERC-721 NFT is
13
+ /// the owner. If `projectId` is 0, the stored `JBOwner.owner` address is used instead. The owner can delegate access
14
+ /// to other addresses by setting a `permissionId` and granting that permission through `JBPermissions`.
20
15
  contract JBOwnable is JBOwnableOverrides {
21
16
  //*********************************************************************//
22
17
  // -------------------------- constructor ---------------------------- //
@@ -10,9 +10,12 @@ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
10
10
  import {IJBOwnable} from "./interfaces/IJBOwnable.sol";
11
11
  import {JBOwner} from "./structs/JBOwner.sol";
12
12
 
13
- /// @notice An abstract base for `JBOwnable`, which restricts functions so they can only be called by a Juicebox
14
- /// project's owner or a specific owner address. The owner can give access permission to other addresses with
15
- /// `JBPermissions`.
13
+ /// @notice Abstract base implementing Juicebox-aware ownership resolution, transfer, and permission delegation.
14
+ /// Ownership is either address-based (a fixed EOA/contract) or project-based (whoever holds the project's ERC-721
15
+ /// NFT). The owner can delegate access to other addresses by configuring a `permissionId` in `JBPermissions`.
16
+ /// @dev Stale permission detection: when ownership changes (e.g. project NFT transferred), the `permissionId` is
17
+ /// effectively ignored until the new owner explicitly re-sets it — preventing the previous owner's delegates from
18
+ /// retaining access.
16
19
  abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
17
20
  //*********************************************************************//
18
21
  // --------------------------- custom errors ------------------------- //
@@ -26,14 +29,14 @@ abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
26
29
  // ---------------- public immutable stored properties --------------- //
27
30
  //*********************************************************************//
28
31
 
29
- /// @notice Mints ERC-721s that represent project ownership and transfers.
32
+ /// @notice The `JBProjects` ERC-721 contract used to resolve project-based ownership.
30
33
  IJBProjects public immutable override PROJECTS;
31
34
 
32
35
  //*********************************************************************//
33
36
  // --------------------- public stored properties -------------------- //
34
37
  //*********************************************************************//
35
38
 
36
- /// @notice This contract's owner information.
39
+ /// @notice The current ownership state — who owns this contract and how permission delegation is configured.
37
40
  JBOwner public override jbOwner;
38
41
 
39
42
  //*********************************************************************//
@@ -98,12 +101,11 @@ abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
98
101
  // -------------------------- public views --------------------------- //
99
102
  //*********************************************************************//
100
103
 
101
- /// @notice Returns the owner's address based on this contract's `JBOwner`.
102
- /// @dev If `projectId` is non-zero, resolves via `PROJECTS.ownerOf()`. If that call reverts (e.g., because the
103
- /// project NFT was burned or invalidated), returns `address(0)` effectively treating the contract as renounced.
104
- /// @dev **Assumption:** `JBProjects` V6 has no burn function, so this scenario cannot occur under normal
105
- /// conditions. The try-catch is a defensive measure against hypothetical future changes to `JBProjects` or
106
- /// unexpected ERC-721 behavior.
104
+ /// @notice Returns the current owner's address. If ownership is project-based, this dynamically resolves to
105
+ /// whoever holds the project's ERC-721 NFT right now.
106
+ /// @dev If `projectId` is non-zero, resolves via `PROJECTS.ownerOf()`. If that call reverts (e.g., burned NFT),
107
+ /// returns `address(0)` — effectively treating the contract as renounced. `JBProjects` V6 has no burn function,
108
+ /// so this is a defensive measure only.
107
109
  function owner() public view virtual returns (address) {
108
110
  JBOwner memory ownerInfo = jbOwner;
109
111
 
@@ -124,9 +126,11 @@ abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
124
126
  // -------------------------- internal views ------------------------- //
125
127
  //*********************************************************************//
126
128
 
127
- /// @notice Reverts if the sender is not the owner.
129
+ /// @notice Reverts if the caller is not the owner (or an authorized delegate when `permissionId` is set).
128
130
  /// @dev If `projectId` is non-zero and `PROJECTS.ownerOf()` reverts (e.g., burned NFT), the resolved owner is
129
131
  /// `address(0)`, causing all `_checkOwner` calls to revert — equivalent to a renounced contract.
132
+ /// @dev Stale permission detection: if the resolved owner differs from `_permissionOwner` (set when
133
+ /// `setPermissionId` was last called), delegation is disabled until the new owner re-configures it.
130
134
  function _checkOwner() internal view virtual {
131
135
  JBOwner memory ownerInfo = jbOwner;
132
136
 
@@ -169,17 +173,18 @@ abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
169
173
  // ---------------------- public transactions ------------------------ //
170
174
  //*********************************************************************//
171
175
 
172
- /// @notice Gives up ownership of this contract, making it impossible to call `onlyOwner` and `_checkOwner`
173
- /// functions.
174
- /// @dev This can only be called by the current owner.
176
+ /// @notice Permanently gives up ownership. After this, no address can call `onlyOwner` functions.
177
+ /// @dev Can only be called by the current owner. This is irreversible.
175
178
  function renounceOwnership() public virtual override {
176
179
  _checkOwner();
177
180
  _transferOwnership({newOwner: address(0), projectId: 0});
178
181
  }
179
182
 
180
- /// @notice Sets the permission ID the owner can use to give other addresses owner access.
181
- /// @dev This can only be called by the current owner.
182
- /// @param permissionId The permission ID to use for `onlyOwner`.
183
+ /// @notice Configures which `JBPermissions` permission ID grants delegate access to `onlyOwner` functions.
184
+ /// Set to 0 to disable delegation entirely (only the direct owner can call).
185
+ /// @dev Can only be called by the current owner. Records the current owner so stale permissions are detected
186
+ /// if ownership later changes.
187
+ /// @param permissionId The permission ID to use for `onlyOwner` delegation.
183
188
  function setPermissionId(uint8 permissionId) public virtual override {
184
189
  _checkOwner();
185
190
  _setPermissionId(permissionId);
@@ -199,10 +204,11 @@ abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
199
204
  _transferOwnership({newOwner: newOwner, projectId: 0});
200
205
  }
201
206
 
202
- /// @notice Transfer ownership of this contract to a new Juicebox project.
203
- /// @dev The `permissionId` is reset to 0 on transfer to prevent permission clashes for the new project owner.
204
- /// The new owner must explicitly call `setPermissionId()` to configure owner-level permission delegation.
205
- /// @dev The `projectId` must fit within a `uint88`.
207
+ /// @notice Transfers ownership to a Juicebox project whoever holds that project's ERC-721 NFT becomes the
208
+ /// owner.
209
+ /// @dev The `permissionId` is reset to 0 on transfer to prevent the previous owner's delegates from retaining
210
+ /// access. The new project owner must call `setPermissionId()` to re-enable delegation.
211
+ /// @dev The `projectId` must fit within a `uint88` and the project must already exist.
206
212
  /// @param projectId The ID of the project to transfer ownership to.
207
213
  function transferOwnershipToProject(uint256 projectId) public virtual override {
208
214
  _checkOwner();
@@ -240,7 +246,7 @@ abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
240
246
  emit PermissionIdChanged({newId: permissionId, caller: _msgSender()});
241
247
  }
242
248
 
243
- /// @notice Helper to allow for drop-in replacement of OpenZeppelin `Ownable`.
249
+ /// @notice Drop-in replacement for OpenZeppelin's `Ownable._transferOwnership(address)`.
244
250
  /// @param newOwner The address that should receive ownership of this contract.
245
251
  function _transferOwnership(address newOwner) internal virtual {
246
252
  _transferOwnership({newOwner: newOwner, projectId: 0});
@@ -3,7 +3,9 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
5
5
 
6
- /// @notice Provides Juicebox-aware ownership with support for project-based and address-based owners.
6
+ /// @notice Interface for Juicebox-aware ownership. Supports two modes: address-based (a fixed EOA/contract owns the
7
+ /// contract) or project-based (whoever holds a specific Juicebox project's ERC-721 NFT is the owner). The owner can
8
+ /// delegate access to other addresses via `JBPermissions`.
7
9
  interface IJBOwnable {
8
10
  /// @notice Emitted when ownership is transferred to a new owner.
9
11
  /// @param previousOwner The address of the previous owner.
@@ -16,14 +18,14 @@ interface IJBOwnable {
16
18
  /// @param caller The address that changed the permission ID.
17
19
  event PermissionIdChanged(uint8 newId, address caller);
18
20
 
19
- /// @notice The contract that mints ERC-721s representing project ownership.
21
+ /// @notice The `JBProjects` ERC-721 contract used to resolve project-based ownership.
20
22
  /// @return projects The `IJBProjects` contract.
21
23
  function PROJECTS() external view returns (IJBProjects projects);
22
24
 
23
- /// @notice This contract's owner information.
25
+ /// @notice The current ownership state — who owns this contract and how permission delegation is configured.
24
26
  /// @return owner The owner address (used when `projectId` is 0).
25
- /// @return projectId The ID of the Juicebox project whose owner is this contract's owner (0 if not project-owned).
26
- /// @return permissionId The permission ID the owner can use to grant other addresses owner access.
27
+ /// @return projectId The Juicebox project whose NFT holder is the owner (0 if address-based ownership).
28
+ /// @return permissionId The permission ID that delegates can use to act as owner via `JBPermissions`.
27
29
  function jbOwner() external view returns (address owner, uint88 projectId, uint8 permissionId);
28
30
 
29
31
  /// @notice Returns the current owner's address.
@@ -1,12 +1,12 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.0;
3
3
 
4
- /// @notice Owner information for a given instance of `JBOwnableOverrides`.
5
- /// @custom:member owner If `projectId` is 0, this address has owner access.
6
- /// @custom:member projectId The owner of the `JBProjects` ERC-721 with this ID has owner access. If this is 0, the
7
- /// `owner` address has owner access.
8
- /// @custom:member permissionId The permission ID which corresponds to owner access. See `JBPermissions` in `nana-core`
9
- /// and `nana-permission-ids`.
4
+ /// @notice Describes who owns a `JBOwnable` contract and how they can delegate access.
5
+ /// @custom:member owner The owner address — only used when `projectId` is 0 (address-based ownership mode).
6
+ /// @custom:member projectId If non-zero, the holder of this Juicebox project's ERC-721 NFT is the owner. When set,
7
+ /// the `owner` field is ignored and ownership resolves dynamically via `JBProjects.ownerOf(projectId)`.
8
+ /// @custom:member permissionId The permission ID that delegates can hold (via `JBPermissions`) to act as owner. Set
9
+ /// to 0 to disable delegation entirely — only the direct owner can call `onlyOwner` functions.
10
10
  struct JBOwner {
11
11
  address owner;
12
12
  uint88 projectId;