@bannynet/core-v6 0.0.17 → 0.0.19

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,102 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ import {Test} from "forge-std/Test.sol";
5
+
6
+ import {IJB721TiersHookStore} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookStore.sol";
7
+
8
+ import {MigrationHelper} from "../../script/helpers/MigrationHelper.sol";
9
+
10
+ contract MigrationHelperVerificationBypassTest is Test {
11
+ address internal constant ALICE = address(0xA11CE);
12
+ address internal constant FALLBACK_RESOLVER = address(0xFA11BAC);
13
+
14
+ MockStore internal v4Store;
15
+ MockStore internal v5Store;
16
+ MockHook internal v4Hook;
17
+ MockHook internal v5Hook;
18
+ MigrationHelperHarness internal harness;
19
+
20
+ function setUp() public {
21
+ v4Store = new MockStore();
22
+ v5Store = new MockStore();
23
+ v4Hook = new MockHook(address(v4Store));
24
+ v5Hook = new MockHook(address(v5Store));
25
+ harness = new MigrationHelperHarness();
26
+ }
27
+
28
+ function test_verifyTierBalances_skipsAllOwnersForTierWhenFallbackResolverOwnsAnyOfTier() public {
29
+ address[] memory owners = new address[](1);
30
+ owners[0] = ALICE;
31
+
32
+ uint256[] memory tierIds = new uint256[](1);
33
+ tierIds[0] = 7;
34
+
35
+ // Alice is over-allocated in V5 versus V4 for tier 7.
36
+ v4Store.setTierBalance(address(v4Hook), ALICE, 7, 1);
37
+ v5Store.setTierBalance(address(v5Hook), ALICE, 7, 2);
38
+
39
+ // One unrelated V4 token of the same tier sits in the fallback resolver.
40
+ v4Store.setTierBalance(address(v4Hook), FALLBACK_RESOLVER, 7, 1);
41
+
42
+ // Intended behavior would reject Alice's inflation, but the helper skips the tier entirely.
43
+ harness.verifyTierBalances(address(v5Hook), address(v4Hook), FALLBACK_RESOLVER, owners, tierIds);
44
+ }
45
+
46
+ function test_verifyTierBalances_revertsWhenFallbackResolverDoesNotOwnTier() public {
47
+ address[] memory owners = new address[](1);
48
+ owners[0] = ALICE;
49
+
50
+ uint256[] memory tierIds = new uint256[](1);
51
+ tierIds[0] = 7;
52
+
53
+ v4Store.setTierBalance(address(v4Hook), ALICE, 7, 1);
54
+ v5Store.setTierBalance(address(v5Hook), ALICE, 7, 2);
55
+
56
+ vm.expectRevert(
57
+ bytes(
58
+ "V5 tier balance exceeds V4: owner=0x00000000000000000000000000000000000a11ce tier=7 v4Balance=1 v5Balance=2"
59
+ )
60
+ );
61
+ harness.verifyTierBalances(address(v5Hook), address(v4Hook), FALLBACK_RESOLVER, owners, tierIds);
62
+ }
63
+ }
64
+
65
+ contract MigrationHelperHarness {
66
+ function verifyTierBalances(
67
+ address hookAddress,
68
+ address v4HookAddress,
69
+ address v4FallbackResolverAddress,
70
+ address[] memory owners,
71
+ uint256[] memory tierIds
72
+ )
73
+ external
74
+ view
75
+ {
76
+ MigrationHelper.verifyTierBalances(hookAddress, v4HookAddress, v4FallbackResolverAddress, owners, tierIds);
77
+ }
78
+ }
79
+
80
+ contract MockHook {
81
+ address internal immutable _store;
82
+
83
+ constructor(address store) {
84
+ _store = store;
85
+ }
86
+
87
+ function STORE() external view returns (IJB721TiersHookStore) {
88
+ return IJB721TiersHookStore(_store);
89
+ }
90
+ }
91
+
92
+ contract MockStore {
93
+ mapping(address hook => mapping(address owner => mapping(uint256 tierId => uint256))) internal _tierBalanceOf;
94
+
95
+ function setTierBalance(address hook, address owner, uint256 tierId, uint256 balance) external {
96
+ _tierBalanceOf[hook][owner][tierId] = balance;
97
+ }
98
+
99
+ function tierBalanceOf(address hook, address owner, uint256 tierId) external view returns (uint256) {
100
+ return _tierBalanceOf[hook][owner][tierId];
101
+ }
102
+ }
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
6
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
6
7
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
7
8
 
