@bananapus/omnichain-deployers-v6 0.0.45 → 0.0.47

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/package.json CHANGED
@@ -1,18 +1,16 @@
1
1
  {
2
2
  "name": "@bananapus/omnichain-deployers-v6",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
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
9
  "files": [
10
- "CHANGELOG.md",
11
10
  "foundry.toml",
12
11
  "references/",
13
12
  "remappings.txt",
14
- "script/Deploy.s.sol",
15
- "script/helpers/",
13
+ "script/",
16
14
  "src/"
17
15
  ],
18
16
  "engines": {
@@ -26,16 +24,16 @@
26
24
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
27
25
  },
28
26
  "dependencies": {
29
- "@bananapus/721-hook-v6": "^0.0.51",
30
- "@bananapus/core-v6": "^0.0.54",
31
- "@bananapus/ownable-v6": "^0.0.25",
32
- "@bananapus/permission-ids-v6": "^0.0.25",
33
- "@bananapus/suckers-v6": "^0.0.47",
27
+ "@bananapus/721-hook-v6": "^0.0.54",
28
+ "@bananapus/core-v6": "^0.0.57",
29
+ "@bananapus/ownable-v6": "^0.0.27",
30
+ "@bananapus/permission-ids-v6": "^0.0.26",
31
+ "@bananapus/suckers-v6": "^0.0.49",
34
32
  "@openzeppelin/contracts": "5.6.1"
35
33
  },
36
34
  "devDependencies": {
37
- "@bananapus/address-registry-v6": "^0.0.25",
38
- "@bananapus/buyback-hook-v6": "^0.0.47",
35
+ "@bananapus/address-registry-v6": "^0.0.26",
36
+ "@bananapus/buyback-hook-v6": "^0.0.49",
39
37
  "@sphinx-labs/plugins": "0.33.3",
40
38
  "@uniswap/v4-core": "1.0.2"
41
39
  }
@@ -8,7 +8,6 @@ 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";
12
11
  import {JBOmnichainDeployer} from "src/JBOmnichainDeployer.sol";
13
12
 
14
13
  contract Deploy is Script, Sphinx {
@@ -62,21 +61,15 @@ contract Deploy is Script, Sphinx {
62
61
  salt: NANA_OMNICHAIN_DEPLOYER_SALT,
63
62
  creationCode: type(JBOmnichainDeployer).creationCode,
64
63
  arguments: abi.encode(
65
- suckers.registry,
66
- hook.hook_deployer,
67
- core.permissions,
68
- core.projects,
69
- core.directory,
70
- core.trustedForwarder
64
+ suckers.registry, hook.hookDeployer, core.permissions, core.controller, core.trustedForwarder
71
65
  ),
72
66
  deployer: safeAddress()
73
67
  })) {
74
68
  new JBOmnichainDeployer{salt: NANA_OMNICHAIN_DEPLOYER_SALT}({
75
69
  suckerRegistry: suckers.registry,
76
- hookDeployer: hook.hook_deployer,
70
+ hookDeployer: hook.hookDeployer,
77
71
  permissions: core.permissions,
78
- projects: core.projects,
79
- directory: IJBDirectory(address(core.directory)),
72
+ controller: core.controller,
80
73
  trustedForwarder: core.trustedForwarder
81
74
  });
82
75
  }
@@ -56,7 +56,7 @@ contract JBOmnichainDeployer is
56
56
  // --------------------------- custom errors ------------------------- //
57
57
  //*********************************************************************//
58
58
 
59
- /// @notice Thrown when the provided controller does not match the project's controller in the directory.
59
+ /// @notice Thrown when a project is not using this deployer's canonical controller.
60
60
  error JBOmnichainDeployer_ControllerMismatch(
61
61
  uint256 projectId, address expectedController, address actualController
62
62
  );
@@ -82,19 +82,21 @@ contract JBOmnichainDeployer is
82
82
  // --------------- public immutable stored properties ---------------- //
83
83
  //*********************************************************************//
84
84
 
85
- /// @notice Mints ERC-721s that represent Juicebox project ownership and transfers.
86
- IJBProjects public immutable PROJECTS;
85
+ /// @notice The canonical controller used for every project launch and ruleset queue.
86
+ IJBController public immutable override CONTROLLER;
87
+
88
+ /// @notice The directory used to confirm existing projects still use this deployer's canonical controller.
89
+ IJBDirectory public immutable DIRECTORY;
87
90
 
88
91
  /// @notice Deploys tiered ERC-721 hooks for projects.
89
92
  IJB721TiersHookDeployer public immutable HOOK_DEPLOYER;
90
93
 
94
+ /// @notice Mints ERC-721s that represent Juicebox project ownership and transfers.
95
+ IJBProjects public immutable PROJECTS;
96
+
91
97
  /// @notice Deploys and tracks suckers for projects.
92
98
  IJBSuckerRegistry public immutable SUCKER_REGISTRY;
93
99
 
