@croptop/core-v6 0.0.38 → 0.0.40

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 (43) hide show
  1. package/README.md +2 -2
  2. package/foundry.toml +2 -1
  3. package/package.json +25 -13
  4. package/script/ConfigureFeeProject.s.sol +8 -5
  5. package/src/CTDeployer.sol +67 -58
  6. package/src/CTProjectOwner.sol +6 -4
  7. package/src/CTPublisher.sol +14 -4
  8. package/src/interfaces/ICTDeployer.sol +2 -2
  9. package/src/structs/CTProjectConfig.sol +7 -6
  10. package/ADMINISTRATION.md +0 -94
  11. package/ARCHITECTURE.md +0 -96
  12. package/AUDIT_INSTRUCTIONS.md +0 -88
  13. package/RISKS.md +0 -78
  14. package/SKILLS.md +0 -46
  15. package/STYLE_GUIDE.md +0 -610
  16. package/USER_JOURNEYS.md +0 -134
  17. package/foundry.lock +0 -11
  18. package/slither-ci.config.json +0 -10
  19. package/sphinx.lock +0 -507
  20. package/test/CTDeployer.t.sol +0 -616
  21. package/test/CTProjectOwner.t.sol +0 -185
  22. package/test/CTPublisher.t.sol +0 -869
  23. package/test/ClaimCollectionOwnership.t.sol +0 -315
  24. package/test/CroptopAttacks.t.sol +0 -437
  25. package/test/Fork.t.sol +0 -227
  26. package/test/TestAuditGaps.sol +0 -696
  27. package/test/Test_MetadataGeneration.t.sol +0 -79
  28. package/test/audit/CodexNemesisCroptopPublisherBoundary.t.sol +0 -329
  29. package/test/audit/CodexNemesisCurrencyPoCs.t.sol +0 -371
  30. package/test/audit/CodexNemesisFreshRound.t.sol +0 -395
  31. package/test/audit/CodexNemesisMetadataShadow.t.sol +0 -196
  32. package/test/audit/CodexNemesisPoCs.t.sol +0 -263
  33. package/test/audit/CodexNemesisPolicyReuse.t.sol +0 -168
  34. package/test/audit/CodexNemesisUriDrift.t.sol +0 -252
  35. package/test/audit/DeployerPermissionBypass.t.sol +0 -213
  36. package/test/audit/EmptyPostFeeBypass.t.sol +0 -53
  37. package/test/audit/FeeBeneficiaryReentrancy.t.sol +0 -247
  38. package/test/audit/FeeFallbackBlackhole.t.sol +0 -263
  39. package/test/audit/Pass12Fixes.t.sol +0 -388
  40. package/test/fork/PublishFork.t.sol +0 -440
  41. package/test/regression/DuplicateUriFeeEvasion.t.sol +0 -312
  42. package/test/regression/FeeEvasion.t.sol +0 -286
  43. package/test/regression/StaleTierIdMapping.t.sol +0 -228
