@bananapus/address-registry-v6 0.0.10 → 0.0.13

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
@@ -26,6 +26,15 @@ Every function is callable by any address. There are no restricted operations.
26
26
  - **Permanent storage, no removal.** There is no `unregister` or `removeAddress` function. Entries can be overwritten but never deleted.
27
27
  - **No approval or queue.** Registrations take effect immediately in the same transaction.
28
28
 
29
+ ## Client-Side Trust
30
+
31
+ The registry stores deployer mappings without making trust judgments. All trust decisions are delegated to clients:
32
+
33
+ - **No on-chain filtering.** The contract does not distinguish "trusted" from "untrusted" deployers. Clients must maintain their own allowlist of known deployer addresses and cross-reference against `deployerOf()`.
34
+ - **Event-based discovery.** The contract emits an `AddressRegistered(address indexed addr, address indexed deployer, address caller)` event on every registration. Clients can monitor this event to discover new registrations in real time.
35
+ - **Overwrite risk.** Since registrations are overwritable, a client that caches `deployerOf(addr)` at time T may see a different result at time T+1 if someone re-registers the same address with a different deployer/nonce or deployer/salt/bytecode combination. Clients should re-check at time of use rather than caching indefinitely.
36
+ - **No validation of deployment.** The registry computes the expected address from the inputs but does not verify that a contract actually exists at that address. A registration can be created for an address that has not yet been deployed (or will never be deployed).
37
+
29
38
  ## Admin Boundaries
30
39
 
31
40
  There are no admins. Specifically:
package/ARCHITECTURE.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Deployer verification registry for Juicebox V6. Allows contracts deployed via `create` or `create2` to publicly register their deployer's address. Frontend clients use this to verify that hooks and other contracts were deployed by trusted deployers.
5
+ Juicebox projects can attach arbitrary hook contracts (pay hooks, cashout hooks, 721 tier hooks, etc.) that execute during payments, cashouts, and payouts. A malicious hook could steal funds or mislead users. Frontends need a way to answer the question: "Was this hook deployed by a trusted deployer like `JB721TiersHookDeployer`?"
6
+
7
+ `JBAddressRegistry` solves this by letting contracts deployed via `create` or `create2` publicly register their deployer's address. A frontend can then call `deployerOf(hookAddress)` and check the result against its own list of trusted deployers — displaying warnings or blocking interactions for hooks with unknown origins.
6
8
 
7
9
  ## Contract Map
8
10
 
@@ -38,6 +40,16 @@ Frontend → JBAddressRegistry.deployerOf(hookAddress)
38
40
  → Frontend checks deployer against trusted deployer list
39
41
  ```
40
42
 
43
+ ## Design Decisions
44
+
45
+ **Deployer verification, not a whitelist.** The registry records *who* deployed a contract, not *whether* a contract is approved. This keeps the registry permissionless and neutral — any deployer can register, and trust decisions are made by each frontend independently. There is no governance or admin role.
46
+
47
+ **Both `create` and `create2` support.** Deployers that use `create` (nonce-based) and `create2` (salt + bytecode) both exist in the Juicebox ecosystem. Supporting both ensures any deployer can register its contracts regardless of deployment strategy.
48
+
49
+ **No validation beyond hash match.** The registry does not check that registered addresses contain code, implement a particular interface, or were recently deployed. It only verifies that the provided deployer + nonce/salt/bytecode deterministically produce the claimed address. This keeps the contract simple and gas-efficient — frontends already perform their own trust checks on the deployer address.
50
+
51
+ **Anyone can call `registerAddress`.** Registration is not restricted to the deployer itself. Any account that knows the deployer address and nonce (or salt + bytecode) can register a contract. This is safe because the mapping is deterministic — providing incorrect inputs simply computes a different address, not a false registration for the target contract.
52
+
41
53
  ## Dependencies
42
54
 
43
55
  - `@sphinx-labs/plugins` — Deployment tooling (devDependency only)
@@ -2,6 +2,31 @@
2
2
 
3
3
  You are auditing a permissionless address registry for Juicebox V6. The contract stores a mapping from deployed contract addresses to their deployers, computed deterministically from `create` or `create2` parameters. It has no owner, no access control, no constructor arguments, and no external dependencies. Read [RISKS.md](./RISKS.md) first -- it documents all known risks and trust assumptions. Then come back here.
4
4
 
5
+ ## Compiler and Version Info
6
+
7
+ | Setting | Value |
8
+ |---------|-------|
9
+ | Solidity version | ^0.8.26 |
10
+ | EVM target | cancun |
11
+ | Optimizer | enabled, 200 runs |
12
+ | via-IR | not enabled |
13
+ | Fuzz runs | 4,096 |
14
+ | Invariant runs | 1,024 (depth 100) |
15
+
16
+ Source: [`foundry.toml`](./foundry.toml)
17
+
18
+ ## Previous Audit Findings
19
+
20
+ A Nemesis automated audit was conducted on 2026-03-17. Results are in [`.audit/findings/nemesis-verified.md`](./.audit/findings/nemesis-verified.md). Summary:
21
+
22
+ | ID | Severity | Title | Status |
23
+ |----|----------|-------|--------|
24
+ | NM-001 | LOW | Deployment library project name mismatch (`"nana-address-registry"` vs `"nana-address-registry-v6"`) | Open (deployment script only, no runtime impact) |
25
+
26
+ The core contract (`JBAddressRegistry`) was verified sound -- RLP encoding is correct across all 10 nonce ranges, CREATE2 computation matches EIP-1014. No CRITICAL, HIGH, or MEDIUM findings were identified.
27
+
28
+ No prior formal audit with finding IDs from an external security firm has been conducted.
29
+
5
30
  ## Scope
6
31
 
7
32
  **In scope -- all Solidity in `src/`:**
package/CHANGE_LOG.md CHANGED
@@ -2,11 +2,17 @@
2
2
 
3
3
  This document describes all changes between `nana-address-registry` (v5) and `nana-address-registry-v6` (v6).
4
4
 
5
+ ## Summary
6
+
7
+ - **Nonce range extended from `uint32` to `uint64`**: Fixes silent address miscalculation for large nonces — previously truncated without error, now correctly RLP-encodes up to `uint64` and reverts above.
8
+ - **New `NonceTooLarge` error**: Explicit revert replaces silent truncation for nonces exceeding `uint64.max`.
9
+ - **No ABI-breaking changes**: Interface and function signatures are identical — only the internal nonce encoding logic changed.
10
+
5
11
  ---
6
12
 
7
13
  ## 1. Breaking Changes
8
14
 
9
- - **Solidity version bump**: `0.8.23` → `0.8.26`. Contracts compiled against v5 ABIs will still be compatible (no ABI-level breaking changes), but the compiler version requirement has changed.
15
+ - **Solidity version bump**: `0.8.23` → `^0.8.26`. Contracts compiled against v5 ABIs will still be compatible (no ABI-level breaking changes), but the compiler version requirement has changed.
10
16
  - **Nonce range extended from `uint32` to `uint64`**: In v5, the `_addressFrom` function silently produced incorrect addresses for nonces at or above `2^32`. In v6, nonces up to `uint64.max` are correctly RLP-encoded, and nonces above `uint64.max` revert with `JBAddressRegistry_NonceTooLarge`. Any off-chain tooling that assumed the `uint32` ceiling must be updated.
11
17
 
12
18
  ## 2. New Features
@@ -81,7 +87,7 @@ No function signatures, parameter types, or return types changed.
81
87
  | v5 | v6 | Action Required |
82
88
  |---|---|---|
83
89
  | `IJBAddressRegistry` | `IJBAddressRegistry` | **None** — ABI-identical. Update import path only. |
84
- | `JBAddressRegistry` | `JBAddressRegistry` | **None** — ABI-compatible. Deploy new instance compiled with Solidity 0.8.26. |
90
+ | `JBAddressRegistry` | `JBAddressRegistry` | **None** — ABI-compatible. Deploy new instance compiled with Solidity ^0.8.26. |
85
91
  | `registerAddress(address, uint256)` | `registerAddress(address, uint256)` | **None** — signature unchanged. Nonces > `uint32` now work correctly; nonces > `uint64` now revert instead of silently producing wrong addresses. |
86
92
  | `registerAddress(address, bytes32, bytes)` | `registerAddress(address, bytes32, bytes)` | **None** — signature unchanged. |
87
93
  | `deployerOf(address)` | `deployerOf(address)` | **None** — signature unchanged. |
package/README.md CHANGED
@@ -46,6 +46,17 @@ In both cases, the registry stores the mapping `deployerOf[computedAddress] = de
46
46
 
