@croptop/core-v6 0.0.36 → 0.0.37

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.
@@ -0,0 +1,371 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.17;
3
+
4
+ import "forge-std/Test.sol";
5
+
6
+ import {JBPermissions} from "@bananapus/core-v6/src/JBPermissions.sol";
7
+ import {JBProjects} from "@bananapus/core-v6/src/JBProjects.sol";
8
+ import {JBDirectory} from "@bananapus/core-v6/src/JBDirectory.sol";
9
+ import {JBRulesets} from "@bananapus/core-v6/src/JBRulesets.sol";
10
+ import {JBTokens} from "@bananapus/core-v6/src/JBTokens.sol";
11
+ import {JBERC20} from "@bananapus/core-v6/src/JBERC20.sol";
12
+ import {JBSplits} from "@bananapus/core-v6/src/JBSplits.sol";
13
+ import {JBPrices} from "@bananapus/core-v6/src/JBPrices.sol";
14
+ import {JBController} from "@bananapus/core-v6/src/JBController.sol";
15
+ import {JBFundAccessLimits} from "@bananapus/core-v6/src/JBFundAccessLimits.sol";
16
+ import {JBMultiTerminal} from "@bananapus/core-v6/src/JBMultiTerminal.sol";
17
+ import {JBTerminalStore} from "@bananapus/core-v6/src/JBTerminalStore.sol";
18
+ import {JBFeelessAddresses} from "@bananapus/core-v6/src/JBFeelessAddresses.sol";
19
+ import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
20
+ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
21
+ import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
22
+ import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
23
+ import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
24
+ import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
25
+ import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
26
+ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
27
+ import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
28
+ import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
29
+
30
+ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
31
+ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
32
+ import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
33
+ import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
34
+ import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
35
+ import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
36
+ import {JB721InitTiersConfig} from "@bananapus/721-hook-v6/src/structs/JB721InitTiersConfig.sol";
37
+ import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfig.sol";
38
+ import {JB721TiersHookFlags} from "@bananapus/721-hook-v6/src/structs/JB721TiersHookFlags.sol";
39
+ import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook-v6/src/structs/JBDeploy721TiersHookConfig.sol";
40
+ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
41
+
42
+ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
43
+ import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
44
+ import {DeployPermit2} from "@uniswap/permit2/test/utils/DeployPermit2.sol";
45
+
46
+ import {CTDeployer} from "../../src/CTDeployer.sol";
47
+ import {CTPublisher} from "../../src/CTPublisher.sol";
48
+ import {CTAllowedPost} from "../../src/structs/CTAllowedPost.sol";
49
+ import {CTDeployerAllowedPost} from "../../src/structs/CTDeployerAllowedPost.sol";
50
+ import {CTPost} from "../../src/structs/CTPost.sol";
51
+ import {CTProjectConfig} from "../../src/structs/CTProjectConfig.sol";
52
+ import {CTSuckerDeploymentConfig} from "../../src/structs/CTSuckerDeploymentConfig.sol";
53
+ import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
54
+
55
+ contract CodexNemesisCurrencyPoCs is Test, DeployPermit2 {
56
+ address internal constant MULTISIG = address(0xBEEF);
57
+ address internal constant PROJECT_OWNER = address(0xA11CE);
58
+ address internal constant POSTER = address(0xB0B);
59
+ address internal constant NFT_BENEFICIARY = address(0xCAFE);
60
+ address internal constant FEE_BENEFICIARY = address(0xFEE);
61
+
62
+ uint104 internal constant POST_PRICE = 0.1 ether;
63
+ uint32 internal constant POST_SUPPLY = 100;
64
+ uint24 internal constant POST_CATEGORY = 1;
65
+ bytes32 internal constant TEST_URI = bytes32("nemesis-uri");
66
+
67
+ JBPermissions internal jbPermissions;
68
+ JBProjects internal jbProjects;
69
+ JBDirectory internal jbDirectory;
70
+ JBRulesets internal jbRulesets;
71
+ JBTokens internal jbTokens;
72
+ JBSplits internal jbSplits;
73
+ JBPrices internal jbPrices;
74
+ JBFundAccessLimits internal jbFundAccessLimits;
75
+ JBController internal jbController;
76
+ JBFeelessAddresses internal jbFeelessAddresses;
77
+ JBTerminalStore internal jbTerminalStore;
78
+ JBMultiTerminal internal jbMultiTerminal;
79
+ JB721TiersHookDeployer internal hookDeployer;
80
+ JBSuckerRegistry internal suckerRegistry;
81
+
82
+ function test_deployerProjectsNeedAnUndeclaredIdentityPriceFeed() public {
83
+ _deployCore();
84
+ _deployTerminal();
85
+ _deployHookInfra();
86
+ suckerRegistry = new JBSuckerRegistry(jbDirectory, jbPermissions, MULTISIG, address(0));
87
+
88
+ uint256 feeProjectId = _launchProject({
89
+ owner: PROJECT_OWNER,
90
+ baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
91
+ dataHook: address(0),
92
+ useDataHookForPay: false,
93
+ useDataHookForCashOut: false
94
+ });
95
+
96
+ CTPublisher publisher = new CTPublisher(jbDirectory, jbPermissions, feeProjectId, address(0));
97
+ CTDeployer deployer =
98
+ new CTDeployer(jbPermissions, jbProjects, hookDeployer, publisher, suckerRegistry, address(0));
99
+
100
+ (uint256 projectId, IJB721TiersHook hook) = _launchViaCTDeployer(deployer, PROJECT_OWNER);
101
+ assertEq(projectId, 2, "expected the first Croptop project to be project 2");
102
+
103
+ vm.deal(POSTER, 1 ether);
104
+
105
+ vm.prank(POSTER);
106
+ vm.expectRevert();
107
+ publisher.mintFrom{value: _totalValue()}(hook, _singlePost(), NFT_BENEFICIARY, FEE_BENEFICIARY, "", "");
108
+
109
+ MockPriceFeed identityFeed = new MockPriceFeed(1e18, 18);
110
+ vm.prank(MULTISIG);
111
+ jbPrices.addPriceFeedFor({
112
+ projectId: 0,
113
+ pricingCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
114
+ unitCurrency: JBCurrencyIds.ETH,
115
+ feed: identityFeed
116
+ });
117
+
118
+ vm.prank(POSTER);
119
+ publisher.mintFrom{value: _totalValue()}(hook, _singlePost(), NFT_BENEFICIARY, FEE_BENEFICIARY, "", "");
120
+ }
121
+
122
+ function test_misconfiguredFeeProjectRefundsAllCroptopFees() public {
123
+ _deployCore();
124
+ _deployTerminal();
125
+ _deployHookInfra();
126
+
127
+ uint256 feeProjectId = _launchProject({
128
+ owner: PROJECT_OWNER,
129
+ baseCurrency: JBCurrencyIds.ETH,
130
+ dataHook: address(0),
131
+ useDataHookForPay: false,
132
+ useDataHookForCashOut: false
133
+ });
134
+
135
+ CTPublisher publisher = new CTPublisher(jbDirectory, jbPermissions, feeProjectId, address(0));
136
+
137
+ (uint256 projectId, IJB721TiersHook hook) = _launchDirectProjectWithHook({
138
+ publisher: publisher, owner: PROJECT_OWNER, baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN))
139
+ });
140
+ assertEq(projectId, 2, "expected the publish target to be project 2");
141
+
142
+ vm.deal(POSTER, 1 ether);
143
+ uint256 posterBalanceBefore = POSTER.balance;
144
+ uint256 feeProjectBalanceBefore =
145
+ jbTerminalStore.balanceOf(address(jbMultiTerminal), feeProjectId, JBConstants.NATIVE_TOKEN);
146
+ uint256 targetBalanceBefore =
147
+ jbTerminalStore.balanceOf(address(jbMultiTerminal), projectId, JBConstants.NATIVE_TOKEN);
148
+
149
+ vm.prank(POSTER);
150
+ publisher.mintFrom{value: _totalValue()}(hook, _singlePost(), NFT_BENEFICIARY, FEE_BENEFICIARY, "", "");
151
+
152
+ uint256 feeProjectBalanceAfter =
153
+ jbTerminalStore.balanceOf(address(jbMultiTerminal), feeProjectId, JBConstants.NATIVE_TOKEN);
154
+ uint256 targetBalanceAfter =
155
+ jbTerminalStore.balanceOf(address(jbMultiTerminal), projectId, JBConstants.NATIVE_TOKEN);
156
+
157
+ assertEq(feeProjectBalanceAfter - feeProjectBalanceBefore, 0, "fee project should not receive the fee");
158
+ assertEq(targetBalanceAfter - targetBalanceBefore, POST_PRICE, "target project should still receive the post");
159
+ assertEq(
160
+ posterBalanceBefore - POSTER.balance,
161
+ POST_PRICE,
162
+ "the fee should be refunded back to the caller after the fee-project pay reverts"
163
+ );
164
+ }
165
+
166
+ function _deployCore() internal {
167
+ jbPermissions = new JBPermissions(address(0));
168
+ jbProjects = new JBProjects(MULTISIG, address(0), address(0));
169
+ jbDirectory = new JBDirectory(jbPermissions, jbProjects, MULTISIG);
170
+ JBERC20 jbErc20 = new JBERC20(jbPermissions, jbProjects);
171
+ jbTokens = new JBTokens(jbDirectory, jbErc20);
172
+ jbRulesets = new JBRulesets(jbDirectory);
173
+ jbPrices = new JBPrices(jbDirectory, jbPermissions, jbProjects, MULTISIG, address(0));
174
+ jbSplits = new JBSplits(jbDirectory);
175
+ jbFundAccessLimits = new JBFundAccessLimits(jbDirectory);
176
+
177
+ jbController = new JBController(
178
+ jbDirectory,
179
+ jbFundAccessLimits,
180
+ jbPermissions,
181
+ jbPrices,
182
+ jbProjects,
183
+ jbRulesets,
184
+ jbSplits,
185
+ jbTokens,
186
+ address(0),
187
+ address(0)
188
+ );
189
+
190
+ vm.prank(MULTISIG);
191
+ jbDirectory.setIsAllowedToSetFirstController(address(jbController), true);
192
+ }
193
+
194
+ function _deployTerminal() internal {
195
+ jbFeelessAddresses = new JBFeelessAddresses(MULTISIG);
196
+ jbTerminalStore = new JBTerminalStore(jbDirectory, jbPrices, jbRulesets);
197
+
198
+ address permit2 = deployPermit2();
199
+
200
+ jbMultiTerminal = new JBMultiTerminal(
201
+ jbFeelessAddresses,
202
+ jbPermissions,
203
+ jbProjects,
204
+ jbSplits,
205
+ jbTerminalStore,
206
+ jbTokens,
207
+ IPermit2(permit2),
208
+ address(0)
209
+ );
210
+ }
211
+
212
+ function _deployHookInfra() internal {
213
+ JB721TiersHookStore store = new JB721TiersHookStore();
214
+ JBAddressRegistry addressRegistry = new JBAddressRegistry();
215
+ JB721CheckpointsDeployer checkpointsDeployer = new JB721CheckpointsDeployer();
216
+
217
+ JB721TiersHook hookImpl = new JB721TiersHook(
218
+ jbDirectory, jbPermissions, jbPrices, jbRulesets, store, jbSplits, checkpointsDeployer, address(0)
219
+ );
220
+
221
+ hookDeployer = new JB721TiersHookDeployer(hookImpl, store, addressRegistry, address(0));
222
+ }
223
+
224
+ function _launchProject(
225
+ address owner,
226
+ uint32 baseCurrency,
227
+ address dataHook,
228
+ bool useDataHookForPay,
229
+ bool useDataHookForCashOut
230
+ )
231
+ internal
232
+ returns (uint256 projectId)
233
+ {
234
+ JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
235
+ rulesetConfigs[0].weight = 1_000_000 * (10 ** 18);
236
+ rulesetConfigs[0].metadata.baseCurrency = baseCurrency;
237
+ rulesetConfigs[0].metadata.dataHook = dataHook;
238
+ rulesetConfigs[0].metadata.useDataHookForPay = useDataHookForPay;
239
+ rulesetConfigs[0].metadata.useDataHookForCashOut = useDataHookForCashOut;
240
+
241
+ projectId = jbController.launchProjectFor({
242
+ owner: owner,
243
+ projectUri: "ipfs://project",
244
+ rulesetConfigurations: rulesetConfigs,
245
+ terminalConfigurations: _ethTerminalConfig(),
246
+ memo: "nemesis launch"
247
+ });
248
+ }
249
+
250
+ function _launchViaCTDeployer(
251
+ CTDeployer deployer,
252
+ address owner
253
+ )
254
+ internal
255
+ returns (uint256 projectId, IJB721TiersHook hook)
256
+ {
257
+ CTDeployerAllowedPost[] memory allowedPosts = new CTDeployerAllowedPost[](1);
258
+ allowedPosts[0] = CTDeployerAllowedPost({
259
+ category: POST_CATEGORY,
260
+ minimumPrice: 0,
261
+ minimumTotalSupply: 1,
262
+ maximumTotalSupply: 10_000,
263
+ maximumSplitPercent: 0,
264
+ allowedAddresses: new address[](0)
265
+ });
266
+
267
+ CTProjectConfig memory config = CTProjectConfig({
268
+ terminalConfigurations: _ethTerminalConfig(),
269
+ projectUri: "ipfs://croptop",
270
+ allowedPosts: allowedPosts,
271
+ contractUri: "ipfs://contract",
272
+ name: "Croptop",
273
+ symbol: "CROP",
274
+ salt: bytes32(uint256(1))
275
+ });
276
+
277
+ CTSuckerDeploymentConfig memory suckerConfig =
278
+ CTSuckerDeploymentConfig({deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: bytes32(0)});
279
+
280
+ (projectId, hook) = deployer.deployProjectFor(owner, config, suckerConfig, jbController);
281
+ }
282
+
283
+ function _launchDirectProjectWithHook(
284
+ CTPublisher publisher,
285
+ address owner,
286
+ uint32 baseCurrency
287
+ )
288
+ internal
289
+ returns (uint256 projectId, IJB721TiersHook hook)
290
+ {
291
+ projectId = jbProjects.count() + 1;
292
+ hook = hookDeployer.deployHookFor({
293
+ projectId: projectId,
294
+ deployTiersHookConfig: JBDeploy721TiersHookConfig({
295
+ name: "DirectCrop",
296
+ symbol: "DIR",
297
+ baseUri: "ipfs://",
298
+ tokenUriResolver: IJB721TokenUriResolver(address(0)),
299
+ contractUri: "ipfs://contract",
300
+ tiersConfig: JB721InitTiersConfig({
301
+ tiers: new JB721TierConfig[](0), currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
302
+ }),
303
+ flags: JB721TiersHookFlags({
304
+ noNewTiersWithReserves: false,
305
+ noNewTiersWithVotes: false,
306
+ noNewTiersWithOwnerMinting: false,
307
+ preventOverspending: false,
308
+ issueTokensForSplits: false
309
+ })
310
+ }),
311
+ salt: bytes32(uint256(2))
312
+ });
313
+
314
+ uint256 launchedProjectId = _launchProject({
315
+ owner: owner,
316
+ baseCurrency: baseCurrency,
317
+ dataHook: address(hook),
318
+ useDataHookForPay: true,
319
+ useDataHookForCashOut: true
320
+ });
321
+ assertEq(launchedProjectId, projectId, "hook/project ids should stay aligned");
322
+
323
+ uint8[] memory permissionIds = new uint8[](1);
324
+ permissionIds[0] = JBPermissionIds.ADJUST_721_TIERS;
325
+ jbPermissions.setPermissionsFor({
326
+ account: address(this),
327
+ permissionsData: JBPermissionsData({
328
+ operator: address(publisher), projectId: uint64(projectId), permissionIds: permissionIds
329
+ })
330
+ });
331
+
332
+ CTAllowedPost[] memory allowedPosts = new CTAllowedPost[](1);
333
+ allowedPosts[0] = CTAllowedPost({
334
+ hook: address(hook),
335
+ category: POST_CATEGORY,
336
+ minimumPrice: 0,
337
+ minimumTotalSupply: 1,
338
+ maximumTotalSupply: 10_000,
339
+ maximumSplitPercent: 0,
340
+ allowedAddresses: new address[](0)
341
+ });
342
+ publisher.configurePostingCriteriaFor(allowedPosts);
343
+ }
344
+
345
+ function _ethTerminalConfig() internal view returns (JBTerminalConfig[] memory configs) {
346
+ JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
347
+ contexts[0] = JBAccountingContext({
348
+ token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
349
+ });
350
+
351
+ configs = new JBTerminalConfig[](1);
352
+ configs[0] =
353
+ JBTerminalConfig({terminal: IJBTerminal(address(jbMultiTerminal)), accountingContextsToAccept: contexts});
354
+ }
355
+
356
+ function _singlePost() internal pure returns (CTPost[] memory posts) {
357
+ posts = new CTPost[](1);
358
+ posts[0] = CTPost({
359
+ encodedIPFSUri: TEST_URI,
360
+ price: POST_PRICE,
361
+ totalSupply: POST_SUPPLY,
362
+ category: POST_CATEGORY,
363
+ splitPercent: 0,
364
+ splits: new JBSplit[](0)
365
+ });
366
+ }
367
+
368
+ function _totalValue() internal pure returns (uint256) {
369
+ return POST_PRICE + (POST_PRICE / 20);
370
+ }
371
+ }