94
- /// @notice The directory used to validate controllers. Stored as immutable to prevent a user-provided
95
- /// controller from returning a fake directory that confirms itself.
96
- IJBDirectory public immutable DIRECTORY;
97
-
98
100
  //*********************************************************************//
99
101
  // -------------------- internal stored properties ------------------- //
100
102
  //*********************************************************************//
@@ -116,24 +118,23 @@ contract JBOmnichainDeployer is
116
118
  /// @param suckerRegistry The registry to use for deploying and tracking each project's suckers.
117
119
  /// @param hookDeployer The deployer to use for project's tiered ERC-721 hooks.
118
120
  /// @param permissions The permissions to use for the contract.
119
- /// @param projects The projects to use for the contract.
120
- /// @param directory The directory used to validate controllers against a trusted source.
121
+ /// @param controller The controller to use for every project launch and ruleset queue.
121
122
  /// @param trustedForwarder The trusted forwarder for the ERC2771Context.
122
123
  constructor(
123
124
  IJBSuckerRegistry suckerRegistry,
124
125
  IJB721TiersHookDeployer hookDeployer,
125
126
  IJBPermissions permissions,
126
- IJBProjects projects,
127
- IJBDirectory directory,
127
+ IJBController controller,
128
128
  address trustedForwarder
129
129
  )
130
130
  JBPermissioned(permissions)
131
131
  ERC2771Context(trustedForwarder)
132
132
  {
133
- PROJECTS = projects;
133
+ CONTROLLER = controller;
134
+ PROJECTS = controller.PROJECTS();
134
135
  SUCKER_REGISTRY = suckerRegistry;
135
136
  HOOK_DEPLOYER = hookDeployer;
136
- DIRECTORY = directory;
137
+ DIRECTORY = controller.DIRECTORY();
137
138
 
138
139
  // Give the sucker registry permission to map tokens for all revnets.
139
140
  uint8[] memory permissionIds = new uint8[](1);
@@ -193,7 +194,6 @@ contract JBOmnichainDeployer is
193
194
  /// @param memo A memo to pass along to the emitted event.
194
195
  /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
195
196
  /// token transfers between peer projects on different networks.
196
- /// @param controller The controller to use for launching the project.
197
197
  /// @return projectId The ID of the newly launched project.
198
198
  /// @return hook The 721 tiers hook that was deployed for the project.
199
199
  /// @return suckers The addresses of the deployed suckers.
@@ -204,8 +204,7 @@ contract JBOmnichainDeployer is
204
204
  JBRulesetConfig[] memory rulesetConfigurations,
205
205
  JBTerminalConfig[] calldata terminalConfigurations,
206
206
  string calldata memo,
207
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
208
- IJBController controller
207
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration
209
208
  )
210
209
  external
211
210
  override
@@ -218,8 +217,7 @@ contract JBOmnichainDeployer is
218
217
  rulesetConfigurations: rulesetConfigurations,
219
218
  terminalConfigurations: terminalConfigurations,
220
219
  memo: memo,
221
- suckerDeploymentConfiguration: suckerDeploymentConfiguration,
222
- controller: controller
220
+ suckerDeploymentConfiguration: suckerDeploymentConfiguration
223
221
  });
224
222
  }
225
223
 
@@ -231,7 +229,6 @@ contract JBOmnichainDeployer is
231
229
  /// @param terminalConfigurations The terminals to set up for the project.
232
230
  /// @param memo A memo to pass along to the emitted event.
233
231
  /// @param suckerDeploymentConfiguration The suckers to set up for the project.
234
- /// @param controller The controller to use for launching the project.
235
232
  /// @return projectId The ID of the newly launched project.
236
233
  /// @return hook The 721 tiers hook that was deployed for the project.
237
234
  /// @return suckers The addresses of the deployed suckers.
@@ -241,8 +238,7 @@ contract JBOmnichainDeployer is
241
238
  JBRulesetConfig[] memory rulesetConfigurations,
242
239
  JBTerminalConfig[] calldata terminalConfigurations,
243
240
  string calldata memo,
244
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
245
- IJBController controller
241
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration
246
242
  )
247
243
  external
248
244
  override
@@ -255,8 +251,7 @@ contract JBOmnichainDeployer is
255
251
  rulesetConfigurations: rulesetConfigurations,
256
252
  terminalConfigurations: terminalConfigurations,
257
253
  memo: memo,
258
- suckerDeploymentConfiguration: suckerDeploymentConfiguration,
259
- controller: controller
254
+ suckerDeploymentConfiguration: suckerDeploymentConfiguration
260
255
  });
261
256
  }
262
257
 
@@ -268,7 +263,6 @@ contract JBOmnichainDeployer is
268
263
  /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
269
264
  /// @param terminalConfigurations The terminals to set up for the project.
270
265
  /// @param memo A memo to pass along to the emitted event.
271
- /// @param controller The controller to use for launching the rulesets.
272
266
  /// @return rulesetId The ID of the newly launched rulesets.