47
47
  No access control is needed -- only the correct deployer + parameters can produce a given address, so registrations cannot be faked.
48
48
 
49
+ ```solidity
50
+ // Register a contract deployed via create.
51
+ registry.registerAddress(deployer, nonce);
52
+
53
+ // Register a contract deployed via create2.
54
+ registry.registerAddress(deployer, salt, bytecode);
55
+
56
+ // Look up who deployed a contract.
57
+ address deployer = registry.deployerOf(contractAddress);
58
+ ```
59
+
49
60
  ### Events
50
61
 
51
62
  | Event | Fields | Emitted When |
@@ -61,7 +72,7 @@ Deployers can be exploited or act maliciously. Clients should still communicate
61
72
  ### Limitations
62
73
 
63
74
  - The `_addressFrom` function for `create` addresses uses RLP encoding that only supports nonces up to `uint64` max (18,446,744,073,709,551,615). Higher nonces revert with `JBAddressRegistry_NonceTooLarge`. In practice, this limit is unreachable.
64
- - No overwrite protection: registering the same computed address again overwrites the previous deployer. This is safe because only the correct deployer + parameters produce a given address -- a second call with the same inputs just re-registers the same deployer.
75
+ - Re-registration is prevented: registering the same computed address again reverts with `JBAddressRegistry_AlreadyRegistered`. Only the first registration is accepted.
65
76
  - Registration is permissionless -- anyone can call `registerAddress`, not just the deployer. Security relies entirely on deterministic address computation.
66
77
 
67
78
  ## Deployment
package/RISKS.md CHANGED
@@ -8,11 +8,18 @@
8
8
  ## 2. Known Risks
9
9
 
10
10
  - **False registration.** Anyone can call `registerAddress` with arbitrary deployer/nonce values, registering a computed address with a deployer that did not actually deploy it. Consumers must verify the deployer is trusted, not just that a mapping exists.
11
- - **Overwrite of existing entries.** There is no check preventing re-registration. A second call with different parameters that computes the same address will overwrite the previous `deployerOf` entry.
12
- - **No removal mechanism.** Once registered, an entry cannot be removed, only overwritten.
13
- - **RLP encoding correctness.** The `_addressFrom` function manually implements RLP encoding for nonces up to uint64. Well-tested pattern; nonce capped at uint64 max with explicit revert.
11
+ - **No removal mechanism.** Once registered, an entry cannot be removed. Re-registration of the same address reverts with `JBAddressRegistry_AlreadyRegistered`.
12
+ - **CREATE2 vs CREATE1 trust model.** CREATE1 registrations (`registerAddress(deployer, nonce)`) can be spoofed: anyone who knows the deployer address and nonce can register the computed address without being the deployer. CREATE2 registrations (`registerAddress(deployer, salt, bytecode)`) have the same trust model — knowing the parameters is sufficient to register. The function accepts raw deployment bytecode and hashes it internally (`keccak256(bytecode)`) for the CREATE2 address computation. In both cases, the registry only proves "this address COULD have been deployed by this deployer with these parameters", not "this deployer DID deploy this address". Consumers must verify the deployer is trusted independently.
13
+ - **Frontend trust example.** A malicious actor could register `deployerOf[uniswapRouter] = attackerDeployer` if they find a `(deployer, nonce)` or `(deployer, salt, bytecode)` combination that computes to the Uniswap router address and register it before the legitimate deployer does. Only the first registration is accepted; subsequent attempts revert with `JBAddressRegistry_AlreadyRegistered`. Frontends that display "deployed by X" based on `deployerOf` should cross-reference against a curated allowlist of trusted deployers.
14
14
 
15
15
  ## 3. Invariants to Verify
16
16
 
17
17
  - `deployerOf[addr]` always corresponds to a valid deployer/nonce pair that produces `addr` (if registered via `registerAddress`).
18
- - `create2` registrations: `deployerOf[addr]` corresponds to a valid deployer/salt/bytecodeHash that produces `addr`.
18
+ - `create2` registrations: `deployerOf[addr]` corresponds to a valid deployer/salt/bytecode combination whose `keccak256(bytecode)` produces `addr` via the CREATE2 formula.
19
+ - Registration idempotency: `deployerOf[addr]` reflects the first and only registration. Subsequent attempts to register the same address revert with `JBAddressRegistry_AlreadyRegistered`.
20
+
21
+ ## 4. Accepted Behaviors
22
+
23
+ ### 4.1 RLP encoding correctness is well-tested and bounded
24
+
25
+ The `_addressFrom` function manually implements RLP encoding for nonces up to uint64. The nonce is capped at uint64 max with an explicit revert, and the encoding logic is a well-tested pattern. This is not an open risk.
package/SKILLS.md CHANGED
@@ -41,6 +41,14 @@ Deployed on: Ethereum, Optimism, Arbitrum, Base, and their Sepolia testnets.
41
41
  | `_addressFrom(address origin, uint256 nonce) returns (address)` | Computes `create` address using RLP encoding. Handles 10 nonce ranges: `0`, `1-0x7f`, `0x80-0xff`, `0x100-0xffff`, `0x10000-0xffffff`, `0x1000000-0xffffffff`, `0x100000000-0xffffffffff`, `0x10000000000-0xffffffffffff`, `0x1000000000000-0xffffffffffffff`, `0x100000000000000-0xffffffffffffffff`. Reverts with `JBAddressRegistry_NonceTooLarge` for nonces above `uint64` max. Uses `keccak256` of the RLP-encoded `[origin, nonce]` and extracts the low 160 bits via assembly. |
42
42
  | `_registerAddress(address addr, address deployer)` | Writes `deployerOf[addr] = deployer` and emits `AddressRegistered`. Shared by both public `registerAddress` overloads. |
43
43
 
44
+ ## Errors
45
+
46
+ | Error | Defined In | Trigger Condition |
47
+ |-------|-----------|-------------------|
48
+ | `JBAddressRegistry_NonceTooLarge(uint256 nonce)` | `JBAddressRegistry` | `registerAddress(deployer, nonce)` is called with a `nonce` greater than `type(uint64).max` (18,446,744,073,709,551,615). Reverts inside `_addressFrom` before any state change. In practice unreachable since no EOA or contract can reach this nonce. |
49
+
50
+ This is the only custom error in the contract. The `create2` overload of `registerAddress` has no revert paths (invalid parameters silently compute the wrong address).
51
+
44
52
  ## Integration Points
