@croptop/core-v6 0.0.66 → 0.0.67

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
@@ -1,6 +1,6 @@
1
1
  # Croptop Core
2
2
 
3
- Croptop turns a Juicebox project with a 721 hook into a permissioned publishing marketplace. Project owners define posting rules, then anyone who meets those rules can publish new NFT tiers and mint the first copy of each post.
3
+ `@croptop/core-v6` turns a Juicebox project with a 721 hook into a permissioned publishing marketplace. Project owners define posting rules, then anyone who meets those rules can publish new NFT tiers and mint the first copy of each post.
4
4
 
5
5
  Site: <https://croptop.eth.limo>
6
6
 
@@ -28,7 +28,7 @@ Every mint collects a 5% Croptop fee unless the target project is itself the fee
28
28
 
29
29
  Use this repo when the product is permissioned publishing on top of a Juicebox project. Do not use it for plain 721 tier sales.
30
30
 
31
- ## Key Contracts
31
+ ## Key contracts
32
32
 
33
33
  | Contract | Role |
34
34
  | --- | --- |
@@ -36,7 +36,7 @@ Use this repo when the product is permissioned publishing on top of a Juicebox p
36
36
  | `CTDeployer` | Launches a project, configures Croptop posting rules, and can wire in omnichain sucker deployments. |
37
37
  | `CTProjectOwner` | Ownership sink that can permanently hold a project NFT while still delegating the posting permissions Croptop needs. |
38
38
 
39
- ## Mental Model
39
+ ## Mental model
40
40
 
41
41
  There are two separate concerns here:
42
42
 
@@ -45,7 +45,7 @@ There are two separate concerns here:
45
45
 
46
46
  Many Croptop bugs are really deployment-shape bugs or posting-policy bugs, not generic 721 bugs.
47
47
 
48
- ## Read These Files First
48
+ ## Read these files first
49
49
 
50
50
  1. `src/CTPublisher.sol`
51
51
  2. `src/CTDeployer.sol`
@@ -53,7 +53,7 @@ Many Croptop bugs are really deployment-shape bugs or posting-policy bugs, not g
53
53
  4. `test/CTPublisher.t.sol`
54
54
  5. `test/ClaimCollectionOwnership.t.sol`
55
55
 
56
- ## High-Signal Tests
56
+ ## High-signal tests
57
57
 
58
58
  1. `test/CTPublisher.t.sol`
59
59
  2. `test/CTDeployer.t.sol`
@@ -61,14 +61,14 @@ Many Croptop bugs are really deployment-shape bugs or posting-policy bugs, not g
61
61
  4. `test/regression/FeeFallbackBlackhole.t.sol`
62
62
  5. `test/regression/DuplicateUriFeeEvasion.t.sol`
63
63
 
64
- ## Integration Traps
64
+ ## Integration traps
65
65
 
66
66
  - Croptop publishing policy is separate from ordinary 721 tier issuance
67
67
  - fee routing is part of the publish path and its fallback behavior matters
68
68
  - `CTProjectOwner` intentionally changes the ownership model and should be reviewed as part of the trust model
69
69
  - duplicate-content, stale-tier, and fee-evasion edge cases are runtime behavior, not only UI concerns
70
70
 
71
- ## Where State Lives
71
+ ## Where state lives
72
72
 
73
73
  - posting criteria and publish-side enforcement live in `CTPublisher`
74
74
  - deployment-time project wiring lives in `CTDeployer`
@@ -96,11 +96,11 @@ Useful scripts:
96
96
  - `npm run deploy:mainnets:project`
97
97
  - `npm run deploy:testnets:project`
98
98
 
99
- ## Deployment Notes
99
+ ## Deployment notes
100
100
 
101
101
  Deployments are handled through Sphinx. `CTDeployer` can also compose cross-chain sucker deployments when a nonzero sucker configuration is supplied. The deploy script expects an explicit nonzero `FEE_PROJECT_ID` for production-style deployments.
102
102
 
103
- ## Repository Layout
103
+ ## Repository layout
104
104
 
