@croptop/core-v6 0.0.54 → 0.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/CTDeployer.sol +54 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croptop/core-v6",
3
- "version": "0.0.54",
3
+ "version": "0.0.55",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -241,6 +241,12 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
241
241
  if (suckerDeploymentConfiguration.salt != bytes32(0)) {
242
242
  bytes32 suckerSalt = keccak256(abi.encode(suckerDeploymentConfiguration.salt, _msgSender()));
243
243
 
244
+ // A launch-time project is still owned by this deployer until the final NFT transfer, so check the
245
+ // intended owner before the registry sees `address(this)` as the current project owner.
246
+ _requireExplicitSuckerPeerPermissionFrom({
247
+ account: owner, projectId: projectId, suckerDeploymentConfiguration: suckerDeploymentConfiguration
248
+ });
249
+
244
250
  // Successful deployments are discoverable from the registry, and failures are reported without reverting
245
251
  // the project launch.
246
252
  try SUCKER_REGISTRY.deploySuckersFor({
@@ -282,7 +288,8 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
282
288
  }
283
289
 
284
290
  /// @notice Deploy new suckers for an existing project.
285
- /// @dev Only the juicebox's owner can deploy new suckers.
291
+ /// @dev Only the juicebox's owner or a `DEPLOY_SUCKERS` operator can deploy new suckers. Supplying an explicit
292
+ /// non-default peer also requires `SET_SUCKER_PEER`, matching the registry's direct-call rule.
286
293
  /// @param projectId The ID of the project to deploy suckers for.
287
294
  /// @param suckerDeploymentConfiguration The suckers to set up for the project.
288
295
  function deploySuckersFor(
@@ -292,11 +299,19 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
292
299
  external
293
300
  returns (address[] memory suckers)
294
301
  {
302
+ // Resolve the project owner once because Juicebox permissions are checked against the owner's permission table.
295
303
  address owner = PROJECTS.ownerOf(projectId);
296
304
 
297
- // First prove the external caller is allowed to request sucker deployment for the project owner.
305
+ // `DEPLOY_SUCKERS` authorizes this wrapper to ask the registry for new suckers, but it does not authorize
306
+ // choosing a non-default remote peer.
298
307
  _requirePermissionFrom({account: owner, projectId: projectId, permissionId: JBPermissionIds.DEPLOY_SUCKERS});
299
308
 
309
+ // Mirror the registry's explicit-peer gate against the original project authority before this wrapper becomes
310
+ // the registry caller.
311
+ _requireExplicitSuckerPeerPermissionFrom({
312
+ account: owner, projectId: projectId, suckerDeploymentConfiguration: suckerDeploymentConfiguration
313
+ });
314
+
300
315
  // Deploy the suckers. The sucker registry performs its own permission check against this forwarding helper,
301
316
  // so an unapproved CTDeployer fails at the downstream registry boundary without an extra preflight read here.
302
317
  suckers = SUCKER_REGISTRY.deploySuckersFor({
@@ -484,4 +499,41 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
484
499
  function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
485
500
  return ERC2771Context._msgSender();
486
501
  }
502
+
503
+ /// @notice Revert unless the caller may set explicit sucker peers for `projectId`.
504
+ /// @dev The registry enforces this against its direct caller. Since this deployer wraps the registry call, it must
505
+ /// mirror the check against the original caller so `DEPLOY_SUCKERS` alone cannot smuggle in arbitrary peers.
506
+ /// @param account The project owner account whose permission table is checked.
507
+ /// @param projectId The ID of the project to deploy suckers for.
508
+ /// @param suckerDeploymentConfiguration The sucker deployment configuration to inspect.
509
+ function _requireExplicitSuckerPeerPermissionFrom(
510
+ address account,
511
+ uint256 projectId,
512
+ CTSuckerDeploymentConfig calldata suckerDeploymentConfiguration
513
+ )
514
+ internal
515
+ view
516
+ {
517
+ // Scan every requested sucker configuration because a single explicit peer changes cross-chain authority.
518
+ for (uint256 i; i < suckerDeploymentConfiguration.deployerConfigurations.length;) {
519
+ // Cache the configured peer so the default/explicit branch is evaluated from the exact value sent onward.
520
+ bytes32 peer = suckerDeploymentConfiguration.deployerConfigurations[i].peer;
521
+
522
+ // `peer == 0` preserves the sucker's deterministic same-address peer behavior.
523
+ // Any nonzero peer is written directly into the new sucker and changes who can deliver remote roots.
524
+ if (peer != bytes32(0)) {
525
+ // Require the original project authority, not this wrapper, to authorize explicit remote peers.
526
+ _requirePermissionFrom({
527
+ account: account, projectId: projectId, permissionId: JBPermissionIds.SET_SUCKER_PEER
528
+ });
529
+ // One explicit peer is enough to prove the caller needs the stronger permission.
530
+ return;
531
+ }
532
+
533
+ unchecked {
534
+ // Skip overflow checks because `i` is bounded by the calldata array length.
535
+ ++i;
536
+ }
537
+ }
538
+ }
487
539
  }