273
267
  /// @return hook The 721 tiers hook that was deployed for the project.
274
268
  function launchRulesetsFor(
@@ -277,8 +271,7 @@ contract JBOmnichainDeployer is
277
271
  JBOmnichain721Config memory deploy721Config,
278
272
  JBRulesetConfig[] memory rulesetConfigurations,
279
273
  JBTerminalConfig[] calldata terminalConfigurations,
280
- string calldata memo,
281
- IJBController controller
274
+ string calldata memo
282
275
  )
283
276
  external
284
277
  override
@@ -290,8 +283,7 @@ contract JBOmnichainDeployer is
290
283
  deploy721Config: deploy721Config,
291
284
  rulesetConfigurations: rulesetConfigurations,
292
285
  terminalConfigurations: terminalConfigurations,
293
- memo: memo,
294
- controller: controller
286
+ memo: memo
295
287
  });
296
288
  }
297
289
 
@@ -302,7 +294,6 @@ contract JBOmnichainDeployer is
302
294
  /// @param rulesetConfigurations The rulesets to launch.
303
295
  /// @param terminalConfigurations The terminals to set up for the project.
304
296
  /// @param memo A memo to pass along to the emitted event.
305
- /// @param controller The controller to use for launching the rulesets.
306
297
  /// @return rulesetId The ID of the newly launched rulesets.
307
298
  /// @return hook The 721 tiers hook that was deployed for the project.
308
299
  function launchRulesetsFor(
@@ -310,8 +301,7 @@ contract JBOmnichainDeployer is
310
301
  string calldata projectUri,
311
302
  JBRulesetConfig[] memory rulesetConfigurations,
312
303
  JBTerminalConfig[] calldata terminalConfigurations,
313
- string calldata memo,
314
- IJBController controller
304
+ string calldata memo
315
305
  )
316
306
  external
317
307
  override
@@ -323,8 +313,7 @@ contract JBOmnichainDeployer is
323
313
  deploy721Config: _default721Config(rulesetConfigurations),
324
314
  rulesetConfigurations: rulesetConfigurations,
325
315
  terminalConfigurations: terminalConfigurations,
326
- memo: memo,
327
- controller: controller
316
+ memo: memo
328
317
  });
329
318
  }
330
319
 
@@ -345,15 +334,13 @@ contract JBOmnichainDeployer is
345
334
  /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt).
346
335
  /// @param rulesetConfigurations The rulesets to queue. Custom data hooks are read from each ruleset's metadata.
347
336
  /// @param memo A memo to pass along to the emitted event.
348
- /// @param controller The controller to use for queuing the rulesets.
349
337
  /// @return rulesetId The ID of the newly queued rulesets.
350
338
  /// @return hook The 721 tiers hook (newly deployed or carried forward from the previous ruleset).
351
339
  function queueRulesetsOf(
352
340
  uint256 projectId,
353
341
  JBOmnichain721Config memory deploy721Config,
354
342
  JBRulesetConfig[] memory rulesetConfigurations,
355
- string calldata memo,
356
- IJBController controller
343
+ string calldata memo
357
344
  )
358
345
  external
359
346
  override
@@ -363,8 +350,7 @@ contract JBOmnichainDeployer is
363
350
  projectId: projectId,
364
351
  deploy721Config: deploy721Config,
365
352
  rulesetConfigurations: rulesetConfigurations,
366
- memo: memo,
367
- controller: controller
353
+ memo: memo
368
354
  });
369
355
  }
370
356
 
@@ -375,14 +361,12 @@ contract JBOmnichainDeployer is
375
361
  /// @param projectId The ID of the project to queue the rulesets for.
376
362
  /// @param rulesetConfigurations The rulesets to queue.
377
363
  /// @param memo A memo to pass along to the emitted event.
378
- /// @param controller The controller to use for queuing the rulesets.
379
364
  /// @return rulesetId The ID of the newly queued rulesets.
380
365
  /// @return hook The 721 tiers hook carried forward from the previous ruleset.
381
366
  function queueRulesetsOf(
382
367
  uint256 projectId,
383
368
  JBRulesetConfig[] memory rulesetConfigurations,
384
- string calldata memo,
385
- IJBController controller
369
+ string calldata memo
386
370
  )
387
371
  external
388
372
  override
@@ -392,8 +376,7 @@ contract JBOmnichainDeployer is
392
376
  projectId: projectId,
393
377
  deploy721Config: _default721Config(rulesetConfigurations),
394
378
  rulesetConfigurations: rulesetConfigurations,
395
- memo: memo,
396
- controller: controller
379
+ memo: memo
397
380
  });
398
381
  }
399
382
 
@@ -428,7 +411,8 @@ contract JBOmnichainDeployer is
428
411
  )
