@bananapus/omnichain-deployers-v6 0.0.4 → 0.0.5

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/foundry.toml CHANGED
@@ -1,7 +1,7 @@
1
1
  [profile.default]
2
2
  solc = '0.8.26'
3
3
  evm_version = 'paris'
4
- optimizer_runs = 100000
4
+ optimizer_runs = 200
5
5
  libs = ["node_modules", "lib"]
6
6
  fs_permissions = [{ access = "read-write", path = "./"}]
7
7
 
package/package.json CHANGED
@@ -1,23 +1,27 @@
1
1
  {
2
2
  "name": "@bananapus/omnichain-deployers-v6",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
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
+ "engines": {
10
+ "node": ">=20.0.0"
11
+ },
9
12
  "scripts": {
10
13
  "test": "forge test",
14
+ "coverage": "forge coverage --match-path \"./src/*.sol\" --report lcov --report summary",
11
15
  "deploy:mainnets": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/Deploy.s.sol --networks mainnets",
12
16
  "deploy:testnets": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/Deploy.s.sol --networks testnets",
13
17
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
14
18
  },
15
19
  "dependencies": {
16
20
  "@bananapus/721-hook-v6": "^0.0.9",
17
- "@bananapus/core-v6": "^0.0.9",
18
- "@bananapus/suckers-v6": "^0.0.6",
19
- "@bananapus/ownable-v6": "^0.0.5",
20
- "@bananapus/permission-ids-v6": "^0.0.4",
21
+ "@bananapus/core-v6": "^0.0.10",
22
+ "@bananapus/suckers-v6": "^0.0.7",
23
+ "@bananapus/ownable-v6": "^0.0.6",
24
+ "@bananapus/permission-ids-v6": "^0.0.5",
21
25
  "@openzeppelin/contracts": "^5.2.0"
22
26
  },
23
27
  "devDependencies": {
@@ -3,14 +3,15 @@ pragma solidity 0.8.26;
3
3
 
4
4
  import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
5
5
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
6
- import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
7
6
  import {Context} from "@openzeppelin/contracts/utils/Context.sol";
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
10
  import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook-v6/src/structs/JBDeploy721TiersHookConfig.sol";
11
11
  import {JBLaunchProjectConfig} from "@bananapus/721-hook-v6/src/structs/JBLaunchProjectConfig.sol";
12
12
  import {JBLaunchRulesetsConfig} from "@bananapus/721-hook-v6/src/structs/JBLaunchRulesetsConfig.sol";
13
13
  import {JBPayDataHookRulesetConfig} from "@bananapus/721-hook-v6/src/structs/JBPayDataHookRulesetConfig.sol";
14
+ import {JBPayDataHookRulesetMetadata} from "@bananapus/721-hook-v6/src/structs/JBPayDataHookRulesetMetadata.sol";
14
15
  import {JBQueueRulesetsConfig} from "@bananapus/721-hook-v6/src/structs/JBQueueRulesetsConfig.sol";
15
16
  import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
16
17
  import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
@@ -20,20 +21,17 @@ import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.s
20
21
  import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
21
22
  import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
22
23
  import {JBBeforeCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
23
-
24
24
  import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
25
25
  import {JBCashOutHookSpecification} from "@bananapus/core-v6/src/structs/JBCashOutHookSpecification.sol";
26
26
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
27
-
28
27
  import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
29
- import {JBPayDataHookRulesetMetadata} from "@bananapus/721-hook-v6/src/structs/JBPayDataHookRulesetMetadata.sol";
28
+ import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
30
29
  import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
31
30
  import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
32
- import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
33
31
  import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
34
- import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
35
32
  import {JBOwnable} from "@bananapus/ownable-v6/src/JBOwnable.sol";
36
33
  import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
34
+ import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
37
35
 
38
36
  import {IJBOmnichainDeployer} from "./interfaces/IJBOmnichainDeployer.sol";
39
37
  import {JBDeployerHookConfig} from "./structs/JBDeployerHookConfig.sol";
@@ -48,12 +46,17 @@ contract JBOmnichainDeployer is
48
46
  IERC721Receiver
49
47
  {
50
48
  //*********************************************************************//
51
- // ------------------------------ errors ------------------------------ //
49
+ // --------------------------- custom errors ------------------------- //
52
50
  //*********************************************************************//
53
51
 
52
+ /// @notice Thrown when the provided controller does not match the project's controller in the directory.
53
+ error JBOmnichainDeployer_ControllerMismatch();
54
+
54
55
  /// @notice Thrown when a data hook is set to this contract.
55
56
  error JBOmnichainDeployer_InvalidHook();
56
- error JBOmnichainDeployer_UnexpectedNFTReceived();
57
+
58
+ /// @notice Thrown when the project ID returned by the controller does not match the expected project ID.
59
+ error JBOmnichainDeployer_ProjectIdMismatch();
57
60
 
58
61
  /// @notice Thrown when queueing rulesets for a project whose latest ruleset was already queued in the same block.
59
62
  /// @dev Ruleset IDs are predicted as `block.timestamp + i`. This prediction fails if
@@ -63,11 +66,7 @@ contract JBOmnichainDeployer is
63
66
  /// @notice Thrown when the contract receives an NFT that is not from the `JBProjects` contract.
64
67
  error JBOmnichainDeployer_UnexpectedNFT();
65
68
 
66
- /// @notice Thrown when the project ID returned by the controller does not match the expected project ID.
67
- error JBOmnichainDeployer_ProjectIdMismatch();
68
-
69
- /// @notice Thrown when the provided controller does not match the project's controller in the directory.
70
- error JBOmnichainDeployer_ControllerMismatch();
69
+ error JBOmnichainDeployer_UnexpectedNFTReceived();
71
70
 
72
71
  //*********************************************************************//
73
72
  // --------------- public immutable stored properties ---------------- //
@@ -131,32 +130,6 @@ contract JBOmnichainDeployer is
131
130
  // ------------------------- external views -------------------------- //
132
131
  //*********************************************************************//
133
132
 
134
- /// @notice Forward the call to the original data hook.
135
- /// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a payment.
136
- /// @param context Standard Juicebox payment context. See `JBBeforePayRecordedContext`.
137
- /// @return weight The weight which project tokens are minted relative to. This can be used to customize how many
138
- /// tokens get minted by a payment.
139
- /// @return hookSpecifications Amounts (out of what's being paid in) to be sent to pay hooks instead of being paid
140
- /// into the project. Useful for automatically routing funds from a treasury as payments come in.
141
- function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
142
- external
143
- view
144
- override
145
- returns (uint256, JBPayHookSpecification[] memory hookSpecifications)
146
- {
147
- // Fetch the datahook for the ruleset.
148
- JBDeployerHookConfig memory hook = _dataHookOf[context.projectId][context.rulesetId];
149
-
150
- // If no data hook is set, return the original values.
151
- if (address(hook.dataHook) == address(0) || !hook.useDataHookForPay) {
152
- return (context.weight, hookSpecifications);
153
- }
154
-
155
- // Otherwise, forward the call to the datahook.
156
- // slither-disable-next-line unused-return
157
- return hook.dataHook.beforePayRecordedWith(context);
158
- }
159
-
160
133
  /// @notice Allow cash outs from suckers without a tax.
161
134
  /// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a cash out.
162
135
  /// @param context Standard Juicebox cash out context. See `JBBeforeCashOutRecordedContext`.
@@ -189,6 +162,32 @@ contract JBOmnichainDeployer is
189
162
  return hook.dataHook.beforeCashOutRecordedWith(context);
190
163
  }
191
164
 
165
+ /// @notice Forward the call to the original data hook.
166
+ /// @dev This function is part of `IJBRulesetDataHook`, and gets called before the revnet processes a payment.
167
+ /// @param context Standard Juicebox payment context. See `JBBeforePayRecordedContext`.
168
+ /// @return weight The weight which project tokens are minted relative to. This can be used to customize how many
169
+ /// tokens get minted by a payment.
170
+ /// @return hookSpecifications Amounts (out of what's being paid in) to be sent to pay hooks instead of being paid
171
+ /// into the project. Useful for automatically routing funds from a treasury as payments come in.
172
+ function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
173
+ external
174
+ view
175
+ override
176
+ returns (uint256, JBPayHookSpecification[] memory hookSpecifications)
177
+ {
178
+ // Fetch the datahook for the ruleset.
179
+ JBDeployerHookConfig memory hook = _dataHookOf[context.projectId][context.rulesetId];
180
+
181
+ // If no data hook is set, return the original values.
182
+ if (address(hook.dataHook) == address(0) || !hook.useDataHookForPay) {
183
+ return (context.weight, hookSpecifications);
184
+ }
185
+
186
+ // Otherwise, forward the call to the datahook.
187
+ // slither-disable-next-line unused-return
188
+ return hook.dataHook.beforePayRecordedWith(context);
189
+ }
190
+
192
191
  /// @notice Get the data hook for a project and ruleset.
193
192
  /// @custom:param projectId The ID of the project to get the data hook for.
194
193
  /// @custom:param rulesetId The ID of the ruleset to get the data hook for.
@@ -286,66 +285,6 @@ contract JBOmnichainDeployer is
286
285
  });
287
286
  }
