@croptop/core-v6 0.0.48 → 0.0.49
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
|
@@ -61,50 +61,29 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
61
61
|
/// @notice tracks the deployment of the router terminal.
|
|
62
62
|
RouterTerminalDeployment routerTerminal;
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
uint32
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
uint256 DECIMAL_MULTIPLIER = 10 ** DECIMALS;
|
|
85
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
86
|
-
bytes32 SUCKER_SALT = "_CPN_SUCKERV6__";
|
|
87
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
88
|
-
bytes32 ERC20_SALT = "_CPN_ERC20_SALTV6__";
|
|
89
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
90
|
-
bytes32 HOOK_SALT = "_CPN_HOOK_SALTV6__";
|
|
91
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
92
|
-
address OPERATOR;
|
|
93
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
94
|
-
address TRUSTED_FORWARDER;
|
|
95
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
96
|
-
uint48 CPN_START_TIME = 1_740_089_444;
|
|
97
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
98
|
-
uint104 CPN_MAINNET_AUTO_ISSUANCE_ = 250_003_875_000_000_000_000_000;
|
|
99
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
100
|
-
uint104 CPN_OP_AUTO_ISSUANCE_ = 844_894_881_600_000_000_000;
|
|
101
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
102
|
-
uint104 CPN_BASE_AUTO_ISSUANCE_ = 844_894_881_600_000_000_000;
|
|
103
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
104
|
-
uint104 CPN_ARB_AUTO_ISSUANCE_ = 3_844_000_000_000_000_000;
|
|
64
|
+
/// @notice The fee project ID configured by the Croptop publisher.
|
|
65
|
+
uint256 private feeProjectId;
|
|
66
|
+
uint32 private constant _PREMINT_CHAIN_ID = 1;
|
|
67
|
+
string private constant _NAME = "Croptop Publishing Network";
|
|
68
|
+
string private constant _SYMBOL = "CPN";
|
|
69
|
+
string private constant _PROJECT_URI = "ipfs://QmUAFevoMn1iqSEQR8LogQYRxm39TNxQTPYnuLuq5BmfEi";
|
|
70
|
+
uint32 private constant _NATIVE_CURRENCY = uint32(uint160(JBConstants.NATIVE_TOKEN));
|
|
71
|
+
uint32 private constant _ETH_CURRENCY = JBCurrencyIds.ETH;
|
|
72
|
+
uint8 private constant _DECIMALS = 18;
|
|
73
|
+
uint256 private constant _DECIMAL_MULTIPLIER = 10 ** _DECIMALS;
|
|
74
|
+
bytes32 private constant _SUCKER_SALT = "_CPN_SUCKERV6__";
|
|
75
|
+
bytes32 private constant _ERC20_SALT = "_CPN_ERC20_SALTV6__";
|
|
76
|
+
bytes32 private constant _HOOK_SALT = "_CPN_HOOK_SALTV6__";
|
|
77
|
+
address private operator;
|
|
78
|
+
address private trustedForwarder;
|
|
79
|
+
uint48 private constant _CPN_START_TIME = 1_740_089_444;
|
|
80
|
+
uint104 private constant _CPN_MAINNET_AUTO_ISSUANCE = 250_003_875_000_000_000_000_000;
|
|
81
|
+
uint104 private constant _CPN_OP_AUTO_ISSUANCE = 844_894_881_600_000_000_000;
|
|
82
|
+
uint104 private constant _CPN_BASE_AUTO_ISSUANCE = 844_894_881_600_000_000_000;
|
|
83
|
+
uint104 private constant _CPN_ARB_AUTO_ISSUANCE = 3_844_000_000_000_000_000;
|
|
105
84
|
|
|
106
85
|
function configureSphinx() public override {
|
|
107
|
-
//
|
|
86
|
+
// Safe owners and threshold are resolved by the Sphinx project config.
|
|
108
87
|
sphinxConfig.projectName = "croptop-core-v6";
|
|
109
88
|
sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
|
|
110
89
|
sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
|
|
@@ -142,11 +121,11 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
142
121
|
require(revnet.basic_deployer.DIRECTORY() == croptop.publisher.DIRECTORY());
|
|
143
122
|
|
|
144
123
|
// Set the operator address to be the multisig.
|
|
145
|
-
|
|
146
|
-
|
|
124
|
+
operator = safeAddress();
|
|
125
|
+
trustedForwarder = core.controller.trustedForwarder();
|
|
147
126
|
|
|
148
127
|
// Get the fee project id from the croptop deployment.
|
|
149
|
-
|
|
128
|
+
feeProjectId = croptop.publisher.FEE_PROJECT_ID();
|
|
150
129
|
|
|
151
130
|
// Check if there should be a new fee project created.
|
|
152
131
|
// Perform the deployment transactions.
|
|
@@ -159,7 +138,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
159
138
|
|
|
160
139
|
// Accept the chain's native currency through the multi terminal.
|
|
161
140
|
accountingContextsToAccept[0] =
|
|
162
|
-
JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals:
|
|
141
|
+
JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: _DECIMALS, currency: _NATIVE_CURRENCY});
|
|
163
142
|
|
|
164
143
|
// The terminals that the project will accept funds through.
|
|
165
144
|
JBTerminalConfig[] memory terminalConfigurations = new JBTerminalConfig[](2);
|
|
@@ -171,16 +150,16 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
171
150
|
});
|
|
172
151
|
|
|
173
152
|
REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](4);
|
|
174
|
-
issuanceConfs[0] = REVAutoIssuance({chainId: 1, count:
|
|
175
|
-
issuanceConfs[1] = REVAutoIssuance({chainId: 10, count:
|
|
176
|
-
issuanceConfs[2] = REVAutoIssuance({chainId: 8453, count:
|
|
177
|
-
issuanceConfs[3] = REVAutoIssuance({chainId: 42_161, count:
|
|
153
|
+
issuanceConfs[0] = REVAutoIssuance({chainId: 1, count: _CPN_MAINNET_AUTO_ISSUANCE, beneficiary: operator});
|
|
154
|
+
issuanceConfs[1] = REVAutoIssuance({chainId: 10, count: _CPN_OP_AUTO_ISSUANCE, beneficiary: operator});
|
|
155
|
+
issuanceConfs[2] = REVAutoIssuance({chainId: 8453, count: _CPN_BASE_AUTO_ISSUANCE, beneficiary: operator});
|
|
156
|
+
issuanceConfs[3] = REVAutoIssuance({chainId: 42_161, count: _CPN_ARB_AUTO_ISSUANCE, beneficiary: operator});
|
|
178
157
|
|
|
179
158
|
JBSplit[] memory splits = new JBSplit[](1);
|
|
180
159
|
splits[0] = JBSplit({
|
|
181
160
|
percent: JBConstants.SPLITS_TOTAL_PERCENT,
|
|
182
161
|
projectId: 0,
|
|
183
|
-
beneficiary: payable(
|
|
162
|
+
beneficiary: payable(operator),
|
|
184
163
|
preferAddToBalance: false,
|
|
185
164
|
lockedUntil: 0,
|
|
186
165
|
hook: IJBSplitHook(address(0))
|
|
@@ -189,12 +168,12 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
189
168
|
// The project's revnet stage configurations.
|
|
190
169
|
REVStageConfig[] memory stageConfigurations = new REVStageConfig[](3);
|
|
191
170
|
stageConfigurations[0] = REVStageConfig({
|
|
192
|
-
startsAtOrAfter:
|
|
171
|
+
startsAtOrAfter: _CPN_START_TIME,
|
|
193
172
|
autoIssuances: issuanceConfs,
|
|
194
173
|
splitPercent: 3800, // 38%
|
|
195
174
|
splits: splits,
|
|
196
175
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
197
|
-
initialIssuance: uint112(10_000 *
|
|
176
|
+
initialIssuance: uint112(10_000 * _DECIMAL_MULTIPLIER),
|
|
198
177
|
issuanceCutFrequency: 120 days,
|
|
199
178
|
issuanceCutPercent: 380_000_000, // 38%
|
|
200
179
|
cashOutTaxRate: 1000, // 0.1
|
|
@@ -227,9 +206,9 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
227
206
|
|
|
228
207
|
// The project's revnet configuration
|
|
229
208
|
REVConfig memory revnetConfiguration = REVConfig({
|
|
230
|
-
description: REVDescription({name:
|
|
231
|
-
baseCurrency:
|
|
232
|
-
operator:
|
|
209
|
+
description: REVDescription({name: _NAME, ticker: _SYMBOL, uri: _PROJECT_URI, salt: _ERC20_SALT}),
|
|
210
|
+
baseCurrency: _ETH_CURRENCY,
|
|
211
|
+
operator: operator,
|
|
233
212
|
scopeCashOutsToLocalBalances: false,
|
|
234
213
|
stageConfigurations: stageConfigurations
|
|
235
214
|
});
|
|
@@ -276,14 +255,14 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
276
255
|
}
|
|
277
256
|
// Specify all sucker deployments.
|
|
278
257
|
suckerDeploymentConfiguration =
|
|
279
|
-
REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt:
|
|
258
|
+
REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt: _SUCKER_SALT});
|
|
280
259
|
}
|
|
281
260
|
|
|
282
261
|
// The project's allowed croptop posts.
|
|
283
262
|
REVCroptopAllowedPost[] memory allowedPosts = new REVCroptopAllowedPost[](5);
|
|
284
263
|
allowedPosts[0] = REVCroptopAllowedPost({
|
|
285
264
|
category: 0,
|
|
286
|
-
minimumPrice: uint104(10 ** (
|
|
265
|
+
minimumPrice: uint104(10 ** (_DECIMALS - 5)),
|
|
287
266
|
minimumTotalSupply: 10_000,
|
|
288
267
|
maximumTotalSupply: 999_999_999,
|
|
289
268
|
maximumSplitPercent: 0,
|
|
@@ -291,7 +270,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
291
270
|
});
|
|
292
271
|
allowedPosts[1] = REVCroptopAllowedPost({
|
|
293
272
|
category: 1,
|
|
294
|
-
minimumPrice: uint104(10 ** (
|
|
273
|
+
minimumPrice: uint104(10 ** (_DECIMALS - 3)),
|
|
295
274
|
minimumTotalSupply: 10_000,
|
|
296
275
|
maximumTotalSupply: 999_999_999,
|
|
297
276
|
maximumSplitPercent: 0,
|
|
@@ -299,7 +278,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
299
278
|
});
|
|
300
279
|
allowedPosts[2] = REVCroptopAllowedPost({
|
|
301
280
|
category: 2,
|
|
302
|
-
minimumPrice: uint104(10 ** (
|
|
281
|
+
minimumPrice: uint104(10 ** (_DECIMALS - 1)),
|
|
303
282
|
minimumTotalSupply: 100,
|
|
304
283
|
maximumTotalSupply: 999_999_999,
|
|
305
284
|
maximumSplitPercent: 0,
|
|
@@ -307,7 +286,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
307
286
|
});
|
|
308
287
|
allowedPosts[3] = REVCroptopAllowedPost({
|
|
309
288
|
category: 3,
|
|
310
|
-
minimumPrice: uint104(10 **
|
|
289
|
+
minimumPrice: uint104(10 ** _DECIMALS),
|
|
311
290
|
minimumTotalSupply: 10,
|
|
312
291
|
maximumTotalSupply: 999_999_999,
|
|
313
292
|
maximumSplitPercent: 0,
|
|
@@ -315,7 +294,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
315
294
|
});
|
|
316
295
|
allowedPosts[4] = REVCroptopAllowedPost({
|
|
317
296
|
category: 4,
|
|
318
|
-
minimumPrice: uint104(10 ** (
|
|
297
|
+
minimumPrice: uint104(10 ** (_DECIMALS + 2)),
|
|
319
298
|
minimumTotalSupply: 10,
|
|
320
299
|
maximumTotalSupply: 999_999_999,
|
|
321
300
|
maximumSplitPercent: 0,
|
|
@@ -328,13 +307,13 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
328
307
|
suckerDeploymentConfiguration: suckerDeploymentConfiguration,
|
|
329
308
|
hookConfiguration: REVDeploy721TiersHookConfig({
|
|
330
309
|
baseline721HookConfiguration: REVBaseline721HookConfig({
|
|
331
|
-
name:
|
|
332
|
-
symbol:
|
|
310
|
+
name: _NAME,
|
|
311
|
+
symbol: _SYMBOL,
|
|
333
312
|
baseUri: "ipfs://",
|
|
334
313
|
tokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
335
314
|
contractUri: "",
|
|
336
315
|
tiersConfig: JB721InitTiersConfig({
|
|
337
|
-
tiers: new JB721TierConfig[](0), currency:
|
|
316
|
+
tiers: new JB721TierConfig[](0), currency: _ETH_CURRENCY, decimals: _DECIMALS
|
|
338
317
|
}),
|
|
339
318
|
flags: REV721TiersHookFlags({
|
|
340
319
|
noNewTiersWithReserves: false,
|
|
@@ -343,7 +322,7 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
343
322
|
preventOverspending: false
|
|
344
323
|
})
|
|
345
324
|
}),
|
|
346
|
-
salt:
|
|
325
|
+
salt: _HOOK_SALT,
|
|
347
326
|
preventOperatorAdjustingTiers: false,
|
|
348
327
|
preventOperatorUpdatingMetadata: false,
|
|
349
328
|
preventOperatorMinting: false,
|
|
@@ -357,14 +336,14 @@ contract ConfigureFeeProjectScript is Script, Sphinx {
|
|
|
357
336
|
FeeProjectConfig memory feeProjectConfig = getCroptopRevnetConfig();
|
|
358
337
|
|
|
359
338
|
// Only deploy if the project hasn't already been configured (restart-safe).
|
|
360
|
-
if (address(core.directory.controllerOf(
|
|
339
|
+
if (address(core.directory.controllerOf(feeProjectId)) == address(0)) {
|
|
361
340
|
// Approve the basic deployer to configure the project and transfer it.
|
|
362
|
-
core.projects.approve({to: address(revnet.basic_deployer), tokenId:
|
|
341
|
+
core.projects.approve({to: address(revnet.basic_deployer), tokenId: feeProjectId});
|
|
363
342
|
|
|
364
343
|
// Deploy the NANA fee project.
|
|
365
344
|
revnet.basic_deployer
|
|
366
345
|
.deployFor({
|
|
367
|
-
revnetId:
|
|
346
|
+
revnetId: feeProjectId,
|
|
368
347
|
configuration: feeProjectConfig.configuration,
|
|
369
348
|
terminalConfigurations: feeProjectConfig.terminalConfigurations,
|
|
370
349
|
suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
|
package/script/Deploy.s.sol
CHANGED
|
@@ -20,20 +20,14 @@ contract DeployScript is Script, Sphinx {
|
|
|
20
20
|
/// @notice tracks the deployment of the sucker contracts for the chain we are deploying to.
|
|
21
21
|
SuckerDeployment suckers;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
26
|
-
uint256 FEE_PROJECT_ID = 0;
|
|
23
|
+
/// @notice Set this to a non-zero value to reuse an existing fee project. Leaving it as 0 deploys a new one.
|
|
24
|
+
uint256 private feeProjectId = 0;
|
|
27
25
|
|
|
28
26
|
/// @notice the salts that are used to deploy the contracts.
|
|
29
|
-
|
|
30
|
-
bytes32
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
34
|
-
bytes32 PROJECT_OWNER_SALT = "_PROJECT_OWNER_SALTV6_";
|
|
35
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
36
|
-
address TRUSTED_FORWARDER;
|
|
27
|
+
bytes32 private constant _PUBLISHER_SALT = "_PUBLISHER_SALTV6_";
|
|
28
|
+
bytes32 private constant _DEPLOYER_SALT = "_DEPLOYER_SALTV6_";
|
|
29
|
+
bytes32 private constant _PROJECT_OWNER_SALT = "_PROJECT_OWNER_SALTV6_";
|
|
30
|
+
address private trustedForwarder;
|
|
37
31
|
|
|
38
32
|
function configureSphinx() public override {
|
|
39
33
|
sphinxConfig.projectName = "croptop-core-v6";
|
|
@@ -57,7 +51,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
57
51
|
);
|
|
58
52
|
|
|
59
53
|
// We use the same trusted forwarder as the core deployment.
|
|
60
|
-
|
|
54
|
+
trustedForwarder = core.controller.trustedForwarder();
|
|
61
55
|
|
|
62
56
|
// Perform the deployment transactions.
|
|
63
57
|
deploy();
|
|
@@ -67,24 +61,24 @@ contract DeployScript is Script, Sphinx {
|
|
|
67
61
|
// Canonical Croptop deployments must bind fees to an explicit fee project. Autodiscovering the first
|
|
68
62
|
// matching publisher by scanning project IDs is unsafe because a preexisting publisher can pin fees to
|
|
69
63
|
// the wrong project forever.
|
|
70
|
-
require(
|
|
64
|
+
require(feeProjectId != 0, "explicit fee project id required");
|
|
71
65
|
|
|
72
66
|
CTPublisher publisher;
|
|
73
67
|
{
|
|
74
68
|
// Perform the check for the publisher.
|
|
75
69
|
(address _publisher, bool _publisherIsDeployed) = _isDeployed({
|
|
76
|
-
salt:
|
|
70
|
+
salt: _PUBLISHER_SALT,
|
|
77
71
|
creationCode: type(CTPublisher).creationCode,
|
|
78
|
-
arguments: abi.encode(core.directory, core.permissions,
|
|
72
|
+
arguments: abi.encode(core.directory, core.permissions, feeProjectId, trustedForwarder)
|
|
79
73
|
});
|
|
80
74
|
|
|
81
75
|
// Deploy it if it has not been deployed yet.
|
|
82
76
|
publisher = !_publisherIsDeployed
|
|
83
|
-
? new CTPublisher{salt:
|
|
77
|
+
? new CTPublisher{salt: _PUBLISHER_SALT}({
|
|
84
78
|
directory: core.directory,
|
|
85
79
|
permissions: core.permissions,
|
|
86
|
-
feeProjectId:
|
|
87
|
-
trustedForwarder:
|
|
80
|
+
feeProjectId: feeProjectId,
|
|
81
|
+
trustedForwarder: trustedForwarder
|
|
88
82
|
})
|
|
89
83
|
: CTPublisher(_publisher);
|
|
90
84
|
}
|
|
@@ -93,22 +87,22 @@ contract DeployScript is Script, Sphinx {
|
|
|
93
87
|
{
|
|
94
88
|
// Perform the check for the publisher.
|
|
95
89
|
(address _deployer, bool _deployerIsDeployed) = _isDeployed({
|
|
96
|
-
salt:
|
|
90
|
+
salt: _DEPLOYER_SALT,
|
|
97
91
|
creationCode: type(CTDeployer).creationCode,
|
|
98
92
|
arguments: abi.encode(
|
|
99
|
-
core.permissions, core.projects, hook.hook_deployer, publisher, suckers.registry,
|
|
93
|
+
core.permissions, core.projects, hook.hook_deployer, publisher, suckers.registry, trustedForwarder
|
|
100
94
|
)
|
|
101
95
|
});
|
|
102
96
|
|
|
103
97
|
// Deploy it if it has not been deployed yet.
|
|
104
98
|
deployer = !_deployerIsDeployed
|
|
105
|
-
? new CTDeployer{salt:
|
|
99
|
+
? new CTDeployer{salt: _DEPLOYER_SALT}({
|
|
106
100
|
permissions: core.permissions,
|
|
107
101
|
projects: core.projects,
|
|
108
102
|
deployer: hook.hook_deployer,
|
|
109
103
|
publisher: publisher,
|
|
110
104
|
suckerRegistry: suckers.registry,
|
|
111
|
-
trustedForwarder:
|
|
105
|
+
trustedForwarder: trustedForwarder
|
|
112
106
|
})
|
|
113
107
|
: CTDeployer(_deployer);
|
|
114
108
|
}
|
|
@@ -117,14 +111,14 @@ contract DeployScript is Script, Sphinx {
|
|
|
117
111
|
{
|
|
118
112
|
// Perform the check for the publisher.
|
|
119
113
|
(address _owner, bool _ownerIsDeployed) = _isDeployed({
|
|
120
|
-
salt:
|
|
114
|
+
salt: _PROJECT_OWNER_SALT,
|
|
121
115
|
creationCode: type(CTProjectOwner).creationCode,
|
|
122
116
|
arguments: abi.encode(core.permissions, core.projects, publisher)
|
|
123
117
|
});
|
|
124
118
|
|
|
125
119
|
// Deploy it if it has not been deployed yet.
|
|
126
120
|
owner = !_ownerIsDeployed
|
|
127
|
-
? new CTProjectOwner{salt:
|
|
121
|
+
? new CTProjectOwner{salt: _PROJECT_OWNER_SALT}({
|
|
128
122
|
permissions: core.permissions, projects: core.projects, publisher: publisher
|
|
129
123
|
})
|
|
130
124
|
: CTProjectOwner(_owner);
|
|
@@ -13,8 +13,7 @@ import {CTProjectOwner} from "../../src/CTProjectOwner.sol";
|
|
|
13
13
|
struct CroptopDeployment {
|
|
14
14
|
CTPublisher publisher;
|
|
15
15
|
CTDeployer deployer;
|
|
16
|
-
|
|
17
|
-
CTProjectOwner project_owner;
|
|
16
|
+
CTProjectOwner projectOwner;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
library CroptopDeploymentLib {
|
|
@@ -24,17 +23,16 @@ library CroptopDeploymentLib {
|
|
|
24
23
|
Vm internal constant vm = Vm(VM_ADDRESS);
|
|
25
24
|
|
|
26
25
|
function getDeployment(string memory path) internal returns (CroptopDeployment memory deployment) {
|
|
27
|
-
//
|
|
26
|
+
// Match the current chain ID to the Sphinx network name used in deployment artifacts.
|
|
28
27
|
uint256 chainId = block.chainid;
|
|
29
28
|
|
|
30
|
-
//
|
|
31
|
-
// TODO: get constants without deploy.
|
|
29
|
+
// `SphinxConstants` exposes Sphinx's supported chain ID to network name mapping.
|
|
32
30
|
SphinxConstants sphinxConstants = new SphinxConstants();
|
|
33
31
|
NetworkInfo[] memory networks = sphinxConstants.getNetworkInfoArray();
|
|
34
32
|
|
|
35
33
|
for (uint256 _i; _i < networks.length; _i++) {
|
|
36
34
|
if (networks[_i].chainId == chainId) {
|
|
37
|
-
return getDeployment(path, networks[_i].name);
|
|
35
|
+
return getDeployment({path: path, networkName: networks[_i].name});
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
|
|
@@ -43,8 +41,7 @@ library CroptopDeploymentLib {
|
|
|
43
41
|
|
|
44
42
|
function getDeployment(
|
|
45
43
|
string memory path,
|
|
46
|
-
|
|
47
|
-
string memory network_name
|
|
44
|
+
string memory networkName
|
|
48
45
|
)
|
|
49
46
|
internal
|
|
50
47
|
view
|
|
@@ -52,17 +49,17 @@ library CroptopDeploymentLib {
|
|
|
52
49
|
{
|
|
53
50
|
deployment.publisher = CTPublisher(
|
|
54
51
|
_getDeploymentAddress({
|
|
55
|
-
path: path,
|
|
52
|
+
path: path, projectName: "croptop-core-v6", networkName: networkName, contractName: "CTPublisher"
|
|
56
53
|
})
|
|
57
54
|
);
|
|
58
55
|
deployment.deployer = CTDeployer(
|
|
59
56
|
_getDeploymentAddress({
|
|
60
|
-
path: path,
|
|
57
|
+
path: path, projectName: "croptop-core-v6", networkName: networkName, contractName: "CTDeployer"
|
|
61
58
|
})
|
|
62
59
|
);
|
|
63
|
-
deployment.
|
|
60
|
+
deployment.projectOwner = CTProjectOwner(
|
|
64
61
|
_getDeploymentAddress({
|
|
65
|
-
path: path,
|
|
62
|
+
path: path, projectName: "croptop-core-v6", networkName: networkName, contractName: "CTProjectOwner"
|
|
66
63
|
})
|
|
67
64
|
);
|
|
68
65
|
}
|
|
@@ -74,10 +71,8 @@ library CroptopDeploymentLib {
|
|
|
74
71
|
/// @return The address of the contract.
|
|
75
72
|
function _getDeploymentAddress(
|
|
76
73
|
string memory path,
|
|
77
|
-
|
|
78
|
-
string memory
|
|
79
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
80
|
-
string memory network_name,
|
|
74
|
+
string memory projectName,
|
|
75
|
+
string memory networkName,
|
|
81
76
|
string memory contractName
|
|
82
77
|
)
|
|
83
78
|
internal
|
|
@@ -86,7 +81,7 @@ library CroptopDeploymentLib {
|
|
|
86
81
|
{
|
|
87
82
|
string memory deploymentJson =
|
|
88
83
|
// forge-lint: disable-next-line(unsafe-cheatcode)
|
|
89
|
-
vm.readFile(string.concat(path,
|
|
84
|
+
vm.readFile(string.concat(path, projectName, "/", networkName, "/", contractName, ".json"));
|
|
90
85
|
return stdJson.readAddress({json: deploymentJson, key: ".address"});
|
|
91
86
|
}
|
|
92
87
|
}
|
package/src/CTPublisher.sol
CHANGED
|
@@ -31,8 +31,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
31
31
|
// --------------------------- custom errors ------------------------- //
|
|
32
32
|
//*********************************************************************//
|
|
33
33
|
|
|
34
|
-
error CTPublisher_DuplicatePost(bytes32
|
|
35
|
-
error
|
|
34
|
+
error CTPublisher_DuplicatePost(bytes32 encodedIpfsUri);
|
|
35
|
+
error CTPublisher_EmptyEncodedIpfsUri(uint256 postIndex);
|
|
36
36
|
error CTPublisher_InsufficientEthSent(uint256 expected, uint256 sent);
|
|
37
37
|
error CTPublisher_MaxTotalSupplyLessThanMin(uint256 min, uint256 max);
|
|
38
38
|
error CTPublisher_NotInAllowList(address addr, address[] allowedAddresses);
|
|
@@ -71,8 +71,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
71
71
|
|
|
72
72
|
/// @notice The ID of the tier that an IPFS metadata has been saved to.
|
|
73
73
|
/// @custom:param hook The hook for which the tier ID applies.
|
|
74
|
-
/// @custom:param
|
|
75
|
-
mapping(address hook => mapping(bytes32
|
|
74
|
+
/// @custom:param encodedIpfsUri The IPFS URI.
|
|
75
|
+
mapping(address hook => mapping(bytes32 encodedIpfsUri => uint256)) public override tierIdForEncodedIpfsUriOf;
|
|
76
76
|
|
|
77
77
|
//*********************************************************************//
|
|
78
78
|
// --------------------- internal stored properties ------------------ //
|
|
@@ -337,27 +337,27 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
337
337
|
/// In that case, the store's tierOf call will return a tier with default/empty values. Callers should check
|
|
338
338
|
/// the returned tier's initialSupply or other fields to confirm the tier still exists.
|
|
339
339
|
/// @param hook The hook from which to get tiers.
|
|
340
|
-
/// @param
|
|
340
|
+
/// @param encodedIpfsUris The URIs to get tiers of.
|
|
341
341
|
/// @return tiers The tiers that correspond to the provided encoded IPFS URIs. If there's no tier yet, an empty tier
|
|
342
342
|
/// is returned.
|
|
343
343
|
function tiersFor(
|
|
344
344
|
address hook,
|
|
345
|
-
bytes32[] memory
|
|
345
|
+
bytes32[] memory encodedIpfsUris
|
|
346
346
|
)
|
|
347
347
|
external
|
|
348
348
|
view
|
|
349
349
|
override
|
|
350
350
|
returns (JB721Tier[] memory tiers)
|
|
351
351
|
{
|
|
352
|
-
uint256
|
|
352
|
+
uint256 numberOfEncodedIpfsUris = encodedIpfsUris.length;
|
|
353
353
|
|
|
354
354
|
// Initialize the tier array being returned.
|
|
355
|
-
tiers = new JB721Tier[](
|
|
355
|
+
tiers = new JB721Tier[](numberOfEncodedIpfsUris);
|
|
356
356
|
|
|
357
357
|
// Get the tier for each provided encoded IPFS URI.
|
|
358
|
-
for (uint256 i; i <
|
|
358
|
+
for (uint256 i; i < numberOfEncodedIpfsUris;) {
|
|
359
359
|
// Check if there's a tier ID stored for the encoded IPFS URI.
|
|
360
|
-
uint256 tierId =
|
|
360
|
+
uint256 tierId = tierIdForEncodedIpfsUriOf[hook][encodedIpfsUris[i]];
|
|
361
361
|
|
|
362
362
|
// If there's a tier ID stored, resolve it.
|
|
363
363
|
if (tierId != 0) {
|
|
@@ -457,16 +457,16 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
457
457
|
// Get the current post being iterated on.
|
|
458
458
|
CTPost memory post = posts[i];
|
|
459
459
|
|
|
460
|
-
// Make sure the post includes an
|
|
460
|
+
// Make sure the post includes an encodedIpfsUri.
|
|
461
461
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
462
|
-
if (post.
|
|
463
|
-
revert
|
|
462
|
+
if (post.encodedIpfsUri == bytes32("")) {
|
|
463
|
+
revert CTPublisher_EmptyEncodedIpfsUri({postIndex: i});
|
|
464
464
|
}
|
|
465
465
|
|
|
466
|
-
// Check for duplicate
|
|
466
|
+
// Check for duplicate encodedIpfsUri within the same batch to prevent fee evasion.
|
|
467
467
|
for (uint256 j; j < i;) {
|
|
468
|
-
if (posts[j].
|
|
469
|
-
revert CTPublisher_DuplicatePost({
|
|
468
|
+
if (posts[j].encodedIpfsUri == post.encodedIpfsUri) {
|
|
469
|
+
revert CTPublisher_DuplicatePost({encodedIpfsUri: post.encodedIpfsUri});
|
|
470
470
|
}
|
|
471
471
|
unchecked {
|
|
472
472
|
++j;
|
|
@@ -475,8 +475,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
475
475
|
|
|
476
476
|
// Scoped section to prevent stack too deep.
|
|
477
477
|
{
|
|
478
|
-
// Check if there's an ID of a tier already minted for this
|
|
479
|
-
uint256 tierId =
|
|
478
|
+
// Check if there's an ID of a tier already minted for this encodedIpfsUri.
|
|
479
|
+
uint256 tierId = tierIdForEncodedIpfsUriOf[address(hook)][post.encodedIpfsUri];
|
|
480
480
|
|
|
481
481
|
if (tierId != 0) {
|
|
482
482
|
// Validate the cached tier still exists and its URI still matches.
|
|
@@ -485,9 +485,11 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
485
485
|
// mapping and fall through to create a new tier.
|
|
486
486
|
JB721Tier memory cachedTier =
|
|
487
487
|
store.tierOf({hook: address(hook), id: tierId, includeResolvedUri: false});
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
|
|
488
|
+
if (
|
|
489
|
+
store.isTierRemoved({hook: address(hook), tierId: tierId})
|
|
490
|
+
|| cachedTier.encodedIPFSUri != post.encodedIpfsUri
|
|
491
|
+
) {
|
|
492
|
+
delete tierIdForEncodedIpfsUriOf[address(hook)][post.encodedIpfsUri];
|
|
491
493
|
} else {
|
|
492
494
|
tierIdsToMint[i] = tierId;
|
|
493
495
|
|
|
@@ -557,7 +559,7 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
557
559
|
votingUnits: 0,
|
|
558
560
|
reserveFrequency: 0,
|
|
559
561
|
reserveBeneficiary: address(0),
|
|
560
|
-
encodedIPFSUri: post.
|
|
562
|
+
encodedIPFSUri: post.encodedIpfsUri,
|
|
561
563
|
category: post.category,
|
|
562
564
|
discountPercent: 0,
|
|
563
565
|
flags: JB721TierConfigFlags({
|
|
@@ -576,8 +578,8 @@ contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
|
|
|
576
578
|
// Set the ID of the tier to mint.
|
|
577
579
|
tierIdsToMint[i] = startingTierId + numberOfTiersBeingAdded++;
|
|
578
580
|
|
|
579
|
-
// Save the
|
|
580
|
-
|
|
581
|
+
// Save the encodedIpfsUri as minted.
|
|
582
|
+
tierIdForEncodedIpfsUriOf[address(hook)][post.encodedIpfsUri] = tierIdsToMint[i];
|
|
581
583
|
|
|
582
584
|
// For new tiers, use the post's price for totalPrice accumulation.
|
|
583
585
|
totalPrice += post.price;
|
|
@@ -71,17 +71,17 @@ interface ICTPublisher {
|
|
|
71
71
|
|
|
72
72
|
/// @notice The tier ID that an IPFS metadata URI has been saved to for a given hook.
|
|
73
73
|
/// @param hook The hook for which the tier ID applies.
|
|
74
|
-
/// @param
|
|
74
|
+
/// @param encodedIpfsUri The encoded IPFS URI to look up.
|
|
75
75
|
/// @return The tier ID, or 0 if the URI has not been published.
|
|
76
76
|
// forge-lint: disable-next-line(mixed-case-function)
|
|
77
|
-
function
|
|
77
|
+
function tierIdForEncodedIpfsUriOf(address hook, bytes32 encodedIpfsUri) external view returns (uint256);
|
|
78
78
|
|
|
79
79
|
/// @notice Get the tiers for the provided encoded IPFS URIs.
|
|
80
80
|
/// @param hook The hook from which to get tiers.
|
|
81
|
-
/// @param
|
|
81
|
+
/// @param encodedIpfsUris The URIs to get tiers of.
|
|
82
82
|
/// @return tiers The tiers that correspond to the provided encoded IPFS URIs. Empty tiers are returned for URIs
|
|
83
83
|
/// without a tier.
|
|
84
|
-
function tiersFor(address hook, bytes32[] memory
|
|
84
|
+
function tiersFor(address hook, bytes32[] memory encodedIpfsUris) external view returns (JB721Tier[] memory tiers);
|
|
85
85
|
|
|
86
86
|
/// @notice Configure the allowed criteria for publishing new NFTs to a hook.
|
|
87
87
|
/// @param allowedPosts An array of criteria for allowed posts.
|
package/src/structs/CTPost.sol
CHANGED
|
@@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
|
|
|
4
4
|
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
5
5
|
|
|
6
6
|
/// @notice A post to be published.
|
|
7
|
-
/// @custom:member
|
|
7
|
+
/// @custom:member encodedIpfsUri The encoded IPFS URI of the post to publish.
|
|
8
8
|
/// @custom:member totalSupply The number of NFTs that should be made available, including the 1 that will be minted
|
|
9
9
|
/// alongside this transaction.
|
|
10
10
|
/// @custom:member price The price to pay for buying the post.
|
|
@@ -13,7 +13,7 @@ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
|
13
13
|
/// JBConstants.SPLITS_TOTAL_PERCENT).
|
|
14
14
|
/// @custom:member splits The splits to route funds to when this tier is minted.
|
|
15
15
|
struct CTPost {
|
|
16
|
-
bytes32
|
|
16
|
+
bytes32 encodedIpfsUri;
|
|
17
17
|
uint32 totalSupply;
|
|
18
18
|
uint104 price;
|
|
19
19
|
uint24 category;
|