@bananapus/omnichain-deployers-v6 0.0.31 → 0.0.34

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +2 -2
  3. package/foundry.toml +2 -1
  4. package/package.json +20 -11
  5. package/src/JBOmnichainDeployer.sol +34 -20
  6. package/src/interfaces/IJBOmnichainDeployer.sol +4 -0
  7. package/ADMINISTRATION.md +0 -29
  8. package/ARCHITECTURE.md +0 -31
  9. package/AUDIT_INSTRUCTIONS.md +0 -29
  10. package/RISKS.md +0 -97
  11. package/SKILLS.md +0 -25
  12. package/STYLE_GUIDE.md +0 -610
  13. package/USER_JOURNEYS.md +0 -63
  14. package/foundry.lock +0 -11
  15. package/slither-ci.config.json +0 -10
  16. package/sphinx.lock +0 -491
  17. package/test/JBOmnichainDeployer.t.sol +0 -639
  18. package/test/JBOmnichainDeployerGuard.t.sol +0 -404
  19. package/test/OmnichainDeployerAttacks.t.sol +0 -432
  20. package/test/OmnichainDeployerEdgeCases.t.sol +0 -827
  21. package/test/OmnichainDeployerReentrancy.t.sol +0 -321
  22. package/test/TestAuditGaps.sol +0 -996
  23. package/test/Tiered721HookComposition.t.sol +0 -909
  24. package/test/audit/AuditFixesC2H6M14.t.sol +0 -480
  25. package/test/audit/CarryForwardRejectedHook.t.sol +0 -328
  26. package/test/audit/CashOutCountPropagation.t.sol +0 -232
  27. package/test/audit/CashOutSpecMerge.t.sol +0 -372
  28. package/test/audit/DeterministicDrift.t.sol +0 -78
  29. package/test/audit/DeterministicPeerDrift.t.sol +0 -79
  30. package/test/audit/ExtraCashOutHookZeroReclaim.t.sol +0 -340
  31. package/test/audit/ForwardedPermissions.t.sol +0 -297
  32. package/test/audit/JBOmnichainDeployer.t.sol +0 -321
  33. package/test/audit/NftCashoutSupplyMismatch.t.sol +0 -224
  34. package/test/audit/OmnichainAudit.t.sol +0 -168
  35. package/test/audit/SplitCreditWeight.t.sol +0 -437
  36. package/test/audit/WeightScalingComparison.t.sol +0 -350
  37. package/test/fork/OmnichainForkTestBase.sol +0 -535
  38. package/test/fork/TestOmnichain721QueueAndAdjust.t.sol +0 -252
  39. package/test/fork/TestOmnichainCashOutFork.t.sol +0 -184
  40. package/test/fork/TestOmnichainStressFork.t.sol +0 -552
  41. package/test/fork/TestOmnichainWeightFork.t.sol +0 -60
  42. package/test/fork/TestSuckerDeploymentFork.t.sol +0 -229
  43. package/test/invariants/CrossChainDeployerInvariant.t.sol +0 -208
  44. package/test/invariants/OmnichainDeployerInvariant.t.sol +0 -150
  45. package/test/invariants/handlers/CrossChainDeployerHandler.sol +0 -394
  46. package/test/invariants/handlers/OmnichainDeployerHandler.sol +0 -262
  47. package/test/regression/EmptyRulesetConfigurations.t.sol +0 -84
  48. package/test/regression/HookOwnershipTransfer.t.sol +0 -152
  49. package/test/regression/ValidateController.t.sol +0 -169
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## v6 post-audit (M-11 fix)
3
+ ## v6 carry-forward hook fix
4
4
 
5
5
  - **Carry-forward hook selection improved.** `queueRulesetsOf` now checks `latestQueuedOf(projectId)` before falling back to `currentOf(projectId)` when carrying forward a 721 hook. Previously it only read `currentOf`, which could miss a recently queued (and approved) ruleset's hook config. The source ruleset must have approval status `Approved` or `Empty` and a stored hook config in the deployer.