8
9
  import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
@@ -180,11 +181,13 @@ contract TryTransferFromStrandsAssetsTest is Test {
180
181
  encodedIPFSUri: bytes32(0),
181
182
  category: category,
182
183
  discountPercent: 0,
183
- allowOwnerMint: false,
184
- transfersPausable: false,
185
- cantBeRemoved: false,
186
- cantIncreaseDiscountPercent: false,
187
- cantBuyWithCredits: false,
184
+ flags: JB721TierFlags({
185
+ allowOwnerMint: false,
186
+ transfersPausable: false,
187
+ cantBeRemoved: false,
188
+ cantIncreaseDiscountPercent: false,
189
+ cantBuyWithCredits: false
190
+ }),
188
191
  splitPercent: 0,
189
192
  resolvedUri: ""
190
193
  });
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
6
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
6
7
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
7
8
 
8
9
  import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
@@ -131,11 +132,13 @@ contract BodyCategoryValidationTest is Test {
131
132
  encodedIPFSUri: bytes32(0),
132
133
  category: category,
133
134
  discountPercent: 0,
134
- allowOwnerMint: false,
135
- transfersPausable: false,
136
- cantBeRemoved: false,
137
- cantIncreaseDiscountPercent: false,
138
- cantBuyWithCredits: false,
135
+ flags: JB721TierFlags({
136
+ allowOwnerMint: false,
137
+ transfersPausable: false,
138
+ cantBeRemoved: false,
139
+ cantIncreaseDiscountPercent: false,
140
+ cantBuyWithCredits: false
141
+ }),
139
142
  splitPercent: 0,
140
143
  resolvedUri: ""
141
144
  });
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
6
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
6
7
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
7
8
 
8
9
  import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
@@ -170,11 +171,13 @@ contract BurnedTokenCheckTest is Test {
170
171
  encodedIPFSUri: bytes32(0),
171
172
  category: category,
172
173
  discountPercent: 0,
173
- allowOwnerMint: false,
174
- transfersPausable: false,
175
- cantBeRemoved: false,
176
- cantIncreaseDiscountPercent: false,
177
- cantBuyWithCredits: false,
174
+ flags: JB721TierFlags({
175
+ allowOwnerMint: false,
176
+ transfersPausable: false,
177
+ cantBeRemoved: false,
178
+ cantIncreaseDiscountPercent: false,
179
+ cantBuyWithCredits: false
180
+ }),
178
181
  splitPercent: 0,
179
182
  resolvedUri: ""
180
183
  });
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
6
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
6
7
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
7
8
 
8
9
  import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
@@ -193,11 +194,13 @@ contract CEIReorderTest is Test {
193
194
  encodedIPFSUri: bytes32(0),
194
195
  category: category,
195
196
  discountPercent: 0,
196
- allowOwnerMint: false,
197
- transfersPausable: false,
198
- cantBeRemoved: false,
199
- cantIncreaseDiscountPercent: false,
200
- cantBuyWithCredits: false,
197
+ flags: JB721TierFlags({
198
+ allowOwnerMint: false,
199
+ transfersPausable: false,
200
+ cantBeRemoved: false,
201
+ cantIncreaseDiscountPercent: false,
202
+ cantBuyWithCredits: false
203
+ }),
201
204
  splitPercent: 0,
202
205
  resolvedUri: ""
203
206
  });
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
3
3
 
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
6
+ import {JB721TierFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierFlags.sol";
6
7
  import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
7
8
 
8
9
  import {Banny721TokenUriResolver} from "../../src/Banny721TokenUriResolver.sol";
@@ -330,11 +331,13 @@ contract RemovedTierDesyncTest is Test {
330
331
  encodedIPFSUri: bytes32(0),
331
332
  category: category,
332
333
  discountPercent: 0,
333
- allowOwnerMint: false,
334
- transfersPausable: false,
335
- cantBeRemoved: false,
336
- cantIncreaseDiscountPercent: false,
337
- cantBuyWithCredits: false,
334
+ flags: JB721TierFlags({
335
+ allowOwnerMint: false,
336
+ transfersPausable: false,
337
+ cantBeRemoved: false,
338
+ cantIncreaseDiscountPercent: false,
339
+ cantBuyWithCredits: false
340
+ }),
338
341
  splitPercent: 0,
339
342
  resolvedUri: ""
340
343
  });