288
287
 
289
- /// @notice Creates a project with suckers.
290
- /// @dev This will mint the project's ERC-721 to the `owner`'s address, queue the specified rulesets, and set up the
291
- /// specified splits and terminals. Each operation within this transaction can be done in sequence separately.
292
- /// @dev Anyone can deploy a project to any `owner`'s address.
293
- /// @param owner The project's owner. The project ERC-721 will be minted to this address.
294
- /// @param projectUri The project's metadata URI. This is typically an IPFS hash, optionally with the `ipfs://`
295
- /// prefix. This can be updated by the project's owner.
296
- /// @param rulesetConfigurations The rulesets to queue.
297
- /// @param terminalConfigurations The terminals to set up for the project.
298
- /// @param memo A memo to pass along to the emitted event.
299
- /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
300
- /// token transfers between peer projects on different networks.
301
- /// @param controller The controller to use for launching the project.
302
- /// @return projectId The project's ID.
303
- function launchProjectFor(
304
- address owner,
305
- string calldata projectUri,
306
- JBRulesetConfig[] memory rulesetConfigurations,
307
- JBTerminalConfig[] calldata terminalConfigurations,
308
- string calldata memo,
309
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
310
- IJBController controller
311
- )
312
- external
313
- override
314
- returns (uint256 projectId, address[] memory suckers)
315
- {
316
- // Get the next project ID.
317
- projectId = PROJECTS.count() + 1;
318
-
319
- rulesetConfigurations = _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations});
320
-
321
- // Launch the project.
322
- if (
323
- projectId
324
- != controller.launchProjectFor({
325
- owner: address(this),
326
- projectUri: projectUri,
327
- rulesetConfigurations: rulesetConfigurations,
328
- terminalConfigurations: terminalConfigurations,
329
- memo: memo
330
- })
331
- ) revert JBOmnichainDeployer_ProjectIdMismatch();
332
-
333
- // Deploy the suckers (if applicable).
334
- if (suckerDeploymentConfiguration.salt != bytes32(0)) {
335
- // Deploy the suckers.
336
- // Note: the salt includes `_msgSender()` for replay protection (see above).
337
- // slither-disable-next-line unused-return
338
- suckers = SUCKER_REGISTRY.deploySuckersFor({
339
- projectId: projectId,
340
- salt: keccak256(abi.encode(suckerDeploymentConfiguration.salt, _msgSender())),
341
- configurations: suckerDeploymentConfiguration.deployerConfigurations
342
- });
343
- }
344
-
345
- // Transfer ownership of the project to the owner.
346
- PROJECTS.transferFrom(address(this), owner, projectId);
347
- }
348
-
349
288
  /// @notice Launches a new project with a 721 tiers hook attached, and with suckers.
