@croptop/core-v6 0.0.30 → 0.0.32

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/README.md CHANGED
@@ -90,4 +90,6 @@ script/
90
90
  - fee routing depends on the designated fee project remaining correctly configured; if its terminal rejects payments,
91
91
  Croptop refunds the fee to `_msgSender()` instead of trapping ETH in `CTPublisher`
92
92
  - burn-lock ownership is intentionally irreversible and should only be used when immutability is desired
93
+ - after burn-locking into `CTProjectOwner`, the previous owner no longer holds the project NFT directly; control is
94
+ intentionally mediated through Croptop's owner helper and hook-admin surface instead of remaining a plain owner EOA
93
95
  - duplicate-content and stale-tier edge cases are guarded by tests, but integrations should still treat metadata reuse carefully
package/USER_JOURNEYS.md CHANGED
@@ -52,6 +52,17 @@
52
52
  2. Restrict future edits to the paths Croptop intentionally exposes.
53
53
  3. Accept that this is a product-shaping choice, not a cosmetic deployment detail.
54
54
 
55
+ ## Journey 5: Support Cross-Chain Payments Through Data Hooks
56
+
57
+ **Starting state:** a sucker pays the Croptop project on behalf of a remote user via `payRemote`, and `CTDeployer.beforePayRecordedWith` needs to forward the correct beneficiary to downstream hooks.
58
+
59
+ **Success:** downstream data hooks see the real remote user so any hook-specific accounting accrues to the right person.
60
+
61
+ **Flow**
62
+ 1. The sucker calls `terminal.pay()` with relay-beneficiary metadata.
63
+ 2. `CTDeployer.beforePayRecordedWith()` resolves the relay beneficiary when the payer is a registered sucker.
64
+ 3. The swapped beneficiary is forwarded to the downstream data hook.
65
+
55
66
  ## Hand-Offs
56
67
 
57
68
  - Use [nana-721-hook-v6](../nana-721-hook-v6/USER_JOURNEYS.md) for the underlying tier issuance behavior Croptop wraps.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croptop/core-v6",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,18 +16,18 @@
16
16
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v5'"
17
17
  },
18
18
  "dependencies": {
19
- "@bananapus/721-hook-v6": "^0.0.31",
20
- "@bananapus/buyback-hook-v6": "^0.0.25",
21
- "@bananapus/core-v6": "^0.0.31",
19
+ "@bananapus/721-hook-v6": "^0.0.33",
20
+ "@bananapus/buyback-hook-v6": "^0.0.27",
21
+ "@bananapus/core-v6": "^0.0.34",
22
22
  "@bananapus/ownable-v6": "^0.0.17",
23
- "@bananapus/permission-ids-v6": "^0.0.15",
24
- "@bananapus/router-terminal-v6": "^0.0.25",
25
- "@bananapus/suckers-v6": "^0.0.21",
23
+ "@bananapus/permission-ids-v6": "^0.0.17",
24
+ "@bananapus/router-terminal-v6": "^0.0.26",
25
+ "@bananapus/suckers-v6": "^0.0.25",
26
26
  "@openzeppelin/contracts": "^5.6.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@bananapus/address-registry-v6": "^0.0.17",
30
- "@rev-net/core-v6": "^0.0.24",
31
- "@sphinx-labs/plugins": "^0.33.1"
30
+ "@rev-net/core-v6": "^0.0.30",
31
+ "@sphinx-labs/plugins": "^0.33.3"
32
32
  }
33
- }
33
+ }
@@ -28,6 +28,7 @@ import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.so
28
28
  import {JBOwnable} from "@bananapus/ownable-v6/src/JBOwnable.sol";
29
29
  import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
30
30
  import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
31
+ import {JBRelayBeneficiary} from "@bananapus/suckers-v6/src/libraries/JBRelayBeneficiary.sol";
31
32
 
32
33
  import {ICTDeployer} from "./interfaces/ICTDeployer.sol";
33
34
  import {ICTPublisher} from "./interfaces/ICTPublisher.sol";
@@ -172,6 +173,24 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
172
173
  if (address(hook) == address(0)) {
173
174
  return (context.weight, hookSpecifications);
174
175
  }
176
+
177
+ // Resolve the relay beneficiary — if the payer is a sucker with relay metadata,
178
+ // swap the beneficiary so downstream hooks see the real user.
179
+ address effectiveBeneficiary = JBRelayBeneficiary.resolve({
180
+ payer: context.payer,
181
+ beneficiary: context.beneficiary,
182
+ projectId: context.projectId,
183
+ metadata: context.metadata,
184
+ registry: SUCKER_REGISTRY
185
+ });
186
+
187
+ // If the beneficiary was swapped, create a memory copy with the new beneficiary.
188
+ if (effectiveBeneficiary != context.beneficiary) {
189
+ JBBeforePayRecordedContext memory hookContext = context;
190
+ hookContext.beneficiary = effectiveBeneficiary;
191
+ return hook.beforePayRecordedWith(hookContext);
192
+ }
193
+
175
194
  // slither-disable-next-line unused-return
176
195
  return hook.beforePayRecordedWith(context);
177
196
  }
@@ -407,7 +426,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
407
426
  CTDeployerAllowedPost memory post;
408
427
 
409
428
  // Iterate through each post to add it to the formatted list.
