@bananapus/omnichain-deployers-v6 0.0.28 → 0.0.29
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/RISKS.md +57 -46
- package/package.json +7 -7
- package/script/Deploy.s.sol +8 -1
- package/src/JBOmnichainDeployer.sol +14 -6
- package/test/JBOmnichainDeployer.t.sol +2 -12
- package/test/JBOmnichainDeployerGuard.t.sol +1 -0
- package/test/OmnichainDeployerAttacks.t.sol +3 -1
- package/test/OmnichainDeployerEdgeCases.t.sol +4 -11
- package/test/TestAuditGaps.sol +4 -18
- package/test/Tiered721HookComposition.t.sol +2 -4
- package/test/audit/AuditFixesC2H6M14.t.sol +495 -0
- package/test/audit/CarryForwardRejectedHook.t.sol +1 -0
- package/test/audit/CodexNemesisAudit.t.sol +8 -5
- package/test/audit/CodexNemesisDeterministicDrift.t.sol +78 -0
- package/test/audit/CodexNemesisDeterministicPeerDrift.t.sol +79 -0
- package/test/audit/CodexNemesisForwardedPermissions.t.sol +297 -0
- package/test/audit/CodexNemesisNftCashoutSupplyMismatch.t.sol +222 -0
- package/test/audit/JBOmnichainDeployer.t.sol +10 -6
- package/test/audit/WeightScalingComparison.t.sol +3 -1
- package/test/fork/OmnichainForkTestBase.sol +3 -2
- package/test/invariants/CrossChainDeployerInvariant.t.sol +204 -0
- package/test/invariants/handlers/CrossChainDeployerHandler.sol +395 -0
- package/test/regression/EmptyRulesetConfigurations.t.sol +3 -5
- package/test/regression/HookOwnershipTransfer.t.sol +2 -5
- package/test/regression/ValidateController.t.sol +3 -11
package/RISKS.md
CHANGED
|
@@ -1,81 +1,92 @@
|
|
|
1
1
|
# Omnichain Deployers Risk Register
|
|
2
2
|
|
|
3
|
-
This file
|
|
3
|
+
This file covers the risks in the deployer layer that launches Juicebox projects across chains while composing 721 hooks, suckers, and optional custom data hooks.
|
|
4
4
|
|
|
5
|
-
## How
|
|
5
|
+
## How To Use This File
|
|
6
6
|
|
|
7
|
-
- Read `Priority risks` first
|
|
8
|
-
-
|
|
9
|
-
- Treat `Invariants to Verify` as required checks for any new omnichain deployment flow.
|
|
7
|
+
- Read `Priority risks` first. They capture the highest-blast-radius deployment and cash-out assumptions.
|
|
8
|
+
- Treat `Invariants to verify` as required checks for any new omnichain deployment flow.
|
|
10
9
|
|
|
11
|
-
## Priority
|
|
10
|
+
## Priority Risks
|
|
12
11
|
|
|
13
12
|
| Priority | Risk | Why it matters | Primary controls |
|
|
14
13
|
|----------|------|----------------|------------------|
|
|
15
14
|
| P0 | Registry-trusted sucker bypass | This deployer gives suckers privileged cash-out behavior based on registry answers. A bad registry entry can affect many projects. | Registry allowlists, deployment verification, and explicit registry scrutiny. |
|
|
16
15
|
| P1 | Cross-chain deployment drift | Omnichain assumptions fail if chain-specific wiring, peers, or composed hooks do not match. | Deterministic deploy ordering, parity checks, and post-deploy peer verification. |
|
|
17
|
-
| P1 | Data-hook composition mistakes | The deployer wraps or forwards custom data hooks
|
|
18
|
-
|
|
16
|
+
| P1 | Data-hook composition mistakes | The deployer wraps or forwards custom data hooks. A bad composition can alter pay or cash-out semantics unexpectedly. | Integration tests and careful forwarding review. |
|
|
19
17
|
|
|
20
18
|
## 1. Trust Assumptions
|
|
21
19
|
|
|
22
|
-
- **Trusted forwarder
|
|
23
|
-
- **Sucker registry
|
|
24
|
-
- **Controller trust.**
|
|
25
|
-
- **Extra data hooks
|
|
20
|
+
- **Trusted forwarder is trusted.**
|
|
21
|
+
- **Sucker registry answers are trusted.**
|
|
22
|
+
- **Controller trust matters.**
|
|
23
|
+
- **Extra data hooks are trusted code.**
|
|
26
24
|
|
|
27
|
-
## 2. Economic
|
|
25
|
+
## 2. Economic Risks
|
|
28
26
|
|
|
29
|
-
- **Sucker cashout bypass
|
|
30
|
-
- **
|
|
31
|
-
- **721 hook amount splitting
|
|
32
|
-
- **Cross-chain sender dependence
|
|
27
|
+
- **Sucker cashout bypass exists for registered suckers.**
|
|
28
|
+
- **Extra data hooks can manipulate weight or cash-out behavior.**
|
|
29
|
+
- **721 hook amount splitting can zero out project amount in edge cases.**
|
|
30
|
+
- **Cross-chain sender dependence affects deterministic sucker salts.**
|
|
33
31
|
|
|
34
32
|
## 3. Access Control
|
|
35
33
|
|
|
36
|
-
- **Wildcard
|
|
37
|
-
-
|
|
38
|
-
-
|
|
34
|
+
- **Wildcard `MAP_SUCKER_TOKEN` permission is broad.**
|
|
35
|
+
- **`launchRulesetsFor` requires combined permissions.**
|
|
36
|
+
- **`launchProjectFor` is intentionally permissionless for new projects.**
|
|
39
37
|
|
|
40
38
|
## 4. DoS Vectors
|
|
41
39
|
|
|
42
|
-
- **Ruleset ID collision
|
|
43
|
-
- **External hook
|
|
44
|
-
- **721 hook deployment revert
|
|
45
|
-
- **
|
|
40
|
+
- **Ruleset ID collision can block queueing.**
|
|
41
|
+
- **External hook reverts can block pay or cash-out flows.**
|
|
42
|
+
- **721 hook deployment revert blocks launch.**
|
|
43
|
+
- **Non-safe NFT transfers can still strand assets.**
|
|
46
44
|
|
|
47
45
|
## 5. Reentrancy Surface
|
|
48
46
|
|
|
49
|
-
- **`launchProjectFor`
|
|
50
|
-
- **`beforePayRecordedWith` delegates to external hooks.**
|
|
51
|
-
- **`beforeCashOutRecordedWith` delegates to external hooks.**
|
|
52
|
-
- **
|
|
47
|
+
- **`launchProjectFor` makes several external calls in sequence.**
|
|
48
|
+
- **`beforePayRecordedWith` delegates to external hooks.**
|
|
49
|
+
- **`beforeCashOutRecordedWith` delegates to external hooks.**
|
|
50
|
+
- **There is no `ReentrancyGuard`.** The deployer relies on being effectively stateless during pay and cash-out operations.
|
|
53
51
|
|
|
54
52
|
## 6. Integration Risks
|
|
55
53
|
|
|
56
|
-
- **Hook config keyed by predicted
|
|
57
|
-
- **Carried-forward 721 hook on queue
|
|
58
|
-
- **ERC721Receiver
|
|
59
|
-
- **Empty simplified launch config reverts.**
|
|
60
|
-
- **Cross-reference: sucker registration.** The deployer grants `MAP_SUCKER_TOKEN` to `SUCKER_REGISTRY` with `projectId=0` (wildcard). This means the registry can map tokens for ALL projects deployed through this deployer. See [nana-suckers-v6 RISKS.md](../nana-suckers-v6/RISKS.md) for the full sucker lifecycle risks.
|
|
61
|
-
- **Cross-reference: core reentrancy.** The deployer delegates to `JBController` and `JBMultiTerminal` for all fund operations. See [nana-core-v6 RISKS.md](../nana-core-v6/RISKS.md) section 3 for the reentrancy surface of these contracts.
|
|
54
|
+
- **Hook config is keyed by predicted ruleset ID.**
|
|
55
|
+
- **Carried-forward 721 hook behavior on queue depends on prior ruleset state.**
|
|
56
|
+
- **ERC721Receiver restrictions are narrow but non-safe transfers can still strand assets.**
|
|
57
|
+
- **Empty simplified launch config reverts.**
|
|
62
58
|
|
|
63
|
-
## 7. Invariants
|
|
59
|
+
## 7. Invariants To Verify
|
|
64
60
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
- Project NFT ownership: after `_launchProjectFor`, the project NFT is owned by `owner`, not the deployer.
|
|
71
|
-
- `deploySuckersFor` uses the same `_msgSender()` on every chain when deterministic cross-chain peer symmetry is expected.
|
|
61
|
+
- launched projects point at the intended controller
|
|
62
|
+
- stored 721 hook config exists for every ruleset created through this deployer
|
|
63
|
+
- sucker cashouts always get the intended zero-tax path
|
|
64
|
+
- self-reference prevention holds after setup
|
|
65
|
+
- the project NFT ends owned by the intended owner
|
|
72
66
|
|
|
73
67
|
## 8. Accepted Behaviors
|
|
74
68
|
|
|
75
|
-
### 8.1 Controller validation skipped during
|
|
69
|
+
### 8.1 Controller validation is skipped during initial launch
|
|
70
|
+
|
|
71
|
+
Pre-launch controller validation is impossible because the project does not yet exist. The accepted safeguard is the post-launch project ID match check.
|
|
72
|
+
|
|
73
|
+
### 8.2 Registered suckers receive 0% cashout tax
|
|
74
|
+
|
|
75
|
+
This is intentional and shares the same trust boundary as the sucker registry.
|
|
76
|
+
|
|
77
|
+
## 9. Accepted Security Risks
|
|
78
|
+
|
|
79
|
+
Documented risks that were reviewed and accepted.
|
|
80
|
+
|
|
81
|
+
### Configuration Risks
|
|
82
|
+
|
|
83
|
+
**Unvalidated extra data hooks can brick live flows.** *(Minor)*
|
|
84
|
+
Extra data hooks provided by the project owner in `_setup721` configuration can fail and brick live pay/cashout flows. Accepted because this is self-inflicted misconfiguration — only the project owner can set these hooks.
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
**Missing hook721 alias check enables double invocation.** *(Minor)*
|
|
87
|
+
If the project owner configures the 721 hook as both the primary hook and as an extra data hook, it could be invoked twice. Accepted because this is self-inflicted misconfiguration — the deployer correctly processes each hook independently.
|
|
78
88
|
|
|
79
|
-
###
|
|
89
|
+
### Cross-Chain Deployment
|
|
80
90
|
|
|
81
|
-
`
|
|
91
|
+
**`_msgSender()` in deployment salt breaks cross-chain determinism.** *(Minor)*
|
|
92
|
+
`deploySuckersFor` includes `_msgSender()` in the CREATE2 salt, which means the same deployment from different callers produces different addresses across chains. Accepted because this is intentional replay protection — prevents frontrunning of cross-chain deployments.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/omnichain-deployers-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
21
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
22
|
-
"@bananapus/core-v6": "^0.0.
|
|
23
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
24
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
25
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
20
|
+
"@bananapus/721-hook-v6": "^0.0.38",
|
|
21
|
+
"@bananapus/buyback-hook-v6": "^0.0.30",
|
|
22
|
+
"@bananapus/core-v6": "^0.0.36",
|
|
23
|
+
"@bananapus/ownable-v6": "^0.0.20",
|
|
24
|
+
"@bananapus/permission-ids-v6": "^0.0.19",
|
|
25
|
+
"@bananapus/suckers-v6": "^0.0.28",
|
|
26
26
|
"@openzeppelin/contracts": "^5.6.1",
|
|
27
27
|
"@uniswap/v4-core": "^1.0.2"
|
|
28
28
|
},
|
package/script/Deploy.s.sol
CHANGED
|
@@ -8,6 +8,7 @@ import {CoreDeployment, CoreDeploymentLib} from "@bananapus/core-v6/script/helpe
|
|
|
8
8
|
import {Hook721Deployment, Hook721DeploymentLib} from "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
9
9
|
import {SuckerDeployment, SuckerDeploymentLib} from "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
10
10
|
|
|
11
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
11
12
|
import {JBOmnichainDeployer} from "src/JBOmnichainDeployer.sol";
|
|
12
13
|
|
|
13
14
|
contract Deploy is Script, Sphinx {
|
|
@@ -61,7 +62,12 @@ contract Deploy is Script, Sphinx {
|
|
|
61
62
|
salt: NANA_OMNICHAIN_DEPLOYER_SALT,
|
|
62
63
|
creationCode: type(JBOmnichainDeployer).creationCode,
|
|
63
64
|
arguments: abi.encode(
|
|
64
|
-
suckers.registry,
|
|
65
|
+
suckers.registry,
|
|
66
|
+
hook.hook_deployer,
|
|
67
|
+
core.permissions,
|
|
68
|
+
core.projects,
|
|
69
|
+
core.directory,
|
|
70
|
+
core.trustedForwarder
|
|
65
71
|
),
|
|
66
72
|
deployer: safeAddress()
|
|
67
73
|
})) {
|
|
@@ -70,6 +76,7 @@ contract Deploy is Script, Sphinx {
|
|
|
70
76
|
hookDeployer: hook.hook_deployer,
|
|
71
77
|
permissions: core.permissions,
|
|
72
78
|
projects: core.projects,
|
|
79
|
+
directory: IJBDirectory(address(core.directory)),
|
|
73
80
|
trustedForwarder: core.trustedForwarder
|
|
74
81
|
});
|
|
75
82
|
}
|
|
@@ -6,6 +6,7 @@ import {IJB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB
|
|
|
6
6
|
import {JBApprovalStatus} from "@bananapus/core-v6/src/enums/JBApprovalStatus.sol";
|
|
7
7
|
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
8
8
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
9
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
9
10
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
10
11
|
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
11
12
|
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
@@ -79,6 +80,10 @@ contract JBOmnichainDeployer is
|
|
|
79
80
|
/// @notice Deploys and tracks suckers for projects.
|
|
80
81
|
IJBSuckerRegistry public immutable SUCKER_REGISTRY;
|
|
81
82
|
|
|
83
|
+
/// @notice The directory used to validate controllers. Stored as immutable to prevent a user-provided
|
|
84
|
+
/// controller from returning a fake directory that confirms itself.
|
|
85
|
+
IJBDirectory public immutable DIRECTORY;
|
|
86
|
+
|
|
82
87
|
//*********************************************************************//
|
|
83
88
|
// -------------------- internal stored properties ------------------- //
|
|
84
89
|
//*********************************************************************//
|
|
@@ -101,12 +106,14 @@ contract JBOmnichainDeployer is
|
|
|
101
106
|
/// @param hookDeployer The deployer to use for project's tiered ERC-721 hooks.
|
|
102
107
|
/// @param permissions The permissions to use for the contract.
|
|
103
108
|
/// @param projects The projects to use for the contract.
|
|
109
|
+
/// @param directory The directory used to validate controllers against a trusted source.
|
|
104
110
|
/// @param trustedForwarder The trusted forwarder for the ERC2771Context.
|
|
105
111
|
constructor(
|
|
106
112
|
IJBSuckerRegistry suckerRegistry,
|
|
107
113
|
IJB721TiersHookDeployer hookDeployer,
|
|
108
114
|
IJBPermissions permissions,
|
|
109
115
|
IJBProjects projects,
|
|
116
|
+
IJBDirectory directory,
|
|
110
117
|
address trustedForwarder
|
|
111
118
|
)
|
|
112
119
|
JBPermissioned(permissions)
|
|
@@ -115,6 +122,7 @@ contract JBOmnichainDeployer is
|
|
|
115
122
|
PROJECTS = projects;
|
|
116
123
|
SUCKER_REGISTRY = suckerRegistry;
|
|
117
124
|
HOOK_DEPLOYER = hookDeployer;
|
|
125
|
+
DIRECTORY = directory;
|
|
118
126
|
|
|
119
127
|
// Give the sucker registry permission to map tokens for all revnets.
|
|
120
128
|
uint8[] memory permissionIds = new uint8[](1);
|
|
@@ -715,8 +723,9 @@ contract JBOmnichainDeployer is
|
|
|
715
723
|
});
|
|
716
724
|
}
|
|
717
725
|
|
|
718
|
-
// Transfer ownership of the project to the owner.
|
|
719
|
-
|
|
726
|
+
// Transfer ownership of the project to the owner. Uses safeTransferFrom so contract receivers
|
|
727
|
+
// get an onERC721Received callback.
|
|
728
|
+
PROJECTS.safeTransferFrom({from: address(this), to: owner, tokenId: projectId});
|
|
720
729
|
}
|
|
721
730
|
|
|
722
731
|
/// @notice Internal implementation of `launchRulesetsFor`.
|
|
@@ -920,13 +929,12 @@ contract JBOmnichainDeployer is
|
|
|
920
929
|
}
|
|
921
930
|
|
|
922
931
|
/// @notice Validates that the provided controller matches the project's controller in the directory.
|
|
923
|
-
/// @dev
|
|
924
|
-
///
|
|
925
|
-
/// malicious controller from being passed in.
|
|
932
|
+
/// @dev Uses the immutable DIRECTORY instead of querying the controller, preventing a malicious
|
|
933
|
+
/// controller from returning a fake directory that confirms itself.
|
|
926
934
|
/// @param projectId The ID of the project to validate the controller for.
|
|
927
935
|
/// @param controller The controller to validate.
|
|
928
936
|
function _validateController(uint256 projectId, IJBController controller) internal view {
|
|
929
|
-
address current = address(
|
|
937
|
+
address current = address(DIRECTORY.controllerOf(projectId));
|
|
930
938
|
// Allow address(0) for fresh projects that haven't launched rulesets yet.
|
|
931
939
|
if (current != address(0) && current != address(controller)) {
|
|
932
940
|
revert JBOmnichainDeployer_ControllerMismatch();
|
|
@@ -42,6 +42,7 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
42
42
|
IJBProjects projects = IJBProjects(makeAddr("projects"));
|
|
43
43
|
IJBSuckerRegistry suckerRegistry = IJBSuckerRegistry(makeAddr("suckerRegistry"));
|
|
44
44
|
IJB721TiersHookDeployer hookDeployer = IJB721TiersHookDeployer(makeAddr("hookDeployer"));
|
|
45
|
+
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
45
46
|
|
|
46
47
|
address projectOwner = makeAddr("projectOwner");
|
|
47
48
|
address sucker = makeAddr("sucker");
|
|
@@ -63,6 +64,7 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
63
64
|
hookDeployer,
|
|
64
65
|
permissions,
|
|
65
66
|
projects,
|
|
67
|
+
directory,
|
|
66
68
|
address(0) // trustedForwarder
|
|
67
69
|
);
|
|
68
70
|
|
|
@@ -331,11 +333,7 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
331
333
|
|
|
332
334
|
function test_launchRulesetsFor_simplified_usesDefaultCurrency() public {
|
|
333
335
|
IJBController controller = IJBController(makeAddr("controller"));
|
|
334
|
-
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
335
336
|
|
|
336
|
-
vm.mockCall(
|
|
337
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
338
|
-
);
|
|
339
337
|
vm.mockCall(
|
|
340
338
|
address(directory),
|
|
341
339
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
|
@@ -369,7 +367,6 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
369
367
|
function test_queueRulesetsOf_simplified_carriesForwardHook() public {
|
|
370
368
|
// First launch a project so there's a hook to carry forward.
|
|
371
369
|
IJBController controller = IJBController(makeAddr("controller"));
|
|
372
|
-
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
373
370
|
IJBRulesets rulesets = IJBRulesets(makeAddr("rulesets"));
|
|
374
371
|
|
|
375
372
|
vm.mockCall(address(projects), abi.encodeWithSelector(IJBProjects.count.selector), abi.encode(uint256(41)));
|
|
@@ -391,9 +388,6 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
391
388
|
deployer.launchProjectFor(projectOwner, "test", configs, terminals, "", _emptySuckerConfig(), controller);
|
|
392
389
|
|
|
393
390
|
// Now set up mocks for queueRulesetsOf.
|
|
394
|
-
vm.mockCall(
|
|
395
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
396
|
-
);
|
|
397
391
|
vm.mockCall(
|
|
398
392
|
address(directory),
|
|
399
393
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
|
@@ -462,7 +456,6 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
462
456
|
function test_queueRulesetsOf_carryForward_preservesCashOutFlag() public {
|
|
463
457
|
// --- Step 1: Launch a project with useDataHookForCashOut = true ---
|
|
464
458
|
IJBController controller = IJBController(makeAddr("controller"));
|
|
465
|
-
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
466
459
|
IJBRulesets rulesets = IJBRulesets(makeAddr("rulesets"));
|
|
467
460
|
|
|
468
461
|
vm.mockCall(address(projects), abi.encodeWithSelector(IJBProjects.count.selector), abi.encode(uint256(41)));
|
|
@@ -495,9 +488,6 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
495
488
|
assertTrue(initialCashOutFlag, "initial launch should store useDataHookForCashOut = true");
|
|
496
489
|
|
|
497
490
|
// --- Step 2: Queue a new ruleset with no tiers (carry-forward) ---
|
|
498
|
-
vm.mockCall(
|
|
499
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
500
|
-
);
|
|
501
491
|
vm.mockCall(
|
|
502
492
|
address(directory),
|
|
503
493
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
|
@@ -126,6 +126,7 @@ contract JBOmnichainDeployerGuardTest is TestBaseWorkflow {
|
|
|
126
126
|
IJB721TiersHookDeployer(mockHookDeployerAddr),
|
|
127
127
|
IJBPermissions(address(jbPermissions())),
|
|
128
128
|
IJBProjects(address(jbProjects())),
|
|
129
|
+
IJBDirectory(address(jbDirectory())),
|
|
129
130
|
trustedForwarder()
|
|
130
131
|
);
|
|
131
132
|
|
|
@@ -4,6 +4,7 @@ pragma solidity 0.8.28;
|
|
|
4
4
|
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
6
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
7
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
7
8
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
8
9
|
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
9
10
|
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
@@ -97,6 +98,7 @@ contract OmnichainDeployerAttacks is Test {
|
|
|
97
98
|
IJBProjects projects = IJBProjects(makeAddr("projects"));
|
|
98
99
|
IJBSuckerRegistry suckerRegistry = IJBSuckerRegistry(makeAddr("suckerRegistry"));
|
|
99
100
|
IJB721TiersHookDeployer hookDeployer = IJB721TiersHookDeployer(makeAddr("hookDeployer"));
|
|
101
|
+
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
100
102
|
|
|
101
103
|
address projectOwner = makeAddr("projectOwner");
|
|
102
104
|
address sucker = makeAddr("sucker");
|
|
@@ -119,7 +121,7 @@ contract OmnichainDeployerAttacks is Test {
|
|
|
119
121
|
address(permissions), abi.encodeWithSelector(IJBPermissions.setPermissionsFor.selector), abi.encode()
|
|
120
122
|
);
|
|
121
123
|
|
|
122
|
-
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, address(0));
|
|
124
|
+
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, directory, address(0));
|
|
123
125
|
|
|
124
126
|
// Default mocks.
|
|
125
127
|
vm.mockCall(
|
|
@@ -113,6 +113,7 @@ contract OmnichainDeployerEdgeCases is Test {
|
|
|
113
113
|
IJBProjects projects = IJBProjects(makeAddr("projects"));
|
|
114
114
|
IJBSuckerRegistry suckerRegistry = IJBSuckerRegistry(makeAddr("suckerRegistry"));
|
|
115
115
|
IJB721TiersHookDeployer hookDeployer = IJB721TiersHookDeployer(makeAddr("hookDeployer"));
|
|
116
|
+
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
116
117
|
|
|
117
118
|
address projectOwner = makeAddr("projectOwner");
|
|
118
119
|
address sucker = makeAddr("sucker");
|
|
@@ -131,7 +132,7 @@ contract OmnichainDeployerEdgeCases is Test {
|
|
|
131
132
|
address(permissions), abi.encodeWithSelector(IJBPermissions.setPermissionsFor.selector), abi.encode()
|
|
132
133
|
);
|
|
133
134
|
|
|
134
|
-
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, address(0));
|
|
135
|
+
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, directory, address(0));
|
|
135
136
|
|
|
136
137
|
vm.mockCall(
|
|
137
138
|
address(projects), abi.encodeWithSelector(IERC721.ownerOf.selector, projectId), abi.encode(projectOwner)
|
|
@@ -575,12 +576,8 @@ contract OmnichainDeployerEdgeCases is Test {
|
|
|
575
576
|
// =========================================================================
|
|
576
577
|
function test_launchRulesetsFor_requiresLaunchRulesetsPermission() public {
|
|
577
578
|
IJBController controller = IJBController(makeAddr("controller"));
|
|
578
|
-
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
579
579
|
|
|
580
|
-
// Mock
|
|
581
|
-
vm.mockCall(
|
|
582
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
583
|
-
);
|
|
580
|
+
// Mock controllerOf on the deployer's immutable DIRECTORY.
|
|
584
581
|
vm.mockCall(
|
|
585
582
|
address(directory),
|
|
586
583
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
|
@@ -609,13 +606,9 @@ contract OmnichainDeployerEdgeCases is Test {
|
|
|
609
606
|
_launchProjectWithHook(address(0));
|
|
610
607
|
|
|
611
608
|
IJBController controller = IJBController(makeAddr("controller"));
|
|
612
|
-
IJBDirectory directory = IJBDirectory(makeAddr("directory"));
|
|
613
609
|
IJBRulesets rulesets = IJBRulesets(makeAddr("rulesets"));
|
|
614
610
|
|
|
615
|
-
// Mock
|
|
616
|
-
vm.mockCall(
|
|
617
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
618
|
-
);
|
|
611
|
+
// Mock controllerOf on the deployer's immutable DIRECTORY.
|
|
619
612
|
vm.mockCall(
|
|
620
613
|
address(directory),
|
|
621
614
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
package/test/TestAuditGaps.sol
CHANGED
|
@@ -279,7 +279,7 @@ contract TestAuditGaps is Test {
|
|
|
279
279
|
vm.mockCall(
|
|
280
280
|
address(permissions), abi.encodeWithSelector(IJBPermissions.setPermissionsFor.selector), abi.encode()
|
|
281
281
|
);
|
|
282
|
-
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, address(0));
|
|
282
|
+
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, directory, address(0));
|
|
283
283
|
|
|
284
284
|
// Default mocks.
|
|
285
285
|
vm.mockCall(
|
|
@@ -292,9 +292,7 @@ contract TestAuditGaps is Test {
|
|
|
292
292
|
vm.mockCall(
|
|
293
293
|
address(controller), abi.encodeWithSelector(IJBController.launchProjectFor.selector), abi.encode(projectId)
|
|
294
294
|
);
|
|
295
|
-
|
|
296
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
297
|
-
);
|
|
295
|
+
// Mock controllerOf on the deployer's immutable DIRECTORY.
|
|
298
296
|
vm.mockCall(
|
|
299
297
|
address(directory),
|
|
300
298
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
|
@@ -804,21 +802,9 @@ contract TestAuditGaps is Test {
|
|
|
804
802
|
|
|
805
803
|
vm.warp(BASE_TIME + 10);
|
|
806
804
|
|
|
807
|
-
//
|
|
805
|
+
// The deployer's immutable DIRECTORY returns `controller` for this project.
|
|
806
|
+
// Passing a different controller should trigger ControllerMismatch.
|
|
808
807
|
IJBController wrongController = IJBController(makeAddr("wrongController"));
|
|
809
|
-
IJBDirectory wrongDirectory = IJBDirectory(makeAddr("wrongDirectory"));
|
|
810
|
-
|
|
811
|
-
vm.mockCall(
|
|
812
|
-
address(wrongController),
|
|
813
|
-
abi.encodeWithSelector(IJBController.DIRECTORY.selector),
|
|
814
|
-
abi.encode(wrongDirectory)
|
|
815
|
-
);
|
|
816
|
-
// controllerOf returns a different address than wrongController.
|
|
817
|
-
vm.mockCall(
|
|
818
|
-
address(wrongDirectory),
|
|
819
|
-
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|
|
820
|
-
abi.encode(IERC165(makeAddr("otherController")))
|
|
821
|
-
);
|
|
822
808
|
|
|
823
809
|
JBRulesetConfig[] memory configs = new JBRulesetConfig[](1);
|
|
824
810
|
configs[0] = _makeRulesetConfig(address(0), false, false);
|
|
@@ -65,7 +65,7 @@ contract Tiered721HookComposition is Test {
|
|
|
65
65
|
vm.mockCall(
|
|
66
66
|
address(permissions), abi.encodeWithSelector(IJBPermissions.setPermissionsFor.selector), abi.encode()
|
|
67
67
|
);
|
|
68
|
-
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, address(0));
|
|
68
|
+
deployer = new JBOmnichainDeployer(suckerRegistry, hookDeployer, permissions, projects, directory, address(0));
|
|
69
69
|
vm.mockCall(
|
|
70
70
|
address(projects), abi.encodeWithSelector(IERC721.ownerOf.selector, projectId), abi.encode(projectOwner)
|
|
71
71
|
);
|
|
@@ -76,9 +76,7 @@ contract Tiered721HookComposition is Test {
|
|
|
76
76
|
vm.mockCall(
|
|
77
77
|
address(controller), abi.encodeWithSelector(IJBController.launchProjectFor.selector), abi.encode(projectId)
|
|
78
78
|
);
|
|
79
|
-
|
|
80
|
-
address(controller), abi.encodeWithSelector(IJBController.DIRECTORY.selector), abi.encode(directory)
|
|
81
|
-
);
|
|
79
|
+
// Mock controllerOf on the deployer's immutable DIRECTORY.
|
|
82
80
|
vm.mockCall(
|
|
83
81
|
address(directory),
|
|
84
82
|
abi.encodeWithSelector(IJBDirectory.controllerOf.selector, projectId),
|