@bananapus/omnichain-deployers-v6 0.0.8 → 0.0.9

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/omnichain-deployers-v6",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -7,12 +7,6 @@ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
7
7
  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
8
8
  import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
9
9
  import {IJB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookProjectDeployer.sol";
10
- import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook-v6/src/structs/JBDeploy721TiersHookConfig.sol";
11
- import {JBLaunchProjectConfig} from "@bananapus/721-hook-v6/src/structs/JBLaunchProjectConfig.sol";
12
- import {JBLaunchRulesetsConfig} from "@bananapus/721-hook-v6/src/structs/JBLaunchRulesetsConfig.sol";
13
- import {JBPayDataHookRulesetConfig} from "@bananapus/721-hook-v6/src/structs/JBPayDataHookRulesetConfig.sol";
14
- import {JBPayDataHookRulesetMetadata} from "@bananapus/721-hook-v6/src/structs/JBPayDataHookRulesetMetadata.sol";
15
- import {JBQueueRulesetsConfig} from "@bananapus/721-hook-v6/src/structs/JBQueueRulesetsConfig.sol";
16
10
  import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
17
11
  import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
18
12
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
@@ -28,7 +22,6 @@ import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsDat
28
22
  import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
29
23
  import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
30
24
  import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
31
- import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
32
25
  import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
33
26
  import {JBOwnable} from "@bananapus/ownable-v6/src/JBOwnable.sol";
34
27
  import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
@@ -38,6 +31,7 @@ import {mulDiv} from "@prb/math/src/Common.sol";
38
31
 
39
32
  import {IJBOmnichainDeployer} from "./interfaces/IJBOmnichainDeployer.sol";
40
33
  import {JBDeployerHookConfig} from "./structs/JBDeployerHookConfig.sol";
34
+ import {JBOmnichain721Config} from "./structs/JBOmnichain721Config.sol";
41
35
  import {JBSuckerDeploymentConfig} from "./structs/JBSuckerDeploymentConfig.sol";
42
36
  import {JBTiered721HookConfig} from "./structs/JBTiered721HookConfig.sol";
43
37
 
@@ -358,174 +352,27 @@ contract JBOmnichainDeployer is
358
352
  });
359
353
  }
360
354
 
361
- /// @notice Launches a new project with a 721 tiers hook attached, and with suckers.
355
+ /// @notice Creates a project, optionally with a 721 tiers hook attached, and with suckers.
356
+ /// @dev If `deploy721Config.deployTiersHookConfig.tiersConfig.tiers.length > 0`, a 721 hook is deployed and
357
+ /// attached.
362
358
  /// @param owner The address to set as the owner of the project. The ERC-721 which confers this project's ownership
363
359
  /// will be sent to this address.
