@croptop/core-v6 0.0.1

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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/SKILLS.md +88 -0
  4. package/deployments/croptop-core-v5/arbitrum/CTDeployer.json +1896 -0
  5. package/deployments/croptop-core-v5/arbitrum/CTProjectOwner.json +186 -0
  6. package/deployments/croptop-core-v5/arbitrum/CTPublisher.json +738 -0
  7. package/deployments/croptop-core-v5/arbitrum_sepolia/CTDeployer.json +1883 -0
  8. package/deployments/croptop-core-v5/arbitrum_sepolia/CTProjectOwner.json +186 -0
  9. package/deployments/croptop-core-v5/arbitrum_sepolia/CTPublisher.json +738 -0
  10. package/deployments/croptop-core-v5/base/CTDeployer.json +1908 -0
  11. package/deployments/croptop-core-v5/base/CTProjectOwner.json +190 -0
  12. package/deployments/croptop-core-v5/base/CTPublisher.json +741 -0
  13. package/deployments/croptop-core-v5/base_sepolia/CTDeployer.json +1894 -0
  14. package/deployments/croptop-core-v5/base_sepolia/CTProjectOwner.json +190 -0
  15. package/deployments/croptop-core-v5/base_sepolia/CTPublisher.json +741 -0
  16. package/deployments/croptop-core-v5/ethereum/CTDeployer.json +1894 -0
  17. package/deployments/croptop-core-v5/ethereum/CTProjectOwner.json +190 -0
  18. package/deployments/croptop-core-v5/ethereum/CTPublisher.json +741 -0
  19. package/deployments/croptop-core-v5/optimism/CTDeployer.json +1894 -0
  20. package/deployments/croptop-core-v5/optimism/CTProjectOwner.json +190 -0
  21. package/deployments/croptop-core-v5/optimism/CTPublisher.json +741 -0
  22. package/deployments/croptop-core-v5/optimism_sepolia/CTDeployer.json +1894 -0
  23. package/deployments/croptop-core-v5/optimism_sepolia/CTProjectOwner.json +190 -0
  24. package/deployments/croptop-core-v5/optimism_sepolia/CTPublisher.json +741 -0
  25. package/deployments/croptop-core-v5/sepolia/CTDeployer.json +1894 -0
  26. package/deployments/croptop-core-v5/sepolia/CTProjectOwner.json +190 -0
  27. package/deployments/croptop-core-v5/sepolia/CTPublisher.json +741 -0
  28. package/foundry.toml +25 -0
  29. package/package.json +31 -0
  30. package/remappings.txt +2 -0
  31. package/script/ConfigureFeeProject.s.sol +386 -0
  32. package/script/Deploy.s.sol +138 -0
  33. package/script/helpers/CroptopDeploymentLib.sol +75 -0
  34. package/slither-ci.config.json +10 -0
  35. package/sphinx.lock +507 -0
  36. package/src/CTDeployer.sol +425 -0
  37. package/src/CTProjectOwner.sol +78 -0
  38. package/src/CTPublisher.sol +540 -0
  39. package/src/interfaces/ICTDeployer.sol +56 -0
  40. package/src/interfaces/ICTProjectOwner.sol +24 -0
  41. package/src/interfaces/ICTPublisher.sol +91 -0
  42. package/src/structs/CTAllowedPost.sol +22 -0
  43. package/src/structs/CTDeployerAllowedPost.sol +20 -0
  44. package/src/structs/CTPost.sol +22 -0
  45. package/src/structs/CTProjectConfig.sol +22 -0
  46. package/src/structs/CTSuckerDeploymentConfig.sol +11 -0
  47. package/test/CTPublisher.t.sol +672 -0
  48. package/test/CroptopAttacks.t.sol +439 -0
  49. package/test/Fork.t.sol +114 -0
  50. package/test/Test_MetadataGeneration.t.sol +70 -0