350
289
  /// @param owner The address to set as the owner of the project. The ERC-721 which confers this project's ownership
351
290
  /// will be sent to this address.
@@ -423,44 +362,6 @@ contract JBOmnichainDeployer is
423
362
  PROJECTS.transferFrom(address(this), owner, projectId);
424
363
  }
425
364
 
426
- /// @notice Launches new rulesets for a project, using this contract as the data hook.
427
- /// @param projectId The ID of the project to launch the rulesets for.
428
- /// @param rulesetConfigurations The rulesets to launch.
429
- /// @param terminalConfigurations The terminals to set up for the project.
430
- /// @param memo A memo to pass along to the emitted event.
431
- /// @param controller The controller to use for launching the rulesets.
432
- /// @return rulesetId The ID of the newly launched rulesets.
433
- function launchRulesetsFor(
434
- uint256 projectId,
435
- JBRulesetConfig[] calldata rulesetConfigurations,
436
- JBTerminalConfig[] calldata terminalConfigurations,
437
- string calldata memo,
438
- IJBController controller
439
- )
440
- external
441
- override
442
- returns (uint256)
443
- {
444
- // Enforce permissions.
445
- _requirePermissionFrom({
446
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.QUEUE_RULESETS
447
- });
448
-
449
- _requirePermissionFrom({
450
- account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
451
- });
452
-
453
- // Validate that the controller matches the project's controller in the directory.
454
- _validateController({projectId: projectId, controller: controller});
455
-
456
- return controller.launchRulesetsFor({
457
- projectId: projectId,
458
- rulesetConfigurations: _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations}),
459
- terminalConfigurations: terminalConfigurations,
460
- memo: memo
461
- });
462
- }
463
-
464
365
  /// @notice Launches new rulesets for a project with a 721 tiers hook attached, using this contract as the data
