@bananapus/omnichain-deployers-v6 0.0.31 → 0.0.35

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 (51) 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 +72 -37
  6. package/src/interfaces/IJBOmnichainDeployer.sol +8 -1
  7. package/src/structs/JBDeployerHookConfig.sol +5 -0
  8. package/src/structs/JBTiered721HookConfig.sol +4 -0
  9. package/ADMINISTRATION.md +0 -29
  10. package/ARCHITECTURE.md +0 -31
  11. package/AUDIT_INSTRUCTIONS.md +0 -29
  12. package/RISKS.md +0 -97
  13. package/SKILLS.md +0 -25
  14. package/STYLE_GUIDE.md +0 -610
  15. package/USER_JOURNEYS.md +0 -63
  16. package/foundry.lock +0 -11
  17. package/slither-ci.config.json +0 -10
  18. package/sphinx.lock +0 -491
  19. package/test/JBOmnichainDeployer.t.sol +0 -639
  20. package/test/JBOmnichainDeployerGuard.t.sol +0 -404
  21. package/test/OmnichainDeployerAttacks.t.sol +0 -432
  22. package/test/OmnichainDeployerEdgeCases.t.sol +0 -827
  23. package/test/OmnichainDeployerReentrancy.t.sol +0 -321
  24. package/test/TestAuditGaps.sol +0 -996
  25. package/test/Tiered721HookComposition.t.sol +0 -909
  26. package/test/audit/AuditFixesC2H6M14.t.sol +0 -480
  27. package/test/audit/CarryForwardRejectedHook.t.sol +0 -328
  28. package/test/audit/CashOutCountPropagation.t.sol +0 -232
  29. package/test/audit/CashOutSpecMerge.t.sol +0 -372
  30. package/test/audit/DeterministicDrift.t.sol +0 -78
  31. package/test/audit/DeterministicPeerDrift.t.sol +0 -79
  32. package/test/audit/ExtraCashOutHookZeroReclaim.t.sol +0 -340
  33. package/test/audit/ForwardedPermissions.t.sol +0 -297
  34. package/test/audit/JBOmnichainDeployer.t.sol +0 -321
  35. package/test/audit/NftCashoutSupplyMismatch.t.sol +0 -224
  36. package/test/audit/OmnichainAudit.t.sol +0 -168
  37. package/test/audit/SplitCreditWeight.t.sol +0 -437
  38. package/test/audit/WeightScalingComparison.t.sol +0 -350
  39. package/test/fork/OmnichainForkTestBase.sol +0 -535
  40. package/test/fork/TestOmnichain721QueueAndAdjust.t.sol +0 -252
  41. package/test/fork/TestOmnichainCashOutFork.t.sol +0 -184
  42. package/test/fork/TestOmnichainStressFork.t.sol +0 -552
  43. package/test/fork/TestOmnichainWeightFork.t.sol +0 -60
  44. package/test/fork/TestSuckerDeploymentFork.t.sol +0 -229
  45. package/test/invariants/CrossChainDeployerInvariant.t.sol +0 -208
  46. package/test/invariants/OmnichainDeployerInvariant.t.sol +0 -150
  47. package/test/invariants/handlers/CrossChainDeployerHandler.sol +0 -394
  48. package/test/invariants/handlers/OmnichainDeployerHandler.sol +0 -262
  49. package/test/regression/EmptyRulesetConfigurations.t.sol +0 -84
  50. package/test/regression/HookOwnershipTransfer.t.sol +0 -152
  51. 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.35",
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
  }
@@ -32,11 +32,17 @@ import {JBSuckerDeploymentConfig} from "./structs/JBSuckerDeploymentConfig.sol";
32
32
  import {JBTiered721HookConfig} from "./structs/JBTiered721HookConfig.sol";
33
33
  import {mulDiv} from "@prb/math/src/Common.sol";
34
34
 