429
412
  {
430
413
  // If the cash out is from a sucker, bypass all taxes and fees.
431
- // Pass through the local surplus so the reclaim calculation can compute the pro-rata share.
414
+ // Sucker cash-outs are the bridge accounting path: the value moving out of this chain must stay proportional
415
+ // to this chain's local backing. Do not add remote supply/surplus here.
432
416
  if (SUCKER_REGISTRY.isSuckerOf({projectId: context.projectId, addr: context.holder})) {
433
417
  return (0, context.cashOutCount, context.totalSupply, context.surplus.value, hookSpecifications);
434
418
  }
@@ -457,8 +441,10 @@ contract JBOmnichainDeployer is
457
441
  // Look up the 721 hook configured for this project's ruleset.
458
442
  JBTiered721HookConfig memory tiered721Config = _tiered721HookOf[context.projectId][context.rulesetId];
459
443
 
444
+ bool hasTiered721CashOut = address(tiered721Config.hook) != address(0) && tiered721Config.useDataHookForCashOut;
445
+
460
446
  // If a 721 hook is set and opted into cash out handling, let it adjust the cash out parameters.
461
- if (address(tiered721Config.hook) != address(0) && tiered721Config.useDataHookForCashOut) {
447
+ if (hasTiered721CashOut) {
462
448
  // Forward to the 721 hook. It may change the tax rate, count, and return hook specs.
463
449
  // Capture the 721 hook's totalSupply and effectiveSurplusValue — NFT cash-outs should use
464
450
  // local-only denominators so holders reclaim against local surplus, not omnichain surplus.
@@ -473,7 +459,10 @@ contract JBOmnichainDeployer is
473
459
  JBDeployerHookConfig memory extraHook = _extraDataHookOf[context.projectId][context.rulesetId];
474
460
 
475
461
  // If an extra hook is set and opted into cash out handling, let it adjust the cash out parameters.
476
- if (address(extraHook.dataHook) != address(0) && extraHook.useDataHookForCashOut) {
462
+ // NFT cash-outs are excluded: the terminal later passes the original fungible burn count to after-hooks,
463
+ // while the 721 hook expresses pricing as NFT cash-out weight. Generic cash-out hooks cannot safely execute
464
+ // against that derived count.
465
+ if (!hasTiered721CashOut && address(extraHook.dataHook) != address(0) && extraHook.useDataHookForCashOut) {
477
466
  // Build a mutable copy of the context with the latest values (possibly updated by the 721 hook).
478
467
  JBBeforeCashOutRecordedContext memory hookContext = context;
479
468
  hookContext.cashOutTaxRate = cashOutTaxRate;
@@ -482,17 +471,10 @@ contract JBOmnichainDeployer is
482
471
  hookContext.surplus.value = effectiveSurplusValue;
483
472
 
484
473
  // Forward to the extra hook. It may further change the tax rate and return hook specs.
485
- // We always discard totalSupply and effectiveSurplusValue — this contract computes
486
- // cross-chain values for both. When the 721 hook is active, we also discard cashOutCount
487
- // because the 721 hook redefines both cashOutCount and totalSupply as NFT cash-out weights
488
- // (sum of tier prices), not fungible token counts. Letting the extra hook override
489
- // cashOutCount would corrupt NFT pricing in the bonding curve.
490
- if (address(tiered721Config.hook) != address(0) && tiered721Config.useDataHookForCashOut) {
491
- (cashOutTaxRate,,,, extraHookSpecifications) = extraHook.dataHook.beforeCashOutRecordedWith(hookContext);
492
- } else {
493
- (cashOutTaxRate, cashOutCount,,, extraHookSpecifications) =
494
- extraHook.dataHook.beforeCashOutRecordedWith(hookContext);
495
- }
474
+ // We always discard totalSupply and effectiveSurplusValue — this contract computes cross-chain values
475
+ // for both.
476
+ (cashOutTaxRate, cashOutCount,,, extraHookSpecifications) =
477
+ extraHook.dataHook.beforeCashOutRecordedWith(hookContext);
496
478
  }
497
479
 
498
480
  // If neither hook returned any specifications, return the adjusted values with no hook specs.
@@ -715,9 +697,8 @@ contract JBOmnichainDeployer is
715
697
  override
716
698
  returns (uint256 supply, uint256 surplus, uint256 balance)
717
699
  {
718
- // Get the current ruleset to look up the stored extra hook.
719
- IJBController controller = IJBController(address(DIRECTORY.controllerOf(projectId)));
720
- (JBRuleset memory ruleset,) = controller.currentRulesetOf(projectId);
700
+ // Get the current ruleset from the canonical controller to look up the stored extra hook.
701
+ (JBRuleset memory ruleset,) = CONTROLLER.currentRulesetOf(projectId);
721
702
 
722
703
  // Look up the extra data hook for this project's current ruleset.
723
704
  JBDeployerHookConfig memory extraHook = _extraDataHookOf[projectId][ruleset.id];
@@ -784,8 +765,7 @@ contract JBOmnichainDeployer is
784
765
  JBRulesetConfig[] memory rulesetConfigurations,
785
766
  JBTerminalConfig[] calldata terminalConfigurations,
786
767
  string calldata memo,
787
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
788
- IJBController controller
768
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration
789
769
  )
790
770
  internal
791
771
  returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers)
@@ -793,6 +773,9 @@ contract JBOmnichainDeployer is
793
773
  // Reserve the project ID up front so permissionless project creations cannot invalidate hook deployment.
794
774
  projectId = PROJECTS.createFor(address(this));
795
775
 
776
+ // A fresh project can start without a controller, but it must not already be assigned elsewhere.
777
+ _requireController({projectId: projectId, allowUnset: true});
778
+
796
779
  // Deploy a 721 hook and set up rulesets.
797
780
  hook = _deploy721Hook({projectId: projectId, config: deploy721Config});
798
781
  rulesetConfigurations = _setup721({
@@ -803,7 +786,7 @@ contract JBOmnichainDeployer is
803
786
  });
804
787
 
805
788
  // Launch the rulesets for the reserved project.
806
- controller.launchRulesetsFor({
789
+ CONTROLLER.launchRulesetsFor({
807
790
  projectId: projectId,
808
791
  projectUri: projectUri,
809
792
  rulesetConfigurations: rulesetConfigurations,
@@ -811,6 +794,9 @@ contract JBOmnichainDeployer is
811
794
  memo: memo
812
795
  });
813
796
 
797
+ // A fresh launch must leave the directory pointing at this deployer's canonical controller.
798
+ _requireController({projectId: projectId, allowUnset: false});
799
+
814
800
  // Transfer the hook's ownership to the project (now that the project NFT has been minted).
815
801
  JBOwnable(address(hook)).transferOwnershipToProject(projectId);
816
802
 
@@ -835,8 +821,7 @@ contract JBOmnichainDeployer is
835
821
  JBOmnichain721Config memory deploy721Config,
836
822
  JBRulesetConfig[] memory rulesetConfigurations,
837
823
  JBTerminalConfig[] calldata terminalConfigurations,
838
- string calldata memo,
839
- IJBController controller
824
+ string calldata memo
840
825
  )
841
826
  internal
842
827
  returns (uint256 rulesetId, IJB721TiersHook hook)
@@ -855,8 +840,8 @@ contract JBOmnichainDeployer is
855
840
  });
856
841
  }
857
842
 
858
- // Validate that the controller matches the project's controller in the directory.
859
- _validateController({projectId: projectId, controller: controller});
843
+ // Existing projects must still be controlled by this deployer's canonical controller.
844
+ _requireController({projectId: projectId, allowUnset: true});
860
845
 
861
846
  // Deploy a 721 hook, transfer its ownership to the project, and set up rulesets.
862
847
  hook = _deploy721Hook({projectId: projectId, config: deploy721Config});
@@ -869,13 +854,16 @@ contract JBOmnichainDeployer is
869
854
  });