45
53
 
46
54
  | Dependency | Import | Used For |
@@ -83,11 +91,11 @@ No arrays, no structs, no linked lists. One mapping, that is all.
83
91
  - **Nonce limit**: `_addressFrom` supports nonces up to `uint64` max (18,446,744,073,709,551,615). Nonces above this revert with `JBAddressRegistry_NonceTooLarge`. In practice this limit is unreachable.
84
92
  - **No overwrite protection**: Calling `registerAddress` with parameters that compute to an already-registered address will overwrite the `deployerOf` entry. This is safe because only the correct deployer + parameters produce a given address. But be aware that the same address can be "re-registered" by anyone at any time (with the same result).
85
93
  - **Permissionless**: Anyone can call `registerAddress`, not just the deployer. `msg.sender` is recorded in the event as `caller` but is NOT stored in the mapping. Only the computed deployer is stored.
86
- - **No validation**: The registry does not check that `addr` has code deployed, or that the deployer is a real deployer. It purely does math and stores the result. If you pass wrong parameters, it silently registers a mapping for the wrong address.
94
+ - **No validation**: The registry does not check that `addr` has code deployed, or that the deployer is a real deployer. It purely does math and stores the result. If you pass wrong parameters, it silently registers a mapping for the wrong address. Frontends MUST verify `addr.code.length > 0` (or `extcodesize(addr) > 0` in assembly) before trusting a registry entry.
87
95
  - **`deployerOf` returns `address(0)` for unregistered addresses**: This is the default mapping value, not a sentinel. There is no way to distinguish "never registered" from "registered with deployer = address(0)" (though the latter would require someone to call `registerAddress(address(0), ...)` deliberately).
88
96
  - **`create2` bytecode must include constructor args**: When registering a `create2` deployment, the `bytecode` parameter must be the full creation bytecode including ABI-encoded constructor arguments: `abi.encodePacked(type(Contract).creationCode, abi.encode(arg1, arg2, ...))`. Omitting constructor args will compute the wrong address.
89
97
  - **Contract nonces start at 1**: When using the `create` overload to register a contract deployed by another contract, remember that contract nonces start at 1 (not 0 like EOAs). The first contract deployed by a factory is at nonce 1.
90
- - **Solidity version**: The implementation uses `pragma solidity 0.8.26` (exact). The interface uses `pragma solidity ^0.8.0` (flexible) so it can be imported by any 0.8.x consumer.
98
+ - **Solidity version**: The implementation uses `pragma solidity ^0.8.26`. The interface uses `pragma solidity ^0.8.0` (flexible) so it can be imported by any 0.8.x consumer.
91
99
 
92
100
  ## Example: Register a `create` Deployment
93
101
 
package/STYLE_GUIDE.md CHANGED
@@ -21,7 +21,7 @@ One contract/interface/struct/enum per file. Name the file after the type it con
21
21
 