@@ -1,228 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.28;
3
-
4
- // forge-lint: disable-next-line(unaliased-plain-import)
5
- import "forge-std/Test.sol";
6
-
7
- import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
8
- import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
9
- import {IJBOwnable} from "@bananapus/ownable-v6/src/interfaces/IJBOwnable.sol";
10
- import {IJB721Hook} from "@bananapus/721-hook-v6/src/interfaces/IJB721Hook.sol";
11
- import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
12
- import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
13
- import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
14
- import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
15
- import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
16
-
17
- import {CTPublisher} from "../../src/CTPublisher.sol";
18
- import {CTAllowedPost} from "../../src/structs/CTAllowedPost.sol";
19
- import {CTPost} from "../../src/structs/CTPost.sol";
20
-
21
- /// @title L52_StaleTierIdMapping
22
- /// @notice Stale tierIdForEncodedIPFSUriOf mapping after external tier removal.
23
- /// When a tier is removed externally via adjustTiers(), the publisher's mapping still pointed
24
- /// to the removed tier ID, blocking re-creation. The fix clears the stale mapping and allows
25
- /// the post to fall through to new-tier creation.
26
- contract L52_StaleTierIdMapping is Test {
27
- CTPublisher publisher;
28
-
29
- IJBPermissions permissions = IJBPermissions(makeAddr("permissions"));
30
- IJBDirectory directory = IJBDirectory(makeAddr("directory"));
31
-
32
- address hookOwner = makeAddr("hookOwner");
33
- address hookAddr = makeAddr("hook");
34
- address hookStoreAddr = makeAddr("hookStore");
35
- address terminalAddr = makeAddr("terminal");
36
- address poster = makeAddr("poster");
37
-
38
- uint256 feeProjectId = 1;
39
- uint256 hookProjectId = 42;
40
-
41
- bytes32 constant TEST_URI = keccak256("removable-content");
42
-
43
- function setUp() public {
44
- publisher = new CTPublisher(directory, permissions, feeProjectId, address(0));
45
-
46
- // Mock hook.owner().
47
- vm.mockCall(hookAddr, abi.encodeWithSelector(IJBOwnable.owner.selector), abi.encode(hookOwner));
48
- // Mock hook.PROJECT_ID().
49
- vm.mockCall(hookAddr, abi.encodeWithSelector(IJB721Hook.PROJECT_ID.selector), abi.encode(hookProjectId));
50
- // Mock hook.STORE().
51
- vm.mockCall(hookAddr, abi.encodeWithSelector(IJB721TiersHook.STORE.selector), abi.encode(hookStoreAddr));
52
-
53
- // Mock permissions to return true by default.
54
- vm.mockCall(
55
- address(permissions), abi.encodeWithSelector(IJBPermissions.hasPermission.selector), abi.encode(true)
56
- );
57
-
58
- // Fund poster.
59
- vm.deal(poster, 100 ether);
60
- }
61
-
62
- function _configureCategory() internal {
63
- CTAllowedPost[] memory posts = new CTAllowedPost[](1);
64
- posts[0] = CTAllowedPost({
65
- hook: hookAddr,
66
- category: 5,
67
- minimumPrice: 0,
68
- minimumTotalSupply: 1,
69
- maximumTotalSupply: 100,
70
- maximumSplitPercent: 0,
71
- allowedAddresses: new address[](0)
72
- });
73
-
74
- vm.prank(hookOwner);
75
- publisher.configurePostingCriteriaFor(posts);
76
- }
77
-
78
- function _setupMintMocks(uint256 maxTierId) internal {
79
- vm.mockCall(
80
- hookStoreAddr, abi.encodeWithSelector(IJB721TiersHookStore.maxTierIdOf.selector), abi.encode(maxTierId)
81
- );
82
- vm.mockCall(hookAddr, abi.encodeWithSelector(IJB721TiersHook.adjustTiers.selector), abi.encode());
83
- vm.mockCall(hookAddr, abi.encodeWithSelector(bytes4(keccak256("METADATA_ID_TARGET()"))), abi.encode(address(0)));
84
- vm.mockCall(
85
- address(directory),
86
- abi.encodeWithSelector(IJBDirectory.primaryTerminalOf.selector),
87
- abi.encode(terminalAddr)
88
- );
89
- vm.mockCall(terminalAddr, "", abi.encode(uint256(0)));
90
- }
91
-
92
- /// @notice After a tier is removed externally, the stale mapping should be cleared
93
- /// so that the same encodedIPFSUri can be re-posted as a new tier.
94
- function test_staleMappingClearedWhenTierRemoved() public {
95
- _configureCategory();
96
-
97
- // First mint: create tier 1 for TEST_URI.
98
- _setupMintMocks(0);
99
-
100
- // Mock isTierRemoved to return false (tier exists).
101
- vm.mockCall(
102
- hookStoreAddr,
103
- abi.encodeWithSelector(IJB721TiersHookStore.isTierRemoved.selector, hookAddr, 1),
104
- abi.encode(false)
105
- );
106
-
107
- CTPost[] memory posts = new CTPost[](1);
108
- posts[0] = CTPost({
109
- encodedIPFSUri: TEST_URI,
110
- totalSupply: 10,
111
- price: 0.1 ether,
112
- category: 5,
113
- splitPercent: 0,
114
- splits: new JBSplit[](0)
115
- });
116
-
117
- vm.prank(poster);
118
- publisher.mintFrom{value: 0.2 ether}(IJB721TiersHook(hookAddr), posts, poster, poster, "", "");
119
-
120
- // Verify tier ID 1 was stored in the mapping.
121
- assertEq(
122
- publisher.tierIdForEncodedIPFSUriOf(hookAddr, TEST_URI), 1, "tier ID should be stored after first mint"
123
- );
124
-
125
- // Now simulate external tier removal: isTierRemoved returns true for tier 1.
126
- vm.mockCall(
127
- hookStoreAddr,
128
- abi.encodeWithSelector(IJB721TiersHookStore.isTierRemoved.selector, hookAddr, 1),
129
- abi.encode(true)
130
- );
131
-
132
- // Mock tierOf for the removed tier — the M-42 fix calls tierOf before checking isTierRemoved.
133
- JB721Tier memory removedTier;
134
- removedTier.id = 1;
135
- removedTier.encodedIPFSUri = TEST_URI;
136
- vm.mockCall(
137
- hookStoreAddr,
138
- abi.encodeWithSelector(IJB721TiersHookStore.tierOf.selector, hookAddr, 1, false),
139
- abi.encode(removedTier)
140
- );
141
-
142
- // Update maxTierId to 1 so new tier gets ID 2.
143
- _setupMintMocks(1);
144
-
145
- // Second mint with the same URI should succeed (creating a new tier),
146
- // because the fix detects the stale mapping and clears it.
147
- vm.prank(poster);
148
- publisher.mintFrom{value: 0.2 ether}(IJB721TiersHook(hookAddr), posts, poster, poster, "", "");
149
-
150
- // Verify the mapping now points to the new tier ID (2).
151
- assertEq(
152
- publisher.tierIdForEncodedIPFSUriOf(hookAddr, TEST_URI),
153
- 2,
154
- "tier ID should be updated to new tier after re-post"
155
- );
156
- }
157
-
158
- /// @notice When a tier is NOT removed, the mapping should be used as-is (no re-creation).
159
- function test_existingTierNotRemovedUsesMapping() public {
160
- _configureCategory();
161
-
162
- // First mint: create tier 1 for TEST_URI.
163
- _setupMintMocks(0);
164
-
165
- // Mock isTierRemoved to return false (tier exists).
166
- vm.mockCall(
167
- hookStoreAddr,
168
- abi.encodeWithSelector(IJB721TiersHookStore.isTierRemoved.selector, hookAddr, 1),
169
- abi.encode(false)
170
- );
171
-
172
- // Mock tierOf for tier 1 so the existing-tier price lookup succeeds.
173
- JB721Tier memory tier = JB721Tier({
174
- id: 1,
175
- price: 0.1 ether,
176
- remainingSupply: 9,
177
- initialSupply: 10,
178
- votingUnits: 0,
179
- reserveFrequency: 0,
180
- reserveBeneficiary: address(0),
181
- encodedIPFSUri: TEST_URI,
182
- category: 5,
183
- discountPercent: 0,
184
- flags: JB721TierFlags({
185
- allowOwnerMint: false,
186
- transfersPausable: false,
187
- cantBeRemoved: false,
188
- cantIncreaseDiscountPercent: false,
189
- cantBuyWithCredits: false
190
- }),
191
- splitPercent: 0,
192
- resolvedUri: ""
193
- });
194
- vm.mockCall(
195
- hookStoreAddr,
196
- abi.encodeWithSelector(IJB721TiersHookStore.tierOf.selector, hookAddr, 1, false),
197
- abi.encode(tier)
198
- );
199
-
200
- CTPost[] memory posts = new CTPost[](1);
201
- posts[0] = CTPost({
202
- encodedIPFSUri: TEST_URI,
203
- totalSupply: 10,
204
- price: 0.1 ether,
205
- category: 5,
206
- splitPercent: 0,
207
- splits: new JBSplit[](0)
208
- });
209
-
210
- vm.prank(poster);
211
- publisher.mintFrom{value: 0.2 ether}(IJB721TiersHook(hookAddr), posts, poster, poster, "", "");
212
-
213
- assertEq(publisher.tierIdForEncodedIPFSUriOf(hookAddr, TEST_URI), 1);
214
-
215
- // Second mint with existing tier (not removed) — should reuse tier ID 1.
216
- _setupMintMocks(1);
217
-
218
- vm.prank(poster);
219
- publisher.mintFrom{value: 0.2 ether}(IJB721TiersHook(hookAddr), posts, poster, poster, "", "");
220
-
221
- // Mapping should still point to tier 1.
222
- assertEq(
223
- publisher.tierIdForEncodedIPFSUriOf(hookAddr, TEST_URI),
224
- 1,
225
- "tier ID should remain unchanged when tier is not removed"
226
- );
227
- }
228
- }