6
6
  - The `useDataHookForCashOut` flag is preserved from whichever source ruleset is selected during carry-forward.
package/README.md CHANGED
@@ -69,8 +69,8 @@ npm install @bananapus/omnichain-deployers-v6
69
69
 
70
70
  ```bash
71
71
  npm install
72
- forge build
73
- forge test
72
+ forge build --deny notes
73
+ forge test --deny notes --fail-fast --summary --detailed --skip "*/script/**"
74
74
  ```
75
75
 
76
76
  Useful scripts:
package/foundry.toml CHANGED
@@ -15,7 +15,8 @@ depth = 100
15
15
  fail_on_revert = false
16
16
 
17
17
  [lint]
18
- exclude_lints = ["pascal-case-struct", "mixed-case-variable"]
18
+ exclude_lints = ["mixed-case-variable", "pascal-case-struct"]
19
+
19
20
  [fmt]
20
21
  number_underscore = "thousands"
21
22
  multiline_func_header = "all"
package/package.json CHANGED
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "name": "@bananapus/omnichain-deployers-v6",
3
- "version": "0.0.31",
3
+ "version": "0.0.34",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Bananapus/nana-omnichain-deployers-v6"
8
8
  },
9
+ "files": [
10
+ "CHANGELOG.md",
11
+ "foundry.toml",
12
+ "references/",
13
+ "remappings.txt",
14
+ "script/Deploy.s.sol",
15
+ "script/helpers/",
16
+ "src/"
17
+ ],
9
18
  "engines": {
10
19
  "node": ">=20.0.0"
11
20
  },
@@ -17,17 +26,17 @@
17
26
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
18
27
  },
19
28
  "dependencies": {
20
- "@bananapus/721-hook-v6": "^0.0.41",
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
- "@openzeppelin/contracts": "^5.6.1",
27
- "@uniswap/v4-core": "^1.0.2"
29
+ "@bananapus/721-hook-v6": "0.0.43",
30
+ "@bananapus/core-v6": "0.0.39",
31
+ "@bananapus/ownable-v6": "0.0.24",
32
+ "@bananapus/permission-ids-v6": "0.0.22",
33
+ "@bananapus/suckers-v6": "0.0.32",
34
+ "@openzeppelin/contracts": "5.6.1"
28
35
  },
29
36
  "devDependencies": {
30
- "@bananapus/address-registry-v6": "^0.0.20",
31
- "@sphinx-labs/plugins": "^0.33.2"
37
+ "@bananapus/address-registry-v6": "0.0.25",
38
+ "@bananapus/buyback-hook-v6": "0.0.37",
39
+ "@sphinx-labs/plugins": "0.33.3",
40
+ "@uniswap/v4-core": "1.0.2"
32
41
  }
33
42
  }
@@ -252,6 +252,7 @@ contract JBOmnichainDeployer is
252
252
  /// @notice Launches new rulesets for a project with a 721 tiers hook attached, using this contract as the data
253
253
  /// hook.
254
254
  /// @param projectId The ID of the project to launch the rulesets for.
255
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
255
256
  /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt).
256
257
  /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
257
258
  /// @param terminalConfigurations The terminals to set up for the project.
@@ -261,6 +262,7 @@ contract JBOmnichainDeployer is
261
262
  /// @return hook The 721 tiers hook that was deployed for the project.