364
- /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
365
- /// deployed.
366
- /// @param launchProjectConfig Configuration which dictates the behavior of the project which is being launched.
367
- /// @param salt A salt to use for the deterministic deployment. Combined with `_msgSender()` internally, so
368
- /// cross-chain deterministic addresses require the same sender on each chain.
369
- /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
370
- /// token transfers between peer projects on different networks.
371
- /// @param controller The controller to use for launching the project.
372
- /// @param dataHookConfig The custom data hook config to use alongside the 721 hook.
373
- /// @return projectId The ID of the newly launched project.
374
- /// @return hook The 721 tiers hook that was deployed for the project.
375
- function launch721ProjectFor(
376
- address owner,
377
- JBDeploy721TiersHookConfig calldata deployTiersHookConfig,
378
- JBLaunchProjectConfig calldata launchProjectConfig,
379
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
380
- IJBController controller,
381
- JBDeployerHookConfig calldata dataHookConfig,
382
- bytes32 salt
383
- )
384
- external
385
- override
386
- returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers)
387
- {
388
- // Get the next project ID.
389
- projectId = PROJECTS.count() + 1;
390
-
391
- // Deploy the hook.
392
- // Note: the salt includes `_msgSender()` for replay protection. Cross-chain deterministic
393
- // address matching requires using the same sender address on each chain.
394
- hook = HOOK_DEPLOYER.deployHookFor({
395
- projectId: projectId,
396
- deployTiersHookConfig: deployTiersHookConfig,
397
- salt: salt == bytes32(0) ? bytes32(0) : keccak256(abi.encode(_msgSender(), salt))
398
- });
399
-
400
- // Convert the 721 ruleset configurations to regular ruleset configurations.
401
- JBRulesetConfig[] memory rulesetConfigurations =
402
- _from721Config({launchProjectConfig: launchProjectConfig.rulesetConfigurations});
403
-
404
- // Store the 721 hook per-ruleset and set this contract as data hook.
405
- rulesetConfigurations = _setup721({
406
- projectId: projectId,
407
- rulesetConfigurations: rulesetConfigurations,
408
- hook721: hook,
409
- customHook: dataHookConfig
410
- });
411
-
412
- // Launch the project, and sanity check the project ID.
413
- // slither-disable-next-line reentrancy-benign
414
- if (
415
- projectId
416
- != controller.launchProjectFor({
417
- owner: address(this),
418
- projectUri: launchProjectConfig.projectUri,
419
- rulesetConfigurations: rulesetConfigurations,
420
- terminalConfigurations: launchProjectConfig.terminalConfigurations,
421
- memo: launchProjectConfig.memo
422
- })
423
- ) revert JBOmnichainDeployer_ProjectIdMismatch();
424
-
425
- // Transfer the hook's ownership to the project.
426
- JBOwnable(address(hook)).transferOwnershipToProject(projectId);
427
-
428
- // Deploy the suckers (if applicable).
429
- if (suckerDeploymentConfiguration.salt != bytes32(0)) {
430
- // Deploy the suckers.
431
- // slither-disable-next-line unused-return
432
- suckers = SUCKER_REGISTRY.deploySuckersFor({
433
- projectId: projectId,
434
- salt: keccak256(abi.encode(suckerDeploymentConfiguration.salt, _msgSender())),
435
- configurations: suckerDeploymentConfiguration.deployerConfigurations
436
- });
437
- }
438
-
439
- // Transfer ownership of the project to the owner.
440
- PROJECTS.transferFrom(address(this), owner, projectId);
441
- }
442
-
443
- /// @notice Launches new rulesets for a project with a 721 tiers hook attached, using this contract as the data
444
- /// hook.
445
- /// @param projectId The ID of the project to launch the rulesets for.
446
- /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
447
- /// deployed.
448
- /// @param launchRulesetsConfig Configuration which dictates the behavior of the rulesets which are being launched.
449
- /// @param salt A salt to use for the deterministic deployment. Combined with `_msgSender()` internally, so
450
- /// cross-chain deterministic addresses require the same sender on each chain.
451
- /// @param dataHookConfig The custom data hook config to use alongside the 721 hook.
452
- /// @return rulesetId The ID of the newly launched rulesets.
453
- /// @return hook The 721 tiers hook that was deployed for the project.
454
- function launch721RulesetsFor(
455
- uint256 projectId,
456
- JBDeploy721TiersHookConfig memory deployTiersHookConfig,
457
- JBLaunchRulesetsConfig calldata launchRulesetsConfig,
458
- IJBController controller,
459
- JBDeployerHookConfig calldata dataHookConfig,
460
- bytes32 salt
461
- )
462
- external
463
- override
464
- returns (uint256 rulesetId, IJB721TiersHook hook)
465
- {
466
- // Enforce permissions.
467
- _requirePermissionFrom({
468
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.QUEUE_RULESETS
469
- });
470
-
471
- _requirePermissionFrom({
472
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
473
- });
474
-
475
- // Validate that the controller matches the project's controller in the directory.
476
- _validateController({projectId: projectId, controller: controller});
477
-
478
- // Deploy the hook.
479
- // Note: the salt includes `_msgSender()` for replay protection. Cross-chain deterministic
480
- // address matching requires using the same sender address on each chain.
481
- hook = HOOK_DEPLOYER.deployHookFor({
482
- projectId: projectId,
483
- deployTiersHookConfig: deployTiersHookConfig,
484
- salt: salt == bytes32(0) ? bytes32(0) : keccak256(abi.encode(_msgSender(), salt))
485
- });
486
-
487
- // Transfer the hook's ownership to the project.
488
- JBOwnable(address(hook)).transferOwnershipToProject(projectId);
489
-
490
- // Convert the 721 ruleset configurations to regular ruleset configurations.
491
- JBRulesetConfig[] memory rulesetConfigurations =
492
- _from721Config({launchProjectConfig: launchRulesetsConfig.rulesetConfigurations});
493
-
494
- // Store the 721 hook per-ruleset and set this contract as data hook.
495
- // slither-disable-next-line reentrancy-benign
496
- rulesetConfigurations = _setup721({
497
- projectId: projectId,
498
- rulesetConfigurations: rulesetConfigurations,
499
- hook721: hook,
500
- customHook: dataHookConfig
501
- });
502
-
503
- // Configure the rulesets.
504
- rulesetId = controller.launchRulesetsFor({
505
- projectId: projectId,
506
- rulesetConfigurations: rulesetConfigurations,
507
- terminalConfigurations: launchRulesetsConfig.terminalConfigurations,
508
- memo: launchRulesetsConfig.memo
509
- });
510
- }
511
-
512
- /// @notice Creates a project with suckers.
513
- /// @dev This will mint the project's ERC-721 to the `owner`'s address, queue the specified rulesets, and set up the
514
- /// specified splits and terminals. Each operation within this transaction can be done in sequence separately.
515
- /// @dev Anyone can deploy a project to any `owner`'s address.
516
- /// @param owner The project's owner. The project ERC-721 will be minted to this address.
517
- /// @param projectUri The project's metadata URI. This is typically an IPFS hash, optionally with the `ipfs://`
518
- /// prefix. This can be updated by the project's owner.
519
- /// @param rulesetConfigurations The rulesets to queue.
360
+ /// @param projectUri The project's metadata URI.
361
+ /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt). If no tiers are
362
+ /// configured, no 721 hook is deployed.
363
+ /// @param rulesetConfigurations The rulesets to queue. Custom data hooks are read from each ruleset's metadata.
520
364
  /// @param terminalConfigurations The terminals to set up for the project.