105
105
  ```text
106
106
  src/
@@ -117,7 +117,7 @@ script/
117
117
  helpers/
118
118
  ```
119
119
 
120
- ## Risks And Notes
120
+ ## Risks and notes
121
121
 
122
122
  - posting criteria are only as safe as the project owner configures them
123
123
  - fee routing depends on the fee project staying correctly configured
@@ -125,7 +125,7 @@ script/
125
125
  - after routing ownership into `CTProjectOwner`, the old owner no longer holds the project NFT directly
126
126
  - duplicate-content and stale-tier edge cases are economically relevant, not cosmetic
127
127
 
128
- ## For AI Agents
128
+ ## For AI agents
129
129
 
130
130
  - Do not describe Croptop as a generic 721 marketplace.
131
131
  - Read `CTPublisher` before `CTDeployer` when the question is about publish eligibility or fee behavior.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croptop/core-v6",
3
- "version": "0.0.66",
3
+ "version": "0.0.67",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,25 +1,25 @@
1
1
  # Croptop Operations
2
2
 
3
- ## Deployment Surface
3
+ ## Deployment surface
4
4
 
5
5
  - [`src/CTDeployer.sol`](../src/CTDeployer.sol) is the first stop for project launch shape, hook forwarding, and optional sucker integration.
6
6
  - [`script/Deploy.s.sol`](../script/Deploy.s.sol) and [`script/ConfigureFeeProject.s.sol`](../script/ConfigureFeeProject.s.sol) cover the current deployment and fee-project wiring.
7
7
  - [`src/structs/`](../src/structs/) contains config types that often drift from memory.
8
8
 
9
- ## Change Checklist
9
+ ## Change checklist
10
10
 
11
11
  - If you edit posting criteria, verify both direct publisher calls and deployer-created project flows.
12
12
  - If you edit fee behavior, check both the designated fee project path and any exemption behavior.
13
13
  - If you edit burn-lock ownership assumptions, confirm the intended irreversibility still holds.
14
14
  - If you edit data-hook forwarding, re-check sucker-related fee-free cash-out behavior.
15
15
 
16
- ## Common Failure Modes
16
+ ## Common failure modes
17
17
 
18
18
  - Publishing bug is blamed on the publisher when the deployer packaged the project or hook incorrectly.
19
19
  - Immutable-owner expectations are missed after ownership moves into [`src/CTProjectOwner.sol`](../src/CTProjectOwner.sol).
20
20
  - Content reuse or duplicate-post behavior changes and silently alters user-facing publishing semantics.
21
21
 
22
- ## Useful Proof Points
22
+ ## Useful proof points
23
23
 
24
24
  - [`test/Fork.t.sol`](../test/Fork.t.sol) when deployment shape matters.
25
25
  - [`script/ConfigureFeeProject.s.sol`](../script/ConfigureFeeProject.s.sol) if the issue is really script/config assembly.
@@ -1,26 +1,26 @@
1
1
  # Croptop Runtime
2
2
 
3
- ## Contract Roles
3
+ ## Contract roles
4
4
 
5
5
  - [`src/CTPublisher.sol`](../src/CTPublisher.sol) validates posts, configures or reuses tiers, mints first copies, and routes Croptop fees.
6
6
  - [`src/CTDeployer.sol`](../src/CTDeployer.sol) packages project deployment, hook forwarding, and optional sucker support.
7
7
  - [`src/CTProjectOwner.sol`](../src/CTProjectOwner.sol) is the burn-lock ownership helper for immutable administration patterns.
8
8
 
9
- ## Runtime Path
9
+ ## Runtime path
10
10
 
11
11
  1. A project is deployed or configured with Croptop posting rules.
12
12
  2. Publishers call into [`src/CTPublisher.sol`](../src/CTPublisher.sol) with content, supply, and pricing data.
13
13
  3. The publisher validates category-level rules, creates or reuses tiers, mints the first copy, and routes fees and proceeds.
