@croptop/core-v6 0.0.51 → 0.0.53

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,19 +1,16 @@
1
1
  {
2
2
  "name": "@croptop/core-v6",
3
- "version": "0.0.51",
3
+ "version": "0.0.53",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/mejango/croptop-core-v6"
8
8
  },
9
9
  "files": [
10
- "CHANGELOG.md",
11
10
  "foundry.toml",
12
11
  "references/",
13
12
  "remappings.txt",
14
- "script/ConfigureFeeProject.s.sol",
15
- "script/Deploy.s.sol",
16
- "script/helpers/",
13
+ "script/",
17
14
  "src/"
18
15
  ],
19
16
  "engines": {
@@ -29,12 +26,12 @@
29
26
  "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v6'"
30
27
  },
31
28
  "dependencies": {
32
- "@bananapus/721-hook-v6": "^0.0.52",
33
- "@bananapus/core-v6": "^0.0.55",
34
- "@bananapus/ownable-v6": "^0.0.26",
35
- "@bananapus/permission-ids-v6": "^0.0.25",
36
- "@bananapus/router-terminal-v6": "^0.0.45",
37
- "@bananapus/suckers-v6": "^0.0.48",
29
+ "@bananapus/721-hook-v6": "^0.0.54",
30
+ "@bananapus/core-v6": "^0.0.57",
31
+ "@bananapus/ownable-v6": "^0.0.27",
32
+ "@bananapus/permission-ids-v6": "^0.0.26",
33
+ "@bananapus/router-terminal-v6": "^0.0.46",
34
+ "@bananapus/suckers-v6": "^0.0.49",
38
35
  "@openzeppelin/contracts": "5.6.1",
39
36
  "@rev-net/core-v6": "^0.0.58"
40
37
  },
@@ -257,8 +257,9 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
257
257
  }
258
258
  }
259
259
 
260
- // Transfer the project NFT to its intended owner.
261
- PROJECTS.transferFrom({from: address(this), to: owner, tokenId: projectId});
260
+ // Transfer the project NFT to its intended owner. Use the safe path so contract owners must explicitly
261
+ // support receiving the project NFT before the launch can finalize.
262
+ PROJECTS.safeTransferFrom(address(this), owner, projectId);
262
263
 
263
264
  // Give the initial project owner direct collection-control permissions while CTDeployer remains the hook's
264
265
  // owner. This preserves the documented Croptop launch tradeoff: the owner can manage the collection directly
@@ -333,6 +334,8 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
333
334
  )
334
335
  {
335
336
  // If the cash out is from a sucker, return the full cash out amount without taxes or fees.
337
+ // Sucker cash-outs are the bridge accounting path: the value moving out of this chain must stay proportional
338
+ // to this chain's local backing. Do not add remote supply/surplus here.
336
339
  if (SUCKER_REGISTRY.isSuckerOf({projectId: context.projectId, addr: context.holder})) {
337
340
  return (0, context.cashOutCount, context.totalSupply, context.surplus.value, hookSpecifications);
338
341
  }
@@ -145,8 +145,9 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
145
145
  revert CTPublisher_ZeroTotalSupply({hook: allowedPost.hook, category: allowedPost.category});
146
146
  }
147
147
 