521
365
  /// @param memo A memo to pass along to the emitted event.
522
366
  /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
523
367
  /// token transfers between peer projects on different networks.
524
368
  /// @param controller The controller to use for launching the project.
525
- /// @return projectId The project's ID.
369
+ /// @return projectId The ID of the newly launched project.
370
+ /// @return hook The 721 tiers hook that was deployed for the project (`address(0)` if none).
371
+ /// @return suckers The addresses of the deployed suckers.
526
372
  function launchProjectFor(
527
373
  address owner,
528
374
  string calldata projectUri,
375
+ JBOmnichain721Config calldata deploy721Config,
529
376
  JBRulesetConfig[] memory rulesetConfigurations,
530
377
  JBTerminalConfig[] calldata terminalConfigurations,
531
378
  string calldata memo,
@@ -534,14 +381,26 @@ contract JBOmnichainDeployer is
534
381
  )
535
382
  external
536
383
  override
537
- returns (uint256 projectId, address[] memory suckers)
384
+ returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers)
538
385
  {
539
386
  // Get the next project ID.
540
387
  projectId = PROJECTS.count() + 1;
541
388
 
542
- rulesetConfigurations = _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations});
389
+ // Deploy the 721 hook if tiers are configured, otherwise use the non-721 setup path.
390
+ if (deploy721Config.deployTiersHookConfig.tiersConfig.tiers.length > 0) {
391
+ hook = _deploy721Hook(projectId, deploy721Config);
392
+ rulesetConfigurations = _setup721({
393
+ projectId: projectId,
394
+ rulesetConfigurations: rulesetConfigurations,
395
+ hook721: hook,
396
+ use721ForCashOut: deploy721Config.useDataHookForCashOut
397
+ });
398
+ } else {
399
+ rulesetConfigurations = _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations});
400
+ }
543
401
 