870
855
 
871
856
  // Configure the rulesets.
872
- rulesetId = controller.launchRulesetsFor({
857
+ rulesetId = CONTROLLER.launchRulesetsFor({
873
858
  projectId: projectId,
874
859
  projectUri: projectUri,
875
860
  rulesetConfigurations: rulesetConfigurations,
876
861
  terminalConfigurations: terminalConfigurations,
877
862
  memo: memo
878
863
  });
864
+
865
+ // A blank project launch must leave the directory pointing at this deployer's canonical controller.
866
+ _requireController({projectId: projectId, allowUnset: false});
879
867
  }
880
868
 
881
869
  /// @notice Internal implementation of `queueRulesetsOf`.
@@ -883,8 +871,7 @@ contract JBOmnichainDeployer is
883
871
  uint256 projectId,
884
872
  JBOmnichain721Config memory deploy721Config,
885
873
  JBRulesetConfig[] memory rulesetConfigurations,
886
- string calldata memo,
887
- IJBController controller
874
+ string calldata memo
888
875
  )
889
876
  internal
890
877
  returns (uint256 rulesetId, IJB721TiersHook hook)
@@ -894,12 +881,12 @@ contract JBOmnichainDeployer is
894
881
  account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.QUEUE_RULESETS
895
882
  });
896
883
 
897
- // Validate that the controller matches the project's controller in the directory.
898
- _validateController({projectId: projectId, controller: controller});
884
+ // Existing projects must still be controlled by this deployer's canonical controller.
885
+ _requireController({projectId: projectId, allowUnset: true});
899
886
 
900
887
  // Revert if the project already had rulesets queued in this block, which would make our
901
888
  // `block.timestamp + i` ruleset ID prediction incorrect.
902
- uint256 latestRulesetId = controller.RULESETS().latestRulesetIdOf(projectId);
889
+ uint256 latestRulesetId = CONTROLLER.RULESETS().latestRulesetIdOf(projectId);
903
890
  // forge-lint: disable-next-line(block-timestamp)
904
891
  uint256 currentTimestamp = block.timestamp;
