@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 +9 -0
- package/ARCHITECTURE.md +13 -1
- package/AUDIT_INSTRUCTIONS.md +25 -0
- package/CHANGE_LOG.md +8 -2
- package/README.md +12 -1
- package/RISKS.md +11 -4
- package/SKILLS.md +10 -2
- package/STYLE_GUIDE.md +1 -1
- package/USER_JOURNEYS.md +50 -97
- package/package.json +1 -1
- package/script/Deploy.s.sol +1 -1
- package/script/helpers/AddressRegistryDeploymentLib.sol +1 -1
- package/src/JBAddressRegistry.sol +7 -1
- package/test/JBAddressRegistry.t.sol +1 -1
- package/test/JBAddressRegistryEdge.t.sol +26 -13
- package/test/JBAddressRegistry_Fork.t.sol +1 -1
- package/test/regression/NonceTruncation.t.sol +1 -1
- package/docs/book.css +0 -13
- package/docs/book.toml +0 -12
- package/docs/solidity.min.js +0 -74
- package/docs/src/README.md +0 -164
- package/docs/src/SUMMARY.md +0 -6
- package/docs/src/src/JBAddressRegistry.sol/contract.JBAddressRegistry.md +0 -106
- package/docs/src/src/README.md +0 -5
- package/docs/src/src/interfaces/IJBAddressRegistry.sol/interface.IJBAddressRegistry.md +0 -33
- package/docs/src/src/interfaces/README.md +0 -4
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
|
-
|
|
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)
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -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` →
|
|
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
|
-
-
|
|
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
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
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/
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9
|
+
**Entry point**: `JBAddressRegistry.registerAddress(address deployer, uint256 nonce)`
|
|
29
10
|
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
##
|
|
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
|
-
|
|
58
|
+
**Entry point**: `JBAddressRegistry.deployerOf(address addr)` (public mapping getter -- `view`)
|
|
96
59
|
|
|
97
|
-
|
|
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
|
-
|
|
62
|
+
**Parameters**:
|
|
63
|
+
- `addr` -- The address of the contract to look up
|
|
101
64
|
|
|
102
|
-
|
|
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
|
-
|
|
67
|
+
**Events**: None. No state mutation occurs.
|
|
107
68
|
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
83
|
+
**Entry point**: `JBAddressRegistry.registerAddress(address deployer, uint256 nonce)` or `JBAddressRegistry.registerAddress(address deployer, bytes32 salt, bytes calldata bytecode)`
|
|
132
84
|
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
+
**State changes**:
|
|
90
|
+
1. `JBAddressRegistry.deployerOf[addr] = deployer` -- unconditional `SSTORE`, overwrites any existing value without checking the previous deployer
|
|
139
91
|
|
|
140
|
-
|
|
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
package/script/Deploy.s.sol
CHANGED
|
@@ -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.
|
|
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
|
|
161
|
+
// Duplicate registration - second registration reverts
|
|
162
162
|
// =========================================================================
|
|
163
163
|
|
|
164
|
-
/// @notice Registering the same computed address twice
|
|
165
|
-
function
|
|
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
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
// =========================================================================
|
package/docs/book.css
DELETED
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
|
package/docs/solidity.min.js
DELETED
|
@@ -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();
|
package/docs/src/README.md
DELETED
|
@@ -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.
|
package/docs/src/SUMMARY.md
DELETED
|
@@ -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
|
-
|
package/docs/src/src/README.md
DELETED
|
@@ -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
|
-
|