544
- // Launch the project.
402
+ // Launch the project, and sanity check the project ID.
403
+ // slither-disable-next-line reentrancy-benign
545
404
  if (
546
405
  projectId
547
406
  != controller.launchProjectFor({
@@ -555,8 +414,6 @@ contract JBOmnichainDeployer is
555
414
 
556
415
  // Deploy the suckers (if applicable).
557
416
  if (suckerDeploymentConfiguration.salt != bytes32(0)) {
558
- // Deploy the suckers.
559
- // Note: the salt includes `_msgSender()` for replay protection (see above).
560
417
  // slither-disable-next-line unused-return
561
418
  suckers = SUCKER_REGISTRY.deploySuckersFor({
562
419
  projectId: projectId,
@@ -569,23 +426,30 @@ contract JBOmnichainDeployer is
569
426
  PROJECTS.transferFrom(address(this), owner, projectId);
570
427
  }
571
428
 
572
- /// @notice Launches new rulesets for a project, using this contract as the data hook.
429
+ /// @notice Launches new rulesets for a project, optionally with a 721 tiers hook attached, using this contract as
430
+ /// the data hook.
431
+ /// @dev If `deploy721Config.deployTiersHookConfig.tiersConfig.tiers.length > 0`, a 721 hook is deployed and
432
+ /// attached.
573
433
  /// @param projectId The ID of the project to launch the rulesets for.
574
- /// @param rulesetConfigurations The rulesets to launch.
434
+ /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt). If no tiers are
435
+ /// configured, no 721 hook is deployed.
436
+ /// @param rulesetConfigurations The rulesets to launch. Custom data hooks are read from each ruleset's metadata.
575
437
  /// @param terminalConfigurations The terminals to set up for the project.
576
438
  /// @param memo A memo to pass along to the emitted event.
577
439
  /// @param controller The controller to use for launching the rulesets.
578
440
  /// @return rulesetId The ID of the newly launched rulesets.
441
+ /// @return hook The 721 tiers hook that was deployed for the project (`address(0)` if none).
579
442
  function launchRulesetsFor(
580
443
  uint256 projectId,
581
- JBRulesetConfig[] calldata rulesetConfigurations,
444
+ JBOmnichain721Config memory deploy721Config,
445
+ JBRulesetConfig[] memory rulesetConfigurations,
582
446
  JBTerminalConfig[] calldata terminalConfigurations,
583
447
  string calldata memo,
584
448
  IJBController controller
585
449
  )
586
450
  external
587
451
  override
588
- returns (uint256)
452
+ returns (uint256 rulesetId, IJB721TiersHook hook)
589
453
  {
590
454
  // Enforce permissions.
591
455
  _requirePermissionFrom({
@@ -599,9 +463,24 @@ contract JBOmnichainDeployer is
599
463
  // Validate that the controller matches the project's controller in the directory.
600
464
  _validateController({projectId: projectId, controller: controller});
601
465
 
602
- return controller.launchRulesetsFor({
466
+ // Deploy the 721 hook if tiers are configured, otherwise use the non-721 setup path.
467
+ if (deploy721Config.deployTiersHookConfig.tiersConfig.tiers.length > 0) {
468
+ hook = _deploy721Hook(projectId, deploy721Config);
469
+ // slither-disable-next-line reentrancy-benign
470
+ rulesetConfigurations = _setup721({
471
+ projectId: projectId,
472
+ rulesetConfigurations: rulesetConfigurations,
473
+ hook721: hook,
474
+ use721ForCashOut: deploy721Config.useDataHookForCashOut
475
+ });
476
+ } else {
477
+ rulesetConfigurations = _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations});
478
+ }
479
+
480
+ // Configure the rulesets.
481
+ rulesetId = controller.launchRulesetsFor({
603
482
  projectId: projectId,
604
- rulesetConfigurations: _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations}),
483
+ rulesetConfigurations: rulesetConfigurations,
605
484
  terminalConfigurations: terminalConfigurations,
606
485
  memo: memo
607
486
  });
@@ -615,88 +494,28 @@ contract JBOmnichainDeployer is
615
494
  return IERC721Receiver.onERC721Received.selector;
616
495
  }
617
496
 
618
- /// @notice Queues new rulesets for a project with a 721 tiers hook attached, using this contract as the data hook.
497
+ /// @notice Queues new rulesets for a project, optionally with a 721 tiers hook attached, using this contract as the
498
+ /// data hook.
499
+ /// @dev If `deploy721Config.deployTiersHookConfig.tiersConfig.tiers.length > 0`, a 721 hook is deployed and
500
+ /// attached.
619
501
  /// @param projectId The ID of the project to queue the rulesets for.
620
- /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
621
- /// deployed.
622
- /// @param queueRulesetsConfig Configuration which dictates the behavior of the rulesets which are being queued.
623
- /// @param salt A salt to use for the deterministic deployment. Combined with `_msgSender()` internally, so
624
- /// cross-chain deterministic addresses require the same sender on each chain.
625
- /// @param dataHookConfig The custom data hook config to use alongside the 721 hook.
626
- /// @return rulesetId The ID of the newly queued rulesets.
627
- /// @return hook The 721 tiers hook that was deployed for the project.
628
- function queue721RulesetsOf(
629
- uint256 projectId,
630
- JBDeploy721TiersHookConfig memory deployTiersHookConfig,
631
- JBQueueRulesetsConfig calldata queueRulesetsConfig,
632
- IJBController controller,
633
- JBDeployerHookConfig calldata dataHookConfig,
634
- bytes32 salt
635
- )
636
- external
637
- override
638
- returns (uint256 rulesetId, IJB721TiersHook hook)
639
- {
640
- // Enforce permissions.
641
- _requirePermissionFrom({
642
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.QUEUE_RULESETS
643
- });
644
-
645
- // Validate that the controller matches the project's controller in the directory.
646
- _validateController({projectId: projectId, controller: controller});
647
-
648
- // Revert if the project already had rulesets queued in this block, which would make our
649
- // `block.timestamp + i` ruleset ID prediction incorrect.
650
- if (controller.RULESETS().latestRulesetIdOf(projectId) >= block.timestamp) {
651
- revert JBOmnichainDeployer_RulesetIdsUnpredictable();
652
- }
653
-
654
- // Deploy the hook.
655
- // Note: the salt includes `_msgSender()` for replay protection. Cross-chain deterministic
656
- // address matching requires using the same sender address on each chain.
657
- hook = HOOK_DEPLOYER.deployHookFor({
658
- projectId: projectId,
659
- deployTiersHookConfig: deployTiersHookConfig,
660
- salt: salt == bytes32(0) ? bytes32(0) : keccak256(abi.encode(_msgSender(), salt))
661
- });
662
-
663
- // Transfer the hook's ownership to the project.
664
- JBOwnable(address(hook)).transferOwnershipToProject(projectId);
665
-
666
- // Convert the 721 ruleset configurations to regular ruleset configurations.
667
- JBRulesetConfig[] memory rulesetConfigurations =
668
- _from721Config({launchProjectConfig: queueRulesetsConfig.rulesetConfigurations});
669
-
670
- // Store the 721 hook per-ruleset and set this contract as data hook.
671
- // slither-disable-next-line reentrancy-benign
672
- rulesetConfigurations = _setup721({
673
- projectId: projectId,
674
- rulesetConfigurations: rulesetConfigurations,
675
- hook721: hook,
676
- customHook: dataHookConfig
677
- });
678
-
679
- // Configure the rulesets.
680
- rulesetId = controller.queueRulesetsOf({
681
- projectId: projectId, rulesetConfigurations: rulesetConfigurations, memo: queueRulesetsConfig.memo
682
- });
683
- }
684
-
685
- /// @notice Queues new rulesets for a project, using this contract as the data hook.
686
- /// @param projectId The ID of the project to queue the rulesets for.
687
- /// @param rulesetConfigurations The rulesets to queue.
502
+ /// @param deploy721Config The 721 hook deployment config (hook config + cash-out flag + salt). If no tiers are
503
+ /// configured, no 721 hook is deployed.
504
+ /// @param rulesetConfigurations The rulesets to queue. Custom data hooks are read from each ruleset's metadata.
688
505
  /// @param memo A memo to pass along to the emitted event.
689
506
  /// @param controller The controller to use for queuing the rulesets.
690
507
  /// @return rulesetId The ID of the newly queued rulesets.
508
+ /// @return hook The 721 tiers hook that was deployed for the project (`address(0)` if none).
691
509
  function queueRulesetsOf(
692
510
  uint256 projectId,
693
- JBRulesetConfig[] calldata rulesetConfigurations,
511
+ JBOmnichain721Config memory deploy721Config,
512
+ JBRulesetConfig[] memory rulesetConfigurations,
694
513
  string calldata memo,
695
514
  IJBController controller
696
515
  )
697
516
  external
698
517
  override
699
- returns (uint256)
518
+ returns (uint256 rulesetId, IJB721TiersHook hook)
700
519
  {
701
520
  // Enforce permissions.
702
521
  _requirePermissionFrom({
@@ -712,10 +531,23 @@ contract JBOmnichainDeployer is
712
531
  revert JBOmnichainDeployer_RulesetIdsUnpredictable();
713
532
  }
714
533
 
715
- return controller.queueRulesetsOf({
716
- projectId: projectId,
717
- rulesetConfigurations: _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations}),
718
- memo: memo
534
+ // Deploy the 721 hook if tiers are configured, otherwise use the non-721 setup path.
535
+ if (deploy721Config.deployTiersHookConfig.tiersConfig.tiers.length > 0) {
536
+ hook = _deploy721Hook(projectId, deploy721Config);
537
+ // slither-disable-next-line reentrancy-benign
538
+ rulesetConfigurations = _setup721({
539
+ projectId: projectId,
540
+ rulesetConfigurations: rulesetConfigurations,
541
+ hook721: hook,
542
+ use721ForCashOut: deploy721Config.useDataHookForCashOut
543
+ });
544
+ } else {
545
+ rulesetConfigurations = _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations});
546
+ }
547
+
548
+ // Configure the rulesets.
549
+ rulesetId = controller.queueRulesetsOf({
550
+ projectId: projectId, rulesetConfigurations: rulesetConfigurations, memo: memo
719
551
  });
720
552
  }
721
553
 
@@ -728,59 +560,6 @@ contract JBOmnichainDeployer is
728
560
  return ERC2771Context._contextSuffixLength();
729
561
  }
730
562
 
731
- /// @notice Converts a 721 ruleset configuration to a regular ruleset configuration.
732
- /// @dev Sets `metadata.dataHook = address(0)` as a placeholder — actual hooks stored in `_tiered721HookOf` /
733
- /// `_extraDataHookOf`.
734
- /// @dev Preserves `metadata.useDataHookForCashOut` from the 721 metadata (used for the 721 hook config).
735
- /// @param launchProjectConfig The 721 ruleset configuration to convert.
736
- /// @return rulesetConfigurations The converted ruleset configuration.
737
- function _from721Config(JBPayDataHookRulesetConfig[] calldata launchProjectConfig)
738
- internal
739
- pure
740
- returns (JBRulesetConfig[] memory rulesetConfigurations)
741
- {
742
- rulesetConfigurations = new JBRulesetConfig[](launchProjectConfig.length);
743
-
744
- for (uint256 i; i < launchProjectConfig.length; i++) {
745
- JBPayDataHookRulesetMetadata calldata hookMetadata = launchProjectConfig[i].metadata;
746
- JBRulesetMetadata memory metadata = JBRulesetMetadata({
747
- // useDataHookForPay is always true — the 721 hook needs it via beforePayRecordedWith.
748
- useDataHookForPay: true,
749
- allowSetCustomToken: false,
750
- // Placeholder — actual hooks stored in _tiered721HookOf / _extraDataHookOf.
751
- dataHook: address(0),
752
- // These fields are present in the 721 metadata.
753
- reservedPercent: hookMetadata.reservedPercent,
754
- cashOutTaxRate: hookMetadata.cashOutTaxRate,
755
- baseCurrency: hookMetadata.baseCurrency,
756
- pausePay: hookMetadata.pausePay,
757
- pauseCreditTransfers: hookMetadata.pauseCreditTransfers,
758
- allowOwnerMinting: hookMetadata.allowOwnerMinting,
759
- allowTerminalMigration: hookMetadata.allowTerminalMigration,
760
- allowSetController: hookMetadata.allowSetController,
761
- allowSetTerminals: hookMetadata.allowSetTerminals,
762
- allowAddAccountingContext: hookMetadata.allowAddAccountingContext,
763
- allowAddPriceFeed: hookMetadata.allowAddPriceFeed,
764
- ownerMustSendPayouts: hookMetadata.ownerMustSendPayouts,
765
- holdFees: hookMetadata.holdFees,
766
- useTotalSurplusForCashOuts: hookMetadata.useTotalSurplusForCashOuts,
767
- useDataHookForCashOut: hookMetadata.useDataHookForCashOut,
768
- metadata: hookMetadata.metadata
769
- });
770
-
771
- rulesetConfigurations[i] = JBRulesetConfig({
772
- mustStartAtOrAfter: launchProjectConfig[i].mustStartAtOrAfter,
773
- duration: launchProjectConfig[i].duration,
774
- weight: launchProjectConfig[i].weight,
775
- weightCutPercent: launchProjectConfig[i].weightCutPercent,
776
- approvalHook: launchProjectConfig[i].approvalHook,
777
- metadata: metadata,
778
- splitGroups: launchProjectConfig[i].splitGroups,
779
- fundAccessLimitGroups: launchProjectConfig[i].fundAccessLimitGroups
780
- });
781
- }
782
- }
783
-
784
563
  /// @notice The calldata. Preferred to use over `msg.data`.
785
564
  /// @return calldata The `msg.data` of this call.
786
565
  function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
@@ -793,6 +572,30 @@ contract JBOmnichainDeployer is
793
572
  return ERC2771Context._msgSender();
794
573
  }
795
574
 
575
+ /// @notice Deploys a 721 tiers hook and transfers its ownership to the project.
576
+ /// @param projectId The ID of the project to deploy the hook for.
577
+ /// @param config The 721 hook deployment config (hook config + cash-out flag + salt).
578
+ /// @return hook The deployed 721 tiers hook.
579
+ function _deploy721Hook(
580
+ uint256 projectId,
581
+ JBOmnichain721Config memory config
582
+ )
583
+ internal
584
+ returns (IJB721TiersHook hook)
585
+ {
586
+ // Deploy the hook.
587
+ // Note: the salt includes `_msgSender()` for replay protection. Cross-chain deterministic
588
+ // address matching requires using the same sender address on each chain.
589
+ hook = HOOK_DEPLOYER.deployHookFor({
590
+ projectId: projectId,
591
+ deployTiersHookConfig: config.deployTiersHookConfig,
592
+ salt: config.salt == bytes32(0) ? bytes32(0) : keccak256(abi.encode(_msgSender(), config.salt))
593
+ });
594
+
595
+ // Transfer the hook's ownership to the project.
596
+ JBOwnable(address(hook)).transferOwnershipToProject(projectId);
597
+ }
598
+
796
599
  /// @notice Sets up a project's rulesets (non-721 path).
797
600
  /// @dev Reads the data hook from each ruleset's metadata and stores it in `_extraDataHookOf`.
798
601
  /// @dev Stores data hook configs keyed by predicted ruleset IDs (`block.timestamp + i`). This prediction is correct
@@ -833,34 +636,38 @@ contract JBOmnichainDeployer is
833
636
  }