465
366
  /// hook.
466
367
  /// @param projectId The ID of the project to launch the rulesets for.
@@ -525,15 +426,77 @@ contract JBOmnichainDeployer is
525
426
  });
526
427
  }
527
428
 
528
- /// @notice Queues new rulesets for a project, using this contract as the data hook.
529
- /// @param projectId The ID of the project to queue the rulesets for.
429
+ /// @notice Creates a project with suckers.
430
+ /// @dev This will mint the project's ERC-721 to the `owner`'s address, queue the specified rulesets, and set up the
431
+ /// specified splits and terminals. Each operation within this transaction can be done in sequence separately.
432
+ /// @dev Anyone can deploy a project to any `owner`'s address.
433
+ /// @param owner The project's owner. The project ERC-721 will be minted to this address.
434
+ /// @param projectUri The project's metadata URI. This is typically an IPFS hash, optionally with the `ipfs://`
435
+ /// prefix. This can be updated by the project's owner.
530
436
  /// @param rulesetConfigurations The rulesets to queue.
437
+ /// @param terminalConfigurations The terminals to set up for the project.
531
438
  /// @param memo A memo to pass along to the emitted event.
532
- /// @param controller The controller to use for queuing the rulesets.
533
- /// @return rulesetId The ID of the newly queued rulesets.
534
- function queueRulesetsOf(
439
+ /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
440
+ /// token transfers between peer projects on different networks.
441
+ /// @param controller The controller to use for launching the project.
442
+ /// @return projectId The project's ID.
443
+ function launchProjectFor(
444
+ address owner,
445
+ string calldata projectUri,
446
+ JBRulesetConfig[] memory rulesetConfigurations,
447
+ JBTerminalConfig[] calldata terminalConfigurations,
448
+ string calldata memo,
449
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
450
+ IJBController controller
451
+ )
452
+ external
453
+ override
454
+ returns (uint256 projectId, address[] memory suckers)
455
+ {
456
+ // Get the next project ID.
457
+ projectId = PROJECTS.count() + 1;
458
+
459
+ rulesetConfigurations = _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations});
460
+
461
+ // Launch the project.
462
+ if (
463
+ projectId
464
+ != controller.launchProjectFor({
465
+ owner: address(this),
466
+ projectUri: projectUri,
467
+ rulesetConfigurations: rulesetConfigurations,
468
+ terminalConfigurations: terminalConfigurations,
469
+ memo: memo
470
+ })
471
+ ) revert JBOmnichainDeployer_ProjectIdMismatch();
472
+
473
+ // Deploy the suckers (if applicable).
474
+ if (suckerDeploymentConfiguration.salt != bytes32(0)) {
475
+ // Deploy the suckers.
476
+ // Note: the salt includes `_msgSender()` for replay protection (see above).
477
+ // slither-disable-next-line unused-return
478
+ suckers = SUCKER_REGISTRY.deploySuckersFor({
479
+ projectId: projectId,
480
+ salt: keccak256(abi.encode(suckerDeploymentConfiguration.salt, _msgSender())),
481
+ configurations: suckerDeploymentConfiguration.deployerConfigurations
482
+ });
483
+ }
484
+
485
+ // Transfer ownership of the project to the owner.
486
+ PROJECTS.transferFrom(address(this), owner, projectId);
487
+ }
488
+
489
+ /// @notice Launches new rulesets for a project, using this contract as the data hook.
490
+ /// @param projectId The ID of the project to launch the rulesets for.
491
+ /// @param rulesetConfigurations The rulesets to launch.
492
+ /// @param terminalConfigurations The terminals to set up for the project.
493
+ /// @param memo A memo to pass along to the emitted event.
494
+ /// @param controller The controller to use for launching the rulesets.
495
+ /// @return rulesetId The ID of the newly launched rulesets.
496
+ function launchRulesetsFor(
535
497
  uint256 projectId,
536
498
  JBRulesetConfig[] calldata rulesetConfigurations,
499
+ JBTerminalConfig[] calldata terminalConfigurations,
537
500
  string calldata memo,
538
501
  IJBController controller
539
502
  )