package/foundry.toml ADDED
@@ -0,0 +1,25 @@
1
+ [profile.default]
2
+ solc = '0.8.23'
3
+ evm_version = 'paris'
4
+ optimizer_runs = 100000000
5
+ libs = ["node_modules", "lib"]
6
+ fs_permissions = [{ access = "read-write", path = "./"}]
7
+
8
+ [profile.ci_sizes]
9
+ optimizer_runs = 200
10
+
11
+ [fuzz]
12
+ runs = 4096
13
+
14
+ [invariant]
15
+ runs = 1024
16
+ depth = 100
17
+ fail_on_revert = false
18
+
19
+ [rpc_endpoints]
20
+ ethereum = "${RPC_ETHEREUM_MAINNET}"
21
+
22
+ [fmt]
23
+ number_underscore = "thousands"
24
+ multiline_func_header = "all"
25
+ wrap_comments = true
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@croptop/core-v6",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/mejango/croptop-core-v6"
8
+ },
9
+ "scripts": {
10
+ "test": "forge test",
11
+ "coverage:integration": "forge coverage --match-path \"./src/*.sol\" --report lcov --report summary",
12
+ "deploy:mainnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks mainnets",
13
+ "deploy:mainnets:project": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/ConfigureFeeProject.s.sol --networks mainnets",
14
+ "deploy:testnets": "source ./.env && npx sphinx propose ./script/Deploy.s.sol --networks testnets",
15
+ "deploy:testnets:project": "source ./.env && export START_TIME=$(date +%s) && npx sphinx propose ./script/ConfigureFeeProject.s.sol --networks testnets",
16
+ "artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'croptop-core-v5'"
17
+ },
18
+ "dependencies": {
19
+ "@bananapus/core-v6": "^0.0.1",
20
+ "@bananapus/721-hook-v6": "^0.0.1",
21
+ "@bananapus/permission-ids-v6": "^0.0.1",
22
+ "@bananapus/ownable-v6": "^0.0.1",
23
+ "@bananapus/suckers-v6": "^0.0.1",
24
+ "@bananapus/buyback-hook-v6": "^0.0.1",
25
+ "@bananapus/swap-terminal-v6": "^0.0.1",
26
+ "@openzeppelin/contracts": "^5.2.0"
27
+ },
28
+ "devDependencies": {
29
+ "@sphinx-labs/plugins": "^0.33.1"
30
+ }
31
+ }
package/remappings.txt ADDED
@@ -0,0 +1,2 @@
1
+ @sphinx-labs/contracts/=node_modules/@sphinx-labs/contracts/contracts/foundry
2
+ @croptop/core-v5=./
@@ -0,0 +1,386 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import "@bananapus/721-hook-v5/script/helpers/Hook721DeploymentLib.sol";
5
+ import "@bananapus/buyback-hook-v5/script/helpers/BuybackDeploymentLib.sol";
6
+ import "@bananapus/core-v5/script/helpers/CoreDeploymentLib.sol";
7
+ import "@bananapus/suckers-v5/script/helpers/SuckerDeploymentLib.sol";
8
+ import "@bananapus/swap-terminal-v5/script/helpers/SwapTerminalDeploymentLib.sol";
9
+ import "@rev-net/core-v5/script/helpers/RevnetCoreDeploymentLib.sol";
10
+ import "./helpers/CroptopDeploymentLib.sol";
11
+
12
+ import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
13
+ import {Script} from "forge-std/Script.sol";
14
+
15
+ import {IJB721TokenUriResolver} from "@bananapus/721-hook-v5/src/interfaces/IJB721TokenUriResolver.sol";
16
+ import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook-v5/src/structs/JBDeploy721TiersHookConfig.sol";
17
+ import {JB721InitTiersConfig} from "@bananapus/721-hook-v5/src/structs/JB721InitTiersConfig.sol";
18
+ import {JB721TierConfig} from "@bananapus/721-hook-v5/src/structs/JB721TierConfig.sol";
19
+ import {JB721TiersHookFlags} from "@bananapus/721-hook-v5/src/structs/JB721TiersHookFlags.sol";
20
+ import {IJBPrices} from "@bananapus/core-v5/src/interfaces/IJBPrices.sol";
21
+ import {IJBSplitHook} from "@bananapus/core-v5/src/interfaces/IJBSplitHook.sol";
22
+ import {IJBTerminal} from "@bananapus/core-v5/src/interfaces/IJBTerminal.sol";
23
+ import {JBAccountingContext} from "@bananapus/core-v5/src/structs/JBAccountingContext.sol";
24
+ import {JBTerminalConfig} from "@bananapus/core-v5/src/structs/JBTerminalConfig.sol";
25
+ import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
26
+ import {JBSplit} from "@bananapus/core-v5/src/structs/JBSplit.sol";
27
+ import {JBCurrencyIds} from "@bananapus/core-v5/src/libraries/JBCurrencyIds.sol";
28
+ import {JBSuckerDeployerConfig} from "@bananapus/suckers-v5/src/structs/JBSuckerDeployerConfig.sol";
29
+ import {JBTokenMapping} from "@bananapus/suckers-v5/src/structs/JBTokenMapping.sol";
30
+ import {REVAutoIssuance} from "@rev-net/core-v5/src/structs/REVAutoIssuance.sol";
31
+ import {REVBuybackHookConfig} from "@rev-net/core-v5/src/structs/REVBuybackHookConfig.sol";
32
+ import {REVBuybackPoolConfig} from "@rev-net/core-v5/src/structs/REVBuybackPoolConfig.sol";
33
+ import {REVConfig} from "@rev-net/core-v5/src/structs/REVConfig.sol";
34
+ import {REVCroptopAllowedPost} from "@rev-net/core-v5/src/structs/REVCroptopAllowedPost.sol";
35
+ import {REVDeploy721TiersHookConfig} from "@rev-net/core-v5/src/structs/REVDeploy721TiersHookConfig.sol";
36
+ import {REVDescription} from "@rev-net/core-v5/src/structs/REVDescription.sol";
37
+ import {REVLoanSource} from "@rev-net/core-v5/src/structs/REVLoanSource.sol";
38
+ import {REVStageConfig} from "@rev-net/core-v5/src/structs/REVStageConfig.sol";
39
+ import {REVSuckerDeploymentConfig} from "@rev-net/core-v5/src/structs/REVSuckerDeploymentConfig.sol";
40
+
41
+ struct FeeProjectConfig {
42
+ REVConfig configuration;
43
+ JBTerminalConfig[] terminalConfigurations;
44
+ REVBuybackHookConfig buybackHookConfiguration;
45
+ REVSuckerDeploymentConfig suckerDeploymentConfiguration;
46
+ REVDeploy721TiersHookConfig hookConfiguration;
47
+ REVCroptopAllowedPost[] allowedPosts;
48
+ }
49
+
50
+ contract ConfigureFeeProjectScript is Script, Sphinx {
51
+ /// @notice tracks the deployment of the buyback hook.
52
+ BuybackDeployment buybackHook;
53
+ /// @notice tracks the deployment of the core contracts for the chain we are deploying to.
54
+ CoreDeployment core;
55
+ /// @notice tracks the latest croptop deployment.
56
+ CroptopDeployment croptop;
57
+ /// @notice tracks the deployment of the 721 hook contracts for the chain we are deploying to.
58
+ Hook721Deployment hook;
59
+ /// @notice tracks the deployment of the revnet contracts for the chain we are deploying to.
60
+ RevnetCoreDeployment revnet;
61
+ /// @notice tracks the deployment of the sucker contracts for the chain we are deploying to.
62
+ SuckerDeployment suckers;
63
+ /// @notice tracks the deployment of the swap terminal.
64
+ SwapTerminalDeployment swapTerminal;
65
+
66
+ // @notice set this to a non-zero value to re-use an existing projectID. Having it set to 0 will deploy a new
67
+ // fee_project.
68
+ uint256 FEE_PROJECT_ID;
69
+
70
+ uint32 PREMINT_CHAIN_ID = 1;
71
+ string NAME = "Croptop Publishing Network";
72
+ string SYMBOL = "CPN";
73
+ string PROJECT_URI = "ipfs://QmUAFevoMn1iqSEQR8LogQYRxm39TNxQTPYnuLuq5BmfEi";
74
+ uint32 NATIVE_CURRENCY = uint32(uint160(JBConstants.NATIVE_TOKEN));
75
+ uint32 ETH_CURRENCY = JBCurrencyIds.ETH;
76
+ uint8 DECIMALS = 18;
77
+ uint256 DECIMAL_MULTIPLIER = 10 ** DECIMALS;
78
+ bytes32 SUCKER_SALT = "_CPN_SUCKERV6__";
79
+ bytes32 ERC20_SALT = "_CPN_ERC20_SALTV6__";
80
+ bytes32 HOOK_SALT = "_CPN_HOOK_SALTV6__";
81
+ address OPERATOR;
82
+ address TRUSTED_FORWARDER;
83
+ uint48 CPN_START_TIME = 1_740_089_444;
84
+ uint104 CPN_MAINNET_AUTO_ISSUANCE_ = 250_003_875_000_000_000_000_000;
85
+ uint104 CPN_BASE_AUTO_ISSUANCE_ = 844_894_881_600_000_000_000;
86
+ uint104 CPN_ARB_AUTO_ISSUANCE_ = 3_844_000_000_000_000_000;
87
+
88
+ function configureSphinx() public override {
89
+ // TODO: Update to contain croptop devs.
90
+ sphinxConfig.projectName = "croptop-core-v5";
91
+ sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
92
+ sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
93
+ }
94
+
95
+ function run() public {
96
+ // Get the deployment addresses for the nana CORE for this chain.
97
+ // We want to do this outside of the `sphinx` modifier.
98
+ core = CoreDeploymentLib.getDeployment(
99
+ vm.envOr("NANA_CORE_DEPLOYMENT_PATH", string("node_modules/@bananapus/core-v5/deployments/"))
100
+ );
101
+ // Get the deployment addresses for the croptop contracts for this chain.
102
+ croptop = CroptopDeploymentLib.getDeployment(vm.envOr("CROPTOP_DEPLOYMENT_PATH", string("deployments/")));
103
+ // Get the deployment addresses for the 721 hook contracts for this chain.
104
+ hook = Hook721DeploymentLib.getDeployment(
105
+ vm.envOr("NANA_721_DEPLOYMENT_PATH", string("node_modules/@bananapus/721-hook-v5/deployments/"))
106
+ );
107
+ // Get the deployment addresses for the 721 hook contracts for this chain.
108
+ revnet = RevnetCoreDeploymentLib.getDeployment(
109
+ vm.envOr("REVNET_CORE_DEPLOYMENT_PATH", string("node_modules/@rev-net/core-v5/deployments/"))
110
+ );
111
+ // Get the deployment addresses for the suckers contracts for this chain.
112
+ suckers = SuckerDeploymentLib.getDeployment(
113
+ vm.envOr("NANA_SUCKERS_DEPLOYMENT_PATH", string("node_modules/@bananapus/suckers-v5/deployments/"))
114
+ );
115
+ // Get the deployment addresses for the 721 hook contracts for this chain.
116
+ swapTerminal = SwapTerminalDeploymentLib.getDeployment(
117
+ vm.envOr(
118
+ "NANA_SWAP_TERMINAL_DEPLOYMENT_PATH", string("node_modules/@bananapus/swap-terminal-v5/deployments/")
119
+ )
120
+ );
121
+
122
+ // We do a quick sanity check to make sure revnet and croptop use the same juicebox core contracts.
123
+ require(revnet.basic_deployer.DIRECTORY() == croptop.publisher.DIRECTORY());
124
+
125
+ // Set the operator address to be the multisig.
126
+ OPERATOR = safeAddress();
127
+ TRUSTED_FORWARDER = core.controller.trustedForwarder();
128
+
129
+ // Get the fee project id from the croptop deployment.
130
+ FEE_PROJECT_ID = croptop.publisher.FEE_PROJECT_ID();
131
+
132
+ // Check if there should be a new fee project created.
133
+ // Perform the deployment transactions.
134
+ deploy();
135
+ }
136
+
137
+ function getCroptopRevnetConfig() internal view returns (FeeProjectConfig memory) {
138
+ // The tokens that the project accepts and stores.
139
+ JBAccountingContext[] memory accountingContextsToAccept = new JBAccountingContext[](1);
140
+
141
+ // Accept the chain's native currency through the multi terminal.
142
+ accountingContextsToAccept[0] =
143
+ JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: DECIMALS, currency: NATIVE_CURRENCY});
144
+
145
+ // The terminals that the project will accept funds through.
146
+ JBTerminalConfig[] memory terminalConfigurations = new JBTerminalConfig[](2);
147
+ terminalConfigurations[0] =
148
+ JBTerminalConfig({terminal: core.terminal, accountingContextsToAccept: accountingContextsToAccept});
149
+ terminalConfigurations[1] = JBTerminalConfig({
150
+ terminal: IJBTerminal(address(swapTerminal.registry)),
151
+ accountingContextsToAccept: new JBAccountingContext[](0)
152
+ });
153
+
154
+ REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](3);
155
+ issuanceConfs[0] = REVAutoIssuance({chainId: 1, count: CPN_MAINNET_AUTO_ISSUANCE_, beneficiary: OPERATOR});
156
+ issuanceConfs[1] = REVAutoIssuance({chainId: 8453, count: CPN_BASE_AUTO_ISSUANCE_, beneficiary: OPERATOR});
157
+ issuanceConfs[2] = REVAutoIssuance({chainId: 42_161, count: CPN_ARB_AUTO_ISSUANCE_, beneficiary: OPERATOR});
158
+
159
+ JBSplit[] memory splits = new JBSplit[](1);
160
+ splits[0] = JBSplit({
161
+ percent: JBConstants.SPLITS_TOTAL_PERCENT,
162
+ projectId: 0,
163
+ beneficiary: payable(OPERATOR),
164
+ preferAddToBalance: false,
165
+ lockedUntil: 0,
166
+ hook: IJBSplitHook(address(0))
167
+ });
168
+
169
+ // The project's revnet stage configurations.
170
+ REVStageConfig[] memory stageConfigurations = new REVStageConfig[](3);
171
+ stageConfigurations[0] = REVStageConfig({
172
+ startsAtOrAfter: CPN_START_TIME,
173
+ autoIssuances: issuanceConfs,
174
+ splitPercent: 3800, // 38%
175
+ splits: splits,
176
+ initialIssuance: uint112(10_000 * DECIMAL_MULTIPLIER),
177
+ issuanceCutFrequency: 120 days,
178
+ issuanceCutPercent: 380_000_000, // 38%
179
+ cashOutTaxRate: 1000, // 0.1
180
+ extraMetadata: 4 // Allow adding suckers.
181
+ });
182
+
183
+ stageConfigurations[1] = REVStageConfig({
184
+ startsAtOrAfter: uint40(stageConfigurations[0].startsAtOrAfter + 720 days),
185
+ autoIssuances: new REVAutoIssuance[](0),
186
+ splitPercent: 3800, // 38%
187
+ splits: splits,
188
+ initialIssuance: 1, // inherit from previous cycle.
189
+ issuanceCutFrequency: 30 days,
190
+ issuanceCutPercent: 70_000_000, // 7%
191
+ cashOutTaxRate: 1000, // 0.1
192
+ extraMetadata: 4 // Allow adding suckers.
193
+ });
194
+
195
+ stageConfigurations[2] = REVStageConfig({
196
+ startsAtOrAfter: uint40(stageConfigurations[1].startsAtOrAfter + 3800 days),
197
+ autoIssuances: new REVAutoIssuance[](0),
198
+ splitPercent: 3800, // 38%
199
+ splits: splits,
200
+ initialIssuance: 0, // no more issuance.
201
+ issuanceCutFrequency: 0,
202
+ issuanceCutPercent: 0,
203
+ cashOutTaxRate: 1000, // 0.1
204
+ extraMetadata: 4 // Allow adding suckers.
205
+ });
206
+
207
+ // The projects loan configuration.
208
+ REVLoanSource[] memory loanSources = new REVLoanSource[](1);
209
+ loanSources[0] = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: core.terminal});
210
+
211
+ // The project's revnet configuration
212
+ REVConfig memory revnetConfiguration = REVConfig({
213
+ description: REVDescription(NAME, SYMBOL, PROJECT_URI, ERC20_SALT),
214
+ baseCurrency: ETH_CURRENCY,
215
+ splitOperator: OPERATOR,
216
+ stageConfigurations: stageConfigurations,
217
+ loanSources: loanSources,
218
+ loans: address(revnet.loans)
219
+ });
220
+
221
+ REVBuybackHookConfig memory buybackHookConfiguration;
222
+ {
223
+ // The project's buyback hook configuration.
224
+ REVBuybackPoolConfig[] memory buybackPoolConfigurations = new REVBuybackPoolConfig[](1);
225
+ buybackPoolConfigurations[0] =
226
+ REVBuybackPoolConfig({token: JBConstants.NATIVE_TOKEN, fee: 10_000, twapWindow: 2 days});
227
+ buybackHookConfiguration = REVBuybackHookConfig({
228
+ dataHook: buybackHook.registry,
229
+ hookToConfigure: buybackHook.hook,
230
+ poolConfigurations: buybackPoolConfigurations
231
+ });
232
+ }
233
+
234
+ // Organize the instructions for how this project will connect to other chains.
235
+ JBTokenMapping[] memory tokenMappings = new JBTokenMapping[](1);
236
+ tokenMappings[0] = JBTokenMapping({
237
+ localToken: JBConstants.NATIVE_TOKEN,
238
+ remoteToken: JBConstants.NATIVE_TOKEN,
239
+ minGas: 200_000,
240
+ minBridgeAmount: 0.01 ether
241
+ });
242
+
243
+ REVSuckerDeploymentConfig memory suckerDeploymentConfiguration;
244
+
245
+ {
246
+ JBSuckerDeployerConfig[] memory suckerDeployerConfigurations;
247
+ if (block.chainid == 1 || block.chainid == 11_155_111) {
248
+ suckerDeployerConfigurations = new JBSuckerDeployerConfig[](3);
249
+ // OP
250
+ suckerDeployerConfigurations[0] =
251
+ JBSuckerDeployerConfig({deployer: suckers.optimismDeployer, mappings: tokenMappings});
252
+
253
+ suckerDeployerConfigurations[1] =
254
+ JBSuckerDeployerConfig({deployer: suckers.baseDeployer, mappings: tokenMappings});
255
+
256
+ suckerDeployerConfigurations[2] =
257
+ JBSuckerDeployerConfig({deployer: suckers.arbitrumDeployer, mappings: tokenMappings});
258
+ } else {
259
+ suckerDeployerConfigurations = new JBSuckerDeployerConfig[](1);
260
+ // L2 -> Mainnet
261
+ suckerDeployerConfigurations[0] = JBSuckerDeployerConfig({
262
+ deployer: address(suckers.optimismDeployer) != address(0)
263
+ ? suckers.optimismDeployer
264
+ : address(suckers.baseDeployer) != address(0) ? suckers.baseDeployer : suckers.arbitrumDeployer,
265
+ mappings: tokenMappings
266
+ });
267
+
268
+ if (address(suckerDeployerConfigurations[0].deployer) == address(0)) {
269
+ revert("L2 > L1 Sucker is not configured");
270
+ }
271
+ }
272
+ // Specify all sucker deployments.
273
+ suckerDeploymentConfiguration =
274
+ REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt: SUCKER_SALT});
275
+ }
276
+
277
+ // The project's allowed croptop posts.
278
+ REVCroptopAllowedPost[] memory allowedPosts = new REVCroptopAllowedPost[](5);
279
+ allowedPosts[0] = REVCroptopAllowedPost({
280
+ category: 0,
281
+ minimumPrice: uint104(10 ** (DECIMALS - 5)),
282
+ minimumTotalSupply: 10_000,
283
+ maximumTotalSupply: 999_999_999,
284
+ allowedAddresses: new address[](0)
285
+ });
286
+ allowedPosts[1] = REVCroptopAllowedPost({
287
+ category: 1,
288
+ minimumPrice: uint104(10 ** (DECIMALS - 3)),
289
+ minimumTotalSupply: 10_000,
290
+ maximumTotalSupply: 999_999_999,
291
+ allowedAddresses: new address[](0)
292
+ });
293
+ allowedPosts[2] = REVCroptopAllowedPost({
294
+ category: 2,
295
+ minimumPrice: uint104(10 ** (DECIMALS - 1)),
296
+ minimumTotalSupply: 100,
297
+ maximumTotalSupply: 999_999_999,
298
+ allowedAddresses: new address[](0)
299
+ });
300
+ allowedPosts[3] = REVCroptopAllowedPost({
301
+ category: 3,
302
+ minimumPrice: uint104(10 ** DECIMALS),
303
+ minimumTotalSupply: 10,
304
+ maximumTotalSupply: 999_999_999,
305
+ allowedAddresses: new address[](0)
306
+ });
307
+ allowedPosts[4] = REVCroptopAllowedPost({
308
+ category: 4,
309
+ minimumPrice: uint104(10 ** (DECIMALS + 2)),
310
+ minimumTotalSupply: 10,
311
+ maximumTotalSupply: 999_999_999,
312
+ allowedAddresses: new address[](0)
313
+ });
314
+
315
+ return FeeProjectConfig({
316
+ configuration: revnetConfiguration,
317
+ terminalConfigurations: terminalConfigurations,
318
+ buybackHookConfiguration: buybackHookConfiguration,
319
+ suckerDeploymentConfiguration: suckerDeploymentConfiguration,
320
+ hookConfiguration: REVDeploy721TiersHookConfig({
321
+ baseline721HookConfiguration: JBDeploy721TiersHookConfig({
322
+ name: NAME,
323
+ symbol: SYMBOL,
324
+ baseUri: "ipfs://",
325
+ tokenUriResolver: IJB721TokenUriResolver(address(0)),
326
+ contractUri: "",
327
+ tiersConfig: JB721InitTiersConfig({
328
+ tiers: new JB721TierConfig[](0), currency: ETH_CURRENCY, decimals: DECIMALS, prices: core.prices
329
+ }),
330
+ reserveBeneficiary: address(0),
331
+ flags: JB721TiersHookFlags({
332
+ noNewTiersWithReserves: false,
333
+ noNewTiersWithVotes: true,
334
+ noNewTiersWithOwnerMinting: true,
335
+ preventOverspending: false
336
+ })
337
+ }),
338
+ salt: HOOK_SALT,
339
+ splitOperatorCanAdjustTiers: true,
340
+ splitOperatorCanUpdateMetadata: false,
341
+ splitOperatorCanMint: false,
342
+ splitOperatorCanIncreaseDiscountPercent: false
343
+ }),
344
+ allowedPosts: allowedPosts
345
+ });
346
+ }
347
+
348
+ function deploy() public sphinx {
349
+ FeeProjectConfig memory feeProjectConfig = getCroptopRevnetConfig();
350
+
351
+ // Approve the basic deployer to configure the project and transfer it.
352
+ core.projects.approve(address(revnet.basic_deployer), FEE_PROJECT_ID);
353
+
354
+ // Deploy the NANA fee project.
355
+ revnet.basic_deployer
356
+ .deployWith721sFor({
357
+ revnetId: FEE_PROJECT_ID,
358
+ configuration: feeProjectConfig.configuration,
359
+ terminalConfigurations: feeProjectConfig.terminalConfigurations,
360
+ buybackHookConfiguration: feeProjectConfig.buybackHookConfiguration,
361
+ suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
362
+ tiered721HookConfiguration: feeProjectConfig.hookConfiguration,
363
+ allowedPosts: feeProjectConfig.allowedPosts
364
+ });
365
+ }
366
+
367
+ function _isDeployed(
368
+ bytes32 salt,
369
+ bytes memory creationCode,
370
+ bytes memory arguments
371
+ )
372
+ internal
373
+ view
374
+ returns (address, bool)
375
+ {
376
+ address _deployedTo = vm.computeCreate2Address({
377
+ salt: salt,
378
+ initCodeHash: keccak256(abi.encodePacked(creationCode, arguments)),
379
+ // Arachnid/deterministic-deployment-proxy address.
380
+ deployer: address(0x4e59b44847b379578588920cA78FbF26c0B4956C)
381
+ });
382
+
383
+ // Return if code is already present at this address.
384
+ return (_deployedTo, address(_deployedTo).code.length != 0);
385
+ }
386
+ }
@@ -0,0 +1,138 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import "@bananapus/721-hook-v5/script/helpers/Hook721DeploymentLib.sol";
5
+ import "@bananapus/core-v5/script/helpers/CoreDeploymentLib.sol";
6
+ import "@bananapus/suckers-v5/script/helpers/SuckerDeploymentLib.sol";
7
+
8
+ import {Sphinx} from "@sphinx-labs/contracts/SphinxPlugin.sol";
9
+ import {Script} from "forge-std/Script.sol";
10
+
11
+ import {CTDeployer} from "./../src/CTDeployer.sol";
12
+ import {CTProjectOwner} from "./../src/CTProjectOwner.sol";
13
+ import {CTPublisher} from "./../src/CTPublisher.sol";
14
+
15
+ contract DeployScript is Script, Sphinx {
16
+ /// @notice tracks the deployment of the core contracts for the chain we are deploying to.
17
+ CoreDeployment core;
18
+ /// @notice tracks the deployment of the 721 hook contracts for the chain we are deploying to.
19
+ Hook721Deployment hook;
20
+ /// @notice tracks the deployment of the sucker contracts for the chain we are deploying to.
21
+ SuckerDeployment suckers;
22
+
23
+ // @notice set this to a non-zero value to re-use an existing projectID. Having it set to 0 will deploy a new
24
+ // fee_project.
25
+ uint256 FEE_PROJECT_ID = 0;
26
+
27
+ /// @notice the salts that are used to deploy the contracts.
28
+ bytes32 PUBLISHER_SALT = "_PUBLISHER_SALTV6_";
29
+ bytes32 DEPLOYER_SALT = "_DEPLOYER_SALTV6_";
30
+ bytes32 PROJECT_OWNER_SALT = "_PROJECT_OWNER_SALTV6_";
31
+ address TRUSTED_FORWARDER;
32
+
33
+ function configureSphinx() public override {
34
+ sphinxConfig.projectName = "croptop-core-v5";
35
+ sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
36
+ sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
37
+ }
38
+
39
+ function run() public {
40
+ // Get the deployment addresses for the nana CORE for this chain.
41
+ // We want to do this outside of the `sphinx` modifier.
42
+ core = CoreDeploymentLib.getDeployment(
43
+ vm.envOr("NANA_CORE_DEPLOYMENT_PATH", string("node_modules/@bananapus/core-v5/deployments/"))
44
+ );
45
+ // Get the deployment addresses for the 721 hook contracts for this chain.
46
+ hook = Hook721DeploymentLib.getDeployment(
47
+ vm.envOr("NANA_721_DEPLOYMENT_PATH", string("node_modules/@bananapus/721-hook-v5/deployments/"))
48
+ );
49
+ // Get the deployment addresses for the suckers contracts for this chain.
50
+ suckers = SuckerDeploymentLib.getDeployment(
51
+ vm.envOr("NANA_SUCKERS_DEPLOYMENT_PATH", string("node_modules/@bananapus/suckers-v5/deployments/"))
52
+ );
53
+
54
+ // We use the same trusted forwarder as the core deployment.
55
+ TRUSTED_FORWARDER = core.controller.trustedForwarder();
56
+
57
+ // Perform the deployment transactions.
58
+ deploy();
59
+ }
60
+
61
+ function deploy() public sphinx {
62
+ // If the fee project id is 0, then we want to deploy a new fee project.
63
+ if (FEE_PROJECT_ID == 0) {
64
+ FEE_PROJECT_ID = core.projects.createFor(safeAddress());
65
+ }
66
+
67
+ CTPublisher publisher;
68
+ {
69
+ // Perform the check for the publisher.
70
+ (address _publisher, bool _publisherIsDeployed) = _isDeployed(
71
+ PUBLISHER_SALT,
72
+ type(CTPublisher).creationCode,
73
+ abi.encode(core.directory, core.permissions, FEE_PROJECT_ID, TRUSTED_FORWARDER)
74
+ );
75
+
76
+ // Deploy it if it has not been deployed yet.
77
+ publisher = !_publisherIsDeployed
78
+ ? new CTPublisher{salt: PUBLISHER_SALT}(
79
+ core.directory, core.permissions, FEE_PROJECT_ID, TRUSTED_FORWARDER
80
+ )
81
+ : CTPublisher(_publisher);
82
+ }
83
+
84
+ CTDeployer deployer;
85
+ {
86
+ // Perform the check for the publisher.
87
+ (address _deployer, bool _deployerIsDeployed) = _isDeployed(
88
+ DEPLOYER_SALT,
89
+ type(CTDeployer).creationCode,
90
+ abi.encode(
91
+ core.permissions, core.projects, hook.hook_deployer, publisher, suckers.registry, TRUSTED_FORWARDER
92
+ )
93
+ );
94
+
95
+ // Deploy it if it has not been deployed yet.
96
+ deployer = !_deployerIsDeployed
97
+ ? new CTDeployer{salt: DEPLOYER_SALT}(
98
+ core.permissions, core.projects, hook.hook_deployer, publisher, suckers.registry, TRUSTED_FORWARDER
99
+ )
100
+ : CTDeployer(_deployer);
101
+ }
102
+
103
+ CTProjectOwner owner;
104
+ {
105
+ // Perform the check for the publisher.
106
+ (address _owner, bool _ownerIsDeployed) = _isDeployed(
107
+ PROJECT_OWNER_SALT,
108
+ type(CTProjectOwner).creationCode,
109
+ abi.encode(core.permissions, core.projects, publisher)
110
+ );
111
+
112
+ // Deploy it if it has not been deployed yet.
113
+ owner = !_ownerIsDeployed
114
+ ? new CTProjectOwner{salt: PROJECT_OWNER_SALT}(core.permissions, core.projects, publisher)
115
+ : CTProjectOwner(_owner);
116
+ }
117
+ }
118
+
119
+ function _isDeployed(
120
+ bytes32 salt,
121
+ bytes memory creationCode,
122
+ bytes memory arguments
123
+ )
124
+ internal
125
+ view
126
+ returns (address, bool)
127
+ {
128
+ address _deployedTo = vm.computeCreate2Address({
129
+ salt: salt,
130
+ initCodeHash: keccak256(abi.encodePacked(creationCode, arguments)),
131
+ // Arachnid/deterministic-deployment-proxy address.
132
+ deployer: address(0x4e59b44847b379578588920cA78FbF26c0B4956C)
133
+ });
134
+
135
+ // Return if code is already present at this address.
136
+ return (_deployedTo, address(_deployedTo).code.length != 0);
137
+ }
138
+ }
@@ -0,0 +1,75 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import {stdJson} from "forge-std/Script.sol";
5
+ import {Vm} from "forge-std/Vm.sol";
6
+
7
+ import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/SphinxConstants.sol";
8
+
9
+ import {CTPublisher} from "../../src/CTPublisher.sol";
10
+ import {CTDeployer} from "../../src/CTDeployer.sol";
11
+ import {CTProjectOwner} from "../../src/CTProjectOwner.sol";
12
+
13
+ struct CroptopDeployment {
14
+ CTPublisher publisher;
15
+ CTDeployer deployer;
16
+ CTProjectOwner project_owner;
17
+ }
18
+
19
+ library CroptopDeploymentLib {
20
+ // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
21
+ address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
22
+ Vm internal constant vm = Vm(VM_ADDRESS);
23
+
24
+ function getDeployment(string memory path) internal returns (CroptopDeployment memory deployment) {
25
+ // get chainId for which we need to get the deployment.
26
+ uint256 chainId = block.chainid;
27
+
28
+ // Deploy to get the constants.
29
+ // TODO: get constants without deploy.
30
+ SphinxConstants sphinxConstants = new SphinxConstants();
31
+ NetworkInfo[] memory networks = sphinxConstants.getNetworkInfoArray();
32
+
33
+ for (uint256 _i; _i < networks.length; _i++) {
34
+ if (networks[_i].chainId == chainId) {
35
+ return getDeployment(path, networks[_i].name);
36
+ }
37
+ }
38
+
39
+ revert("ChainID is not (currently) supported by Sphinx.");
40
+ }
41
+
42
+ function getDeployment(
43
+ string memory path,
44
+ string memory network_name
45
+ )
46
+ internal
47
+ view
48
+ returns (CroptopDeployment memory deployment)
49
+ {
50
+ deployment.publisher = CTPublisher(_getDeploymentAddress(path, "croptop-core-v5", network_name, "CTPublisher"));
51
+ deployment.deployer = CTDeployer(_getDeploymentAddress(path, "croptop-core-v5", network_name, "CTDeployer"));
52
+ deployment.project_owner =
53
+ CTProjectOwner(_getDeploymentAddress(path, "croptop-core-v5", network_name, "CTProjectOwner"));
54
+ }
55
+
56
+ /// @notice Get the address of a contract that was deployed by the Deploy script.
57
+ /// @dev Reverts if the contract was not found.
58
+ /// @param path The path to the deployment file.
59
+ /// @param contractName The name of the contract to get the address of.
60
+ /// @return The address of the contract.
61
+ function _getDeploymentAddress(
62
+ string memory path,
63
+ string memory project_name,
64
+ string memory network_name,
65
+ string memory contractName
66
+ )
67
+ internal
68
+ view
69
+ returns (address)
70
+ {
71
+ string memory deploymentJson =
72
+ vm.readFile(string.concat(path, project_name, "/", network_name, "/", contractName, ".json"));
73
+ return stdJson.readAddress(deploymentJson, ".address");
74
+ }
75
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "detectors_to_exclude": "timestamp,uninitialized-local,naming-convention,solc-version,shadowing-local",
3
+ "exclude_informational": true,
4
+ "exclude_low": false,
5
+ "exclude_medium": false,
6
+ "exclude_high": false,
7
+ "disable_color": false,
8
+ "filter_paths": "(mocks/|test/|node_modules/|lib/)",
9
+ "legacy_ast": false
10
+ }