834
637
 
835
638
  /// @notice Sets up a project's rulesets for 721 projects.
836
- /// @dev Stores the 721 hook in `_tiered721HookOf` per-ruleset and the custom hook in `_extraDataHookOf`.
639
+ /// @dev Stores the 721 hook in `_tiered721HookOf` per-ruleset and any custom hook (from metadata) in
640
+ /// `_extraDataHookOf`.
837
641
  /// @param projectId The ID of the project to set up.
838
- /// @param rulesetConfigurations The rulesets to set up (already converted from 721 config).
642
+ /// @param rulesetConfigurations The rulesets to set up.
839
643
  /// @param hook721 The 721 tiers hook.
840
- /// @param customHook The custom data hook config (if dataHook != address(0)).
644
+ /// @param use721ForCashOut Whether the 721 hook should handle cash outs.
841
645
  /// @return rulesetConfigurations The rulesets that were set up.
842
646
  function _setup721(
843
647
  uint256 projectId,
844
648
  JBRulesetConfig[] memory rulesetConfigurations,
845
649
  IJB721TiersHook hook721,
846
- JBDeployerHookConfig calldata customHook
650
+ bool use721ForCashOut
847
651
  )
848
652
  internal
849
653
  returns (JBRulesetConfig[] memory)
850
654
  {
851
- // Validate the custom hook isn't this contract.
852
- if (address(customHook.dataHook) == address(this)) revert JBOmnichainDeployer_InvalidHook();
853
-
854
655
  for (uint256 i; i < rulesetConfigurations.length; i++) {
656
+ // Validate no self-reference.
657
+ if (rulesetConfigurations[i].metadata.dataHook == address(this)) revert JBOmnichainDeployer_InvalidHook();
658
+
855
659
  // Store the 721 hook config per-ruleset.
856
660
  // slither-disable-next-line reentrancy-benign
857
- _tiered721HookOf[projectId][block.timestamp + i] = JBTiered721HookConfig({
858
- hook: hook721, useDataHookForCashOut: rulesetConfigurations[i].metadata.useDataHookForCashOut
859
- });
661
+ _tiered721HookOf[projectId][block.timestamp + i] =
662
+ JBTiered721HookConfig({hook: hook721, useDataHookForCashOut: use721ForCashOut});
860
663
 
861
- // Store the custom hook if set.
862
- if (address(customHook.dataHook) != address(0)) {
863
- _extraDataHookOf[projectId][block.timestamp + i] = customHook;
664
+ // Store custom hook from metadata (same as _setup).
665
+ if (rulesetConfigurations[i].metadata.dataHook != address(0)) {
666
+ _extraDataHookOf[projectId][block.timestamp + i] = JBDeployerHookConfig({
667
+ dataHook: IJBRulesetDataHook(rulesetConfigurations[i].metadata.dataHook),
668
+ useDataHookForPay: rulesetConfigurations[i].metadata.useDataHookForPay,
669
+ useDataHookForCashOut: rulesetConfigurations[i].metadata.useDataHookForCashOut
670
+ });
864
671
  }
865
672
 
866
673
  // Set this contract as the data hook, force both pay and cashout through this wrapper.