35
- /// @notice Deploys, manages, and operates Juicebox projects with suckers.
36
- // Project NFTs sent to this contract are not recoverable. The deployer does not
37
- // implement any NFT rescue mechanism beyond onERC721Received for JBProjects. This is acceptable
38
- // because the deployer should never own project NFTs it creates projects and transfers ownership
39
- // in the same transaction.
35
+ /// @notice One-stop deployer and data hook wrapper for omnichain Juicebox projects. Launches a project with a tiered
36
+ /// 721 hook and cross-chain suckers in a single transaction, then inserts itself as every ruleset's data hook so it can
37
+ /// coordinate between the 721 hook, an optional extra hook (e.g. buyback), and the sucker registry at pay/cash-out
38
+ /// time. At pay time it merges weight and hook specifications from both the 721 hook and the extra hook. At cash-out
39
+ /// time it
40
+ /// computes cross-chain total supply and surplus (so the bonding curve reflects all chains), grants suckers 0% cash-out
41
+ /// tax, and delegates tax-rate adjustments to the underlying hooks.
42
+ /// @dev Project NFTs sent to this contract are not recoverable. The deployer does not implement any NFT rescue
43
+ /// mechanism beyond `onERC721Received` for `JBProjects`. This is acceptable because the deployer should never own
44
+ /// project NFTs —
45
+ /// it creates projects and transfers ownership in the same transaction.
40
46
  contract JBOmnichainDeployer is
41
47
  ERC2771Context,
42
48
  JBPermissioned,
@@ -142,10 +148,12 @@ contract JBOmnichainDeployer is
142
148
  // ---------------------- external transactions ---------------------- //
143
149
  //*********************************************************************//
144
150
 
145
- /// @notice Deploy new suckers for an existing project.
146
- /// @dev Only the juicebox's owner or an operator with `JBPermissionIds.DEPLOY_SUCKERS` can call this entrypoint.
147
- /// The downstream registry call also maps the configured tokens on each newly created sucker, so the same
148
- /// end-to-end operation depends on the project's token-mapping authority being arranged for the registry.
151
+ /// @notice Deploy new cross-chain suckers for an existing project. Each sucker enables token bridging between this
152
+ /// chain and a peer chain. The registry also maps configured tokens on each new sucker in the same call.
153
+ /// @dev Only the project's owner or an operator with `JBPermissionIds.DEPLOY_SUCKERS` can call this. The salt
154
+ /// includes `msg.sender` for replay protection — the same sender must call on both chains for deterministic
155
+ /// address
156
+ /// matching.
149
157
  /// @param projectId The ID of the project to deploy suckers for.
150
158
  /// @param suckerDeploymentConfiguration The suckers to set up for the project.
151
159
  function deploySuckersFor(
@@ -252,6 +260,7 @@ contract JBOmnichainDeployer is
252
260
  /// @notice Launches new rulesets for a project with a 721 tiers hook attached, using this contract as the data
253
261
  /// hook.
254
262
  /// @param projectId The ID of the project to launch the rulesets for.
263
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
255
264
  /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt).
256
265
  /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
257
266
  /// @param terminalConfigurations The terminals to set up for the project.
@@ -261,6 +270,7 @@ contract JBOmnichainDeployer is
261
270
  /// @return hook The 721 tiers hook that was deployed for the project.