@@ -546,22 +509,29 @@ contract JBOmnichainDeployer is
546
509
  account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.QUEUE_RULESETS
547
510
  });
548
511
 
512
+ _requirePermissionFrom({
513
+ account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.SET_TERMINALS
514
+ });
515
+
549
516
  // Validate that the controller matches the project's controller in the directory.
550
517
  _validateController({projectId: projectId, controller: controller});
551
518
 
552
- // Revert if the project already had rulesets queued in this block, which would make our
553
- // `block.timestamp + i` ruleset ID prediction incorrect.
554
- if (controller.RULESETS().latestRulesetIdOf(projectId) >= block.timestamp) {
555
- revert JBOmnichainDeployer_RulesetIdsUnpredictable();
556
- }
557
-
558
- return controller.queueRulesetsOf({
519
+ return controller.launchRulesetsFor({
559
520
  projectId: projectId,
560
521
  rulesetConfigurations: _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations}),
522
+ terminalConfigurations: terminalConfigurations,
561
523
  memo: memo
562
524
  });
563
525
  }
564
526
 
527
+ /// @dev Make sure this contract can only receive project NFTs from `JBProjects`.
528
+ function onERC721Received(address, address, uint256, bytes calldata) external view returns (bytes4) {
529
+ // Make sure the 721 received is from the `JBProjects` contract.
530
+ if (msg.sender != address(PROJECTS)) revert JBOmnichainDeployer_UnexpectedNFTReceived();
531
+
532
+ return IERC721Receiver.onERC721Received.selector;
533
+ }
534
+
565
535
  /// @notice Queues new rulesets for a project with a 721 tiers hook attached, using this contract as the data hook.
566
536
  /// @param projectId The ID of the project to queue the rulesets for.
567
537
  /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
@@ -624,60 +594,50 @@ contract JBOmnichainDeployer is
624
594
  });
625
595
  }
626
596
 