14
14
  4. If the project uses the deployer wrapper, data-hook calls forward through [`src/CTDeployer.sol`](../src/CTDeployer.sol).
15
15
 
16
- ## High-Risk Areas
16
+ ## High-risk areas
17
17
 
18
18
  - Posting criteria: category rules are the policy surface that protects the project from bad content or bad economics.
19
19
  - Fee routing: fee-project assumptions and fee exemptions are operationally important.
20
20
  - Tier reuse and duplicate content: content identity is part of runtime behavior, not only metadata.
21
21
  - Burn-lock ownership: once ownership moves into the lock helper, reversibility expectations change drastically.
22
22
 
23
- ## Tests To Trust First
23
+ ## Tests to trust first
24
24
 
25
25
  - [`test/CTPublisher.t.sol`](../test/CTPublisher.t.sol) and [`test/Test_MetadataGeneration.t.sol`](../test/Test_MetadataGeneration.t.sol) for content and metadata behavior.
26
26
  - [`test/CTDeployer.t.sol`](../test/CTDeployer.t.sol) and [`test/Fork.t.sol`](../test/Fork.t.sol) for live deployment assumptions.
@@ -48,6 +48,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
48
48
 
49
49
  /// @notice Thrown when a caller is not the Juicebox project owner for the hook being claimed.
50
50
  error CTDeployer_NotOwnerOfProject(uint256 projectId, address hook, address caller);
51
+
51
52
  //*********************************************************************//
52
53
  // ---------------------------- events -------------------------------- //
53
54
  //*********************************************************************//
@@ -83,7 +84,6 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
83
84
 
84
85
  /// @notice Each project's data hook provided on deployment.
85
86
  /// @custom:param projectId The ID of the project to get the data hook for.
86
- /// @custom:param rulesetId The ID of the ruleset to get the data hook for.
87
87
  mapping(uint256 projectId => IJBRulesetDataHook) public dataHookOf;
88
88
 
89
89
  //*********************************************************************//
@@ -303,6 +303,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
303
303
  /// explicit non-default peer also requires `SET_SUCKER_PEER`, matching the registry's direct-call rule.
304
304
  /// @param projectId The ID of the project to deploy suckers for.
305
305
  /// @param suckerDeploymentConfiguration The suckers to set up for the project.
306
+ /// @return suckers The addresses of the deployed suckers.
306
307
  function deploySuckersFor(
307
308
  uint256 projectId,
308
309
  CTSuckerDeploymentConfig calldata suckerDeploymentConfiguration
@@ -414,7 +415,14 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
414
415
  return SUCKER_REGISTRY.isSuckerOf({projectId: projectId, addr: addr});
415
416
  }
416
417
 