22
22
  ```solidity
23
23
  // Contracts — pin to exact version
24
- pragma solidity 0.8.26;
24
+ pragma solidity ^0.8.26;
25
25
 
26
26
  // Interfaces, structs, enums — caret for forward compatibility
27
27
  pragma solidity ^0.8.0;
package/USER_JOURNEYS.md CHANGED
@@ -1,144 +1,97 @@
1
1
  # User Journeys -- nana-address-registry-v6
2
2
 
3
- Concrete end-to-end flows through the address registry. Each journey traces the exact function calls, state changes, and external interactions.
3
+ All user paths through the Juicebox V6 address registry. For each journey: entry point, key parameters, state changes, events, and edge cases.
4
4
 
5
- ## Journey 1: Register a Contract Deployed via create
6
-
7
- **Actor:** Anyone (the deployer, the deployed contract itself, or a third party).
8
- **Goal:** Record the deployer of a contract that was deployed using the `create` opcode (standard deployment).
9
-
10
- ### Precondition
11
-
12
- A contract exists on-chain at the address that `create` would produce from `(deployer, nonce)`. The caller knows the deployer's address and the nonce used during deployment.
13
-
14
- ### Steps
15
-
16
- 1. **Caller invokes `registerAddress(address deployer, uint256 nonce)`**
17
-
18
- - The `nonce` must be `<= type(uint64).max` or the call reverts with `JBAddressRegistry_NonceTooLarge(nonce)`
19
- - The contract calls `_addressFrom(deployer, nonce)` internally
20
-
21
- 2. **`_addressFrom` computes the create address via RLP encoding**
5
+ ---
22
6
 
23
- - Selects the correct RLP encoding branch based on the nonce range (10 branches: `0`, `1-0x7f`, `0x80-0xff`, ..., up to `uint64` max)
24
- - Builds the RLP-encoded byte sequence: `[list_prefix, 0x94, deployer_20_bytes, nonce_encoding]`
25
- - Hashes the sequence with `keccak256`
26
- - Extracts the low 160 bits as the computed address via inline assembly
7
+ ## 1. Register a Contract Deployed via `create`
27
8
 
28
- 3. **`_registerAddress` stores the mapping and emits the event**
9
+ **Entry point**: `JBAddressRegistry.registerAddress(address deployer, uint256 nonce)`
29
10
 
30
- - Sets `deployerOf[computedAddress] = deployer`
31
- - Emits `AddressRegistered(addr: computedAddress, deployer: deployer, caller: msg.sender)`
11
+ **Who can call**: Anyone. No access control -- any account can register any `(deployer, nonce)` pair. The caller does not need to be the deployer.
32
12
 
33
- ### Result
13
+ **Parameters**:
14
+ - `deployer` -- Address of the account that deployed the contract via `create`
15
+ - `nonce` -- The nonce the deployer's account had when deploying the contract (must be `<= type(uint64).max`)
34
16
 
35
- `deployerOf[computedAddress]` now returns `deployer`. Anyone querying the registry for that address can verify who deployed it.
17
+ **State changes**:
18
+ 1. Computes the deterministic `create` address via RLP encoding of `[deployer, nonce]` -- selects one of 10 encoding branches based on nonce range (`0`, `1..0x7f`, `0x80..0xff`, ..., up to `uint64` max)
19
+ 2. `JBAddressRegistry.deployerOf[computedAddress] = deployer` -- stores the deployer mapping
36
20
 
37
- ### What to verify
21
+ **Events**: `AddressRegistered(addr, deployer, caller)` -- where `addr` is the computed `create` address, `deployer` is the registered deployer, and `caller` is `msg.sender`. Both `addr` and `deployer` are `indexed`.
38
22
 
39
- - The computed address matches the actual on-chain contract address for the given `(deployer, nonce)` pair.
23
+ **Edge cases**:
24
+ - `nonce > type(uint64).max` reverts with `JBAddressRegistry_NonceTooLarge(nonce)`
40
25
  - If the caller provides incorrect parameters (wrong nonce, wrong deployer), a mapping is still created -- but for a different (likely nonexistent) address. The registry does not verify that code exists at the computed address.
41
- - Calling `registerAddress` again with the same parameters is idempotent (same result, no revert).
26
+ - Calling `registerAddress` again with the same parameters is idempotent (same mapping value, same event, no revert).
42
27
  - The `caller` in the event is `msg.sender`, which may differ from `deployer`.
43
28
 
44
29
  ---
45
30
 
46
- ## Journey 2: Register a Contract Deployed via create2
47
-
48
- **Actor:** Anyone (the deployer, the deployed contract itself, or a third party).
49
- **Goal:** Record the deployer of a contract that was deployed using the `create2` opcode (deterministic deployment).
50
-
51
- ### Precondition
52
-
53
- A contract exists on-chain at the address that `create2` would produce from `(deployer, salt, bytecode)`. The caller knows the deployer's address, the salt, and the full creation bytecode (including constructor arguments).
54
-
55
- ### Steps
56
-
57
- 1. **Caller invokes `registerAddress(address deployer, bytes32 salt, bytes calldata bytecode)`**
31
+ ## 2. Register a Contract Deployed via `create2`
58
32
 
59
- - No parameter validation beyond the address computation itself
60
- - `bytecode` must include ABI-encoded constructor arguments appended to the creation code
33
+ **Entry point**: `JBAddressRegistry.registerAddress(address deployer, bytes32 salt, bytes calldata bytecode)`
61
34
 
62
- 2. **The contract computes the create2 address inline**
35
+ **Who can call**: Anyone. No access control -- any account can register any `(deployer, salt, bytecode)` tuple. The caller does not need to be the deployer.
63
36
 
64
- - `address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, keccak256(bytecode))))))`
65
- - This matches the EIP-1014 specification
37
+ **Parameters**:
38
+ - `deployer` -- Address of the account or factory contract that deployed the contract via `create2`
39
+ - `salt` -- The `create2` salt used during deployment
40
+ - `bytecode` -- The full creation bytecode including ABI-encoded constructor arguments (not runtime bytecode)
66
41
 
67
- 3. **`_registerAddress` stores the mapping and emits the event**
42
+ **State changes**:
43
+ 1. Computes the deterministic `create2` address: `address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, keccak256(bytecode))))))`
44
+ 2. `JBAddressRegistry.deployerOf[computedAddress] = deployer` -- stores the deployer mapping
68
45
 
69
- - Sets `deployerOf[computedAddress] = deployer`
70
- - Emits `AddressRegistered(addr: computedAddress, deployer: deployer, caller: msg.sender)`
71
-
72
- ### Result
73
-
74
- `deployerOf[computedAddress]` now returns `deployer`. Frontends and other contracts can look up the deployer of any `create2`-deployed contract.
75
-
76
- ### What to verify
46
+ **Events**: `AddressRegistered(addr, deployer, caller)` -- where `addr` is the computed `create2` address, `deployer` is the registered deployer, and `caller` is `msg.sender`. Both `addr` and `deployer` are `indexed`.
77
47
 
48
+ **Edge cases**:
78
49
  - The `bytecode` parameter must be the full creation bytecode, not the deployed (runtime) bytecode. Omitting constructor arguments produces a different hash and therefore a different address.
79
- - For large bytecode payloads, `keccak256(bytecode)` is computed in memory. The gas cost scales with bytecode size, but the registry itself has no gas limit issues -- the caller pays.
50
+ - For large bytecode payloads, `keccak256(bytecode)` is computed in memory. Gas cost scales with bytecode size, but the registry itself has no gas limit issues -- the caller pays.
80
51
  - The salt is deployer-specific. Different deployers with the same salt and bytecode produce different addresses (because the deployer address is part of the hash input).
52
+ - No parameter validation beyond the address computation itself. No revert conditions exist for this overload.
81
53
 
82
54
  ---
83
55
 
84
- ## Journey 3: Frontend Verifies a Hook's Deployer
85
-
86
- **Actor:** Frontend application or off-chain service.
87
- **Goal:** Determine whether a Juicebox hook was deployed by a trusted factory.
88
-
89
- ### Precondition
90
-
91
- The hook contract has been registered in the registry (via Journey 1 or 2). The frontend maintains a list of trusted deployer addresses.
92
-
93
- ### Steps
56
+ ## 3. Frontend Verifies a Hook's Deployer
94
57
 
95
- 1. **Frontend reads `deployerOf(hookAddress)`**
58
+ **Entry point**: `JBAddressRegistry.deployerOf(address addr)` (public mapping getter -- `view`)
96
59
 
97
- - This is a simple public mapping getter -- a `staticcall` with no state changes
98
- - Returns `address(0)` if the hook has never been registered
60
+ **Who can call**: Anyone. This is a `view` function (auto-generated mapping getter) with no access control or state changes.
99
61
 
100
- 2. **Frontend evaluates the result**
62
+ **Parameters**:
63
+ - `addr` -- The address of the contract to look up
101
64
 
102
- - If `deployer == address(0)`: the hook is unregistered. The frontend should treat it as untrusted.
103
- - If `deployer` is in the trusted deployer set: the hook was deployed by a known, audited factory. The frontend can display it with confidence.
104
- - If `deployer` is a non-zero address not in the trusted set: the hook is registered but was deployed by an unknown deployer. The frontend should flag it as unverified.
65
+ **State changes**: None. Read-only query.
105
66
 
106
- ### Result
67
+ **Events**: None. No state mutation occurs.
107
68
 
108
- The frontend can make a trust decision about the hook without needing to trace the deployment transaction on-chain.
69
+ **Result**: Returns the registered deployer address, or `address(0)` if the address has never been registered. The frontend evaluates:
70
+ - `deployer == address(0)` -- the hook is unregistered; treat as untrusted
71
+ - `deployer` is in the trusted deployer set -- the hook was deployed by a known, audited factory; display with confidence
72
+ - `deployer` is a non-zero address not in the trusted set -- registered but deployed by an unknown deployer; flag as unverified
109
73
 
110
- ### What to verify
111
-
112
- - `deployerOf` returns `address(0)` for any address that has never been registered. There is no way to distinguish "never registered" from "registered with `deployer = address(0)`", though the latter requires deliberately passing `address(0)` as the deployer.
74
+ **Edge cases**:
75
+ - `deployerOf` returns `address(0)` for any address never registered. There is no way to distinguish "never registered" from "registered with `deployer = address(0)`", though the latter requires deliberately passing `address(0)` as the deployer.
113
76
  - The registry provides no guarantee that code exists at the registered address. A deployer could register an address for a contract that has been self-destructed or was never deployed.
114
77
  - The registry provides no guarantee that the registered deployer is the *actual* deployer. It only guarantees that the deterministic computation of `(deployer, nonce)` or `(deployer, salt, bytecode)` produces that address. Since the EVM's address derivation is deterministic, this is equivalent -- but the registry itself does not verify the deployment transaction.
115
78
 
116
79
  ---
117
80
 
118
- ## Journey 4: Overwrite a Previous Registration
119
-
120
- **Actor:** Anyone.
121
- **Goal:** Re-register an address with the same or different parameters.
122
-
123
- ### Precondition
124
-
125
- `deployerOf[addr]` already has a non-zero value from a previous registration.
126
-
127
- ### Steps
128
-
129
- 1. **Caller invokes either `registerAddress` overload with parameters that compute to `addr`**
81
+ ## 4. Overwrite a Previous Registration
130
82
 
131
- 2. **`_registerAddress` overwrites the existing value**
83
+ **Entry point**: `JBAddressRegistry.registerAddress(address deployer, uint256 nonce)` or `JBAddressRegistry.registerAddress(address deployer, bytes32 salt, bytes calldata bytecode)`
132
84
 
133
- - `deployerOf[addr] = deployer` (unconditional write, no check for existing value)
134
- - Emits a new `AddressRegistered` event
85
+ **Who can call**: Anyone. There is no access control on overwrites. No owner check or deployer verification is performed.
135
86
 
136
- ### Result
87
+ **Parameters**: Same as Journey 1 or Journey 2, depending on which `registerAddress` overload is called. The provided parameters must compute to the same target address as the previous registration.
137
88
 
138
- The previous deployer mapping is overwritten. The event log contains both the old and new registrations.
89
+ **State changes**:
90
+ 1. `JBAddressRegistry.deployerOf[addr] = deployer` -- unconditional `SSTORE`, overwrites any existing value without checking the previous deployer
139
91
 
140
- ### What to verify
92
+ **Events**: `AddressRegistered(addr, deployer, caller)` -- emitted on every call, even if the stored value is unchanged.
141
93
 
94
+ **Edge cases**:
142
95
  - Overwriting is safe because the same address can only be computed from the same `(deployer, nonce)` or `(deployer, salt, bytecode)` parameters (assuming no keccak256 collision). Re-registering with the same parameters produces the same result.
143
96
  - There is no way to "unregister" an address. Setting `deployerOf[addr] = address(0)` would require computing a `(deployer, nonce)` pair that produces `addr` with `deployer = address(0)`, which is theoretically possible but practically useless.
144
97
  - The event log preserves the full history of registrations. Off-chain indexers can detect overwrites by tracking multiple `AddressRegistered` events for the same `addr`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/address-registry-v6",
3
- "version": "0.0.10",
3
+ "version": "0.0.13",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.26;
2
+ pragma solidity ^0.8.28;
3
3
 
4
4
  import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
5
5
  import {Script} from "forge-std/Script.sol";
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
2
+ pragma solidity ^0.8.26;
3
3
 
4
4
  import {stdJson} from "forge-std/Script.sol";
5
5
  import {Vm} from "forge-std/Vm.sol";
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
2
+ pragma solidity ^0.8.26;
3
3
 
4
4
  import {IJBAddressRegistry} from "./interfaces/IJBAddressRegistry.sol";
5
5
 
@@ -16,6 +16,10 @@ contract JBAddressRegistry is IJBAddressRegistry {
16
16
  // --------------------------- custom errors ------------------------- //
17
17
  //*********************************************************************//
18
18
 
19
+ /// @notice Thrown when attempting to register an address that has already been registered.
20
+ /// @param addr The address that is already registered.
21
+ error JBAddressRegistry_AlreadyRegistered(address addr);
22
+
19
23
  /// @notice Thrown when a nonce exceeds the maximum value supported by the RLP encoding (uint64 max).
20
24
  error JBAddressRegistry_NonceTooLarge(uint256 nonce);
21
25
 
@@ -120,6 +124,8 @@ contract JBAddressRegistry is IJBAddressRegistry {
120
124
  /// @param addr The deployed contract's address.
121
125
  /// @param deployer The deployer's address.
122
126
  function _registerAddress(address addr, address deployer) internal {
127
+ if (deployerOf[addr] != address(0)) revert JBAddressRegistry_AlreadyRegistered(addr);
128
+
123
129
  deployerOf[addr] = deployer;
124
130
 
125
131
  emit AddressRegistered({addr: addr, deployer: deployer, caller: msg.sender});
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.26;
2
+ pragma solidity ^0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JBAddressRegistry} from "../src/JBAddressRegistry.sol";
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.26;
2
+ pragma solidity ^0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JBAddressRegistry} from "../src/JBAddressRegistry.sol";
@@ -158,30 +158,43 @@ contract JBAddressRegistryEdge is Test {
158
158
  }
159
159
 
160
160
  // =========================================================================
161
- // Duplicate registration - second registration overwrites first
161
+ // Duplicate registration - second registration reverts
162
162
  // =========================================================================
163
163
 
164
- /// @notice Registering the same computed address twice overwrites the deployer.
165
- function test_duplicateRegistration_overwrites() public {
164
+ /// @notice Registering the same computed address twice reverts with AlreadyRegistered.
165
+ function test_duplicateRegistration_reverts() public {
166
166
  uint64 nonce = 1;
167
167
 
168
168
  vm.setNonce(deployer, nonce);
169
169
  vm.prank(deployer);
170
170
  address deployed = address(new MockDeployment());
171
171
 
172
- // First registration.
172
+ // First registration succeeds.
173
173
  registry.registerAddress(deployer, nonce);
174
174
  assertEq(registry.deployerOf(deployed), deployer);
175
175
 
176
- // Register again with a different deployer (same computed address via different params).
177
- // Actually, same deployer + nonce = same address. Let's just call register again.
178
- address fakeDeployer = makeAddr("fake");
179
- registry.registerAddress(fakeDeployer, nonce);
176
+ // Second registration of the same address reverts.
177
+ vm.expectRevert(
178
+ abi.encodeWithSelector(JBAddressRegistry.JBAddressRegistry_AlreadyRegistered.selector, deployed)
179
+ );
180
+ registry.registerAddress(deployer, nonce);
181
+ }
180
182
 
181
- // The real deployed address still has the correct deployer.
182
- // But the address computed from (fakeDeployer, nonce) is a DIFFERENT address.
183
- // So deployed's mapping is unchanged.
184
- assertEq(registry.deployerOf(deployed), deployer, "Original registration should be unchanged");
183
+ /// @notice Registering the same create2 address twice reverts with AlreadyRegistered.
184
+ function test_duplicateRegistration_create2_reverts() public {
185
+ Factory factory = new Factory();
186
+ bytes32 salt = bytes32(uint256(42));
187
+ address deployed = factory.deploy(salt);
188
+
189
+ // First registration succeeds.
190
+ registry.registerAddress(address(factory), salt, type(MockDeployment).creationCode);
191
+ assertEq(registry.deployerOf(deployed), address(factory));
192
+
193
+ // Second registration of the same address reverts.
194
+ vm.expectRevert(
195
+ abi.encodeWithSelector(JBAddressRegistry.JBAddressRegistry_AlreadyRegistered.selector, deployed)
196
+ );
197
+ registry.registerAddress(address(factory), salt, type(MockDeployment).creationCode);
185
198
  }
186
199
 
187
200
  // =========================================================================
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.26;
2
+ pragma solidity ^0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JBAddressRegistry} from "../src/JBAddressRegistry.sol";
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.26;
2
+ pragma solidity ^0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JBAddressRegistry} from "../../src/JBAddressRegistry.sol";
package/docs/book.css DELETED
@@ -1,13 +0,0 @@
1
- table {
2
- margin: 0 auto;
3
- border-collapse: collapse;
4
- width: 100%;
5
- }
6
-
7
- table td:first-child {
8
- width: 15%;
9
- }
10
-
11
- table td:nth-child(2) {
12
- width: 25%;
13
- }
package/docs/book.toml DELETED
@@ -1,12 +0,0 @@
1
- [book]
2
- src = "src"
3
- title = ""
4
-
5
- [output.html]
6
- no-section-label = true
7
- additional-js = ["solidity.min.js"]
8
- additional-css = ["book.css"]
9
- git-repository-url = "https://github.com/Bananapus/nana-address-registry"
10
-
11
- [output.html.fold]
12
- enable = true
@@ -1,74 +0,0 @@
1
- hljs.registerLanguage("solidity",(()=>{"use strict";function e(){try{return!0
2
- }catch(e){return!1}}
3
- var a=/-?(\b0[xX]([a-fA-F0-9]_?)*[a-fA-F0-9]|(\b[1-9](_?\d)*(\.((\d_?)*\d)?)?|\.\d(_?\d)*)([eE][-+]?\d(_?\d)*)?|\b0)(?!\w|\$)/
4
- ;e()&&(a=a.source.replace(/\\b/g,"(?<!\\$)\\b"));var s={className:"number",
5
- begin:a,relevance:0},n={
6
- keyword:"assembly let function if switch case default for leave break continue u256 jump jumpi stop return revert selfdestruct invalid",
7
- built_in:"add sub mul div sdiv mod smod exp not lt gt slt sgt eq iszero and or xor byte shl shr sar addmod mulmod signextend keccak256 pc pop dup1 dup2 dup3 dup4 dup5 dup6 dup7 dup8 dup9 dup10 dup11 dup12 dup13 dup14 dup15 dup16 swap1 swap2 swap3 swap4 swap5 swap6 swap7 swap8 swap9 swap10 swap11 swap12 swap13 swap14 swap15 swap16 mload mstore mstore8 sload sstore msize gas address balance selfbalance caller callvalue calldataload calldatasize calldatacopy codesize codecopy extcodesize extcodecopy returndatasize returndatacopy extcodehash create create2 call callcode delegatecall staticcall log0 log1 log2 log3 log4 chainid origin gasprice basefee blockhash coinbase timestamp number difficulty gaslimit",
8
- literal:"true false"},i={className:"string",
9
- begin:/\bhex'(([0-9a-fA-F]{2}_?)*[0-9a-fA-F]{2})?'/},t={className:"string",
10
- begin:/\bhex"(([0-9a-fA-F]{2}_?)*[0-9a-fA-F]{2})?"/};function r(e){
11
- return e.inherit(e.APOS_STRING_MODE,{begin:/(\bunicode)?'/})}function l(e){
12
- return e.inherit(e.QUOTE_STRING_MODE,{begin:/(\bunicode)?"/})}var o={
13
- SOL_ASSEMBLY_KEYWORDS:n,baseAssembly:e=>{
14
- var a=r(e),o=l(e),c=/[A-Za-z_$][A-Za-z_$0-9.]*/,d=e.inherit(e.TITLE_MODE,{
15
- begin:/[A-Za-z$_][0-9A-Za-z$_]*/,lexemes:c,keywords:n}),u={className:"params",
16
- begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,lexemes:c,keywords:n,
17
- contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,o,s]},_={
18
- className:"operator",begin:/:=|->/};return{keywords:n,lexemes:c,
19
- contains:[a,o,i,t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,_,{
20
- className:"function",lexemes:c,beginKeywords:"function",end:"{",excludeEnd:!0,
21
- contains:[d,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,_]}]}},
22
- solAposStringMode:r,solQuoteStringMode:l,HEX_APOS_STRING_MODE:i,
23
- HEX_QUOTE_STRING_MODE:t,SOL_NUMBER:s,isNegativeLookbehindAvailable:e}
24
- ;const{baseAssembly:c,solAposStringMode:d,solQuoteStringMode:u,HEX_APOS_STRING_MODE:_,HEX_QUOTE_STRING_MODE:m,SOL_NUMBER:b,isNegativeLookbehindAvailable:E}=o
25
- ;return e=>{for(var a=d(e),s=u(e),n=[],i=0;i<32;i++)n[i]=i+1
26
- ;var t=n.map((e=>8*e)),r=[];for(i=0;i<=80;i++)r[i]=i
27
- ;var l=n.map((e=>"bytes"+e)).join(" ")+" ",o=t.map((e=>"uint"+e)).join(" ")+" ",g=t.map((e=>"int"+e)).join(" ")+" ",M=[].concat.apply([],t.map((e=>r.map((a=>e+"x"+a))))),p={
28
- keyword:"var bool string int uint "+g+o+"byte bytes "+l+"fixed ufixed "+M.map((e=>"fixed"+e)).join(" ")+" "+M.map((e=>"ufixed"+e)).join(" ")+" enum struct mapping address new delete if else for while continue break return throw emit try catch revert unchecked _ function modifier event constructor fallback receive error virtual override constant immutable anonymous indexed storage memory calldata external public internal payable pure view private returns import from as using pragma contract interface library is abstract type assembly",
29
- literal:"true false wei gwei szabo finney ether seconds minutes hours days weeks years",
30
- built_in:"self this super selfdestruct suicide now msg block tx abi blockhash gasleft assert require Error Panic sha3 sha256 keccak256 ripemd160 ecrecover addmod mulmod log0 log1 log2 log3 log4"
31
- },O={className:"operator",begin:/[+\-!~*\/%<>&^|=]/
32
- },C=/[A-Za-z_$][A-Za-z_$0-9]*/,N={className:"params",begin:/\(/,end:/\)/,
33
- excludeBegin:!0,excludeEnd:!0,lexemes:C,keywords:p,
34
- contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,b,"self"]},f={
35
- begin:/\.\s*/,end:/[^A-Za-z0-9$_\.]/,excludeBegin:!0,excludeEnd:!0,keywords:{
36
- built_in:"gas value selector address length push pop send transfer call callcode delegatecall staticcall balance code codehash wrap unwrap name creationCode runtimeCode interfaceId min max"
37
- },relevance:2},y=e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/,
38
- lexemes:C,keywords:p}),w={className:"built_in",
39
- begin:(E()?"(?<!\\$)\\b":"\\b")+"(gas|value|salt)(?=:)"};function x(e,a){return{
40
- begin:(E()?"(?<!\\$)\\b":"\\b")+e+"\\.\\s*",end:/[^A-Za-z0-9$_\.]/,
41
- excludeBegin:!1,excludeEnd:!0,lexemes:C,keywords:{built_in:e+" "+a},
42
- contains:[f],relevance:10}}var h=c(e),v=e.inherit(h,{
43
- contains:h.contains.concat([{begin:/\./,end:/[^A-Za-z0-9$.]/,excludeBegin:!0,
44
- excludeEnd:!0,keywords:{built_in:"slot offset length address selector"},
45
- relevance:2},{begin:/_/,end:/[^A-Za-z0-9$.]/,excludeBegin:!0,excludeEnd:!0,
46
- keywords:{built_in:"slot offset"},relevance:2}])});return{aliases:["sol"],
47
- keywords:p,lexemes:C,
48
- contains:[a,s,_,m,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,b,w,O,{
49
- className:"function",lexemes:C,
50
- beginKeywords:"function modifier event constructor fallback receive error",
51
- end:/[{;]/,excludeEnd:!0,
52
- contains:[y,N,w,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:/%/
53
- },x("msg","gas value data sender sig"),x("block","blockhash coinbase difficulty gaslimit basefee number timestamp chainid"),x("tx","gasprice origin"),x("abi","decode encode encodePacked encodeWithSelector encodeWithSignature encodeCall"),x("bytes","concat"),f,{
54
- className:"class",lexemes:C,beginKeywords:"contract interface library",end:"{",
55
- excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"is",lexemes:C
56
- },y,N,w,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{lexemes:C,
57
- beginKeywords:"struct enum",end:"{",excludeEnd:!0,illegal:/[:"\[\]]/,
58
- contains:[y,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{
59
- beginKeywords:"import",end:";",lexemes:C,keywords:"import from as",
60
- contains:[y,a,s,_,m,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,O]},{
61
- beginKeywords:"using",end:";",lexemes:C,keywords:"using for",
62
- contains:[y,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,O]},{className:"meta",
63
- beginKeywords:"pragma",end:";",lexemes:C,keywords:{
64
- keyword:"pragma solidity experimental abicoder",
65
- built_in:"ABIEncoderV2 SMTChecker v1 v2"},
66
- contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.inherit(a,{
67
- className:"meta-string"}),e.inherit(s,{className:"meta-string"})]},{
68
- beginKeywords:"assembly",end:/\b\B/,
69
- contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.inherit(v,{begin:"{",
70
- end:"}",endsParent:!0,contains:v.contains.concat([e.inherit(v,{begin:"{",
71
- end:"}",contains:v.contains.concat(["self"])})])})]}],illegal:/#/}}})());
72
-
73
- // Ugly hack to reload HLJS
74
- hljs.initHighlightingOnLoad();
@@ -1,164 +0,0 @@
1
- # Bananapus Address Registry
2
-
3
- Frontend clients need a way to verify that a Juicebox contract has a deployer they trust. `JBAddressRegistry` allows any contract deployed with `create` or `create2` to publicly register its deployer's address. Whoever deploys a contract is responsible for registering it.
4
-
5
- <details>
6
- <summary>Table of Contents</summary>
7
- <ol>
8
- <li><a href="#usage">Usage</a></li>
9
- <ul>
10
- <li><a href="#install">Install</a></li>
11
- <li><a href="#develop">Develop</a></li>
12
- <li><a href="#scripts">Scripts</a></li>
13
- <li><a href="#deployments">Deployments</a></li>
14
- <li><a href="#tips">Tips</a></li>
15
- </ul>
16
- <li><a href="#repository-layout">Repository Layout</a></li>
17
- <li><a href="#description">Description</a></li>
18
- <ul>
19
- <li><a href="#overview">Overview</a></li>
20
- <li><a href="#implementation-details">Implementation Details</a></li>
21
- <li><a href="#risks">Risks</a></li>
22
- </ul>
23
- </ul>
24
- </ol>
25
- </details>
26
-
27
- ## Usage
28
-
29
- ### Install
30
-
31
- How to install `nana-address-registry` in another project.
32
-
33
- For projects using `npm` to manage dependencies (recommended):
34
-
35
- ```bash
36
- npm install @bananapus/address-registry
37
- ```
38
-
39
- For projects using `forge` to manage dependencies (not recommended):
40
-
41
- ```bash
42
- forge install Bananapus/nana-address-registry
43
- ```
44
-
45
- If you're using `forge` to manage dependencies, add `@bananapus/address-registry/=lib/nana-address-registry/` to `remappings.txt`.
46
-
47
- ### Develop
48
-
49
- `nana-address-registry` uses the [Foundry](https://github.com/foundry-rs/foundry) development toolchain for builds, tests, and deployments. To get set up, install [Foundry](https://github.com/foundry-rs/foundry):
50
-
51
- ```bash
52
- curl -L https://foundry.paradigm.xyz | sh
53
- ```
54
-
55
- You can download and install dependencies with:
56
-
57
- ```bash
58
- forge install
59
- ```
60
-
61
- If you run into trouble with `forge install`, try using `git submodule update --init --recursive` to ensure that nested submodules have been properly initialized.
62
-
63
- Some useful commands:
64
-
65
- | Command | Description |
66
- | --------------------- | --------------------------------------------------- |
67
- | `forge build` | Compile the contracts and write artifacts to `out`. |
68
- | `forge fmt` | Lint. |
69
- | `forge test` | Run the tests. |
70
- | `forge build --sizes` | Get contract sizes. |
71
- | `forge coverage` | Generate a test coverage report. |
72
- | `foundryup` | Update foundry. Run this periodically. |
73
- | `forge clean` | Remove the build artifacts and cache directories. |
74
-
75
- To learn more, visit the [Foundry Book](https://book.getfoundry.sh/) docs.
76
-
77
- ### Scripts
78
-
79
- For convenience, several utility commands are available in `package.json`.
80
-
81
- | Command | Description |
82
- | --------------------------------- | -------------------------------------- |
83
- | `npm test` | Run local tests. |
84
- | `npm run test:fork` | Run fork tests (for use in CI). |
85
- | `npm run coverage` | Generate an LCOV test coverage report. |
86
-
87
- ### Deployments
88
-
89
- To deploy, you'll need to set up a `.env` file based on `.example.env`. Then run one of the following commands:
90
-
91
- | Command | Description |
92
- | --------------------------------- | -------------------------------------- |
93
- | `npm run deploy:ethereum-mainnet` | Deploy to Ethereum mainnet |
94
- | `npm run deploy:ethereum-sepolia` | Deploy to Ethereum Sepolia testnet |
95
- | `npm run deploy:optimism-mainnet` | Deploy to Optimism mainnet |
96
- | `npm run deploy:optimism-testnet` | Deploy to Optimism testnet |
97
-
98
- ### Tips
99
-
100
- To view test coverage, run `npm run coverage` to generate an LCOV test report. You can use an extension like [Coverage Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) to view coverage in your editor.
101
-
102
- If you're using Nomic Foundation's [Solidity](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity) extension in VSCode, you may run into LSP errors because the extension cannot find dependencies outside of `lib`. You can often fix this by running:
103
-
104
- ```bash
105
- forge remappings >> remappings.txt
106
- ```
107
-
108
- This makes the extension aware of default remappings.
109
-
110
- ## Repository Layout
111
-
112
- The root directory contains this README, an MIT license, and config files.
113
-
114
- The important source directories are:
115
-
116
- ```
117
- nana-address-registry/
118
- ├── script/
119
- │ └── Deploy.s.sol - The deployment script.
120
- ├── src/ - The contract source code.
121
- │ ├── JBAddressRegistry.sol - The main address registry contract.
122
- │ └── interfaces/
123
- │ └── IJBAddressRegistry.sol - The address registry interface.
124
- └── test/
125
- ├── JBAddressRegistry.t.sol - Unit tests.
126
- └── JBAddressRegistry_Fork.t.sol - Fork tests.
127
- ```
128
-
129
- Other directories:
130
-
131
- ```
132
- nana-address-registry/
133
- ├── .github/
134
- │ └── workflows/ - CI/CD workflows.
135
- └── broadcast/ - Deployment logs.
136
- ```
137
-
138
- ## Description
139
-
140
- ### Overview
141
-
142
- `JBAddressRegistry` is intended for registering the deployers of Juicebox pay/redeem hooks, but does not enforce adherence to an interface, and can be used for any `create`/`create2` deployer.
143
-
144
- The addresses of the deployed contracts are computed deterministically based on the deployer's address, and a nonce (for `create`) or `create2` salt and deployment bytecode (for `create2`). That address is then used as a key to store the deployer's address. This allows clients to easily and trustlessly check a given hook's deployer, which can be used to help figure out whether a hook is "safe" or not, as determined by the client's developers.
145
-
146
- _If you're having trouble understanding this contract, take a look at the [core protocol contracts](https://github.com/Bananapus/nana-core) and the [documentation](https://docs.juicebox.money/) first. If you have questions, reach out on [Discord](https://discord.com/invite/ErQYmth4dS)._
147
-
148
-
149
- ### Implementation Details
150
-
151
- - After deploying a Juicebox pay/redeem hook, any addresses can call `JBAddressRegistry.registerAddress(address deployer, uint256 nonce)` to add it to the registry. The registry will compute and store the corresponding hook address.
152
- - Alternatively, `JBAddressRegistry.registerAddress(address deployer, bytes32 salt, bytes calldata bytecode)` will compute and store the hook deployed from a contract using `create2`.
153
-
154
- The registry doesn't enforce `IERC165` or the implementation of any hook interfaces, meaning it can be used for any contract deployed with `create`/`create2`.
155
-
156
- Clients can retrieve the nonce for the contract and an EOA using `provider.getTransactionCount(address)` from `ethers.js` or `web3.eth.getTransactionCount` from `web3.js` just *before* the hook's deployment. If registering a hook later on, clients may need to manually calculate the nonce.
157
-
158
- The `create2` salt is determined by a given deployer's logic. The deployment bytecode can be retrieved offchain (from the deployment transaction) or onchain (with `abi.encodePacked(type(deployedContract).creationCode, abi.encode(constructorArguments))`).
159
-
160
- ### Risks
161
-
162
- Hooks have token minting access, making malicious hooks dangerous. Clients should warn project owners and users about any potential for unintended or adversarial behaviour, especially for unknown hooks.
163
-
164
- Deployers can be exploited. Clients should still communicate risk to users.
@@ -1,6 +0,0 @@
1
- # Summary
2
- - [Home](README.md)
3
- # src
4
- - [❱ interfaces](src/interfaces/README.md)
5
- - [IJBAddressRegistry](src/interfaces/IJBAddressRegistry.sol/interface.IJBAddressRegistry.md)
6
- - [JBAddressRegistry](src/JBAddressRegistry.sol/contract.JBAddressRegistry.md)
@@ -1,106 +0,0 @@
1
- # JBAddressRegistry
2
- [Git Source](https://github.com/Bananapus/nana-address-registry/blob/922b48185d8a792b44854cf6d3257339a9d73eaa/src/JBAddressRegistry.sol)
3
-
4
- **Inherits:**
5
- [IJBAddressRegistry](/src/interfaces/IJBAddressRegistry.sol/interface.IJBAddressRegistry.md)
6
-
7
- Frontend clients need a way to verify that a Juicebox contract has a deployer they trust. `JBAddressRegistry`
8
- allows any contract deployed with `create` or `create2` to publicly register its deployer's address. Whoever deploys
9
- a contract is reponsible for registering it.
10
-
11
- *`JBAddressRegistry` is intended for registering the deployers of Juicebox pay/redeem hooks, but does not
12
- enforce adherence to an interface, and can be used for any `create`/`create2` deployer.*
13
-
14
- *The addresses of the deployed contracts are computed deterministically based on the deployer's address, and a
15
- nonce (for `create`) or `create2` salt and deployment bytecode (for `create2`).*
16
-
17
-
18
- ## State Variables
19
- ### deployerOf
20
- Returns the deployer of a given contract which has been registered.
21
-
22
- *Whoever deploys a contract is responsible for registering it.*
23
-
24
-
25
- ```solidity
26
- mapping(address addr => address deployer) public override deployerOf;
27
- ```
28
-
29
-
30
- ## Functions
31
- ### registerAddress
32
-
33
- Register a deployed contract's address.
34
-
35
- *The contract must be deployed using `create`.*
36
-
37
-
38
- ```solidity
39
- function registerAddress(address deployer, uint256 nonce) external override;
40
- ```
41
- **Parameters**
42
-
43
- |Name|Type|Description|
44
- |----|----|-----------|
45
- |`deployer`|`address`|The address of the contract's deployer.|
46
- |`nonce`|`uint256`|The nonce used to deploy the contract.|
47
-
48
-
49
- ### registerAddress
50
-
51
- Register a deployed contract's address.
52
-
53
- *The contract must be deployed using `create2`.*
54
-
55
- *The `create2` salt is determined by the deployer's logic. The deployment bytecode can be retrieved offchain
56
- (from the deployment transaction) or onchain (with `abi.encodePacked(type(deployedContract).creationCode,
57
- abi.encode(constructorArguments))`).*
58
-
59
-
60
- ```solidity
61
- function registerAddress(address deployer, bytes32 salt, bytes calldata bytecode) external override;
62
- ```
63
- **Parameters**
64
-
65
- |Name|Type|Description|
66
- |----|----|-----------|
67
- |`deployer`|`address`|The address of the contract's deployer.|
68
- |`salt`|`bytes32`|The `create2` salt used to deploy the contract.|
69
- |`bytecode`|`bytes`|The contract's deployment bytecode, including the constructor arguments.|
70
-
71
-
72
- ### _addressFrom
73
-
74
- Compute the address of a contract deployed using `create` based on the deployer's address and nonce.
75
-
76
- *Taken from https://ethereum.stackexchange.com/a/87840/68134 - this won't work for nonces > 2**32. If
77
- you reach that nonce please: 1) ping us, because wow 2) use another deployer.*
78
-
79
-
80
- ```solidity
81
- function _addressFrom(address origin, uint256 nonce) internal pure returns (address addr);
82
- ```
83
- **Parameters**
84
-
85
- |Name|Type|Description|
86
- |----|----|-----------|
87
- |`origin`|`address`|The deployer's address.|
88
- |`nonce`|`uint256`|The nonce used to deploy the contract.|
89
-
90
-
91
- ### _registerAddress
92
-
93
- Register a contract's deployer in the `deployerOf` mapping.
94
-
95
-
96
- ```solidity
97
- function _registerAddress(address addr, address deployer) internal;
98
- ```
99
- **Parameters**
100
-
101
- |Name|Type|Description|
102
- |----|----|-----------|
103
- |`addr`|`address`|The deployed contract's address.|
104
- |`deployer`|`address`|The deployer's address.|
105
-
106
-
@@ -1,5 +0,0 @@
1
-
2
-
3
- # Contents
4
- - [interfaces](/src/interfaces)
5
- - [JBAddressRegistry](JBAddressRegistry.sol/contract.JBAddressRegistry.md)
@@ -1,33 +0,0 @@
1
- # IJBAddressRegistry
2
- [Git Source](https://github.com/Bananapus/nana-address-registry/blob/922b48185d8a792b44854cf6d3257339a9d73eaa/src/interfaces/IJBAddressRegistry.sol)
3
-
4
-
5
- ## Functions
6
- ### deployerOf
7
-
8
-
9
- ```solidity
10
- function deployerOf(address addr) external view returns (address deployer);
11
- ```
12
-
13
- ### registerAddress
14
-
15
-
16
- ```solidity
17
- function registerAddress(address deployer, uint256 nonce) external;
18
- ```
19
-
20
- ### registerAddress
21
-
22
-
23
- ```solidity
24
- function registerAddress(address deployer, bytes32 salt, bytes calldata bytecode) external;
25
- ```
26
-
27
- ## Events
28
- ### AddressRegistered
29
-
30
- ```solidity
31
- event AddressRegistered(address indexed addr, address indexed deployer, address caller);
32
- ```
33
-
@@ -1,4 +0,0 @@
1
-
2
-
3
- # Contents
4
- - [IJBAddressRegistry](IJBAddressRegistry.sol/interface.IJBAddressRegistry.md)