627
- /// @dev Make sure this contract can only receive project NFTs from `JBProjects`.
628
- function onERC721Received(address, address, uint256, bytes calldata) external view returns (bytes4) {
629
- // Make sure the 721 received is from the `JBProjects` contract.
630
- if (msg.sender != address(PROJECTS)) revert JBOmnichainDeployer_UnexpectedNFTReceived();
631
-
632
- return IERC721Receiver.onERC721Received.selector;
633
- }
634
-
635
- //*********************************************************************//
636
- // ------------------------ internal functions ----------------------- //
637
- //*********************************************************************//
638
-
639
- /// @notice Validates that the provided controller matches the project's controller in the directory.
640
- /// @param projectId The ID of the project to validate the controller for.
641
- /// @param controller The controller to validate.
642
- function _validateController(uint256 projectId, IJBController controller) internal view {
643
- if (address(controller.DIRECTORY().controllerOf(projectId)) != address(controller)) {
644
- revert JBOmnichainDeployer_ControllerMismatch();
645
- }
646
- }
647
-
648
- /// @notice Sets up a project's rulesets.
649
- /// @dev Stores data hook configs keyed by predicted ruleset IDs (`block.timestamp + i`). This prediction is correct
650
- /// because `JBRulesets.queueFor` assigns IDs as: `latestId >= block.timestamp ? latestId + 1 : block.timestamp`.
651
- /// For new projects (launch*) and first rulesets (launchRulesets*), `latestId` starts at 0, so the first ID is
652
- /// always `block.timestamp` and subsequent IDs increment from there. For `queueRulesetsOf` on existing projects,
653
- /// callers must ensure `latestRulesetIdOf < block.timestamp` (i.e., no rulesets were queued earlier in this block).
654
- /// @param projectId The ID of the project to set up.
655
- /// @param rulesetConfigurations The rulesets to set up.
656
- /// @return rulesetConfigurations The rulesets that were set up.
657
- function _setup(
597
+ /// @notice Queues new rulesets for a project, using this contract as the data hook.
598
+ /// @param projectId The ID of the project to queue the rulesets for.
599
+ /// @param rulesetConfigurations The rulesets to queue.
600
+ /// @param memo A memo to pass along to the emitted event.
601
+ /// @param controller The controller to use for queuing the rulesets.
602
+ /// @return rulesetId The ID of the newly queued rulesets.
603
+ function queueRulesetsOf(
658
604
  uint256 projectId,
659
- JBRulesetConfig[] memory rulesetConfigurations
605
+ JBRulesetConfig[] calldata rulesetConfigurations,
606
+ string calldata memo,
607
+ IJBController controller
660
608
  )
661
- internal
662
- returns (JBRulesetConfig[] memory)
609
+ external
610
+ override
611
+ returns (uint256)
663
612
  {
664
- for (uint256 i; i < rulesetConfigurations.length; i++) {
665
- // Make sure there's no infinite loop.
666
- if (rulesetConfigurations[i].metadata.dataHook == address(this)) revert JBOmnichainDeployer_InvalidHook();
613
+ // Enforce permissions.
614
+ _requirePermissionFrom({
615
+ account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.QUEUE_RULESETS
616
+ });
667
617
 
668
- // Store the data hook keyed by predicted ruleset ID.
669
- _dataHookOf[projectId][block.timestamp + i] = JBDeployerHookConfig({
670
- useDataHookForPay: rulesetConfigurations[i].metadata.useDataHookForPay,
671
- useDataHookForCashOut: rulesetConfigurations[i].metadata.useDataHookForCashOut,
672
- dataHook: IJBRulesetDataHook(rulesetConfigurations[i].metadata.dataHook)
673
- });
618
+ // Validate that the controller matches the project's controller in the directory.
619
+ _validateController({projectId: projectId, controller: controller});
674
620
 
675
- // Set this contract as the data hook.
676
- rulesetConfigurations[i].metadata.dataHook = address(this);
677
- rulesetConfigurations[i].metadata.useDataHookForCashOut = true;
621
+ // Revert if the project already had rulesets queued in this block, which would make our
622
+ // `block.timestamp + i` ruleset ID prediction incorrect.
623
+ if (controller.RULESETS().latestRulesetIdOf(projectId) >= block.timestamp) {
624
+ revert JBOmnichainDeployer_RulesetIdsUnpredictable();
678
625
  }
679
626
 
680
- return rulesetConfigurations;
627
+ return controller.queueRulesetsOf({
628
+ projectId: projectId,
629
+ rulesetConfigurations: _setup({projectId: projectId, rulesetConfigurations: rulesetConfigurations}),
630
+ memo: memo
631
+ });
632
+ }
633
+
634
+ //*********************************************************************//
635
+ // ------------------------ internal functions ----------------------- //
636
+ //*********************************************************************//
637
+
638
+ /// @dev ERC-2771 specifies the context as being a single address (20 bytes).
639
+ function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
640
+ return ERC2771Context._contextSuffixLength();
681
641
  }
682
642
 
683
643
  /// @notice Converts a 721 ruleset configuration to a regular ruleset configuration.
@@ -745,8 +705,47 @@ contract JBOmnichainDeployer is
745
705
  return ERC2771Context._msgSender();
746
706
  }
747
707
 