410
- for (uint256 i; i < numberOfAllowedPosts; i++) {
429
+ for (uint256 i; i < numberOfAllowedPosts;) {
411
430
  // Set the post being iterated on.
412
431
  post = allowedPosts[i];
413
432
 
@@ -421,6 +440,10 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
421
440
  maximumSplitPercent: post.maximumSplitPercent,
422
441
  allowedAddresses: post.allowedAddresses
423
442
  });
443
+
444
+ unchecked {
445
+ ++i;
446
+ }
424
447
  }
425
448
 
426
449
  // Set up the allowed posts in the publisher.
@@ -134,7 +134,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
134
134
  tiers = new JB721Tier[](numberOfEncodedIPFSUris);
135
135
 
136
136
  // Get the tier for each provided encoded IPFS URI.
137
- for (uint256 i; i < numberOfEncodedIPFSUris; i++) {
137
+ for (uint256 i; i < numberOfEncodedIPFSUris;) {
138
138
  // Check if there's a tier ID stored for the encoded IPFS URI.
139
139
  uint256 tierId = tierIdForEncodedIPFSUriOf[hook][encodedIPFSUris[i]];
140
140
 
@@ -143,6 +143,10 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
143
143
  // slither-disable-next-line calls-loop
144
144
  tiers[i] = IJB721TiersHook(hook).STORE().tierOf({hook: hook, id: tierId, includeResolvedUri: false});
145
145
  }
146
+
147
+ unchecked {
148
+ ++i;
149
+ }
146
150
  }
147
151
  }
148
152
 
@@ -214,8 +218,11 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
214
218
  uint256 numberOfAddresses = addresses.length;
215
219
 
216
220
  // Check if the address is included
217
- for (uint256 i; i < numberOfAddresses; i++) {
221
+ for (uint256 i; i < numberOfAddresses;) {
218
222
  if (addrs == addresses[i]) return true;
223
+ unchecked {
224
+ ++i;
225
+ }
219
226
  }
220
227
 
221
228
  return false;
@@ -247,7 +254,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
247
254
  uint256 numberOfAllowedPosts = allowedPosts.length;
248
255
 
249
256
  // For each post criteria, save the specifications.
250
- for (uint256 i; i < numberOfAllowedPosts; i++) {
257
+ for (uint256 i; i < numberOfAllowedPosts;) {
251
258
  // Set the post criteria being iterated on.
252
259
  CTAllowedPost memory allowedPost = allowedPosts[i];
253
260
 
@@ -285,16 +292,16 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
285
292
  // Store the packed value.
286
293
  _packedAllowanceFor[allowedPost.hook][allowedPost.category] = packed;
287
294
 
288
- // Store the allow list.
289
- uint256 numberOfAddresses = allowedPost.allowedAddresses.length;
290
- // Reset the addresses.
291
- delete _allowedAddresses[allowedPost.hook][allowedPost.category];
292
- // Add the number allowed addresses.
293
- if (numberOfAddresses != 0) {
294
- // Keep a reference to the storage of the allowed addresses.
295
- for (uint256 j = 0; j < numberOfAddresses; j++) {
296
- _allowedAddresses[allowedPost.hook][allowedPost.category].push(allowedPost.allowedAddresses[j]);
297
- }
295
+ // Store the allow list. Direct assignment replaces the entire storage array in one operation,
296
+ // avoiding the gas overhead of delete + individual push() calls in a loop.
297
+ if (allowedPost.allowedAddresses.length != 0) {
298
+ _allowedAddresses[allowedPost.hook][allowedPost.category] = allowedPost.allowedAddresses;
299
+ } else {
300
+ delete _allowedAddresses[allowedPost.hook][allowedPost.category];
301
+ }
302
+
303
+ unchecked {
304
+ ++i;
298
305
  }
299
306
  }
300
307
  }
@@ -473,7 +480,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
473
480
 
474
481
  // For each post, create tiers after validating to make sure they fulfill the allowance specified by the
475
482
  // project's owner.
476
- for (uint256 i; i < posts.length; i++) {
483
+ for (uint256 i; i < posts.length;) {
477
484
  // Get the current post being iterated on.
478
485
  CTPost memory post = posts[i];
479
486
 
@@ -484,10 +491,13 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
484
491
  }
485
492
 
486
493
  // Check for duplicate encodedIPFSUri within the same batch to prevent fee evasion.
487
- for (uint256 j; j < i; j++) {
494
+ for (uint256 j; j < i;) {
488
495
  if (posts[j].encodedIPFSUri == post.encodedIPFSUri) {
489
496
  revert CTPublisher_DuplicatePost(post.encodedIPFSUri);
490
497
  }
498
+ unchecked {
499
+ ++j;
500
+ }
491
501
  }
492
502
 
493
503
  // Scoped section to prevent stack too deep.
@@ -499,7 +509,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
499
509
  // If the tier was removed externally (via adjustTiers), clear the stale mapping
500
510
  // so the code falls through to create a new tier.
501
511
  // slither-disable-next-line calls-loop
502
- if (hook.STORE().isTierRemoved(address(hook), tierId)) {
512
+ if (store.isTierRemoved(address(hook), tierId)) {
503
513
  delete tierIdForEncodedIPFSUriOf[address(hook)][post.encodedIPFSUri];
504
514
  } else {
505
515
  tierIdsToMint[i] = tierId;
@@ -590,6 +600,10 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
590
600
  // For new tiers, use the post's price for totalPrice accumulation.
591
601
  totalPrice += post.price;
592
602
  }
603
+
604
+ unchecked {
605
+ ++i;
606
+ }
593
607
  }
594
608
 
595
609
  // Resize the array if there's a mismatch in length.