905
892
  if (latestRulesetId >= currentTimestamp) {
@@ -926,7 +913,7 @@ contract JBOmnichainDeployer is
926
913
  // excluded because hook selection is irreversible — if the pending ruleset is later rejected
927
914
  // by the approval hook, we'd have locked in a hook from a ruleset that never became active.
928
915
  (JBRuleset memory latestQueued, JBApprovalStatus approvalStatus) =
929
- controller.RULESETS().latestQueuedOf(projectId);
916
+ CONTROLLER.RULESETS().latestQueuedOf(projectId);
930
917
  if (
931
918
  latestQueued.id != 0
932
919
  && (approvalStatus == JBApprovalStatus.Approved || approvalStatus == JBApprovalStatus.Empty)
@@ -935,7 +922,7 @@ contract JBOmnichainDeployer is
935
922
  sourceRulesetId = latestQueued.id;
936
923
  } else {
937
924
  // Fall back to the current (active, approved) ruleset.
938
- sourceRulesetId = controller.RULESETS().currentOf(projectId).id;
925
+ sourceRulesetId = CONTROLLER.RULESETS().currentOf(projectId).id;
939
926
  }
940
927
  }
941
928
  JBTiered721HookConfig memory previousConfig = _tiered721HookOf[projectId][sourceRulesetId];
@@ -959,7 +946,7 @@ contract JBOmnichainDeployer is
959
946
  });
960
947
 
961
948
  // Configure the rulesets.
962
- rulesetId = controller.queueRulesetsOf({
949
+ rulesetId = CONTROLLER.queueRulesetsOf({
963
950
  projectId: projectId, rulesetConfigurations: rulesetConfigurations, memo: memo
964
951
  });
965
952
  }
@@ -1055,17 +1042,17 @@ contract JBOmnichainDeployer is
1055
1042
  return ERC2771Context._msgSender();
1056
1043
  }
1057
1044
 