417
- /// @dev Make sure only mints can be received.
418
+ /// @notice Accepts only freshly minted project NFTs sent directly by `JBProjects`.
419
+ /// @dev Rejecting transfers from a non-zero `from` ensures the deployer cannot be handed a project after launch.
420
+ /// @param operator Unused; the transfer is authenticated by `msg.sender` and `from`, not the operator.
421
+ /// @param from Unused except to gate acceptance; a non-zero prior owner means this is not a fresh mint, so it
422
+ /// reverts.
423
+ /// @param tokenId Unused; any freshly minted project NFT is accepted.
424
+ /// @param data Unused; no payload is expected on a project mint.
425
+ /// @return magicValue The `IERC721Receiver.onERC721Received` selector that signals a successful receipt.
418
426
  function onERC721Received(
419
427
  address operator,
420
428
  address from,
@@ -49,6 +49,11 @@ contract CTProjectOwner is IERC721Receiver, ICTProjectOwner {
49
49
 
50
50
  /// @notice Give the Croptop publisher permission to post to the project on this contract's behalf.
51
51
  /// @dev Configure posting criteria before transferring ownership here; this contract has no transfer-out function.
52
+ /// @param operator Unused; the transfer is authenticated by `msg.sender`, not the operator.
53
+ /// @param from Unused; any project NFT is accepted regardless of its prior owner.
54
+ /// @param tokenId The ID of the project NFT being received, used as the project ID the publisher is permitted on.
55
+ /// @param data Unused; no payload is expected on the transfer.
56
+ /// @return magicValue The `IERC721Receiver.onERC721Received` selector that signals a successful receipt.
52
57
  function onERC721Received(
53
58
  address operator,
54
59
  address from,
@@ -43,34 +43,84 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
43
43
  // --------------------------- custom errors ------------------------- //
44
44
  //*********************************************************************//
45
45
 
46
+ /// @notice Thrown when the caller's additional pay metadata already contains a pay ID, which would shadow Croptop's
47
+ /// tier selection.
46
48
  error CTPublisher_DuplicatePayMetadata(bytes4 payMetadataId);
49
+
50
+ /// @notice Thrown when the same encoded IPFS URI appears more than once within a single mint batch.
47
51
  error CTPublisher_DuplicatePost(bytes32 encodedIpfsUri);
52
+
53
+ /// @notice Thrown when a post is supplied without an encoded IPFS URI.
48
54
  error CTPublisher_EmptyEncodedIpfsUri(uint256 postIndex);
55
+
56
+ /// @notice Thrown when refunding a native-token fee to the caller fails.
49
57
  error CTPublisher_FeePaymentFailed(uint256 feeAmount);
58
+
59
+ /// @notice Thrown when the native token sent is less than the required price plus fee.
50
60
  error CTPublisher_InsufficientEthSent(uint256 expected, uint256 sent);
61
+
62
+ /// @notice Thrown when the ERC-20 amount supplied is less than the required price plus fee.
51
63
  error CTPublisher_InsufficientPayment(uint256 expected, uint256 sent);
64
+
65
+ /// @notice Thrown when the fee beneficiary is the zero address.
52
66
  error CTPublisher_InvalidFeeBeneficiary();
67
+
68
+ /// @notice Thrown when the terminal's accounting context does not match the payment token or has no currency.
53
69
  error CTPublisher_InvalidPaymentTokenContext(
54
70
  address token, uint256 tokenCurrency, uint256 tokenDecimals, uint256 pricingCurrency, uint256 pricingDecimals
55
71
  );
72
+
73
+ /// @notice Thrown when a configured maximum total supply is below the minimum total supply.
56
74
  error CTPublisher_MaxTotalSupplyLessThanMin(uint256 min, uint256 max);
75
+
76
+ /// @notice Thrown when the beneficiary's NFT balance did not increase by the expected mint count after payment.
57
77
  error CTPublisher_MintNotDelivered(
58
78
  address hook, address beneficiary, uint256 expectedBalance, uint256 actualBalance
59
79
  );
80
+
81
+ /// @notice Thrown when native value is sent alongside an ERC-20 payment.
60
82
  error CTPublisher_MsgValueNotAllowed(uint256 value);
83
+
84
+ /// @notice Thrown when the supplied amount does not equal `msg.value` for a native-token payment.
61
85
  error CTPublisher_NativeTokenAmountMismatch(uint256 amount, uint256 msgValue);
86
+
87
+ /// @notice Thrown when no posts are supplied to publish.
62
88
  error CTPublisher_NoPosts(address caller);
89
+
90
+ /// @notice Thrown when the caller is not in the category's allowlist.
63
91
  error CTPublisher_NotInAllowList(address addr, address[] allowedAddresses);
92
+
93
+ /// @notice Thrown when the amount exceeds the `uint160` maximum that Permit2 can transfer.
64
94
  error CTPublisher_OverflowAlert(uint256 value, uint256 limit);
95
+
96
+ /// @notice Thrown when the amount to pull exceeds the signed Permit2 allowance.
65
97
  error CTPublisher_PermitAllowanceNotEnough(uint256 amount, uint256 allowance);
98
+
99
+ /// @notice Thrown when no price feed is available to convert between the payment and pricing currencies.
66
100
  error CTPublisher_PriceFeedUnavailable(uint256 paymentCurrency, uint256 pricingCurrency);
101
+
102
+ /// @notice Thrown when a post's price is below the category's minimum price.
67
103
  error CTPublisher_PriceTooSmall(uint256 price, uint256 minimumPrice);
104
+
105
+ /// @notice Thrown when a token transfer is nested inside an in-progress balance-delta measurement.
68
106
  error CTPublisher_ReentrantTokenTransfer(address token);
107
+
108
+ /// @notice Thrown when a post's split percent exceeds the category's maximum split percent.
69
109
  error CTPublisher_SplitPercentExceedsMaximum(uint256 splitPercent, uint256 maximumSplitPercent);
110
+
111
+ /// @notice Thrown when a downstream terminal did not consume the exact-use ERC-20 allowance it was granted.
70
112
  error CTPublisher_TemporaryAllowanceNotConsumed(address token, address spender, uint256 allowance);
113
+
114
+ /// @notice Thrown when a post's total supply exceeds the category's maximum total supply.
71
115
  error CTPublisher_TotalSupplyTooBig(uint256 totalSupply, uint256 maximumTotalSupply);
116
+
117
+ /// @notice Thrown when a post's total supply is below the category's minimum total supply.
72
118
  error CTPublisher_TotalSupplyTooSmall(uint256 totalSupply, uint256 minimumTotalSupply);
119
+
120
+ /// @notice Thrown when posting to a category that has not been configured to allow posts.
73
121
  error CTPublisher_UnauthorizedToPostInCategory(address hook, uint24 category);
122
+
123
+ /// @notice Thrown when a category is configured with a zero minimum total supply.
74
124
  error CTPublisher_ZeroTotalSupply(address hook, uint24 category);
75
125
 
76
126
  //*********************************************************************//
@@ -78,7 +128,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
78
128
  //*********************************************************************//
79
129
 
80
130
  /// @notice The divisor that describes the fee that should be taken.
81
- /// @dev This is equal to 100 divided by the fee percent.
131
+ /// @dev A divisor of 20 yields a 5% fee. Expressing the fee as an integer divisor keeps the deduction
132
+ /// (`totalPrice / FEE_DIVISOR`) exact and rounds any dust in the payer's favor.
82
133
  uint256 public constant override FEE_DIVISOR = 20;
83
134
 
84
135
  //*********************************************************************//
@@ -5,13 +5,15 @@ import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.
5
5
  import {CTDeployerAllowedPost} from "../structs/CTDeployerAllowedPost.sol";
6
6
 
7
7
  /// @notice Configuration for deploying a new Croptop project via CTDeployer.
8
- /// @param terminalConfigurations The terminals that the project uses to accept payments through.
9
- /// @param projectUri The metadata URI containing project info.
10
- /// @param allowedPosts The posting criteria that define what kinds of NFTs can be published to each category.
11
- /// @param contractUri A link to the 721 collection's metadata (e.g. OpenSea-compatible contract-level metadata).
12
- /// @param name The name of the 721 collection where posts will be minted.
13
- /// @param symbol The symbol of the 721 collection where posts will be minted.
14
- /// @param salt A salt for deterministic deployment of the 721 hook (includes msg.sender for replay protection).
8
+ /// @custom:member terminalConfigurations The terminals that the project uses to accept payments through.
9
+ /// @custom:member projectUri The metadata URI containing project info.
10
+ /// @custom:member allowedPosts The posting criteria that define what kinds of NFTs can be published to each category.
11
+ /// @custom:member contractUri A link to the 721 collection's metadata (e.g. OpenSea-compatible contract-level
12
+ /// metadata).
13
+ /// @custom:member name The name of the 721 collection where posts will be minted.
14
+ /// @custom:member symbol The symbol of the 721 collection where posts will be minted.
15
+ /// @custom:member salt A salt for deterministic deployment of the 721 hook (includes msg.sender for replay
16
+ /// protection).
15
17
  struct CTProjectConfig {
16
18
  JBTerminalConfig[] terminalConfigurations;
17
19
  string projectUri;