262
271
  function launchRulesetsFor(
263
272
  uint256 projectId,
273
+ string calldata projectUri,
264
274
  JBOmnichain721Config memory deploy721Config,
265
275
  JBRulesetConfig[] memory rulesetConfigurations,
266
276
  JBTerminalConfig[] calldata terminalConfigurations,
@@ -273,6 +283,7 @@ contract JBOmnichainDeployer is
273
283
  {
274
284
  return _launchRulesetsFor({
275
285
  projectId: projectId,
286
+ projectUri: projectUri,
276
287
  deploy721Config: deploy721Config,
277
288
  rulesetConfigurations: rulesetConfigurations,
278
289
  terminalConfigurations: terminalConfigurations,
@@ -284,6 +295,7 @@ contract JBOmnichainDeployer is
284
295
  /// @notice Launches new rulesets for a project with a default (empty-tier) 721 hook.
285
296
  /// @dev Uses `baseCurrency` from the first ruleset and `decimals = 18` for the default 721 config.
286
297
  /// @param projectId The ID of the project to launch the rulesets for.
298
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
287
299
  /// @param rulesetConfigurations The rulesets to launch.
288
300
  /// @param terminalConfigurations The terminals to set up for the project.
289
301
  /// @param memo A memo to pass along to the emitted event.
@@ -292,6 +304,7 @@ contract JBOmnichainDeployer is
292
304
  /// @return hook The 721 tiers hook that was deployed for the project.
293
305
  function launchRulesetsFor(
294
306
  uint256 projectId,
307
+ string calldata projectUri,
295
308
  JBRulesetConfig[] memory rulesetConfigurations,
296
309
  JBTerminalConfig[] calldata terminalConfigurations,
297
310
  string calldata memo,
@@ -303,6 +316,7 @@ contract JBOmnichainDeployer is
303
316
  {
304
317
  return _launchRulesetsFor({
305
318
  projectId: projectId,
319
+ projectUri: projectUri,
306
320
  deploy721Config: _default721Config(rulesetConfigurations),
307
321
  rulesetConfigurations: rulesetConfigurations,
308
322
  terminalConfigurations: terminalConfigurations,
@@ -382,8 +396,13 @@ contract JBOmnichainDeployer is
382
396
  // ------------------------- external views -------------------------- //
383
397
  //*********************************************************************//
384
398
 
385
- /// @notice Allow cash outs from suckers without a tax, and compute cross-chain tax supply for non-sucker cash outs.
386
- /// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a cash out.
399
+ /// @notice Called by the terminal before recording a cash out. Suckers get 0% tax so bridged tokens redeem at face
400
+ /// value. For all other holders, this function aggregates total supply and surplus across all peer chains so the
401
+ /// bonding curve reflects the project's global state, then delegates to the 721 hook and extra hook for further
402
+ /// adjustments.
403
+ /// @dev Part of `IJBRulesetDataHook`. The 721 hook's returned `totalSupply` and `effectiveSurplusValue` are used
404
+ /// when it handles cash outs (NFT redemptions use local denominators). Otherwise this contract's cross-chain values
405
+ /// take precedence.
387
406
  /// @param context Standard Juicebox cash out context. See `JBBeforeCashOutRecordedContext`.
388
407
  /// @return cashOutTaxRate The cash out tax rate, which influences the amount of terminal tokens which get cashed
389
408
  /// out.
@@ -497,8 +516,13 @@ contract JBOmnichainDeployer is
497
516
  return (cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, hookSpecifications);
498
517
  }
499
518
 
500
- /// @notice Forward the call to the original data hook.
501
- /// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a payment.
519
+ /// @notice Called by the terminal before recording a payment. Coordinates the 721 hook (which handles tier-based
520
+ /// NFT minting and split deductions) with the extra hook (e.g. buyback, which may swap for a better token price).
521
+ /// Merges
522
+ /// their weight adjustments and hook specifications into a single response for the terminal.
523
+ /// @dev Part of `IJBRulesetDataHook`. The 721 hook's weight already accounts for tier-split deductions. The extra
524
+ /// hook receives the post-split amount so it only routes funds actually entering the project. If both return
525
+ /// specifications, the 721 spec comes first.
502
526
  /// @param context Standard Juicebox payment context. See `JBBeforePayRecordedContext`.
503
527
  /// @return weight The weight which project tokens are minted relative to. This can be used to customize how many
504
528
  /// tokens get minted by a payment.
@@ -618,8 +642,9 @@ contract JBOmnichainDeployer is
618
642
  return _extraDataHookOf[projectId][rulesetId];
619
643
  }
620
644
 
621
- /// @notice A flag indicating whether an address has permission to mint a project's tokens on-demand.
622
- /// @dev A project's data hook can allow any address to mint its tokens.
645
+ /// @notice Returns whether an address may mint a project's tokens on-demand. Suckers always get mint permission (so
646
+ /// bridged tokens can be minted on the destination chain). Otherwise delegates to the extra data hook.
647
+ /// @dev Part of `IJBRulesetDataHook`. The 721 hook never grants mint permission, so only the extra hook is checked.
623
648
  /// @param projectId The ID of the project whose token can be minted.
624
649
  /// @param ruleset The ruleset to check the token minting permission of.
625
650
  /// @param addr The address to check the token minting permission of.
@@ -720,11 +745,12 @@ contract JBOmnichainDeployer is
720
745
  internal
721
746
  returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers)
722
747
  {
723
- // Get the next project ID.
724
- projectId = PROJECTS.count() + 1;
748
+ // Reserve the project ID up front so permissionless project creations cannot invalidate hook deployment.
749
+ projectId = PROJECTS.createFor(address(this));
725
750
 
726
751
  // Deploy a 721 hook and set up rulesets.
727
752
  hook = _deploy721Hook({projectId: projectId, config: deploy721Config});
753
+ // slither-disable-next-line reentrancy-benign
728
754
  rulesetConfigurations = _setup721({
729
755
  projectId: projectId,
730
756
  rulesetConfigurations: rulesetConfigurations,
@@ -732,18 +758,16 @@ contract JBOmnichainDeployer is
732
758
  use721ForCashOut: deploy721Config.useDataHookForCashOut
733
759
  });
734
760
 
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();
761
+ // Launch the rulesets for the reserved project.
762
+ // slither-disable-start unused-return
763
+ controller.launchRulesetsFor({
764
+ projectId: projectId,
765
+ projectUri: projectUri,
766
+ rulesetConfigurations: rulesetConfigurations,
767
+ terminalConfigurations: terminalConfigurations,
768
+ memo: memo
769
+ });
770
+ // slither-disable-end unused-return
747
771
 
748
772
  // Transfer the hook's ownership to the project (now that the project NFT has been minted).
749
773
  JBOwnable(address(hook)).transferOwnershipToProject(projectId);
@@ -766,6 +790,7 @@ contract JBOmnichainDeployer is
766
790
  /// @notice Internal implementation of `launchRulesetsFor`.
767
791
  function _launchRulesetsFor(
768
792
  uint256 projectId,
793
+ string calldata projectUri,
769
794
  JBOmnichain721Config memory deploy721Config,
770
795
  JBRulesetConfig[] memory rulesetConfigurations,
771
796
  JBTerminalConfig[] calldata terminalConfigurations,
@@ -775,15 +800,19 @@ contract JBOmnichainDeployer is
775
800
  internal
776
801
  returns (uint256 rulesetId, IJB721TiersHook hook)
777
802
  {
803
+ address owner = PROJECTS.ownerOf(projectId);
804
+
778
805
  // Enforce permissions. Use LAUNCH_RULESETS (not QUEUE_RULESETS) because this function calls
779
806
  // 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
- });
807
+ _requirePermissionFrom({account: owner, projectId: projectId, permissionId: JBPermissionIds.LAUNCH_RULESETS});
783
808
 
784
- _requirePermissionFrom({
785
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
786
- });
809
+ _requirePermissionFrom({account: owner, projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS});
810
+
811
+ if (bytes(projectUri).length != 0) {
812
+ _requirePermissionFrom({
813
+ account: owner, projectId: projectId, permissionId: JBPermissionIds.SET_PROJECT_URI
814
+ });
815
+ }
787
816
 
788
817
  // Validate that the controller matches the project's controller in the directory.
789
818
  _validateController({projectId: projectId, controller: controller});
@@ -802,6 +831,7 @@ contract JBOmnichainDeployer is
802
831
  // Configure the rulesets.
803
832
  rulesetId = controller.launchRulesetsFor({
804
833
  projectId: projectId,
834
+ projectUri: projectUri,
805
835
  rulesetConfigurations: rulesetConfigurations,
806
836
  terminalConfigurations: terminalConfigurations,
807
837
  memo: memo
@@ -830,6 +860,7 @@ contract JBOmnichainDeployer is
830
860
  // Revert if the project already had rulesets queued in this block, which would make our
831
861
  // `block.timestamp + i` ruleset ID prediction incorrect.
832
862
  uint256 latestRulesetId = controller.RULESETS().latestRulesetIdOf(projectId);
863
+ // forge-lint: disable-next-line(block-timestamp)
833
864
  if (latestRulesetId >= block.timestamp) {
834
865
  revert JBOmnichainDeployer_RulesetIdsUnpredictable();
835
866
  }
@@ -887,9 +918,11 @@ contract JBOmnichainDeployer is
887
918
  });
888
919
  }
889
920
 
890
- /// @notice Sets up a project's rulesets with a 721 hook.
921
+ /// @notice Wires up each ruleset so this contract acts as the data hook wrapper. Stores the 721 hook and any extra
922
+ /// data hook (from the ruleset's metadata) in per-project/per-ruleset mappings, then overwrites each ruleset's
923
+ /// metadata to point at this contract with both pay and cash-out delegation enabled.
891
924
  /// @dev Stores the 721 hook in `_tiered721HookOf` per-ruleset and any custom hook (from metadata) in
892
- /// `_extraDataHookOf`.
925
+ /// `_extraDataHookOf`. Ruleset IDs are predicted as `block.timestamp + i`.
893
926
  /// @param projectId The ID of the project to set up.
894
927
  /// @param rulesetConfigurations The rulesets to set up.
895
928
  /// @param hook721 The 721 tiers hook.
@@ -910,11 +943,13 @@ contract JBOmnichainDeployer is
910
943
 
911
944
  // Store the 721 hook config per-ruleset.
912
945
  // slither-disable-next-line reentrancy-benign
946
+ // forge-lint: disable-next-line(block-timestamp)
913
947
  _tiered721HookOf[projectId][block.timestamp + i] =
914
948
  JBTiered721HookConfig({hook: hook721, useDataHookForCashOut: use721ForCashOut});
915
949
 
916
950
  // Store custom hook from metadata (same as _setup).
917
951
  if (rulesetConfigurations[i].metadata.dataHook != address(0)) {
952
+ // forge-lint: disable-next-line(block-timestamp)
918
953
  _extraDataHookOf[projectId][block.timestamp + i] = JBDeployerHookConfig({
919
954
  dataHook: IJBRulesetDataHook(rulesetConfigurations[i].metadata.dataHook),
920
955
  useDataHookForPay: rulesetConfigurations[i].metadata.useDataHookForPay,
@@ -9,7 +9,10 @@ import {JBDeployerHookConfig} from "../structs/JBDeployerHookConfig.sol";
9
9
  import {JBOmnichain721Config} from "../structs/JBOmnichain721Config.sol";
10
10
  import {JBSuckerDeploymentConfig} from "../structs/JBSuckerDeploymentConfig.sol";
11
11
 
12
- /// @notice Deploys Juicebox projects with omnichain sucker support.
12
+ /// @notice Interface for the omnichain deployer — a one-stop contract that launches Juicebox projects with a tiered
13
+ /// 721
14
+ /// hook and cross-chain suckers, then serves as the data hook wrapper that coordinates pay/cash-out logic across all
15
+ /// chains.
13
16
  interface IJBOmnichainDeployer {
14
17
  /// @notice Get the extra data hook for a project and ruleset.
15
18
  /// @param projectId The ID of the project to get the extra data hook for.
@@ -100,6 +103,7 @@ interface IJBOmnichainDeployer {
100
103
 
101
104
  /// @notice Launches new rulesets for a project with a 721 tiers hook attached.
102
105
  /// @param projectId The ID of the project to launch the rulesets for.
106
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
103
107
  /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt).
104
108
  /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
105
109
  /// @param terminalConfigurations The terminals to set up for the project.
@@ -109,6 +113,7 @@ interface IJBOmnichainDeployer {
109
113
  /// @return hook The 721 tiers hook that was deployed for the project.
110
114
  function launchRulesetsFor(
111
115
  uint256 projectId,
116
+ string calldata projectUri,
112
117
  JBOmnichain721Config memory deploy721Config,
113
118
  JBRulesetConfig[] memory rulesetConfigurations,
114
119
  JBTerminalConfig[] calldata terminalConfigurations,
@@ -120,6 +125,7 @@ interface IJBOmnichainDeployer {
120
125
 
121
126
  /// @notice Launches new rulesets for a project with a default (empty-tier) 721 hook.
122
127
  /// @param projectId The ID of the project to launch the rulesets for.
128
+ /// @param projectUri The project's metadata URI. Pass an empty string to leave it unchanged.
123
129
  /// @param rulesetConfigurations The rulesets to launch.
124
130
  /// @param terminalConfigurations The terminals to set up for the project.
125
131
  /// @param memo A memo to pass along to the emitted event.
@@ -128,6 +134,7 @@ interface IJBOmnichainDeployer {
128
134
  /// @return hook The 721 tiers hook that was deployed for the project.
129
135
  function launchRulesetsFor(
130
136
  uint256 projectId,
137
+ string calldata projectUri,
131
138
  JBRulesetConfig[] memory rulesetConfigurations,
132
139
  JBTerminalConfig[] calldata terminalConfigurations,
133
140
  string calldata memo,
@@ -3,6 +3,11 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
5
5
 
6
+ /// @notice Configuration for an extra data hook (e.g. a buyback hook) that the omnichain deployer delegates to
7
+ /// alongside the primary 721 hook. Stored per project per ruleset.
8
+ /// @param dataHook The extra data hook contract to delegate to.
9
+ /// @param useDataHookForPay Whether to call this hook's `beforePayRecordedWith` during payments.
10
+ /// @param useDataHookForCashOut Whether to call this hook's `beforeCashOutRecordedWith` during cash outs.
6
11
  struct JBDeployerHookConfig {
7
12
  IJBRulesetDataHook dataHook;
8
13
  bool useDataHookForPay;
@@ -3,6 +3,10 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
5
5
 
6
+ /// @notice Stored configuration for a project's tiered 721 hook within a specific ruleset.
7
+ /// @param hook The tiered 721 hook contract used for NFT minting on payments.
8
+ /// @param useDataHookForCashOut Whether the 721 hook should participate in cash-out tax calculations and NFT
9
+ /// redemptions.
6
10
  struct JBTiered721HookConfig {
7
11
  IJB721TiersHook hook;
8
12
  bool useDataHookForCashOut;
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.