1058
- /// @notice Validates that the provided controller matches the project's controller in the directory.
1059
- /// @dev Uses the immutable DIRECTORY instead of querying the controller, preventing a malicious
1060
- /// controller from returning a fake directory that confirms itself.
1061
- /// @param projectId The ID of the project to validate the controller for.
1062
- /// @param controller The controller to validate.
1063
- function _validateController(uint256 projectId, IJBController controller) internal view {
1045
+ /// @notice Revert unless the trusted directory records `CONTROLLER` for `projectId`.
1046
+ /// @dev Use `allowUnset = true` as a pre-launch check: a fresh project with no controller wired yet is accepted.
1047
+ /// Use `allowUnset = false` as a post-launch check: `CONTROLLER` must be live in the directory.
1048
+ /// @param projectId The ID of the project to check.
1049
+ /// @param allowUnset Whether `address(0)` (no controller assigned yet) is treated as valid.
1050
+ function _requireController(uint256 projectId, bool allowUnset) internal view {
1064
1051
  address current = address(DIRECTORY.controllerOf(projectId));
1065
- // Allow address(0) for fresh projects that haven't launched rulesets yet.
1066
- if (current != address(0) && current != address(controller)) {
1052
+ if (allowUnset && current == address(0)) return;
1053
+ if (current != address(CONTROLLER)) {
1067
1054
  revert JBOmnichainDeployer_ControllerMismatch({
1068
- projectId: projectId, expectedController: current, actualController: address(controller)
1055
+ projectId: projectId, expectedController: address(CONTROLLER), actualController: current
1069
1056
  });
1070
1057
  }
1071
1058
  }
@@ -14,6 +14,9 @@ import {JBSuckerDeploymentConfig} from "../structs/JBSuckerDeploymentConfig.sol"
14
14
  /// hook and cross-chain suckers, then serves as the data hook wrapper that coordinates pay/cash-out logic across all
15
15
  /// chains.
16
16
  interface IJBOmnichainDeployer {
17
+ /// @notice The controller used for every project launch and ruleset queue.
18
+ function CONTROLLER() external view returns (IJBController);
19
+
17
20
  /// @notice Get the extra data hook for a project and ruleset.
18
21
  /// @param projectId The ID of the project to get the extra data hook for.
19
22
  /// @param rulesetId The ID of the ruleset to get the extra data hook for.
@@ -61,7 +64,6 @@ interface IJBOmnichainDeployer {
61
64
  /// @param terminalConfigurations The terminals to set up for the project.
62
65
  /// @param memo A memo to pass along to the emitted event.
63
66
  /// @param suckerDeploymentConfiguration The suckers to set up for the project.
64
- /// @param controller The controller to use for launching the project.
65
67
  /// @return projectId The ID of the newly launched project.
66
68
  /// @return hook The 721 tiers hook that was deployed for the project.
67
69
  /// @return suckers The addresses of the deployed suckers.
@@ -72,8 +74,7 @@ interface IJBOmnichainDeployer {
72
74
  JBRulesetConfig[] memory rulesetConfigurations,
73
75
  JBTerminalConfig[] calldata terminalConfigurations,
74
76
  string calldata memo,
75
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
76
- IJBController controller
77
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration
77
78
  )
78
79
  external
79
80
  returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers);
@@ -85,7 +86,6 @@ interface IJBOmnichainDeployer {
85
86
  /// @param terminalConfigurations The terminals to set up for the project.
86
87
  /// @param memo A memo to pass along to the emitted event.
87
88
  /// @param suckerDeploymentConfiguration The suckers to set up for the project.
88
- /// @param controller The controller to use for launching the project.
89
89
  /// @return projectId The ID of the newly launched project.
90
90
  /// @return hook The 721 tiers hook that was deployed for the project.
91
91
  /// @return suckers The addresses of the deployed suckers.
@@ -95,8 +95,7 @@ interface IJBOmnichainDeployer {
95
95
  JBRulesetConfig[] memory rulesetConfigurations,
96
96
  JBTerminalConfig[] calldata terminalConfigurations,
97
97
  string calldata memo,
98
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
99
- IJBController controller
98
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration
100
99
  )
101
100
  external
102
101
  returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers);
@@ -108,7 +107,6 @@ interface IJBOmnichainDeployer {
108
107
  /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
109
108
  /// @param terminalConfigurations The terminals to set up for the project.
110
109
  /// @param memo A memo to pass along to the emitted event.
111
- /// @param controller The controller to use for launching the rulesets.
112
110
  /// @return rulesetId The ID of the newly launched rulesets.
113
111
  /// @return hook The 721 tiers hook that was deployed for the project.
114
112
  function launchRulesetsFor(
@@ -117,8 +115,7 @@ interface IJBOmnichainDeployer {
117
115
  JBOmnichain721Config memory deploy721Config,
118
116
  JBRulesetConfig[] memory rulesetConfigurations,
119
117
  JBTerminalConfig[] calldata terminalConfigurations,
120
- string calldata memo,
121
- IJBController controller
118
+ string calldata memo
122
119
  )
123
120
  external
124
121
  returns (uint256 rulesetId, IJB721TiersHook hook);
@@ -129,7 +126,6 @@ interface IJBOmnichainDeployer {
129
126
  /// @param rulesetConfigurations The rulesets to launch.
130
127
  /// @param terminalConfigurations The terminals to set up for the project.
131
128
  /// @param memo A memo to pass along to the emitted event.
132
- /// @param controller The controller to use for launching the rulesets.
133
129
  /// @return rulesetId The ID of the newly launched rulesets.
134
130
  /// @return hook The 721 tiers hook that was deployed for the project.
135
131
  function launchRulesetsFor(
@@ -137,8 +133,7 @@ interface IJBOmnichainDeployer {
137
133
  string calldata projectUri,
138
134
  JBRulesetConfig[] memory rulesetConfigurations,
139
135
  JBTerminalConfig[] calldata terminalConfigurations,
140
- string calldata memo,
141
- IJBController controller
136
+ string calldata memo
142
137
  )
143
138
  external
144
139
  returns (uint256 rulesetId, IJB721TiersHook hook);
@@ -150,15 +145,13 @@ interface IJBOmnichainDeployer {
150
145
  /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt).
151
146
  /// @param rulesetConfigurations The rulesets to queue. Custom data hooks are read from each ruleset's metadata.
152
147
  /// @param memo A memo to pass along to the emitted event.
153
- /// @param controller The controller to use for queuing the rulesets.
154
148
  /// @return rulesetId The ID of the newly queued rulesets.
155
149
  /// @return hook The 721 tiers hook (newly deployed or carried forward from the previous ruleset).
156
150
  function queueRulesetsOf(
157
151
  uint256 projectId,
158
152
  JBOmnichain721Config memory deploy721Config,
159
153
  JBRulesetConfig[] memory rulesetConfigurations,
160
- string calldata memo,
161
- IJBController controller
154
+ string calldata memo
162
155
  )
163
156
  external
164
157
  returns (uint256 rulesetId, IJB721TiersHook hook);
@@ -167,14 +160,12 @@ interface IJBOmnichainDeployer {
167
160
  /// @param projectId The ID of the project to queue the rulesets for.
168
161
  /// @param rulesetConfigurations The rulesets to queue.
169
162
  /// @param memo A memo to pass along to the emitted event.
170
- /// @param controller The controller to use for queuing the rulesets.
171
163
  /// @return rulesetId The ID of the newly queued rulesets.
172
164
  /// @return hook The 721 tiers hook carried forward from the previous ruleset.
173
165
  function queueRulesetsOf(
174
166
  uint256 projectId,
175
167
  JBRulesetConfig[] memory rulesetConfigurations,
176
- string calldata memo,
177
- IJBController controller
168
+ string calldata memo
178
169
  )
179
170
  external
180
171
  returns (uint256 rulesetId, IJB721TiersHook hook);
package/CHANGELOG.md DELETED
@@ -1,76 +0,0 @@
1
- # Changelog
2
-
3
- ## 0.0.43 — Bump v6 deps to nana-core-v6 0.0.53 cohort
4
-
5
- - `@bananapus/core-v6`: `^0.0.48 → ^0.0.53` ([PR #145](https://github.com/Bananapus/nana-core-v6/pull/145) — drops `via_ir` requirement on `JBCashOutHookSpecsLib`; new `pauseCrossProjectFeeFreeInflows` ruleset metadata flag for cross-project cashout opt-out).
6
- - `@bananapus/721-hook-v6`: `^0.0.47 → ^0.0.50`.
7
- - `@bananapus/suckers-v6`: `^0.0.37 → ^0.0.46`.
8
- - `@bananapus/buyback-hook-v6`: `^0.0.39 → ^0.0.46`.
9
- - `@bananapus/permission-ids-v6`: `^0.0.23 → ^0.0.25` (new `SET_SUCKER_PEER` permission referenced by suckers 0.0.46).
10
- - `JBBuybackHook` constructor signature changed in 0.0.45 (V4 PoolManager + Hooks moved to a one-shot setter). Updated `OmnichainForkTestBase` to construct then call `setChainSpecificConstants`.
11
- - All `JBRulesetMetadata` test literals patched to include `pauseCrossProjectFeeFreeInflows: false`. `JBBeforeCashOutRecordedContext` literals are unchanged (no new fields).
12
-
13
- ## v6 carry-forward hook fix
14
-
15
- - **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.
16
- - The `useDataHookForCashOut` flag is preserved from whichever source ruleset is selected during carry-forward.
17
-
18
- ## Scope
19
-
20
- This file describes the verified change from `nana-omnichain-deployers-v5` to the current `nana-omnichain-deployers-v6` repo.
21
-
22
- ## Current v6 surface
23
-
24
- - `JBOmnichainDeployer`
25
- - `IJBOmnichainDeployer`
26
- - `JBDeployerHookConfig`
27
- - `JBOmnichain721Config`
28
- - `JBTiered721HookConfig`
29
-
30
- ## Summary
31
-
32
- - The deployer now assumes a 721 hook is part of the standard deployment path instead of a special-case path.
33
- - Hook composition is more explicit. The current repo separates 721-hook behavior from extra data-hook behavior and combines them deliberately.
34
- - The v6 test suite includes dedicated coverage for ownership transfer, controller validation, empty ruleset edge cases, hook composition, and invariants that were not present in the small v5 tree.
35
- - The repo moved to the v6 Solidity and dependency baseline.
36
-
37
- ## Verified deltas
38
-
39
- - `launch721ProjectFor(...)`, `launch721RulesetsFor(...)`, and `queue721RulesetsOf(...)` no longer define the public API shape.
40
- - Their role is covered by overloaded `launchProjectFor(...)`, `launchRulesetsFor(...)`, and `queueRulesetsOf(...)` entry points that accept `JBOmnichain721Config`.
41
- - `extraDataHookOf(...)` and `tiered721HookOf(...)` replace the older single `dataHookOf(...)` view model.
42
- - The overloaded launch and queue functions now return the `IJB721TiersHook` they deploy or carry forward.
43
-
44
- ## Breaking ABI changes
45
-
46
- - The 721-specific launch and queue entry points were removed from the public API shape.
47
- - `dataHookOf(...)` was replaced by `extraDataHookOf(...)` plus `tiered721HookOf(...)`.
48
- - `launchProjectFor(...)`, `launchRulesetsFor(...)`, and `queueRulesetsOf(...)` now have overloads that return the hook.
49
- - `JBOmnichain721Config` replaces the old direct 721 deploy config entrypoint model.
50
-
51
- ## Indexer impact
52
-
53
- - Hook composition is now split across two tracked hook sources instead of one.
54
- - Launch and queue flows should expect a 721 hook in the returned state and in the deployer's stored per-ruleset data.
55
-
56
- ## Migration notes
57
-
58
- - Update any code that expected separate "with 721" and "without 721" deployment paths to behave like v5.
59
- - Re-check ownership assumptions after hook deployment. The current repo is stricter and more explicit about that flow.
60
- - If you decode launch or queue inputs, use the current v6 structs instead of v5 layouts.
61
-
62
- ## ABI appendix
63
-
64
- - Removed public API shape
65
- - `launch721ProjectFor(...)`
66
- - `launch721RulesetsFor(...)`
67
- - `queue721RulesetsOf(...)`
68
- - Replaced with overload families
69
- - `launchProjectFor(...)`
70
- - `launchRulesetsFor(...)`
71
- - `queueRulesetsOf(...)`
72
- - Replaced hook lookup model
73
- - `dataHookOf(...)` -> `extraDataHookOf(...)` + `tiered721HookOf(...)`
74
- - New migration-sensitive structs
75
- - `JBOmnichain721Config`
76
- - `JBTiered721HookConfig`