148
- // Make sure the minimum supply does not surpass the maximum supply.
149
- if (allowedPost.minimumTotalSupply > allowedPost.maximumTotalSupply) {
148
+ // Make sure the minimum supply does not surpass the maximum supply. A max of 0 means unlimited.
149
+ if (allowedPost.maximumTotalSupply != 0 && allowedPost.minimumTotalSupply > allowedPost.maximumTotalSupply)
150
+ {
150
151
  revert CTPublisher_MaxTotalSupplyLessThanMin({
151
152
  min: allowedPost.minimumTotalSupply, max: allowedPost.maximumTotalSupply
152
153
  });
@@ -380,8 +381,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
380
381
  /// @return minimumPrice The minimum price that a poster must pay to record a new NFT.
381
382
  /// @return minimumTotalSupply The minimum total number of available tokens that a minter must set to record a new
382
383
  /// NFT.
383
- /// @return maximumTotalSupply The max total supply of NFTs that can be made available when minting. Must be >=
384
- /// minimumTotalSupply.
384
+ /// @return maximumTotalSupply The max total supply of NFTs that can be made available when minting. 0 means
385
+ /// unlimited.
385
386
  /// @return maximumSplitPercent The maximum split percent that a poster can set. 0 means splits are not allowed.
386
387
  /// @return allowedAddresses The addresses allowed to post. Returns empty if all addresses are allowed.
387
388
  function allowanceFor(
@@ -423,6 +424,10 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
423
424
  //*********************************************************************//
424
425
 
425
426
  /// @notice Setup the posts.
427
+ /// @dev `adjustTiers` expects newly added tiers to be sorted by ascending category, while the pay metadata must
428
+ /// keep `tierIdsToMint` aligned with the caller's `posts` array so the requested NFTs are minted in the requested
429
+ /// order. `_setupPosts` therefore sorts only the tier configs and carries each config's original post index beside
430
+ /// it.
426
431
  /// @param hook The NFT hook on which the posts will apply.
427
432
  /// @param posts An array of posts that should be published as NFTs to the specified project.
428
433
  /// @return tiersToAdd The tiers that will be created to represent the posts.
@@ -441,6 +446,10 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
441
446
  // Set the size of the tier IDs of the posts that should be minted once published.
442
447
  tierIdsToMint = new uint256[](posts.length);
443
448
 
449
+ // Track which post produced each new tier. `tiersToAdd` may be sorted before it is sent to the 721 hook, but
450
+ // `tierIdsToMint` must stay indexed by the original `posts` array for the mint metadata below.
451
+ uint256[] memory newTierPostIndexes = new uint256[](posts.length);
452
+
444
453
  // Keep a reference to the hook's store for tier lookups.
445
454
  IJB721TiersHookStore store = hook.STORE();
446
455
 
@@ -532,8 +541,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
532
541
  }
533
542
 
534
543
  // Make sure the total supply being made available for the post is at most the allowed maximum total
535
- // supply.
536
- if (post.totalSupply > maximumTotalSupply) {
544
+ // supply. A max of 0 means unlimited.
545
+ if (maximumTotalSupply != 0 && post.totalSupply > maximumTotalSupply) {
537
546
  revert CTPublisher_TotalSupplyTooBig({
538
547
  totalSupply: post.totalSupply, maximumTotalSupply: maximumTotalSupply
539
548
  });
@@ -575,11 +584,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
575
584
  splits: post.splits
576
585
  });
577
586
 
578
- // Set the ID of the tier to mint.
579
- tierIdsToMint[i] = startingTierId + numberOfTiersBeingAdded++;
580
-
581
- // Save the encodedIpfsUri as minted.
582
- tierIdForEncodedIpfsUriOf[address(hook)][post.encodedIpfsUri] = tierIdsToMint[i];
587
+ newTierPostIndexes[numberOfTiersBeingAdded] = i;
588
+ numberOfTiersBeingAdded++;
583
589
 
584
590
  // For new tiers, use the post's price for totalPrice accumulation.
585
591
  totalPrice += post.price;
@@ -590,6 +596,49 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
590
596
  }
591
597
  }
592
598
 
599
+ // The 721 store requires new tiers sorted by ascending category. This insertion sort normalizes caller input
600
+ // so multi-category publishes do not revert just because posts were supplied in mint order instead of category
601
+ // order. It is intentionally stable: equal-category posts stay in caller order because the loop only moves
602
+ // prior tiers with a strictly greater category.
603
+ for (uint256 i = 1; i < numberOfTiersBeingAdded;) {
604
+ // Keep the tier and its original post index together while shifting larger-category tiers to the right.
605
+ JB721TierConfig memory tierToSort = tiersToAdd[i];
606
+ uint256 postIndexToSort = newTierPostIndexes[i];
607
+ uint256 j = i;
608
+
609
+ while (j != 0 && tiersToAdd[j - 1].category > tierToSort.category) {
610
+ // Shift both arrays in lockstep; otherwise the tier ID assigned after sorting would be written back to
611
+ // the wrong post in `tierIdsToMint`.
612
+ tiersToAdd[j] = tiersToAdd[j - 1];
613
+ newTierPostIndexes[j] = newTierPostIndexes[j - 1];
614
+
615
+ unchecked {
616
+ --j;
617
+ }
618
+ }
619
+
620
+ // Insert the tier at its category-sorted position with the original post index still attached.
621
+ tiersToAdd[j] = tierToSort;
622
+ newTierPostIndexes[j] = postIndexToSort;
623
+
624
+ unchecked {
625
+ ++i;
626
+ }
627
+ }
628
+
629
+ // Now that the store-facing tier order is final, translate each newly-created tier ID back into the caller's
630
+ // post order. The pay metadata expects this array to line up with `posts`, not with the sorted `tiersToAdd`.
631
+ for (uint256 i; i < numberOfTiersBeingAdded;) {
632
+ uint256 postIndex = newTierPostIndexes[i];
633
+ uint256 tierId = startingTierId + i;
634
+ tierIdsToMint[postIndex] = tierId;
635
+ tierIdForEncodedIpfsUriOf[address(hook)][posts[postIndex].encodedIpfsUri] = tierId;
636
+
637
+ unchecked {
638
+ ++i;
639
+ }
640
+ }
641
+
593
642
  // Resize the array if there's a mismatch in length.
