@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.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/SKILLS.md +88 -0
- package/deployments/croptop-core-v5/arbitrum/CTDeployer.json +1896 -0
- package/deployments/croptop-core-v5/arbitrum/CTProjectOwner.json +186 -0
- package/deployments/croptop-core-v5/arbitrum/CTPublisher.json +738 -0
- package/deployments/croptop-core-v5/arbitrum_sepolia/CTDeployer.json +1883 -0
- package/deployments/croptop-core-v5/arbitrum_sepolia/CTProjectOwner.json +186 -0
- package/deployments/croptop-core-v5/arbitrum_sepolia/CTPublisher.json +738 -0
- package/deployments/croptop-core-v5/base/CTDeployer.json +1908 -0
- package/deployments/croptop-core-v5/base/CTProjectOwner.json +190 -0
- package/deployments/croptop-core-v5/base/CTPublisher.json +741 -0
- package/deployments/croptop-core-v5/base_sepolia/CTDeployer.json +1894 -0
- package/deployments/croptop-core-v5/base_sepolia/CTProjectOwner.json +190 -0
- package/deployments/croptop-core-v5/base_sepolia/CTPublisher.json +741 -0
- package/deployments/croptop-core-v5/ethereum/CTDeployer.json +1894 -0
- package/deployments/croptop-core-v5/ethereum/CTProjectOwner.json +190 -0
- package/deployments/croptop-core-v5/ethereum/CTPublisher.json +741 -0
- package/deployments/croptop-core-v5/optimism/CTDeployer.json +1894 -0
- package/deployments/croptop-core-v5/optimism/CTProjectOwner.json +190 -0
- package/deployments/croptop-core-v5/optimism/CTPublisher.json +741 -0
- package/deployments/croptop-core-v5/optimism_sepolia/CTDeployer.json +1894 -0
- package/deployments/croptop-core-v5/optimism_sepolia/CTProjectOwner.json +190 -0
- package/deployments/croptop-core-v5/optimism_sepolia/CTPublisher.json +741 -0
- package/deployments/croptop-core-v5/sepolia/CTDeployer.json +1894 -0
- package/deployments/croptop-core-v5/sepolia/CTProjectOwner.json +190 -0
- package/deployments/croptop-core-v5/sepolia/CTPublisher.json +741 -0
- package/foundry.toml +25 -0
- package/package.json +31 -0
- package/remappings.txt +2 -0
- package/script/ConfigureFeeProject.s.sol +386 -0
- package/script/Deploy.s.sol +138 -0
- package/script/helpers/CroptopDeploymentLib.sol +75 -0
- package/slither-ci.config.json +10 -0
- package/sphinx.lock +507 -0
- package/src/CTDeployer.sol +425 -0
- package/src/CTProjectOwner.sol +78 -0
- package/src/CTPublisher.sol +540 -0
- package/src/interfaces/ICTDeployer.sol +56 -0
- package/src/interfaces/ICTProjectOwner.sol +24 -0
- package/src/interfaces/ICTPublisher.sol +91 -0
- package/src/structs/CTAllowedPost.sol +22 -0
- package/src/structs/CTDeployerAllowedPost.sol +20 -0
- package/src/structs/CTPost.sol +22 -0
- package/src/structs/CTProjectConfig.sol +22 -0
- package/src/structs/CTSuckerDeploymentConfig.sol +11 -0
- package/test/CTPublisher.t.sol +672 -0
- package/test/CroptopAttacks.t.sol +439 -0
- package/test/Fork.t.sol +114 -0
- 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,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
|
+
}
|