@bananapus/721-hook-v6 0.0.39 → 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.
package/package.json
CHANGED
|
@@ -1291,6 +1291,12 @@ contract JB721TiersHookStore is IJB721TiersHookStore {
|
|
|
1291
1291
|
// Set the tier being iterated upon (0-indexed).
|
|
1292
1292
|
uint256 tierId = tierIds[i];
|
|
1293
1293
|
|
|
1294
|
+
// Reject tier IDs that don't exist yet — removing a future tier would cause it
|
|
1295
|
+
// to be born already removed when later added.
|
|
1296
|
+
if (tierId == 0 || tierId > maxTierIdOf[msg.sender]) {
|
|
1297
|
+
revert JB721TiersHookStore_UnrecognizedTier(tierId);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1294
1300
|
// Get a reference to the stored tier.
|
|
1295
1301
|
JBStored721Tier storage storedTier = _storedTierOf[msg.sender][tierId];
|
|
1296
1302
|
|
|
@@ -21,21 +21,12 @@ contract Test_20260425CodexNemesisFutureTierRemoval is Test {
|
|
|
21
21
|
uint256[] memory firstIds = store.recordAddTiers(firstTier);
|
|
22
22
|
assertEq(firstIds[0], 1);
|
|
23
23
|
|
|
24
|
+
// Attempting to remove a future (nonexistent) tier ID should now revert
|
|
25
|
+
// thanks to the L-18 fix, preventing the "born removed" bug.
|
|
24
26
|
uint256[] memory futureIds = new uint256[](1);
|
|
25
27
|
futureIds[0] = 2;
|
|
28
|
+
vm.expectRevert(abi.encodeWithSignature("JB721TiersHookStore_UnrecognizedTier(uint256)", 2));
|
|
26
29
|
store.recordRemoveTierIds(futureIds);
|
|
27
|
-
assertTrue(store.isTierRemoved({hook: address(this), tierId: 2}));
|
|
28
|
-
|
|
29
|
-
JB721TierConfig[] memory secondTier = new JB721TierConfig[](1);
|
|
30
|
-
secondTier[0] = _tier(2);
|
|
31
|
-
uint256[] memory secondIds = store.recordAddTiers(secondTier);
|
|
32
|
-
assertEq(secondIds[0], 2);
|
|
33
|
-
assertTrue(store.isTierRemoved({hook: address(this), tierId: 2}));
|
|
34
|
-
|
|
35
|
-
uint16[] memory tierIds = new uint16[](1);
|
|
36
|
-
tierIds[0] = 2;
|
|
37
|
-
vm.expectRevert(abi.encodeWithSignature("JB721TiersHookStore_TierRemoved(uint256)", 2));
|
|
38
|
-
store.recordMint({amount: 1, tierIds: tierIds, isOwnerMint: false});
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
function _tier(uint24 category) internal pure returns (JB721TierConfig memory tier) {
|
|
@@ -13,23 +13,11 @@ contract CodexNemesisFutureTierPoC is UnitTestSetup {
|
|
|
13
13
|
uint256[] memory futureTierIds = new uint256[](1);
|
|
14
14
|
futureTierIds[0] = 1;
|
|
15
15
|
|
|
16
|
+
// L-18 FIX: Removing a future (nonexistent) tier ID now reverts,
|
|
17
|
+
// preventing the "born removed" bug entirely.
|
|
16
18
|
vm.prank(owner);
|
|
19
|
+
vm.expectRevert(abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_UnrecognizedTier.selector, 1));
|
|
17
20
|
hook.adjustTiers(new JB721TierConfig[](0), futureTierIds);
|
|
18
|
-
|
|
19
|
-
(JB721TierConfig[] memory tiersToAdd,) = _createTiers(defaultTierConfig, 1);
|
|
20
|
-
|
|
21
|
-
vm.prank(owner);
|
|
22
|
-
hook.adjustTiers(tiersToAdd, new uint256[](0));
|
|
23
|
-
|
|
24
|
-
assertTrue(hook.STORE().isTierRemoved(address(hook), 1), "future removal flag should persist");
|
|
25
|
-
|
|
26
|
-
uint16[] memory tierIds = new uint16[](1);
|
|
27
|
-
tierIds[0] = 1;
|
|
28
|
-
JB721TiersHookStore store = JB721TiersHookStore(address(hook.STORE()));
|
|
29
|
-
|
|
30
|
-
vm.expectRevert(abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_TierRemoved.selector, 1));
|
|
31
|
-
vm.prank(address(hook));
|
|
32
|
-
store.recordMint(type(uint256).max, tierIds, false);
|
|
33
21
|
}
|
|
34
22
|
|
|
35
23
|
function test_futureTierUriCanBePoisonedBeforeTierExists() external {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
6
|
+
|
|
7
|
+
import {JB721TiersHookStore} from "../../src/JB721TiersHookStore.sol";
|
|
8
|
+
import {JB721TierConfig} from "../../src/structs/JB721TierConfig.sol";
|
|
9
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
10
|
+
|
|
11
|
+
/// @notice L-18: Prevent future tier pre-removal.
|
|
12
|
+
/// Removing a tier ID that does not yet exist should revert with `UnrecognizedTier`.
|
|
13
|
+
contract Pass12L18 is Test {
|
|
14
|
+
JB721TiersHookStore internal store;
|
|
15
|
+
|
|
16
|
+
function setUp() external {
|
|
17
|
+
store = new JB721TiersHookStore();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// @notice Removing a future tier ID (beyond maxTierIdOf) must revert.
|
|
21
|
+
function test_L18_fix_reverts_future_removal() external {
|
|
22
|
+
// Add 5 tiers so maxTierIdOf == 5.
|
|
23
|
+
JB721TierConfig[] memory tiers = new JB721TierConfig[](5);
|
|
24
|
+
for (uint256 i; i < 5; i++) {
|
|
25
|
+
tiers[i] = _tier(uint24(i + 1));
|
|
26
|
+
}
|
|
27
|
+
store.recordAddTiers(tiers);
|
|
28
|
+
assertEq(store.maxTierIdOf(address(this)), 5);
|
|
29
|
+
|
|
30
|
+
// Attempt to remove tier 10 (future) — should revert.
|
|
31
|
+
uint256[] memory futureTierIds = new uint256[](1);
|
|
32
|
+
futureTierIds[0] = 10;
|
|
33
|
+
vm.expectRevert(abi.encodeWithSignature("JB721TiersHookStore_UnrecognizedTier(uint256)", 10));
|
|
34
|
+
store.recordRemoveTierIds(futureTierIds);
|
|
35
|
+
|
|
36
|
+
// Attempt to remove tier 0 (invalid) — should also revert.
|
|
37
|
+
uint256[] memory zeroTierIds = new uint256[](1);
|
|
38
|
+
zeroTierIds[0] = 0;
|
|
39
|
+
vm.expectRevert(abi.encodeWithSignature("JB721TiersHookStore_UnrecognizedTier(uint256)", 0));
|
|
40
|
+
store.recordRemoveTierIds(zeroTierIds);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// @notice Removing an existing tier still works as before.
|
|
44
|
+
function test_L18_existing_tier_removal_works() external {
|
|
45
|
+
// Add 5 tiers.
|
|
46
|
+
JB721TierConfig[] memory tiers = new JB721TierConfig[](5);
|
|
47
|
+
for (uint256 i; i < 5; i++) {
|
|
48
|
+
tiers[i] = _tier(uint24(i + 1));
|
|
49
|
+
}
|
|
50
|
+
store.recordAddTiers(tiers);
|
|
51
|
+
|
|
52
|
+
// Remove tier 3 — should succeed.
|
|
53
|
+
uint256[] memory removeTierIds = new uint256[](1);
|
|
54
|
+
removeTierIds[0] = 3;
|
|
55
|
+
store.recordRemoveTierIds(removeTierIds);
|
|
56
|
+
assertTrue(store.isTierRemoved({hook: address(this), tierId: 3}));
|
|
57
|
+
|
|
58
|
+
// Other tiers should not be affected.
|
|
59
|
+
assertFalse(store.isTierRemoved({hook: address(this), tierId: 1}));
|
|
60
|
+
assertFalse(store.isTierRemoved({hook: address(this), tierId: 2}));
|
|
61
|
+
assertFalse(store.isTierRemoved({hook: address(this), tierId: 4}));
|
|
62
|
+
assertFalse(store.isTierRemoved({hook: address(this), tierId: 5}));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _tier(uint24 category) internal pure returns (JB721TierConfig memory tier) {
|
|
66
|
+
tier.price = 1;
|
|
67
|
+
tier.initialSupply = 10;
|
|
68
|
+
tier.category = category;
|
|
69
|
+
tier.flags = JB721TierConfigFlags({
|
|
70
|
+
allowOwnerMint: false,
|
|
71
|
+
useReserveBeneficiaryAsDefault: false,
|
|
72
|
+
transfersPausable: false,
|
|
73
|
+
useVotingUnits: false,
|
|
74
|
+
cantBeRemoved: false,
|
|
75
|
+
cantIncreaseDiscountPercent: false,
|
|
76
|
+
cantBuyWithCredits: false
|
|
77
|
+
});
|
|
78
|
+
tier.splits = new JBSplit[](0);
|
|
79
|
+
}
|
|
80
|
+
}
|