594
643
  if (numberOfTiersBeingAdded != posts.length) {
595
644
  assembly ("memory-safe") {
@@ -40,7 +40,7 @@ interface ICTPublisher {
40
40
  /// @param category The category for which this allowance applies.
41
41
  /// @return minimumPrice The minimum price a poster must pay to publish a new NFT.
42
42
  /// @return minimumTotalSupply The minimum total supply a poster must set for a new NFT.
43
- /// @return maximumTotalSupply The maximum total supply allowed for a new NFT. Must be >= minimumTotalSupply.
43
+ /// @return maximumTotalSupply The maximum total supply allowed for a new NFT. 0 means unlimited.
44
44
  /// @return maximumSplitPercent The maximum split percent allowed for a new NFT.
45
45
  /// @return allowedAddresses The addresses allowed to post. Empty if all addresses are allowed.
46
46
  function allowanceFor(
package/CHANGELOG.md DELETED
@@ -1,64 +0,0 @@
1
- # Changelog
2
-
3
- ## Scope
4
-
5
- This file describes the verified change from `croptop-core-v5` to the current `croptop-core-v6` repo.
6
-
7
- ## Current v6 surface
8
-
9
- - `CTDeployer`
10
- - `CTProjectOwner`
11
- - `CTPublisher`
12
- - `CTAllowedPost`
13
- - `CTDeployerAllowedPost`
14
- - `CTPost`
15
-
16
- ## 0.0.47 — Bump v6 deps to nana-core-v6 0.0.53 cohort
17
-
18
- - `@bananapus/core-v6`: `^0.0.49 → ^0.0.53` ([PR #145](https://github.com/Bananapus/nana-core-v6/pull/145)).
19
- - `@bananapus/721-hook-v6`: `^0.0.49 → ^0.0.50`.
20
- - `@bananapus/suckers-v6`: `^0.0.43 → ^0.0.46`.
21
- - All `JBRulesetMetadata` test literals patched to include `pauseCrossProjectFeeFreeInflows: false`.
22
-
23
- ## Summary
24
-
25
- - `CTPost` and the related allowlist structs now carry split-routing data, so a post can route part of its payment through `JBSplit[]` recipients.
26
- - The deployer now acts as the data-hook entry point instead of wiring the 721 hook directly, which is what enables the intended omnichain and sucker-aware cash-out behavior.
27
- - v6 closes several correctness gaps that were easy to miss in v5: duplicate posts in a batch are rejected, existing tiers use on-chain pricing instead of caller-supplied pricing, and stale tier mappings are recreated when tiers were removed externally.
28
- - The repo was moved to the v6 dependency set and Solidity `0.8.28`.
29
-
30
- ## Verified deltas
31
-
32
- - `CTPost` gained `splitPercent` and `JBSplit[] splits`.
33
- - `CTAllowedPost` and `CTDeployerAllowedPost` gained `maximumSplitPercent`.
34
- - `ICTPublisher.allowanceFor(...)` now returns five values instead of four because `maximumSplitPercent` is part of the result.
35
- - `CTDeployer` now points project metadata to itself as the data hook instead of pointing directly at the 721 hook.
36
- - The repo carries dedicated regression tests for duplicate-URI fee evasion, stale tier mappings, and existing-tier pricing.
37
-
38
- ## Breaking ABI changes
39
-
40
- - `CTPost` is not v5-compatible because it now includes `splitPercent` and `splits`.
41
- - `CTAllowedPost` and `CTDeployerAllowedPost` are not v5-compatible because they now include `maximumSplitPercent`.
42
- - `ICTPublisher.allowanceFor(...)` return decoding changed because of the added field.
43
-
44
- ## Indexer impact
45
-
46
- - Any event or log decoding path that embeds `CTPost` or `CTAllowedPost` must be updated for the new struct layouts.
47
- - Post-publishing integrations should not assume the old "all payment goes to treasury" model once split-bearing posts are live.
48
-
49
- ## Migration notes
50
-
51
- - Rebuild any ABI or indexer code that decodes `CTPost` or `CTAllowedPost`. Their layouts are not v5-compatible.
52
- - If you integrated the deployer as if the 721 hook were the direct data hook, update that assumption. The deployer is now part of the routing path.
53
- - Re-check any fee logic that trusted caller-supplied prices for existing tiers. That is not the v6 behavior.
54
-
55
- ## ABI appendix
56
-
57
- - Changed structs
58
- - `CTPost`
59
- - `CTAllowedPost`
60
- - `CTDeployerAllowedPost`
61
- - Changed decoding expectations
62
- - `ICTPublisher.allowanceFor(...)`
63
- - Behaviorally important surface shift
64
- - deployer acts as the data-hook entrypoint