748
- /// @dev ERC-2771 specifies the context as being a single address (20 bytes).
749
- function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
750
- return ERC2771Context._contextSuffixLength();
708
+ /// @notice Sets up a project's rulesets.
709
+ /// @dev Stores data hook configs keyed by predicted ruleset IDs (`block.timestamp + i`). This prediction is correct
710
+ /// because `JBRulesets.queueFor` assigns IDs as: `latestId >= block.timestamp ? latestId + 1 : block.timestamp`.
711
+ /// For new projects (launch*) and first rulesets (launchRulesets*), `latestId` starts at 0, so the first ID is
712
+ /// always `block.timestamp` and subsequent IDs increment from there. For `queueRulesetsOf` on existing projects,
713
+ /// callers must ensure `latestRulesetIdOf < block.timestamp` (i.e., no rulesets were queued earlier in this block).
714
+ /// @param projectId The ID of the project to set up.
715
+ /// @param rulesetConfigurations The rulesets to set up.
716
+ /// @return rulesetConfigurations The rulesets that were set up.
717
+ function _setup(
718
+ uint256 projectId,
719
+ JBRulesetConfig[] memory rulesetConfigurations
720
+ )
721
+ internal
722
+ returns (JBRulesetConfig[] memory)
723
+ {
724
+ for (uint256 i; i < rulesetConfigurations.length; i++) {
725
+ // Make sure there's no infinite loop.
726
+ if (rulesetConfigurations[i].metadata.dataHook == address(this)) revert JBOmnichainDeployer_InvalidHook();
727
+
728
+ // Store the data hook keyed by predicted ruleset ID.
729
+ _dataHookOf[projectId][block.timestamp + i] = JBDeployerHookConfig({
730
+ useDataHookForPay: rulesetConfigurations[i].metadata.useDataHookForPay,
731
+ useDataHookForCashOut: rulesetConfigurations[i].metadata.useDataHookForCashOut,
732
+ dataHook: IJBRulesetDataHook(rulesetConfigurations[i].metadata.dataHook)
733
+ });
734
+
735
+ // Set this contract as the data hook.
736
+ rulesetConfigurations[i].metadata.dataHook = address(this);
737
+ rulesetConfigurations[i].metadata.useDataHookForCashOut = true;
738
+ }
739
+
740
+ return rulesetConfigurations;
741
+ }
742
+
743
+ /// @notice Validates that the provided controller matches the project's controller in the directory.
744
+ /// @param projectId The ID of the project to validate the controller for.
745
+ /// @param controller The controller to validate.
746
+ function _validateController(uint256 projectId, IJBController controller) internal view {
747
+ if (address(controller.DIRECTORY().controllerOf(projectId)) != address(controller)) {
748
+ revert JBOmnichainDeployer_ControllerMismatch();
749
+ }
751
750
  }
752
751
  }
@@ -13,6 +13,7 @@ import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.
13
13
  import {JBDeployerHookConfig} from "../structs/JBDeployerHookConfig.sol";
14
14
  import {JBSuckerDeploymentConfig} from "../structs/JBSuckerDeploymentConfig.sol";
15
15
 