package/CHANGE_LOG.md DELETED
@@ -1,243 +0,0 @@
1
- # banny-retail-v6 Changelog (v5 → v6)
2
-
3
- This document describes all changes between `banny-retail` (v5) and `banny-retail-v6` (v6).
4
-
5
- ## Summary
6
-
7
- - **Key bug fixes**: Default eyes incorrectly selected based on outfit UPC instead of body UPC; decoration operations blocked when a previously-equipped item was burned/removed.
8
- - **Fault-tolerant transfers**: New `_tryTransferFrom()` wraps returns of previously-equipped items in try-catch — a burned or removed outfit no longer blocks the entire `decorateBannyWith()` operation.
9
- - **Richer metadata**: `setSvgBaseUri()` replaced by `setMetadata()` which sets description, external URL, and base URI together. Token JSON `description` and `external_url` are now dynamic.
10
- - **Body category validation**: `decorateBannyWith()` now verifies the `bannyBodyId` actually belongs to a body-category tier before proceeding.
11
- - **Batch setter safety**: Array length mismatch checks added to `setProductNames()`, `setSvgContentsOf()`, and `setSvgHashesOf()`.
12
-
13
- ---
14
-
15
- ## 1. Breaking Changes
16
-
17
- ### Solidity Version Bump
18
- - **v5:** `pragma solidity 0.8.23;`
19
- - **v6:** `pragma solidity 0.8.28;`
20
-
21
- ### Dependency Imports Updated
22
- All `@bananapus/721-hook-v5` imports replaced with `@bananapus/721-hook-v6`:
23
- - `IERC721`, `IJB721TiersHook`, `IJB721TiersHookStore`, `IJB721TokenUriResolver`, `JB721Tier`, `JBIpfsDecoder`
24
-
25
- ### `setSvgBaseUri()` Removed and Replaced by `setMetadata()`
26
- - **v5:** `setSvgBaseUri(string calldata baseUri)` -- sets only the SVG base URI. Emits `SetSvgBaseUri`.
27
- - **v6:** `setMetadata(string calldata description, string calldata url, string calldata baseUri)` -- sets description, external URL, and base URI in a single call. Emits `SetMetadata`.
28
- - Callers that previously used `setSvgBaseUri()` must migrate to `setMetadata()`.
29
-
30
- ### `setSvgHashsOf()` Renamed to `setSvgHashesOf()`
31
- - **v5:** `setSvgHashsOf(uint256[] memory upcs, bytes32[] memory svgHashs)`
32
- - **v6:** `setSvgHashesOf(uint256[] memory upcs, bytes32[] memory svgHashes)`
33
- - Function name and parameter name corrected for proper English pluralization.
34
-
35
- ### `pricingContext()` Return Value Change
36
- - **v5:** `(uint256 currency, uint256 decimals,) = IJB721TiersHook(hook).pricingContext();` -- three return values (third ignored).
37
- - **v6:** `(uint256 currency, uint256 decimals) = IJB721TiersHook(hook).pricingContext();` -- two return values.
38
- - Reflects an upstream change in `IJB721TiersHook` where `pricingContext()` now returns only two values.
39
-
40
- ### Token Metadata `description` and `external_url` Are Now Dynamic
41
- - **v5:** Hardcoded in `tokenUriOf()`: `"description":"A piece of Banny Retail."` and `"external_url":"https://retail.banny.eth.sucks"`.
42
- - **v6:** Read from state variables `svgDescription` and `svgExternalUrl`, set via `setMetadata()`. These default to empty strings until the owner sets them.
43
-
44
- ---
45
-
46
- ## 2. New Features
47
-
48
- ### New State Variables: `svgDescription` and `svgExternalUrl`
49
- - `string public svgDescription` -- the description used in token metadata JSON.
50
- - `string public svgExternalUrl` -- the external URL used in token metadata JSON.
51
- - Both are settable via the new `setMetadata()` function.
52
-
53
- ### Body Category Validation in `decorateBannyWith()`
54
- - **v6 adds:** A check that the `bannyBodyId` actually belongs to a body-category tier (`_BODY_CATEGORY == 0`). If not, reverts with `Banny721TokenUriResolver_BannyBodyNotBodyCategory()`.
55
- - **v5:** No such check existed; any token ID could be passed as a banny body.
56
-
57
- ### `_tryTransferFrom()` -- Fault-Tolerant, Return-Aware Transfers
58
- - **v6 adds:** `_tryTransferFrom(address hook, address from, address to, uint256 assetId) returns (bool success)` -- wraps `safeTransferFrom` in a try-catch and returns whether the transfer succeeded.
59
- - Used in `_decorateBannyWithBackground()` and `_decorateBannyWithOutfits()` when returning previously equipped items.
60
- - When the return transfer fails, **state is preserved** instead of cleared — preventing NFT stranding:
61
- - **Backgrounds**: Failed return aborts the entire background change (old background stays attached, new one is not equipped).
62
- - **Outfits**: Failed-to-return outfits are retained in the attached list via `_storeOutfitsWithRetained()`.
63
- - **v5:** Used `_transferFrom()` (which reverts on failure) for all transfers, meaning a single burned/removed outfit could block the entire decoration operation.
64
-
65
- > **Why this mattered**: In v5, if a project owner removed a tier that contained an equipped outfit, the Banny body owner could never change decorations again — the `safeTransferFrom` for the removed item would revert, permanently blocking the `decorateBannyWith()` function. This was the most-reported user issue.
66
-
67
- ### `_storeOutfitsWithRetained()` -- Anti-Stranding Merge
68
- - **v6 adds:** `_storeOutfitsWithRetained(address hook, uint256 bannyBodyId, uint256[] memory outfitIds, uint256[] memory previousOutfitIds)` -- stores the new outfit array, appending any previously equipped outfits whose return transfer failed (non-zero entries in `previousOutfitIds`).
69
- - This ensures that NFTs held by the resolver but not successfully returned to the owner remain tracked and recoverable in subsequent `decorateBannyWith` calls.
70
-
71
- ### `_isInArray()` Helper
72
- - **v6 adds:** `_isInArray(uint256 value, uint256[] memory array)` -- checks if a value is present in an array.
73
- - Used during outfit cleanup to skip outfits being re-equipped rather than transferring them out and back in.
74
-
75
- ### Array Length Validation on Batch Setters
76
- - **v6 adds:** `Banny721TokenUriResolver_ArrayLengthMismatch()` error.
77
- - `setProductNames()`, `setSvgContentsOf()`, and `setSvgHashesOf()` now validate that the `upcs` and values arrays have matching lengths. v5 had no such check, risking out-of-bounds reverts.
78
-
79
- ### `assetIdsOf()` Array Resize via Assembly
80
- - **v6 adds:** After filtering outfits, the returned `outfitIds` array is resized via inline assembly (`mstore(outfitIds, numberOfIncludedOutfits)`) to remove trailing zeros.
81
- - **v5:** Returned the full-length array with trailing zero entries for unincluded outfits.
82
-
83
- ### `_encodeTokenUri()` Extracted Helper
84
- - **v6 adds:** `_encodeTokenUri(uint256 tokenId, JB721Tier memory product, string memory extraMetadata, string memory imageContents)` -- an internal view function that encodes the token URI JSON with base64.
85
- - Uses nested `abi.encodePacked()` calls to avoid "stack too deep" errors.
86
- - **v5:** Inlined the entire JSON encoding in `tokenUriOf()` as a single large `abi.encodePacked()` call.
87
-
88
- ### Default Eyes Bug Fix (`_outfitContentsFor`)
89
- - **v5 (bug):** `_outfitContentsFor()` used the current outfit's `upc` to decide alien vs. standard default eyes: `if (upc == ALIEN_UPC)`. This checked the UPC of the *outfit being iterated*, not the banny body.
90
- - **v6 (fix):** `_outfitContentsFor()` now accepts an additional `bodyUpc` parameter and uses `if (bodyUpc == ALIEN_UPC)` to correctly select default eyes based on the banny body type.
91
-
92
- > **Why this mattered**: The bug caused alien Bannys to get standard eyes and vice versa when any outfit was equipped, breaking the visual identity of the NFT. The fix ensures default eyes are always selected based on the body type, not whatever outfit happens to be iterated.
93
-
94
- ### Improved Background Authorization Logic
95
- - **v6:** `_decorateBannyWithBackground()` now explicitly checks if an unused background (where `userId == 0`) can only be attached by its owner. In v5, the authorization check `_msgSender() != owner && _msgSender() != IERC721(hook).ownerOf(userOf(hook, backgroundId))` could behave unexpectedly when `userOf()` returned 0 (querying `ownerOf(0)` on the hook).
96
-
97
- ### CEI Pattern in `_decorateBannyWithBackground()`
98
- - **v6:** Updates all state (`_attachedBackgroundIdOf`, `_userOf`) before any external transfers. Previous background transfer-out happens after state updates.
99
- - **v5:** Transferred the previous background out *before* updating state for the new background, creating a less safe interaction ordering.
100
-
101
- ### Background Category Validation
102
- - **v5:** Only checked `backgroundProduct.id == 0` to reject invalid backgrounds.
103
- - **v6:** Also checks `backgroundProduct.category != _BACKGROUND_CATEGORY`, ensuring only actual background-category items can be used as backgrounds.
104
-
105
- ### Outfit Re-equip Optimization
106
- - **v6:** When cleaning up remaining previous outfits, checks `_isInArray(previousOutfitId, outfitIds)` to skip outfits being re-equipped, avoiding unnecessary transfer-out-and-back-in cycles.
107
- - **v5:** Would transfer the outfit out and then transfer it back in during the same transaction.
108
-
109
- ### Improved Loop Guard in `_decorateBannyWithOutfits()`
110
- - **v5:** `while (previousOutfitProductCategory <= outfitProductCategory && previousOutfitProductCategory != 0)` -- stops on category 0 but could re-enter after exhaustion.
111
- - **v6:** `while (previousOutfitId != 0 && previousOutfitProductCategory <= outfitProductCategory)` -- guards on `previousOutfitId != 0` as primary condition, correctly handling removed tiers (category 0) by always processing and advancing past them.
112
-
113
- ### Re-check Ownership Before Transfer in `_decorateBannyWithOutfits()`
114
- - **v5:** Cached `owner = IERC721(hook).ownerOf(outfitId)` at the top of the loop, then later checked `if (owner != address(this))`.
115
- - **v6:** Re-checks `IERC721(hook).ownerOf(outfitId) != address(this)` at transfer time, avoiding stale ownership data after intermediate transfers.
116
-
117
- ---
118
-
119
- ## 3. Event Changes
120
-
121
- ### Added
122
- | Event | Signature |
123
- |-------|-----------|
124
- | `SetMetadata` | `SetMetadata(string description, string externalUrl, string baseUri, address caller)` |
125
-
126
- ### Removed
127
- | Event | Signature |
128
- |-------|-----------|
129
- | `SetSvgBaseUri` | `SetSvgBaseUri(string baseUri, address caller)` |
130
-
131
- ### Unchanged
132
- | Event | Notes |
133
- |-------|-------|
134
- | `DecorateBanny` | Same signature in both versions |
135
- | `SetProductName` | Same signature in both versions |
136
- | `SetSvgContent` | Same signature in both versions |
137
- | `SetSvgHash` | Same signature in both versions |
138
-
139
- ### `msg.sender` Replaced with `_msgSender()` in Event Emissions
140
- - **v5:** `setProductNames()`, `setSvgBaseUri()`, `setSvgContentsOf()`, and `setSvgHashsOf()` used `msg.sender` in event emissions.
141
- - **v6:** All event emissions consistently use `_msgSender()` (ERC-2771 compatible).
142
-
143
- ---
144
-
145
- ## 4. Error Changes
146
-
147
- ### Added
148
- | Error | Purpose |
149
- |-------|---------|
150
- | `Banny721TokenUriResolver_ArrayLengthMismatch()` | Reverts when batch setter arrays have mismatched lengths |
151
- | `Banny721TokenUriResolver_BannyBodyNotBodyCategory()` | Reverts when `decorateBannyWith()` is called with a non-body-category token |
152
-
153
- ### Unchanged
154
- | Error |
155
- |-------|
156
- | `Banny721TokenUriResolver_CantAccelerateTheLock()` |
157
- | `Banny721TokenUriResolver_ContentsAlreadyStored()` |
158
- | `Banny721TokenUriResolver_ContentsMismatch()` |
159
- | `Banny721TokenUriResolver_HashAlreadyStored()` |
160
- | `Banny721TokenUriResolver_HashNotFound()` |
161
- | `Banny721TokenUriResolver_HeadAlreadyAdded()` |
162
- | `Banny721TokenUriResolver_OutfitChangesLocked()` |
163
- | `Banny721TokenUriResolver_SuitAlreadyAdded()` |
164
- | `Banny721TokenUriResolver_UnauthorizedBackground()` |
165
- | `Banny721TokenUriResolver_UnauthorizedBannyBody()` |
166
- | `Banny721TokenUriResolver_UnauthorizedOutfit()` |
167
- | `Banny721TokenUriResolver_UnauthorizedTransfer()` |
168
- | `Banny721TokenUriResolver_UnorderedCategories()` |
169
- | `Banny721TokenUriResolver_UnrecognizedBackground()` |
170
- | `Banny721TokenUriResolver_UnrecognizedCategory()` |
171
- | `Banny721TokenUriResolver_UnrecognizedProduct()` |
172
-
173
- ---
174
-
175
- ## 5. Struct Changes
176
-
177
- No struct changes. Both versions use `JB721Tier` from the respective `721-hook` dependency. Any changes to `JB721Tier` are upstream in `nana-721-hook-v6`.
178
-
179
- ---
180
-
181
- ## 6. Implementation Changes (Non-Interface)
182
-
183
- ### Token URI JSON Encoding Refactored
184
- - **v5:** Single large `abi.encodePacked()` call with all JSON fields inlined in `tokenUriOf()`.
185
- - **v6:** Split into `pricingMetadata` string built separately, then delegated to `_encodeTokenUri()`. Uses nested `abi.encodePacked()` to avoid "stack too deep".
186
-
187
- ### `_outfitContentsFor()` Signature Change
188
- - **v5:** `_outfitContentsFor(address hook, uint256[] memory outfitIds)`
189
- - **v6:** `_outfitContentsFor(address hook, uint256[] memory outfitIds, uint256 bodyUpc)` -- added `bodyUpc` parameter for correct default eyes selection.
190
-
191
- ### `_bannyBodySvgOf()` Relocated
192
- - **v5:** Located after `_msgSender()` / `_msgData()` overrides (line ~700).
193
- - **v6:** Relocated to immediately before `_categoryNameOf()` (line ~538), grouped with other internal view functions.
194
-
195
- ### `_contextSuffixLength()` Relocated
196
- - **v5:** Located before `_bannyBodySvgOf()` (line ~562).
197
- - **v6:** Relocated after `_categoryNameOf()` and `_bannyBodySvgOf()` (line ~616).
198
-
199
- ### Named Parameters in `JBIpfsDecoder.decode()` Calls
200
- - **v5:** Positional arguments: `JBIpfsDecoder.decode(baseUri, ...)`.
201
- - **v6:** Named arguments: `JBIpfsDecoder.decode({baseUri: baseUri, hexString: ...})`.
202
-
203
- ### NatDoc / Comment Improvements
204
- - Typo fixes: "Nakes" to "Naked", "receieved" to "received", "prefered" to "preferred", "categorie's" to "category's", "transfered" to "transferred", "scg" to "svg", "lateset" to "latest".
205
- - Added detailed NatDoc to all interface functions (v5 interface had no NatDoc).
206
- - Added documentation for outfit travel behavior on banny body transfer.
207
- - Added documentation for unbounded array gas considerations on `_attachedOutfitIdsOf`.
208
- - Added detailed authorization rules in `decorateBannyWith()` NatDoc (6-point checklist).
209
- - Added warning about outfit/background travel on banny body transfer.
210
- - Added comment about `transferFrom` vs `safeTransferFrom` limitation in `onERC721Received`.
211
-
212
- ### Lint Suppression Comments
213
- - **v6 adds:** `// forge-lint: disable-next-line(mixed-case-variable)` above `DEFAULT_ALIEN_EYES`, `DEFAULT_MOUTH`, `DEFAULT_NECKLACE`, `DEFAULT_STANDARD_EYES`, and `BANNY_BODY`.
214
-
215
- ### Import Order Change
216
- - **v5:** `@bananapus` imports first, then OpenZeppelin imports.
217
- - **v6:** OpenZeppelin imports first, then `@bananapus` imports.
218
-
219
- ### Slither Annotations
220
- - **v6 adds:** `// slither-disable-next-line calls-loop` on several `IERC721(hook).ownerOf()` calls inside loops.
221
- - **v6 adds:** `// slither-disable-next-line encode-packed-collision` on the `_encodeTokenUri()` return.
222
-
223
- ---
224
-
225
- ## 7. Migration Table
226
-
227
- | v5 Function / Event | v6 Equivalent | Notes |
228
- |---|---|---|
229
- | `setSvgBaseUri(string)` | `setMetadata(string, string, string)` | Now sets description + external URL + base URI together |
230
- | `setSvgHashsOf(uint256[], bytes32[])` | `setSvgHashesOf(uint256[], bytes32[])` | Renamed (typo fix) |
231
- | `SetSvgBaseUri` event | `SetMetadata` event | Different parameters |
232
- | N/A | `svgDescription` (state variable) | New |
233
- | N/A | `svgExternalUrl` (state variable) | New |
234
- | N/A | `Banny721TokenUriResolver_ArrayLengthMismatch` | New error |
235
- | N/A | `Banny721TokenUriResolver_BannyBodyNotBodyCategory` | New error |
236
- | `_transferFrom()` (for returning items) | `_tryTransferFrom()` | Fault-tolerant; `_transferFrom()` still exists for mandatory transfers |
237
- | N/A | `_isInArray()` | New helper |
238
- | N/A | `_encodeTokenUri()` | Extracted from `tokenUriOf()` |
239
- | `_outfitContentsFor(hook, outfitIds)` | `_outfitContentsFor(hook, outfitIds, bodyUpc)` | Added `bodyUpc` param (bug fix) |
240
- | `@bananapus/721-hook-v5` | `@bananapus/721-hook-v6` | Dependency upgrade |
241
- | `pragma solidity 0.8.23` | `pragma solidity 0.8.28` | Compiler version bump |
242
-
243
- > **Cross-repo impact**: The `pricingContext()` return change (3 values → 2) is driven by the upstream `nana-721-hook-v6` `IJB721TiersHook` interface change. The `@bananapus/721-hook-v6` dependency brings in the new tier splits system, though Banny Retail does not use tier splits.
@@ -1,34 +0,0 @@
1
- # 🔐 Security Review — banny-retail-v6
2
-
3
- ---
4
-
5
- ## Scope
6
-
7
- | | |
8
- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
9
- | **Mode** | ALL / default |
10
- | **Files reviewed** | `Add.Denver.s.sol` · `Deploy.s.sol` · `Drop1.s.sol`<br>`BannyverseDeploymentLib.sol` · `MigrationHelper.sol` · `Banny721TokenUriResolver.sol` |
11
- | **Confidence threshold (1-100)** | 75 |
12
-
13
- ---
14
-
15
- ## Findings
16
-
17
- _No confirmed findings._
18
-
19
- ---
20
-
21
- Findings List
22
-
23
- | # | Confidence | Title |
24
- |---|---|---|
25
-
26
- ---
27
-
28
- ## Leads
29
-
30
- _None._
31
-
32
- ---
33
-
34
- > ⚠️ This review was performed by an AI assistant. AI analysis can never verify the complete absence of vulnerabilities and no guarantee of security is given. Team security reviews, bug bounty programs, and on-chain monitoring are strongly recommended. For a consultation regarding your projects' security, visit [https://www.pashov.com](https://www.pashov.com)