262
263
  function launchRulesetsFor(
263
264
  uint256 projectId,
265
+ string calldata projectUri,
264
266
  JBOmnichain721Config memory deploy721Config,
265
267
  JBRulesetConfig[] memory rulesetConfigurations,
266
268
  JBTerminalConfig[] calldata terminalConfigurations,
@@ -273,6 +275,7 @@ contract JBOmnichainDeployer is
273
275
  {
274
276
  return _launchRulesetsFor({
275
277
  projectId: projectId,
278
+ projectUri: projectUri,
276
279
  deploy721Config: deploy721Config,
277
280
  rulesetConfigurations: rulesetConfigurations,
278
281
  terminalConfigurations: terminalConfigurations,
@@ -284,6 +287,7 @@ contract JBOmnichainDeployer is
284
287
  /// @notice Launches new rulesets for a project with a default (empty-tier) 721 hook.
285
288
  /// @dev Uses `baseCurrency` from the first ruleset and `decimals = 18` for the default 721 config.
286
289
  /// @param projectId The ID of the project to launch the rulesets for.
290
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
287
291
  /// @param rulesetConfigurations The rulesets to launch.
288
292
  /// @param terminalConfigurations The terminals to set up for the project.
289
293
  /// @param memo A memo to pass along to the emitted event.
@@ -292,6 +296,7 @@ contract JBOmnichainDeployer is
292
296
  /// @return hook The 721 tiers hook that was deployed for the project.
293
297
  function launchRulesetsFor(
294
298
  uint256 projectId,
299
+ string calldata projectUri,
295
300
  JBRulesetConfig[] memory rulesetConfigurations,
296
301
  JBTerminalConfig[] calldata terminalConfigurations,
297
302
  string calldata memo,
@@ -303,6 +308,7 @@ contract JBOmnichainDeployer is
303
308
  {
304
309
  return _launchRulesetsFor({
305
310
  projectId: projectId,
311
+ projectUri: projectUri,
306
312
  deploy721Config: _default721Config(rulesetConfigurations),
307
313
  rulesetConfigurations: rulesetConfigurations,
308
314
  terminalConfigurations: terminalConfigurations,
@@ -720,11 +726,12 @@ contract JBOmnichainDeployer is
720
726
  internal
721
727
  returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers)
722
728
  {
723
- // Get the next project ID.
724
- projectId = PROJECTS.count() + 1;
729
+ // Reserve the project ID up front so permissionless project creations cannot invalidate hook deployment.
730
+ projectId = PROJECTS.createFor(address(this));
725
731
 
726
732
  // Deploy a 721 hook and set up rulesets.
727
733
  hook = _deploy721Hook({projectId: projectId, config: deploy721Config});
734
+ // slither-disable-next-line reentrancy-benign
728
735
  rulesetConfigurations = _setup721({
729
736
  projectId: projectId,
730
737
  rulesetConfigurations: rulesetConfigurations,
@@ -732,18 +739,16 @@ contract JBOmnichainDeployer is
732
739
  use721ForCashOut: deploy721Config.useDataHookForCashOut
733
740
  });
734
741
 
735
- // Launch the project, and sanity check the project ID.
736
- // slither-disable-next-line reentrancy-benign
737
- if (
738
- projectId
739
- != controller.launchProjectFor({
740
- owner: address(this),
741
- projectUri: projectUri,
742
- rulesetConfigurations: rulesetConfigurations,
743
- terminalConfigurations: terminalConfigurations,
744
- memo: memo
745
- })
746
- ) revert JBOmnichainDeployer_ProjectIdMismatch();
742
+ // Launch the rulesets for the reserved project.
743
+ // slither-disable-start unused-return
744
+ controller.launchRulesetsFor({
745
+ projectId: projectId,
746
+ projectUri: projectUri,
747
+ rulesetConfigurations: rulesetConfigurations,
748
+ terminalConfigurations: terminalConfigurations,
749
+ memo: memo
750
+ });
751
+ // slither-disable-end unused-return
747
752
 
748
753
  // Transfer the hook's ownership to the project (now that the project NFT has been minted).
749
754
  JBOwnable(address(hook)).transferOwnershipToProject(projectId);
@@ -766,6 +771,7 @@ contract JBOmnichainDeployer is
766
771
  /// @notice Internal implementation of `launchRulesetsFor`.
767
772
  function _launchRulesetsFor(
768
773
  uint256 projectId,
774
+ string calldata projectUri,
769
775
  JBOmnichain721Config memory deploy721Config,
770
776
  JBRulesetConfig[] memory rulesetConfigurations,
771
777
  JBTerminalConfig[] calldata terminalConfigurations,
@@ -775,15 +781,19 @@ contract JBOmnichainDeployer is
775
781
  internal
776
782
  returns (uint256 rulesetId, IJB721TiersHook hook)
777
783
  {
784
+ address owner = PROJECTS.ownerOf(projectId);
785
+
778
786
  // Enforce permissions. Use LAUNCH_RULESETS (not QUEUE_RULESETS) because this function calls
779
787
  // controller.launchRulesetsFor, which sets terminals and requires the broader launch permission.
780
- _requirePermissionFrom({
781
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.LAUNCH_RULESETS
782
- });
788
+ _requirePermissionFrom({account: owner, projectId: projectId, permissionId: JBPermissionIds.LAUNCH_RULESETS});
783
789
 
784
- _requirePermissionFrom({
785
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
786
- });
790
+ _requirePermissionFrom({account: owner, projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS});
791
+
792
+ if (bytes(projectUri).length != 0) {
793
+ _requirePermissionFrom({
794
+ account: owner, projectId: projectId, permissionId: JBPermissionIds.SET_PROJECT_URI
795
+ });
796
+ }
787
797
 
788
798
  // Validate that the controller matches the project's controller in the directory.
789
799
  _validateController({projectId: projectId, controller: controller});
@@ -802,6 +812,7 @@ contract JBOmnichainDeployer is
802
812
  // Configure the rulesets.
803
813
  rulesetId = controller.launchRulesetsFor({
804
814
  projectId: projectId,
815
+ projectUri: projectUri,
805
816
  rulesetConfigurations: rulesetConfigurations,
806
817
  terminalConfigurations: terminalConfigurations,
807
818
  memo: memo
@@ -830,6 +841,7 @@ contract JBOmnichainDeployer is
830
841
  // Revert if the project already had rulesets queued in this block, which would make our
831
842
  // `block.timestamp + i` ruleset ID prediction incorrect.
832
843
  uint256 latestRulesetId = controller.RULESETS().latestRulesetIdOf(projectId);
844
+ // forge-lint: disable-next-line(block-timestamp)
833
845
  if (latestRulesetId >= block.timestamp) {
834
846
  revert JBOmnichainDeployer_RulesetIdsUnpredictable();
835
847
  }
@@ -910,11 +922,13 @@ contract JBOmnichainDeployer is
910
922
 
911
923
  // Store the 721 hook config per-ruleset.
912
924
  // slither-disable-next-line reentrancy-benign
925
+ // forge-lint: disable-next-line(block-timestamp)
913
926
  _tiered721HookOf[projectId][block.timestamp + i] =
914
927
  JBTiered721HookConfig({hook: hook721, useDataHookForCashOut: use721ForCashOut});
915
928
 
916
929
  // Store custom hook from metadata (same as _setup).
917
930
  if (rulesetConfigurations[i].metadata.dataHook != address(0)) {
931
+ // forge-lint: disable-next-line(block-timestamp)
918
932
  _extraDataHookOf[projectId][block.timestamp + i] = JBDeployerHookConfig({
919
933
  dataHook: IJBRulesetDataHook(rulesetConfigurations[i].metadata.dataHook),
920
934
  useDataHookForPay: rulesetConfigurations[i].metadata.useDataHookForPay,
@@ -100,6 +100,7 @@ interface IJBOmnichainDeployer {
100
100
 
101
101
  /// @notice Launches new rulesets for a project with a 721 tiers hook attached.
102
102
  /// @param projectId The ID of the project to launch the rulesets for.
103
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
103
104
  /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt).
104
105
  /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
105
106
  /// @param terminalConfigurations The terminals to set up for the project.
@@ -109,6 +110,7 @@ interface IJBOmnichainDeployer {
109
110
  /// @return hook The 721 tiers hook that was deployed for the project.
110
111
  function launchRulesetsFor(
111
112
  uint256 projectId,
113
+ string calldata projectUri,
112
114
  JBOmnichain721Config memory deploy721Config,
113
115
  JBRulesetConfig[] memory rulesetConfigurations,
114
116
  JBTerminalConfig[] calldata terminalConfigurations,
@@ -120,6 +122,7 @@ interface IJBOmnichainDeployer {
120
122
 
121
123
  /// @notice Launches new rulesets for a project with a default (empty-tier) 721 hook.
122
124
  /// @param projectId The ID of the project to launch the rulesets for.
125
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
123
126
  /// @param rulesetConfigurations The rulesets to launch.
124
127
  /// @param terminalConfigurations The terminals to set up for the project.
125
128
  /// @param memo A memo to pass along to the emitted event.
@@ -128,6 +131,7 @@ interface IJBOmnichainDeployer {
128
131
  /// @return hook The 721 tiers hook that was deployed for the project.
129
132
  function launchRulesetsFor(
130
133
  uint256 projectId,
134
+ string calldata projectUri,
131
135
  JBRulesetConfig[] memory rulesetConfigurations,
132
136
  JBTerminalConfig[] calldata terminalConfigurations,
133
137
  string calldata memo,
package/ADMINISTRATION.md DELETED
@@ -1,29 +0,0 @@
1
- # Administration
2
-
3
- ## At A Glance
4
-
5
- | Item | Details |
6
- | --- | --- |
7
- | Scope | Omnichain launch orchestration and wrapper behavior |
8
- | Control posture | Mixed deployer logic, project permissions, and registry trust |
9
- | Highest-risk actions | Wrong hook composition, wrong sucker wiring, and bad registry trust |
10
- | Recovery posture | Often requires redeploying or re-launching around bad wiring |
11
-
12
- ## Purpose
13
-
14
- This repo controls how omnichain projects are launched and wrapped, not the low-level runtime logic of suckers or 721 hooks.
15
-
16
- ## Control Model
17
-
18
- - launch paths are largely permissionless for new projects
19
- - later ruleset changes depend on project permissions
20
- - registry and sucker trust surfaces can widen authority if misconfigured
21
-
22
- ## Recovery
23
-
24
- - bad launch wiring usually means a new deployment path rather than a local patch
25
-
26
- ## Admin Boundaries
27
-
28
- - this repo does not override locked runtime behavior in sibling repos
29
-
package/ARCHITECTURE.md DELETED
@@ -1,31 +0,0 @@
1
- # Architecture
2
-
3
- ## Purpose
4
-
5
- `nana-omnichain-deployers-v6` packages a Juicebox project, a 721 hook, and sucker deployment into one omnichain launch surface.
6
-
7
- ## System Overview
8
-
9
- `JBOmnichainDeployer` launches the project, stores per-ruleset hook composition, and wraps sucker behavior so bridge-triggered flows can bypass project-specific logic where intended.
10
-
11
- ## Core Invariants
12
-
13
- - launch wiring must match the intended omnichain project shape
14
- - hook composition must stay consistent with the created ruleset IDs
15
- - sucker-specific privileged paths must remain limited to trusted suckers
16
- - project NFT ownership and hook ownership must end in the intended place
17
-
18
- ## Trust Boundaries
19
-
20
- - bridge runtime trust lives in `nana-suckers-v6`
21
- - 721 runtime trust lives in `nana-721-hook-v6`
22
- - this repo mainly owns orchestration and wrapper semantics
23
-
24
- ## Security Model
25
-
26
- - the main risks are hook composition, ruleset ID prediction, and registry-trusted sucker bypasses
27
- - this repo is not the source of underlying bridge or 721 behavior, but it can wire them together incorrectly
28
-
29
- ## Source Map
30
-
31
- - `src/JBOmnichainDeployer.sol`
@@ -1,29 +0,0 @@
1
- # Audit Instructions
2
-
3
- Audit this repo as an orchestration layer that composes 721 hooks, suckers, and optional extra data hooks.
4
-
5
- ## Audit Objective
6
-
7
- Find issues that:
8
-
9
- - launch the wrong project shape
10
- - miscompose hooks or wrapper behavior
11
- - grant privileged sucker behavior to the wrong addresses
12
- - create cross-chain drift or bad deterministic assumptions
13
-
14
- ## Scope
15
-
16
- In scope:
17
-
18
- - `src/JBOmnichainDeployer.sol`
19
- - related tests under `test/`
20
-
21
- ## Start Here
22
-
23
- 1. `src/JBOmnichainDeployer.sol`
24
-
25
- ## Verification
26
-
27
- - `npm install`
28
- - `forge build`
29
- - `forge test`
package/RISKS.md DELETED
@@ -1,97 +0,0 @@
1
- # Omnichain Deployers Risk Register
2
-
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
-
5
- ## How To Use This File
6
-
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.
9
-
10
- ## Priority Risks
11
-
12
- | Priority | Risk | Why it matters | Primary controls |
13
- |----------|------|----------------|------------------|
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. |
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. |
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. |
17
-
18
- ## 1. Trust Assumptions
19
-
20
- - **Trusted forwarder is trusted.**
21
- - **Sucker registry answers are trusted.**
22
- - **Controller trust matters.**
23
- - **Extra data hooks are trusted code.**
24
-
25
- ## 2. Economic Risks
26
-
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.**
31
-
32
- ## 3. Access Control
33
-
34
- - **Wildcard `MAP_SUCKER_TOKEN` permission is broad.**
35
- - **`launchRulesetsFor` requires combined permissions.**
36
- - **`launchProjectFor` is intentionally permissionless for new projects.**
37
-
38
- ## 4. DoS Vectors
39
-
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.**
44
-
45
- ## 5. Reentrancy Surface
46
-
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.
51
-
52
- ## 6. Integration Risks
53
-
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.**
58
-
59
- ## 7. Invariants To Verify
60
-
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
66
-
67
- ## 8. Accepted Behaviors
68
-
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.
85
-
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.
88
-
89
- ### Hook Selection
90
-
91
- **ApprovalExpected rulesets excluded from hook carry-forward.**
92
- When no new tiers are provided, the deployer carries forward the 721 hook from the most recent approved ruleset. Rulesets with `ApprovalExpected` status are intentionally excluded even though they may become active. Hook selection is irreversible — if the pending ruleset is later rejected by the approval hook, we'd have locked in a hook from a ruleset that never became active. The deployer falls back to the current (already-approved) ruleset in this case.
93
-
94
- ### Cross-Chain Deployment
95
-
96
- **`_msgSender()` in deployment salt breaks cross-chain determinism.** *(Minor)*
97
- `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/SKILLS.md DELETED
@@ -1,25 +0,0 @@
1
- # Juicebox Omnichain Deployers
2
-
3
- ## Use This File For
4
-
5
- - Use this file when the task involves omnichain project launch, sucker deployment, or wrapped 721-hook composition.
6
- - Start here, then decide whether the issue is in launch orchestration, hook composition, or bridge-specific runtime behavior.
7
-
8
- ## Read This Next
9
-
10
- | If you need... | Open this next |
11
- |---|---|
12
- | Repo overview and architecture | [`README.md`](./README.md), [`ARCHITECTURE.md`](./ARCHITECTURE.md) |
13
- | Main deployer | [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol) |
14
- | Bridge runtime | [`../nana-suckers-v6/src/JBSucker.sol`](../nana-suckers-v6/src/JBSucker.sol) |
15
- | 721 hook runtime | [`../nana-721-hook-v6/src/JB721TiersHook.sol`](../nana-721-hook-v6/src/JB721TiersHook.sol) |
16
-
17
- ## Purpose
18
-
19
- Orchestration and wrapper layer for launching projects with suckers and a 721 hook already wired in.
20
-
21
- ## Working Rules
22
-
23
- - Start in [`src/JBOmnichainDeployer.sol`](./src/JBOmnichainDeployer.sol).
24
- - Treat ruleset ID prediction as a real implementation dependency.
25
- - Keep wrapper behavior and the underlying 721 or sucker behavior separate in your reasoning.