16
+ /// @notice Deploys Juicebox projects with omnichain sucker support.
16
17
  interface IJBOmnichainDeployer {
17
18
  /// @notice Get the data hook for a project and ruleset.
18
19
  /// @param projectId The ID of the project to get the data hook for.
@@ -39,29 +40,6 @@ interface IJBOmnichainDeployer {
39
40
  external
40
41
  returns (address[] memory suckers);
41
42
 
42
- /// @notice Creates a project with suckers.
43
- /// @param owner The project's owner. The project ERC-721 will be minted to this address.
44
- /// @param projectUri The project's metadata URI.
45
- /// @param rulesetConfigurations The rulesets to queue.
46
- /// @param terminalConfigurations The terminals to set up for the project.
47
- /// @param memo A memo to pass along to the emitted event.
48
- /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
49
- /// token transfers between peer projects on different networks.
50
- /// @param controller The controller to use for launching the project.
51
- /// @return projectId The project's ID.
52
- /// @return suckers The addresses of the deployed suckers.
53
- function launchProjectFor(
54
- address owner,
55
- string calldata projectUri,
56
- JBRulesetConfig[] memory rulesetConfigurations,
57
- JBTerminalConfig[] calldata terminalConfigurations,
58
- string calldata memo,
59
- JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
60
- IJBController controller
61
- )
62
- external
63
- returns (uint256 projectId, address[] memory suckers);
64
-
65
43
  /// @notice Launches a new project with a 721 tiers hook attached, and with suckers.
66
44
  /// @param owner The address to set as the owner of the project.
67
45
  /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
@@ -84,23 +62,6 @@ interface IJBOmnichainDeployer {
84
62
  external
85
63
  returns (uint256 projectId, IJB721TiersHook hook, address[] memory suckers);
86
64
 
87
- /// @notice Launches new rulesets for a project, using this contract as the data hook.
88
- /// @param projectId The ID of the project to launch the rulesets for.
89
- /// @param rulesetConfigurations The rulesets to launch.
90
- /// @param terminalConfigurations The terminals to set up for the project.
91
- /// @param memo A memo to pass along to the emitted event.
92
- /// @param controller The controller to use for launching the rulesets.
93
- /// @return rulesetId The ID of the newly launched rulesets.
94
- function launchRulesetsFor(
95
- uint256 projectId,
96
- JBRulesetConfig[] calldata rulesetConfigurations,
97
- JBTerminalConfig[] calldata terminalConfigurations,
98
- string calldata memo,
99
- IJBController controller
100
- )
101
- external
102
- returns (uint256 rulesetId);
103
-
104
65
  /// @notice Launches new rulesets for a project with a 721 tiers hook attached.
105
66
  /// @param projectId The ID of the project to launch the rulesets for.
106
67
  /// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook.
@@ -119,15 +80,40 @@ interface IJBOmnichainDeployer {
119
80
  external
120
81
  returns (uint256 rulesetId, IJB721TiersHook hook);
121
82
 
122
- /// @notice Queues new rulesets for a project, using this contract as the data hook.
123
- /// @param projectId The ID of the project to queue the rulesets for.
83
+ /// @notice Creates a project with suckers.
84
+ /// @param owner The project's owner. The project ERC-721 will be minted to this address.
85
+ /// @param projectUri The project's metadata URI.
124
86
  /// @param rulesetConfigurations The rulesets to queue.
87
+ /// @param terminalConfigurations The terminals to set up for the project.
125
88
  /// @param memo A memo to pass along to the emitted event.
126
- /// @param controller The controller to use for queuing the rulesets.
127
- /// @return rulesetId The ID of the newly queued rulesets.
128
- function queueRulesetsOf(
89
+ /// @param suckerDeploymentConfiguration The suckers to set up for the project. Suckers facilitate cross-chain
90
+ /// token transfers between peer projects on different networks.
91
+ /// @param controller The controller to use for launching the project.
92
+ /// @return projectId The project's ID.
93
+ /// @return suckers The addresses of the deployed suckers.
94
+ function launchProjectFor(
95
+ address owner,
96
+ string calldata projectUri,
97
+ JBRulesetConfig[] memory rulesetConfigurations,
98
+ JBTerminalConfig[] calldata terminalConfigurations,
99
+ string calldata memo,
100
+ JBSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
101
+ IJBController controller
102
+ )
103
+ external
104
+ returns (uint256 projectId, address[] memory suckers);
105
+
106
+ /// @notice Launches new rulesets for a project, using this contract as the data hook.
107
+ /// @param projectId The ID of the project to launch the rulesets for.
108
+ /// @param rulesetConfigurations The rulesets to launch.
109
+ /// @param terminalConfigurations The terminals to set up for the project.
110
+ /// @param memo A memo to pass along to the emitted event.
111
+ /// @param controller The controller to use for launching the rulesets.
112
+ /// @return rulesetId The ID of the newly launched rulesets.
113
+ function launchRulesetsFor(
129
114
  uint256 projectId,
130
115
  JBRulesetConfig[] calldata rulesetConfigurations,
116
+ JBTerminalConfig[] calldata terminalConfigurations,
131
117
  string calldata memo,
132
118
  IJBController controller
133
119
  )
@@ -151,4 +137,19 @@ interface IJBOmnichainDeployer {
151
137
  )
152
138
  external
153
139
  returns (uint256 rulesetId, IJB721TiersHook hook);
140
+
141
+ /// @notice Queues new rulesets for a project, using this contract as the data hook.
142
+ /// @param projectId The ID of the project to queue the rulesets for.
143
+ /// @param rulesetConfigurations The rulesets to queue.
144
+ /// @param memo A memo to pass along to the emitted event.
145
+ /// @param controller The controller to use for queuing the rulesets.
146
+ /// @return rulesetId The ID of the newly queued rulesets.
147
+ function queueRulesetsOf(
148
+ uint256 projectId,
149
+ JBRulesetConfig[] calldata rulesetConfigurations,
150
+ string calldata memo,
151
+ IJBController controller
152
+ )
153
+ external
154
+ returns (uint256 rulesetId);
154
155
  }