@croptop/core-v6 0.0.59 → 0.0.61
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 +13 -8
- package/package.json +8 -8
- package/references/runtime.md +1 -1
- package/script/ConfigureFeeProject.s.sol +13 -14
- package/script/Deploy.s.sol +8 -8
- package/src/CTDeployer.sol +8 -7
- package/src/CTProjectOwner.sol +7 -7
- package/src/CTPublisher.sol +12 -18
- package/src/interfaces/ICTProjectOwner.sol +1 -1
- package/src/structs/CTAllowedPost.sol +2 -2
- package/src/structs/CTDeployerAllowedPost.sol +2 -2
- package/src/structs/CTSuckerDeploymentConfig.sol +2 -2
package/README.md
CHANGED
|
@@ -2,14 +2,19 @@
|
|
|
2
2
|
|
|
3
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.
|
|
4
4
|
|
|
5
|
-
Docs: <https://docs.juicebox.money>
|
|
6
5
|
Site: <https://croptop.eth.limo>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
|
|
7
|
+
## Documentation
|
|
8
|
+
|
|
9
|
+
- [ARCHITECTURE.md](./ARCHITECTURE.md) — system overview, contract roles, data flow
|
|
10
|
+
- [INVARIANTS.md](./INVARIANTS.md) — scoped guarantees that must hold across users, owners, deployers, and integrators
|
|
11
|
+
- [RISKS.md](./RISKS.md) — runtime, admin, deployment, and integration risks
|
|
12
|
+
- [USER_JOURNEYS.md](./USER_JOURNEYS.md) — end-to-end flows for posters, owners, and deployers
|
|
13
|
+
- [ADMINISTRATION.md](./ADMINISTRATION.md) — owner and operator playbook
|
|
14
|
+
- [AUDIT_INSTRUCTIONS.md](./AUDIT_INSTRUCTIONS.md) — what auditors should focus on
|
|
15
|
+
- [SKILLS.md](./SKILLS.md) — domain knowledge for working in this repo
|
|
16
|
+
- [STYLE_GUIDE.md](./STYLE_GUIDE.md) — code-style conventions
|
|
17
|
+
- [CHANGELOG.md](./CHANGELOG.md) — release notes
|
|
13
18
|
|
|
14
19
|
## Overview
|
|
15
20
|
|
|
@@ -61,7 +66,7 @@ Many Croptop bugs are really deployment-shape bugs or posting-policy bugs, not g
|
|
|
61
66
|
- Croptop publishing policy is separate from ordinary 721 tier issuance
|
|
62
67
|
- fee routing is part of the publish path and its fallback behavior matters
|
|
63
68
|
- `CTProjectOwner` intentionally changes the ownership model and should be reviewed as part of the trust model
|
|
64
|
-
- duplicate-content, stale-tier, and fee-evasion edge cases are runtime behavior, not
|
|
69
|
+
- duplicate-content, stale-tier, and fee-evasion edge cases are runtime behavior, not only UI concerns
|
|
65
70
|
|
|
66
71
|
## Where State Lives
|
|
67
72
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@croptop/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.61",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/mejango/croptop-core-v6"
|
|
7
|
+
"url": "git+https://github.com/mejango/croptop-core-v6.git"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"foundry.toml",
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v6'"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
30
|
-
"@bananapus/core-v6": "^0.0.
|
|
31
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
29
|
+
"@bananapus/721-hook-v6": "^0.0.59",
|
|
30
|
+
"@bananapus/core-v6": "^0.0.72",
|
|
31
|
+
"@bananapus/ownable-v6": "^0.0.32",
|
|
32
32
|
"@bananapus/permission-ids-v6": "^0.0.27",
|
|
33
|
-
"@bananapus/router-terminal-v6": "^0.0.
|
|
34
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
33
|
+
"@bananapus/router-terminal-v6": "^0.0.55",
|
|
34
|
+
"@bananapus/suckers-v6": "^0.0.60",
|
|
35
35
|
"@openzeppelin/contracts": "5.6.1",
|
|
36
36
|
"@rev-net/core-v6": "^0.0.75"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
39
|
+
"@bananapus/address-registry-v6": "^0.0.29",
|
|
40
40
|
"@sphinx-labs/plugins": "0.33.3"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/references/runtime.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
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
|
-
- Tier reuse and duplicate content: content identity is part of runtime behavior, not
|
|
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
23
|
## Tests To Trust First
|
|
@@ -116,17 +116,17 @@ library RevnetCoreDeploymentLib {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
119
|
-
/// @notice
|
|
119
|
+
/// @notice Tracks the core deployment for the current chain.
|
|
120
120
|
CoreDeployment core;
|
|
121
|
-
/// @notice
|
|
121
|
+
/// @notice Tracks the latest Croptop deployment.
|
|
122
122
|
CroptopDeployment croptop;
|
|
123
|
-
/// @notice
|
|
123
|
+
/// @notice Tracks the 721 hook deployment for the current chain.
|
|
124
124
|
Hook721Deployment hook;
|
|
125
|
-
/// @notice
|
|
125
|
+
/// @notice Tracks the Revnet deployment for the current chain.
|
|
126
126
|
RevnetCoreDeployment revnet;
|
|
127
|
-
/// @notice
|
|
127
|
+
/// @notice Tracks the sucker deployment for the current chain.
|
|
128
128
|
SuckerDeployment suckers;
|
|
129
|
-
/// @notice
|
|
129
|
+
/// @notice Tracks the router terminal deployment for the current chain.
|
|
130
130
|
RouterTerminalDeployment routerTerminal;
|
|
131
131
|
|
|
132
132
|
/// @notice The fee project ID configured by the Croptop publisher.
|
|
@@ -158,18 +158,18 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
function run() public {
|
|
161
|
-
// Get the deployment addresses for
|
|
161
|
+
// Get the core deployment addresses for this chain.
|
|
162
162
|
// We want to do this outside of the `sphinx` modifier.
|
|
163
163
|
core = CoreDeploymentLib.getDeployment(
|
|
164
164
|
vm.envOr("NANA_CORE_DEPLOYMENT_PATH", string("node_modules/@bananapus/core-v6/deployments/"))
|
|
165
165
|
);
|
|
166
|
-
// Get the deployment addresses for
|
|
166
|
+
// Get the Croptop deployment addresses for this chain.
|
|
167
167
|
croptop = CroptopDeploymentLib.getDeployment(vm.envOr("CROPTOP_DEPLOYMENT_PATH", string("deployments/")));
|
|
168
168
|
// Get the deployment addresses for the 721 hook contracts for this chain.
|
|
169
169
|
hook = Hook721DeploymentLib.getDeployment(
|
|
170
170
|
vm.envOr("NANA_721_DEPLOYMENT_PATH", string("node_modules/@bananapus/721-hook-v6/deployments/"))
|
|
171
171
|
);
|
|
172
|
-
// Get the deployment addresses for
|
|
172
|
+
// Get the Revnet deployment addresses for this chain.
|
|
173
173
|
revnet = RevnetCoreDeploymentLib.getDeployment(
|
|
174
174
|
vm.envOr("REVNET_CORE_DEPLOYMENT_PATH", string("node_modules/@rev-net/core-v6/deployments/"))
|
|
175
175
|
);
|
|
@@ -185,18 +185,17 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
185
185
|
)
|
|
186
186
|
);
|
|
187
187
|
|
|
188
|
-
//
|
|
188
|
+
// Make sure Revnet and Croptop use the same Juicebox directory.
|
|
189
189
|
require(revnet.basicDeployer.DIRECTORY() == croptop.publisher.DIRECTORY());
|
|
190
190
|
|
|
191
191
|
// Set the operator address to be the multisig.
|
|
192
192
|
operator = safeAddress();
|
|
193
193
|
trustedForwarder = core.controller.trustedForwarder();
|
|
194
194
|
|
|
195
|
-
// Get the fee project
|
|
195
|
+
// Get the fee project ID from the Croptop deployment.
|
|
196
196
|
feeProjectId = croptop.publisher.FEE_PROJECT_ID();
|
|
197
197
|
|
|
198
|
-
//
|
|
199
|
-
// Perform the deployment transactions.
|
|
198
|
+
// Perform the fee-project deployment if the project is not already configured.
|
|
200
199
|
deploy();
|
|
201
200
|
}
|
|
202
201
|
|
|
@@ -326,7 +325,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
326
325
|
REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt: _SUCKER_SALT});
|
|
327
326
|
}
|
|
328
327
|
|
|
329
|
-
// The project's allowed
|
|
328
|
+
// The project's allowed Croptop posts.
|
|
330
329
|
REVCroptopAllowedPost[] memory allowedPosts = new REVCroptopAllowedPost[](5);
|
|
331
330
|
allowedPosts[0] = REVCroptopAllowedPost({
|
|
332
331
|
category: 0,
|
package/script/Deploy.s.sol
CHANGED
|
@@ -13,17 +13,17 @@ import {CTPublisher} from "./../src/CTPublisher.sol";
|
|
|
13
13
|
import {CoreDeployment, CoreDeploymentLib} from "./helpers/CoreDeploymentLib.sol";
|
|
14
14
|
|
|
15
15
|
contract DeployScript is Script, Sphinx {
|
|
16
|
-
/// @notice
|
|
16
|
+
/// @notice Tracks the core deployment for the current chain.
|
|
17
17
|
CoreDeployment core;
|
|
18
|
-
/// @notice
|
|
18
|
+
/// @notice Tracks the 721 hook deployment for the current chain.
|
|
19
19
|
Hook721Deployment hook;
|
|
20
|
-
/// @notice
|
|
20
|
+
/// @notice Tracks the sucker deployment for the current chain.
|
|
21
21
|
SuckerDeployment suckers;
|
|
22
22
|
|
|
23
23
|
/// @notice Set this to a non-zero value to reuse an existing fee project. Leaving it as 0 deploys a new one.
|
|
24
24
|
uint256 private feeProjectId = 0;
|
|
25
25
|
|
|
26
|
-
/// @notice
|
|
26
|
+
/// @notice CREATE2 salts used to deploy the Croptop contracts.
|
|
27
27
|
bytes32 private constant _PUBLISHER_SALT = "_PUBLISHER_SALTV6_";
|
|
28
28
|
bytes32 private constant _DEPLOYER_SALT = "_DEPLOYER_SALTV6_";
|
|
29
29
|
bytes32 private constant _PROJECT_OWNER_SALT = "_PROJECT_OWNER_SALTV6_";
|
|
@@ -36,7 +36,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function run() public {
|
|
39
|
-
// Get the deployment addresses for
|
|
39
|
+
// Get the core deployment addresses for this chain.
|
|
40
40
|
// We want to do this outside of the `sphinx` modifier.
|
|
41
41
|
core = CoreDeploymentLib.getDeployment(
|
|
42
42
|
vm.envOr("NANA_CORE_DEPLOYMENT_PATH", string("node_modules/@bananapus/core-v6/deployments/"))
|
|
@@ -65,7 +65,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
65
65
|
|
|
66
66
|
CTPublisher publisher;
|
|
67
67
|
{
|
|
68
|
-
//
|
|
68
|
+
// Check whether the publisher is already deployed.
|
|
69
69
|
(address _publisher, bool _publisherIsDeployed) = _isDeployed({
|
|
70
70
|
salt: _PUBLISHER_SALT,
|
|
71
71
|
creationCode: type(CTPublisher).creationCode,
|
|
@@ -85,7 +85,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
85
85
|
|
|
86
86
|
CTDeployer deployer;
|
|
87
87
|
{
|
|
88
|
-
//
|
|
88
|
+
// Check whether the deployer is already deployed.
|
|
89
89
|
(address _deployer, bool _deployerIsDeployed) = _isDeployed({
|
|
90
90
|
salt: _DEPLOYER_SALT,
|
|
91
91
|
creationCode: type(CTDeployer).creationCode,
|
|
@@ -109,7 +109,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
109
109
|
|
|
110
110
|
CTProjectOwner owner;
|
|
111
111
|
{
|
|
112
|
-
//
|
|
112
|
+
// Check whether the project-owner sink is already deployed.
|
|
113
113
|
(address _owner, bool _ownerIsDeployed) = _isDeployed({
|
|
114
114
|
salt: _PROJECT_OWNER_SALT,
|
|
115
115
|
creationCode: type(CTProjectOwner).creationCode,
|
package/src/CTDeployer.sol
CHANGED
|
@@ -46,6 +46,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
46
46
|
// --------------------------- custom errors ------------------------- //
|
|
47
47
|
//*********************************************************************//
|
|
48
48
|
|
|
49
|
+
/// @notice Thrown when a caller is not the Juicebox project owner for the hook being claimed.
|
|
49
50
|
error CTDeployer_NotOwnerOfProject(uint256 projectId, address hook, address caller);
|
|
50
51
|
//*********************************************************************//
|
|
51
52
|
// ---------------------------- events -------------------------------- //
|
|
@@ -92,7 +93,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
92
93
|
/// @param permissions The permissions contract.
|
|
93
94
|
/// @param projects The projects contract.
|
|
94
95
|
/// @param deployer The deployer to launch Croptop projects from.
|
|
95
|
-
/// @param publisher The
|
|
96
|
+
/// @param publisher The Croptop publisher.
|
|
96
97
|
/// @param suckerRegistry The sucker registry.
|
|
97
98
|
/// @param trustedForwarder The trusted forwarder.
|
|
98
99
|
constructor(
|
|
@@ -298,8 +299,8 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
298
299
|
}
|
|
299
300
|
|
|
300
301
|
/// @notice Deploy new suckers for an existing project.
|
|
301
|
-
/// @dev Only the
|
|
302
|
-
/// non-default peer also requires `SET_SUCKER_PEER`, matching the registry's direct-call rule.
|
|
302
|
+
/// @dev Only the Juicebox project owner or a `DEPLOY_SUCKERS` operator can deploy new suckers. Supplying an
|
|
303
|
+
/// explicit non-default peer also requires `SET_SUCKER_PEER`, matching the registry's direct-call rule.
|
|
303
304
|
/// @param projectId The ID of the project to deploy suckers for.
|
|
304
305
|
/// @param suckerDeploymentConfiguration The suckers to set up for the project.
|
|
305
306
|
function deploySuckersFor(
|
|
@@ -365,7 +366,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
365
366
|
return (0, context.cashOutCount, context.totalSupply, context.surplus.value, hookSpecifications);
|
|
366
367
|
}
|
|
367
368
|
|
|
368
|
-
// If the ruleset has a data hook, forward the call to the
|
|
369
|
+
// If the ruleset has a data hook, forward the call to the data hook.
|
|
369
370
|
IJBRulesetDataHook hook = dataHookOf[context.projectId];
|
|
370
371
|
if (address(hook) == address(0)) {
|
|
371
372
|
return (
|
|
@@ -428,9 +429,9 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
428
429
|
tokenId;
|
|
429
430
|
operator;
|
|
430
431
|
|
|
431
|
-
//
|
|
432
|
+
// Only accept project NFTs from JBProjects.
|
|
432
433
|
if (msg.sender != address(PROJECTS)) revert();
|
|
433
|
-
//
|
|
434
|
+
// Only accept freshly minted project NFTs.
|
|
434
435
|
if (from != address(0)) revert();
|
|
435
436
|
return IERC721Receiver.onERC721Received.selector;
|
|
436
437
|
}
|
|
@@ -451,7 +452,7 @@ contract CTDeployer is ERC2771Context, JBPermissioned, IJBRulesetDataHook, IERC7
|
|
|
451
452
|
// --------------------- internal transactions ----------------------- //
|
|
452
453
|
//*********************************************************************//
|
|
453
454
|
|
|
454
|
-
/// @notice Configure
|
|
455
|
+
/// @notice Configure Croptop posting criteria for a newly deployed hook.
|
|
455
456
|
/// @param hook The hook that will be posted to.
|
|
456
457
|
/// @param allowedPosts The type of posts that should be allowed.
|
|
457
458
|
function _configurePostingCriteriaFor(address hook, CTDeployerAllowedPost[] memory allowedPosts) internal {
|
package/src/CTProjectOwner.sol
CHANGED
|
@@ -24,7 +24,7 @@ contract CTProjectOwner is IERC721Receiver, ICTProjectOwner {
|
|
|
24
24
|
/// @notice The contract where operator permissions are stored.
|
|
25
25
|
IJBPermissions public immutable override PERMISSIONS;
|
|
26
26
|
|
|
27
|
-
/// @notice The contract from which
|
|
27
|
+
/// @notice The contract from which projects are minted.
|
|
28
28
|
IJBProjects public immutable override PROJECTS;
|
|
29
29
|
|
|
30
30
|
/// @notice The Croptop publisher contract that manages post allowances and content rules.
|
|
@@ -35,7 +35,7 @@ contract CTProjectOwner is IERC721Receiver, ICTProjectOwner {
|
|
|
35
35
|
//*********************************************************************//
|
|
36
36
|
|
|
37
37
|
/// @param permissions The contract where operator permissions are stored.
|
|
38
|
-
/// @param projects The contract from which
|
|
38
|
+
/// @param projects The contract from which projects are minted.
|
|
39
39
|
/// @param publisher The Croptop publisher.
|
|
40
40
|
constructor(IJBPermissions permissions, IJBProjects projects, ICTPublisher publisher) {
|
|
41
41
|
PERMISSIONS = permissions;
|
|
@@ -47,8 +47,8 @@ contract CTProjectOwner is IERC721Receiver, ICTProjectOwner {
|
|
|
47
47
|
// ---------------------- external transactions ---------------------- //
|
|
48
48
|
//*********************************************************************//
|
|
49
49
|
|
|
50
|
-
/// @notice Give the
|
|
51
|
-
/// @dev
|
|
50
|
+
/// @notice Give the Croptop publisher permission to post to the project on this contract's behalf.
|
|
51
|
+
/// @dev Configure posting criteria before transferring ownership here; this contract has no transfer-out function.
|
|
52
52
|
function onERC721Received(
|
|
53
53
|
address operator,
|
|
54
54
|
address from,
|
|
@@ -63,14 +63,14 @@ contract CTProjectOwner is IERC721Receiver, ICTProjectOwner {
|
|
|
63
63
|
from;
|
|
64
64
|
operator;
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Only accept project NFTs from JBProjects.
|
|
67
67
|
if (msg.sender != address(PROJECTS)) revert();
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Grant Croptop permission to add tiers for this locked project.
|
|
70
70
|
uint8[] memory permissionIds = new uint8[](1);
|
|
71
71
|
permissionIds[0] = JBPermissionIds.ADJUST_721_TIERS;
|
|
72
72
|
|
|
73
|
-
// Give the
|
|
73
|
+
// Give the Croptop publisher permission to post on this contract's behalf.
|
|
74
74
|
PERMISSIONS.setPermissionsFor({
|
|
75
75
|
account: address(this),
|
|
76
76
|
permissionsData: JBPermissionsData({
|
package/src/CTPublisher.sol
CHANGED
|
@@ -86,7 +86,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
86
86
|
|
|
87
87
|
/// @notice Packed values that determine the allowance of posts.
|
|
88
88
|
/// @custom:param hook The hook for which this allowance applies.
|
|
89
|
-
/// @custom:param category The category for which the allowance applies
|
|
89
|
+
/// @custom:param category The category for which the allowance applies.
|
|
90
90
|
mapping(address hook => mapping(uint256 category => uint256)) internal _packedAllowanceFor;
|
|
91
91
|
|
|
92
92
|
//*********************************************************************//
|
|
@@ -114,8 +114,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
114
114
|
// ---------------------- external transactions ---------------------- //
|
|
115
115
|
//*********************************************************************//
|
|
116
116
|
|
|
117
|
-
/// @notice Lets collection owners define the rules for what can be posted in each category
|
|
118
|
-
/// supply
|
|
117
|
+
/// @notice Lets collection owners define the rules for what can be posted in each category: minimum price, supply
|
|
119
118
|
/// bounds, maximum split percent, and an optional allowlist of addresses. Each call replaces the existing criteria
|
|
120
119
|
/// for the specified categories.
|
|
121
120
|
/// @param allowedPosts An array of criteria for allowed posts.
|
|
@@ -181,16 +180,13 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
181
180
|
|
|
182
181
|
/// @notice Publish one or more NFT posts to a project's 721 hook and mint a first copy of each. For each new post,
|
|
183
182
|
/// a tier is created on the hook. A 5% fee (1/FEE_DIVISOR) is taken from the total tier prices and routed to the
|
|
184
|
-
/// fee
|
|
185
|
-
/// project; the remainder is paid into the project's terminal, minting NFTs for the beneficiary.
|
|
183
|
+
/// fee project; the remainder is paid into the project's terminal, minting NFTs for the beneficiary.
|
|
186
184
|
/// @dev Reverts if any post violates the category's configured allowance (price, supply, split, allowlist).
|
|
187
185
|
/// @param hook The hook to mint from.
|
|
188
186
|
/// @param posts An array of posts that should be published as NFTs to the specified project.
|
|
189
187
|
/// @param nftBeneficiary The beneficiary of the NFT mints.
|
|
190
188
|
/// @param feeBeneficiary The beneficiary of the fee project's token.
|
|
191
|
-
/// @param additionalPayMetadata Metadata bytes
|
|
192
|
-
/// prepends the
|
|
193
|
-
/// payload needed for NFT creation.
|
|
189
|
+
/// @param additionalPayMetadata Metadata bytes to include in the payment after Croptop prepends NFT mint metadata.
|
|
194
190
|
function mintFrom(
|
|
195
191
|
IJB721TiersHook hook,
|
|
196
192
|
CTPost[] calldata posts,
|
|
@@ -256,16 +252,14 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
256
252
|
if (exists) revert CTPublisher_DuplicatePayMetadata(payId);
|
|
257
253
|
}
|
|
258
254
|
|
|
259
|
-
//
|
|
260
|
-
// original metadata, following
|
|
261
|
-
// the specifications from the JBMetadataResolver library.
|
|
255
|
+
// Add Croptop's pay metadata entry while preserving caller-provided metadata.
|
|
262
256
|
mintMetadata = JBMetadataResolver.addToMetadata({
|
|
263
257
|
originalMetadata: additionalPayMetadata,
|
|
264
258
|
idToAdd: JBMetadataResolver.getId({purpose: "pay", target: metadataIdTarget}),
|
|
265
259
|
dataToAdd: abi.encode(true, tierIdsToMint)
|
|
266
260
|
});
|
|
267
261
|
|
|
268
|
-
// Store the
|
|
262
|
+
// Store the referral project ID in the first 32 bytes of the metadata.
|
|
269
263
|
uint256 feeProjectId = FEE_PROJECT_ID;
|
|
270
264
|
|
|
271
265
|
assembly {
|
|
@@ -597,7 +591,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
597
591
|
}
|
|
598
592
|
|
|
599
593
|
// The 721 store requires new tiers sorted by ascending category. This insertion sort normalizes caller input
|
|
600
|
-
// so multi-category publishes do not revert
|
|
594
|
+
// so multi-category publishes do not revert when posts are supplied in mint order instead of category
|
|
601
595
|
// order. It is intentionally stable: equal-category posts stay in caller order because the loop only moves
|
|
602
596
|
// prior tiers with a strictly greater category.
|
|
603
597
|
for (uint256 i = 1; i < numberOfTiersBeingAdded;) {
|
|
@@ -654,10 +648,10 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
654
648
|
/// @param addrs The candidate address.
|
|
655
649
|
/// @param addresses An array of allowed addresses.
|
|
656
650
|
function _isAllowed(address addrs, address[] memory addresses) internal pure returns (bool) {
|
|
657
|
-
// Keep a reference to the number of
|
|
651
|
+
// Keep a reference to the number of addresses to check against.
|
|
658
652
|
uint256 numberOfAddresses = addresses.length;
|
|
659
653
|
|
|
660
|
-
// Check
|
|
654
|
+
// Check whether the address is included.
|
|
661
655
|
for (uint256 i; i < numberOfAddresses;) {
|
|
662
656
|
if (addrs == addresses[i]) return true;
|
|
663
657
|
unchecked {
|
|
@@ -677,13 +671,13 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
677
671
|
return super._contextSuffixLength();
|
|
678
672
|
}
|
|
679
673
|
|
|
680
|
-
/// @notice Returns the calldata
|
|
681
|
-
/// @return calldata the `msg.data` of this call
|
|
674
|
+
/// @notice Returns the calldata; preferred over `msg.data`.
|
|
675
|
+
/// @return calldata the `msg.data` of this call.
|
|
682
676
|
function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
|
|
683
677
|
return ERC2771Context._msgData();
|
|
684
678
|
}
|
|
685
679
|
|
|
686
|
-
/// @notice Returns the sender
|
|
680
|
+
/// @notice Returns the sender; preferred over `msg.sender`.
|
|
687
681
|
/// @return sender the sender address of this call.
|
|
688
682
|
function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
|
|
689
683
|
return ERC2771Context._msgSender();
|
|
@@ -7,7 +7,7 @@ import {ICTPublisher} from "./ICTPublisher.sol";
|
|
|
7
7
|
|
|
8
8
|
/// @notice A contract that can receive a Juicebox project NFT (via `safeTransferFrom`) and automatically grants the
|
|
9
9
|
/// Croptop publisher permission to manage the project's 721 tiers. Once the project is transferred to this contract,
|
|
10
|
-
/// its ownership is effectively burned while still allowing
|
|
10
|
+
/// its ownership is effectively burned while still allowing Croptop posts.
|
|
11
11
|
interface ICTProjectOwner {
|
|
12
12
|
/// @notice The contract where operator permissions are stored.
|
|
13
13
|
/// @return The permissions contract.
|
|
@@ -6,8 +6,8 @@ pragma solidity ^0.8.0;
|
|
|
6
6
|
/// @custom:member category A category that should allow posts.
|
|
7
7
|
/// @custom:member minimumPrice The minimum price that a post to the specified category should cost.
|
|
8
8
|
/// @custom:member minimumTotalSupply The minimum total supply of NFTs that can be made available when minting.
|
|
9
|
-
/// @custom:member
|
|
10
|
-
///
|
|
9
|
+
/// @custom:member maximumTotalSupply The maximum total supply of NFTs that can be made available when minting. 0 means
|
|
10
|
+
/// unlimited.
|
|
11
11
|
/// @custom:member maximumSplitPercent The maximum split percent (out of JBConstants.SPLITS_TOTAL_PERCENT) that a
|
|
12
12
|
/// poster can set. 0 means splits are not allowed.
|
|
13
13
|
/// @custom:member allowedAddresses A list of addresses that are allowed to post on the category through Croptop.
|
|
@@ -5,8 +5,8 @@ pragma solidity ^0.8.0;
|
|
|
5
5
|
/// @custom:member category A category that should allow posts.
|
|
6
6
|
/// @custom:member minimumPrice The minimum price that a post to the specified category should cost.
|
|
7
7
|
/// @custom:member minimumTotalSupply The minimum total supply of NFTs that can be made available when minting.
|
|
8
|
-
/// @custom:member
|
|
9
|
-
///
|
|
8
|
+
/// @custom:member maximumTotalSupply The maximum total supply of NFTs that can be made available when minting. 0 means
|
|
9
|
+
/// unlimited.
|
|
10
10
|
/// @custom:member maximumSplitPercent The maximum split percent (out of JBConstants.SPLITS_TOTAL_PERCENT) that a
|
|
11
11
|
/// poster can set. 0 means splits are not allowed.
|
|
12
12
|
/// @custom:member allowedAddresses A list of addresses that are allowed to post on the category through Croptop.
|
|
@@ -3,8 +3,8 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
5
5
|
|
|
6
|
-
/// @custom:member deployerConfigurations
|
|
7
|
-
/// @custom:member salt The salt
|
|
6
|
+
/// @custom:member deployerConfigurations Sucker deployer configs and token mappings for peer chains.
|
|
7
|
+
/// @custom:member salt The salt combined with `_msgSender()` to create deterministic sucker addresses.
|
|
8
8
|
struct CTSuckerDeploymentConfig {
|
|
9
9
|
JBSuckerDeployerConfig[] deployerConfigurations;
|
|
10
10
|
bytes32 salt;
|