@bannynet/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.
Files changed (48) hide show
  1. package/README.md +53 -0
  2. package/SKILLS.md +94 -0
  3. package/deployments/banny-core-v5/arbitrum/Banny721TokenUriResolver.json +1809 -0
  4. package/deployments/banny-core-v5/arbitrum_sepolia/Banny721TokenUriResolver.json +1795 -0
  5. package/deployments/banny-core-v5/base/Banny721TokenUriResolver.json +1810 -0
  6. package/deployments/banny-core-v5/base_sepolia/Banny721TokenUriResolver.json +1796 -0
  7. package/deployments/banny-core-v5/ethereum/Banny721TokenUriResolver.json +1795 -0
  8. package/deployments/banny-core-v5/optimism/Banny721TokenUriResolver.json +1810 -0
  9. package/deployments/banny-core-v5/optimism_sepolia/Banny721TokenUriResolver.json +1796 -0
  10. package/deployments/banny-core-v5/sepolia/Banny721TokenUriResolver.json +1795 -0
  11. package/foundry.toml +22 -0
  12. package/package.json +53 -0
  13. package/remappings.txt +1 -0
  14. package/script/1.Fix.s.sol +290 -0
  15. package/script/Add.Denver.s.sol +75 -0
  16. package/script/AirdropOutfits.s.sol +2302 -0
  17. package/script/Deploy.s.sol +440 -0
  18. package/script/Drop1.s.sol +979 -0
  19. package/script/MigrationContractArbitrum.sol +494 -0
  20. package/script/MigrationContractArbitrum1.sol +170 -0
  21. package/script/MigrationContractArbitrum2.sol +204 -0
  22. package/script/MigrationContractArbitrum3.sol +174 -0
  23. package/script/MigrationContractArbitrum4.sol +478 -0
  24. package/script/MigrationContractBase1.sol +444 -0
  25. package/script/MigrationContractBase2.sol +175 -0
  26. package/script/MigrationContractBase3.sol +309 -0
  27. package/script/MigrationContractBase4.sol +350 -0
  28. package/script/MigrationContractBase5.sol +259 -0
  29. package/script/MigrationContractEthereum1.sol +468 -0
  30. package/script/MigrationContractEthereum2.sol +306 -0
  31. package/script/MigrationContractEthereum3.sol +349 -0
  32. package/script/MigrationContractEthereum4.sol +352 -0
  33. package/script/MigrationContractEthereum5.sol +354 -0
  34. package/script/MigrationContractEthereum6.sol +270 -0
  35. package/script/MigrationContractEthereum7.sol +439 -0
  36. package/script/MigrationContractEthereum8.sol +385 -0
  37. package/script/MigrationContractOptimism.sol +196 -0
  38. package/script/helpers/BannyverseDeploymentLib.sol +73 -0
  39. package/script/helpers/MigrationHelper.sol +155 -0
  40. package/script/outfit_drop/generate-migration.js +3441 -0
  41. package/script/outfit_drop/raw.json +43276 -0
  42. package/slither-ci.config.json +10 -0
  43. package/sphinx.lock +521 -0
  44. package/src/Banny721TokenUriResolver.sol +1288 -0
  45. package/src/interfaces/IBanny721TokenUriResolver.sol +137 -0
  46. package/test/Banny721TokenUriResolver.t.sol +669 -0
  47. package/test/BannyAttacks.t.sol +322 -0
  48. package/test/DecorateFlow.t.sol +1056 -0
@@ -0,0 +1,306 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
5
+ import {JB721TiersHook} from "@bananapus/721-hook-v5/src/JB721TiersHook.sol";
6
+ import {Banny721TokenUriResolver} from "../src/Banny721TokenUriResolver.sol";
7
+ import {MigrationHelper} from "./helpers/MigrationHelper.sol";
8
+
9
+ contract MigrationContractEthereum2 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[13] upc3;
13
+ uint256[7] upc4;
14
+ uint256[1] upc5;
15
+ uint256[2] upc6;
16
+ uint256[1] upc14;
17
+ uint256[1] upc15;
18
+ uint256[1] upc19;
19
+ uint256[2] upc25;
20
+ uint256[1] upc28;
21
+ uint256[1] upc29;
22
+ uint256[1] upc37;
23
+ uint256[1] upc38;
24
+ uint256[1] upc39;
25
+ uint256[1] upc42;
26
+ uint256[1] upc48;
27
+ uint256[1] upc49;
28
+ }
29
+
30
+ address[] private transferOwners;
31
+
32
+ constructor(address[] memory _transferOwners) {
33
+ transferOwners = _transferOwners;
34
+ }
35
+
36
+ function executeMigration(
37
+ address hookAddress,
38
+ address resolverAddress,
39
+ address v4HookAddress,
40
+ address v4ResolverAddress,
41
+ address fallbackV4ResolverAddress
42
+ )
43
+ external
44
+ {
45
+ // Validate addresses
46
+ require(hookAddress != address(0), "Hook address not set");
47
+ require(resolverAddress != address(0), "Resolver address not set");
48
+ require(v4HookAddress != address(0), "V4 Hook address not set");
49
+ require(v4ResolverAddress != address(0), "V4 Resolver address not set");
50
+ require(fallbackV4ResolverAddress != address(0), "V4 fallback resolver address not set");
51
+
52
+ JB721TiersHook hook = JB721TiersHook(hookAddress);
53
+ Banny721TokenUriResolver resolver = Banny721TokenUriResolver(resolverAddress);
54
+ IERC721 v4Hook = IERC721(v4HookAddress);
55
+ Banny721TokenUriResolver v4Resolver = Banny721TokenUriResolver(v4ResolverAddress);
56
+ Banny721TokenUriResolver fallbackV4Resolver = Banny721TokenUriResolver(fallbackV4ResolverAddress);
57
+
58
+ // Ethereum migration chunk 2/6 - 36 items
59
+
60
+ // Step 1: Assets are already minted to this contract by the deployer
61
+
62
+ // Assets are already minted to this contract by the deployer
63
+
64
+ // Create and populate the struct
65
+ // Token IDs follow formula: upc * 1000000000 + unitNumber (unitNumber starts at 1 per UPC)
66
+ MintedIds memory sortedMintedIds;
67
+
68
+ // Populate UPC 3 minted tokenIds (13 items)
69
+ sortedMintedIds.upc3[0] = 3_000_000_014; // Token ID: 3 * 1000000000 + 1
70
+ sortedMintedIds.upc3[1] = 3_000_000_015; // Token ID: 3 * 1000000000 + 2
71
+ sortedMintedIds.upc3[2] = 3_000_000_016; // Token ID: 3 * 1000000000 + 3
72
+ sortedMintedIds.upc3[3] = 3_000_000_017; // Token ID: 3 * 1000000000 + 4
73
+ sortedMintedIds.upc3[4] = 3_000_000_018; // Token ID: 3 * 1000000000 + 5
74
+ sortedMintedIds.upc3[5] = 3_000_000_019; // Token ID: 3 * 1000000000 + 6
75
+ sortedMintedIds.upc3[6] = 3_000_000_020; // Token ID: 3 * 1000000000 + 7
76
+ sortedMintedIds.upc3[7] = 3_000_000_021; // Token ID: 3 * 1000000000 + 8
77
+ sortedMintedIds.upc3[8] = 3_000_000_022; // Token ID: 3 * 1000000000 + 9
78
+ sortedMintedIds.upc3[9] = 3_000_000_023; // Token ID: 3 * 1000000000 + 10
79
+ sortedMintedIds.upc3[10] = 3_000_000_024; // Token ID: 3 * 1000000000 + 11
80
+ sortedMintedIds.upc3[11] = 3_000_000_025; // Token ID: 3 * 1000000000 + 12
81
+ sortedMintedIds.upc3[12] = 3_000_000_026; // Token ID: 3 * 1000000000 + 13
82
+ // Populate UPC 4 minted tokenIds (7 items)
83
+ sortedMintedIds.upc4[0] = 4_000_000_001; // Token ID: 4 * 1000000000 + 1
84
+ sortedMintedIds.upc4[1] = 4_000_000_002; // Token ID: 4 * 1000000000 + 2
85
+ sortedMintedIds.upc4[2] = 4_000_000_003; // Token ID: 4 * 1000000000 + 3
86
+ sortedMintedIds.upc4[3] = 4_000_000_004; // Token ID: 4 * 1000000000 + 4
87
+ sortedMintedIds.upc4[4] = 4_000_000_005; // Token ID: 4 * 1000000000 + 5
88
+ sortedMintedIds.upc4[5] = 4_000_000_006; // Token ID: 4 * 1000000000 + 6
89
+ sortedMintedIds.upc4[6] = 4_000_000_007; // Token ID: 4 * 1000000000 + 7
90
+ // Populate UPC 5 minted tokenIds (1 items)
91
+ sortedMintedIds.upc5[0] = 5_000_000_004; // Token ID: 5 * 1000000000 + 1
92
+ // Populate UPC 6 minted tokenIds (2 items)
93
+ sortedMintedIds.upc6[0] = 6_000_000_004; // Token ID: 6 * 1000000000 + 1
94
+ sortedMintedIds.upc6[1] = 6_000_000_005; // Token ID: 6 * 1000000000 + 2
95
+ // Populate UPC 14 minted tokenIds (1 items)
96
+ sortedMintedIds.upc14[0] = 14_000_000_003; // Token ID: 14 * 1000000000 + 1
97
+ // Populate UPC 15 minted tokenIds (1 items)
98
+ sortedMintedIds.upc15[0] = 15_000_000_001; // Token ID: 15 * 1000000000 + 1
99
+ // Populate UPC 19 minted tokenIds (1 items)
100
+ sortedMintedIds.upc19[0] = 19_000_000_004; // Token ID: 19 * 1000000000 + 1
101
+ // Populate UPC 25 minted tokenIds (2 items)
102
+ sortedMintedIds.upc25[0] = 25_000_000_002; // Token ID: 25 * 1000000000 + 1
103
+ sortedMintedIds.upc25[1] = 25_000_000_003; // Token ID: 25 * 1000000000 + 2
104
+ // Populate UPC 28 minted tokenIds (1 items)
105
+ sortedMintedIds.upc28[0] = 28_000_000_001; // Token ID: 28 * 1000000000 + 1
106
+ // Populate UPC 29 minted tokenIds (1 items)
107
+ sortedMintedIds.upc29[0] = 29_000_000_001; // Token ID: 29 * 1000000000 + 1
108
+ // Populate UPC 37 minted tokenIds (1 items)
109
+ sortedMintedIds.upc37[0] = 37_000_000_002; // Token ID: 37 * 1000000000 + 1
110
+ // Populate UPC 38 minted tokenIds (1 items)
111
+ sortedMintedIds.upc38[0] = 38_000_000_001; // Token ID: 38 * 1000000000 + 1
112
+ // Populate UPC 39 minted tokenIds (1 items)
113
+ sortedMintedIds.upc39[0] = 39_000_000_002; // Token ID: 39 * 1000000000 + 1
114
+ // Populate UPC 42 minted tokenIds (1 items)
115
+ sortedMintedIds.upc42[0] = 42_000_000_001; // Token ID: 42 * 1000000000 + 1
116
+ // Populate UPC 48 minted tokenIds (1 items)
117
+ sortedMintedIds.upc48[0] = 48_000_000_002; // Token ID: 48 * 1000000000 + 1
118
+ // Populate UPC 49 minted tokenIds (1 items)
119
+ sortedMintedIds.upc49[0] = 49_000_000_001; // Token ID: 49 * 1000000000 + 1
120
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
121
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
122
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
123
+
124
+ // Step 2: Process each Banny body and dress them
125
+
126
+ // Dress Banny 3000000017 (Orange)
127
+ {
128
+ uint256[] memory outfitIds = new uint256[](2);
129
+ outfitIds[0] = 25_000_000_002; // V4: 25000000005 -> V5: 25000000002
130
+ outfitIds[1] = 49_000_000_001; // V4: 49000000002 -> V5: 49000000001
131
+
132
+ resolver.decorateBannyWith(address(hook), 3_000_000_017, 5_000_000_004, outfitIds);
133
+
134
+ MigrationHelper.verifyV4AssetMatch(
135
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_017
136
+ );
137
+ }
138
+
139
+ // Dress Banny 3000000022 (Orange)
140
+ {
141
+ uint256[] memory outfitIds = new uint256[](3);
142
+ outfitIds[0] = 19_000_000_004; // V4: 19000000015 -> V5: 19000000004
143
+ outfitIds[1] = 38_000_000_001; // V4: 38000000002 -> V5: 38000000001
144
+ outfitIds[2] = 48_000_000_002; // V4: 48000000005 -> V5: 48000000002
145
+
146
+ resolver.decorateBannyWith(address(hook), 3_000_000_022, 6_000_000_004, outfitIds);
147
+
148
+ MigrationHelper.verifyV4AssetMatch(
149
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_022
150
+ );
151
+ }
152
+
153
+ // Dress Banny 3000000023 (Orange)
154
+ {
155
+ uint256[] memory outfitIds = new uint256[](4);
156
+ outfitIds[0] = 14_000_000_003; // V4: 14000000005 -> V5: 14000000003
157
+ outfitIds[1] = 25_000_000_003; // V4: 25000000008 -> V5: 25000000003
158
+ outfitIds[2] = 37_000_000_002; // V4: 37000000003 -> V5: 37000000002
159
+ outfitIds[3] = 42_000_000_001; // V4: 42000000007 -> V5: 42000000001
160
+
161
+ resolver.decorateBannyWith(address(hook), 3_000_000_023, 0, outfitIds);
162
+
163
+ MigrationHelper.verifyV4AssetMatch(
164
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_023
165
+ );
166
+ }
167
+
168
+ // Dress Banny 3000000026 (Orange)
169
+ {
170
+ uint256[] memory outfitIds = new uint256[](3);
171
+ outfitIds[0] = 15_000_000_001; // V4: 15000000004 -> V5: 15000000001
172
+ outfitIds[1] = 29_000_000_001; // V4: 29000000003 -> V5: 29000000001
173
+ outfitIds[2] = 39_000_000_002; // V4: 39000000003 -> V5: 39000000002
174
+
175
+ resolver.decorateBannyWith(address(hook), 3_000_000_026, 6_000_000_005, outfitIds);
176
+
177
+ MigrationHelper.verifyV4AssetMatch(
178
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_026
179
+ );
180
+ }
181
+
182
+ // Dress Banny 4000000004 (Original)
183
+ {
184
+ uint256[] memory outfitIds = new uint256[](1);
185
+ outfitIds[0] = 28_000_000_001; // V4: 28000000002 -> V5: 28000000001
186
+
187
+ resolver.decorateBannyWith(address(hook), 4_000_000_004, 0, outfitIds);
188
+
189
+ MigrationHelper.verifyV4AssetMatch(
190
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_004
191
+ );
192
+ }
193
+
194
+ // Step 3: Transfer all assets to rightful owners using constructor data
195
+ // Generate token IDs in the same order as items appear (matching mint order)
196
+ // Token ID format: UPC * 1000000000 + unitNumber
197
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
198
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
199
+ // since they're not transferred (only used in decorateBannyWith calls).
200
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
201
+
202
+ generatedTokenIds[0] = 3_000_000_014; // Token ID (V4: 3000000014)
203
+ generatedTokenIds[1] = 3_000_000_015; // Token ID (V4: 3000000015)
204
+ generatedTokenIds[2] = 3_000_000_016; // Token ID (V4: 3000000016)
205
+ generatedTokenIds[3] = 3_000_000_017; // Token ID (V4: 3000000017)
206
+ generatedTokenIds[4] = 3_000_000_018; // Token ID (V4: 3000000018)
207
+ generatedTokenIds[5] = 3_000_000_019; // Token ID (V4: 3000000019)
208
+ generatedTokenIds[6] = 3_000_000_020; // Token ID (V4: 3000000020)
209
+ generatedTokenIds[7] = 3_000_000_021; // Token ID (V4: 3000000021)
210
+ generatedTokenIds[8] = 3_000_000_022; // Token ID (V4: 3000000022)
211
+ generatedTokenIds[9] = 3_000_000_023; // Token ID (V4: 3000000023)
212
+ generatedTokenIds[10] = 3_000_000_024; // Token ID (V4: 3000000024)
213
+ generatedTokenIds[11] = 3_000_000_025; // Token ID (V4: 3000000025)
214
+ generatedTokenIds[12] = 3_000_000_026; // Token ID (V4: 3000000026)
215
+ generatedTokenIds[13] = 4_000_000_001; // Token ID (V4: 4000000001)
216
+ generatedTokenIds[14] = 4_000_000_002; // Token ID (V4: 4000000002)
217
+ generatedTokenIds[15] = 4_000_000_003; // Token ID (V4: 4000000003)
218
+ generatedTokenIds[16] = 4_000_000_004; // Token ID (V4: 4000000004)
219
+ generatedTokenIds[17] = 4_000_000_005; // Token ID (V4: 4000000005)
220
+ generatedTokenIds[18] = 4_000_000_006; // Token ID (V4: 4000000006)
221
+ generatedTokenIds[19] = 4_000_000_007; // Token ID (V4: 4000000007)
222
+
223
+ uint256 successfulTransfers = 0;
224
+ uint256 skippedResolverOwned = 0;
225
+
226
+ for (uint256 i = 0; i < transferOwners.length; i++) {
227
+ uint256 tokenId = generatedTokenIds[i];
228
+ // Verify V4 ownership before transferring V5
229
+ address v4Owner = v4Hook.ownerOf(tokenId);
230
+ require(
231
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
232
+ "V4/V5 ownership mismatch for token"
233
+ );
234
+
235
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
236
+ // resolver)
237
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
238
+ // Token is held by resolver, skip transfer
239
+ skippedResolverOwned++;
240
+ continue;
241
+ }
242
+
243
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
244
+ successfulTransfers++;
245
+ }
246
+
247
+ // Verify all expected items were processed (transferred or skipped as expected)
248
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
249
+
250
+ // Final verification: Ensure this contract no longer owns any tokens
251
+ // This ensures all transfers completed successfully and no tokens were left behind
252
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
253
+
254
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
255
+
256
+ // // Collect unique owners
257
+ // address[] memory uniqueOwners = new address[](17);
258
+
259
+ // uniqueOwners[0] = 0x87084347AeBADc626e8569E0D386928dade2ba09;
260
+ // uniqueOwners[1] = 0x79d1E7F1A6E0Bbb3278a9d2B782e3A8983444cb6;
261
+ // uniqueOwners[2] = 0x546B4A7A30b3193Badf70E1d43D8142928F3db0b;
262
+ // uniqueOwners[3] = 0x08cF1208e638a5A3623be58d600e35c6199baa9C;
263
+ // uniqueOwners[4] = 0x1Ae766cc5947e1E4C3538EE1F3f47063D2B40E79;
264
+ // uniqueOwners[5] = 0xe21A272c4D22eD40678a0168b4acd006bca8A482;
265
+ // uniqueOwners[6] = 0xaECD6D9172d602b93dBA3981991268b44af8096e;
266
+ // uniqueOwners[7] = 0x45C3d8Aacc0d537dAc234AD4C20Ef05d6041CeFe;
267
+ // uniqueOwners[8] = 0x7D0068d0D8fC2Aa15d897448B348Fa9B30f6d4c9;
268
+ // uniqueOwners[9] = 0x823b92d6a4b2AED4b15675c7917c9f922ea8ADAD;
269
+ // uniqueOwners[10] = 0x898e24EBC9dAf5a9930f10def8B6a373F859C101;
270
+ // uniqueOwners[11] = 0x961d4191965C49537c88F764D88318872CE405bE;
271
+ // uniqueOwners[12] = 0x21a8f5A6bF893D43d3964dDaf4E04766BBBE9b07;
272
+ // uniqueOwners[13] = 0x7a16eABD1413Bfd468aE9fEBF7C26c62f1fFdc59;
273
+ // uniqueOwners[14] = 0x8b80755C441d355405CA7571443Bb9247B77Ec16;
274
+ // uniqueOwners[15] = 0xa13d49fCbf79EAF6A0a58cBDD3361422DB4eAfF1;
275
+ // uniqueOwners[16] = 0xe7879a2D05dBA966Fcca34EE9C3F99eEe7eDEFd1;
276
+
277
+ // // Collect unique tier IDs
278
+ // uint256[] memory uniqueTierIds = new uint256[](16);
279
+
280
+ // uniqueTierIds[0] = 3;
281
+ // uniqueTierIds[1] = 4;
282
+ // uniqueTierIds[2] = 5;
283
+ // uniqueTierIds[3] = 6;
284
+ // uniqueTierIds[4] = 14;
285
+ // uniqueTierIds[5] = 15;
286
+ // uniqueTierIds[6] = 19;
287
+ // uniqueTierIds[7] = 25;
288
+ // uniqueTierIds[8] = 28;
289
+ // uniqueTierIds[9] = 29;
290
+ // uniqueTierIds[10] = 37;
291
+ // uniqueTierIds[11] = 38;
292
+ // uniqueTierIds[12] = 39;
293
+ // uniqueTierIds[13] = 42;
294
+ // uniqueTierIds[14] = 48;
295
+ // uniqueTierIds[15] = 49;
296
+
297
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
298
+ // MigrationHelper.verifyTierBalances(
299
+ // hookAddress,
300
+ // v4HookAddress,
301
+ // fallbackV4ResolverAddress,
302
+ // uniqueOwners,
303
+ // uniqueTierIds
304
+ // );
305
+ }
306
+ }
@@ -0,0 +1,349 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
5
+ import {JB721TiersHook} from "@bananapus/721-hook-v5/src/JB721TiersHook.sol";
6
+ import {Banny721TokenUriResolver} from "../src/Banny721TokenUriResolver.sol";
7
+ import {MigrationHelper} from "./helpers/MigrationHelper.sol";
8
+
9
+ contract MigrationContractEthereum3 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[20] upc4;
13
+ uint256[1] upc5;
14
+ uint256[1] upc6;
15
+ uint256[4] upc10;
16
+ uint256[1] upc15;
17
+ uint256[1] upc18;
18
+ uint256[3] upc19;
19
+ uint256[1] upc20;
20
+ uint256[2] upc25;
21
+ uint256[1] upc26;
22
+ uint256[2] upc31;
23
+ uint256[1] upc35;
24
+ uint256[1] upc42;
25
+ uint256[1] upc43;
26
+ uint256[1] upc44;
27
+ uint256[1] upc49;
28
+ }
29
+
30
+ address[] private transferOwners;
31
+
32
+ constructor(address[] memory _transferOwners) {
33
+ transferOwners = _transferOwners;
34
+ }
35
+
36
+ function executeMigration(
37
+ address hookAddress,
38
+ address resolverAddress,
39
+ address v4HookAddress,
40
+ address v4ResolverAddress,
41
+ address fallbackV4ResolverAddress
42
+ )
43
+ external
44
+ {
45
+ // Validate addresses
46
+ require(hookAddress != address(0), "Hook address not set");
47
+ require(resolverAddress != address(0), "Resolver address not set");
48
+ require(v4HookAddress != address(0), "V4 Hook address not set");
49
+ require(v4ResolverAddress != address(0), "V4 Resolver address not set");
50
+ require(fallbackV4ResolverAddress != address(0), "V4 fallback resolver address not set");
51
+
52
+ JB721TiersHook hook = JB721TiersHook(hookAddress);
53
+ Banny721TokenUriResolver resolver = Banny721TokenUriResolver(resolverAddress);
54
+ IERC721 v4Hook = IERC721(v4HookAddress);
55
+ Banny721TokenUriResolver v4Resolver = Banny721TokenUriResolver(v4ResolverAddress);
56
+ Banny721TokenUriResolver fallbackV4Resolver = Banny721TokenUriResolver(fallbackV4ResolverAddress);
57
+
58
+ // Ethereum migration chunk 3/6 - 42 items
59
+
60
+ // Step 1: Assets are already minted to this contract by the deployer
61
+
62
+ // Assets are already minted to this contract by the deployer
63
+
64
+ // Create and populate the struct
65
+ // Token IDs follow formula: upc * 1000000000 + unitNumber (unitNumber starts at 1 per UPC)
66
+ MintedIds memory sortedMintedIds;
67
+
68
+ // Populate UPC 4 minted tokenIds (20 items)
69
+ sortedMintedIds.upc4[0] = 4_000_000_008; // Token ID: 4 * 1000000000 + 1
70
+ sortedMintedIds.upc4[1] = 4_000_000_009; // Token ID: 4 * 1000000000 + 2
71
+ sortedMintedIds.upc4[2] = 4_000_000_010; // Token ID: 4 * 1000000000 + 3
72
+ sortedMintedIds.upc4[3] = 4_000_000_011; // Token ID: 4 * 1000000000 + 4
73
+ sortedMintedIds.upc4[4] = 4_000_000_012; // Token ID: 4 * 1000000000 + 5
74
+ sortedMintedIds.upc4[5] = 4_000_000_013; // Token ID: 4 * 1000000000 + 6
75
+ sortedMintedIds.upc4[6] = 4_000_000_014; // Token ID: 4 * 1000000000 + 7
76
+ sortedMintedIds.upc4[7] = 4_000_000_015; // Token ID: 4 * 1000000000 + 8
77
+ sortedMintedIds.upc4[8] = 4_000_000_016; // Token ID: 4 * 1000000000 + 9
78
+ sortedMintedIds.upc4[9] = 4_000_000_017; // Token ID: 4 * 1000000000 + 10
79
+ sortedMintedIds.upc4[10] = 4_000_000_018; // Token ID: 4 * 1000000000 + 11
80
+ sortedMintedIds.upc4[11] = 4_000_000_019; // Token ID: 4 * 1000000000 + 12
81
+ sortedMintedIds.upc4[12] = 4_000_000_020; // Token ID: 4 * 1000000000 + 13
82
+ sortedMintedIds.upc4[13] = 4_000_000_021; // Token ID: 4 * 1000000000 + 14
83
+ sortedMintedIds.upc4[14] = 4_000_000_022; // Token ID: 4 * 1000000000 + 15
84
+ sortedMintedIds.upc4[15] = 4_000_000_023; // Token ID: 4 * 1000000000 + 16
85
+ sortedMintedIds.upc4[16] = 4_000_000_024; // Token ID: 4 * 1000000000 + 17
86
+ sortedMintedIds.upc4[17] = 4_000_000_025; // Token ID: 4 * 1000000000 + 18
87
+ sortedMintedIds.upc4[18] = 4_000_000_026; // Token ID: 4 * 1000000000 + 19
88
+ sortedMintedIds.upc4[19] = 4_000_000_027; // Token ID: 4 * 1000000000 + 20
89
+ // Populate UPC 5 minted tokenIds (1 items)
90
+ sortedMintedIds.upc5[0] = 5_000_000_005; // Token ID: 5 * 1000000000 + 1
91
+ // Populate UPC 6 minted tokenIds (1 items)
92
+ sortedMintedIds.upc6[0] = 6_000_000_006; // Token ID: 6 * 1000000000 + 1
93
+ // Populate UPC 10 minted tokenIds (4 items)
94
+ sortedMintedIds.upc10[0] = 10_000_000_002; // Token ID: 10 * 1000000000 + 1
95
+ sortedMintedIds.upc10[1] = 10_000_000_003; // Token ID: 10 * 1000000000 + 2
96
+ sortedMintedIds.upc10[2] = 10_000_000_004; // Token ID: 10 * 1000000000 + 3
97
+ sortedMintedIds.upc10[3] = 10_000_000_005; // Token ID: 10 * 1000000000 + 4
98
+ // Populate UPC 15 minted tokenIds (1 items)
99
+ sortedMintedIds.upc15[0] = 15_000_000_002; // Token ID: 15 * 1000000000 + 1
100
+ // Populate UPC 18 minted tokenIds (1 items)
101
+ sortedMintedIds.upc18[0] = 18_000_000_002; // Token ID: 18 * 1000000000 + 1
102
+ // Populate UPC 19 minted tokenIds (3 items)
103
+ sortedMintedIds.upc19[0] = 19_000_000_005; // Token ID: 19 * 1000000000 + 1
104
+ sortedMintedIds.upc19[1] = 19_000_000_006; // Token ID: 19 * 1000000000 + 2
105
+ sortedMintedIds.upc19[2] = 19_000_000_007; // Token ID: 19 * 1000000000 + 3
106
+ // Populate UPC 20 minted tokenIds (1 items)
107
+ sortedMintedIds.upc20[0] = 20_000_000_001; // Token ID: 20 * 1000000000 + 1
108
+ // Populate UPC 25 minted tokenIds (2 items)
109
+ sortedMintedIds.upc25[0] = 25_000_000_004; // Token ID: 25 * 1000000000 + 1
110
+ sortedMintedIds.upc25[1] = 25_000_000_005; // Token ID: 25 * 1000000000 + 2
111
+ // Populate UPC 26 minted tokenIds (1 items)
112
+ sortedMintedIds.upc26[0] = 26_000_000_004; // Token ID: 26 * 1000000000 + 1
113
+ // Populate UPC 31 minted tokenIds (2 items)
114
+ sortedMintedIds.upc31[0] = 31_000_000_003; // Token ID: 31 * 1000000000 + 1
115
+ sortedMintedIds.upc31[1] = 31_000_000_004; // Token ID: 31 * 1000000000 + 2
116
+ // Populate UPC 35 minted tokenIds (1 items)
117
+ sortedMintedIds.upc35[0] = 35_000_000_005; // Token ID: 35 * 1000000000 + 1
118
+ // Populate UPC 42 minted tokenIds (1 items)
119
+ sortedMintedIds.upc42[0] = 42_000_000_002; // Token ID: 42 * 1000000000 + 1
120
+ // Populate UPC 43 minted tokenIds (1 items)
121
+ sortedMintedIds.upc43[0] = 43_000_000_004; // Token ID: 43 * 1000000000 + 1
122
+ // Populate UPC 44 minted tokenIds (1 items)
123
+ sortedMintedIds.upc44[0] = 44_000_000_003; // Token ID: 44 * 1000000000 + 1
124
+ // Populate UPC 49 minted tokenIds (1 items)
125
+ sortedMintedIds.upc49[0] = 49_000_000_002; // Token ID: 49 * 1000000000 + 1
126
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
127
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
128
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
129
+
130
+ // Step 2: Process each Banny body and dress them
131
+
132
+ // Dress Banny 4000000009 (Original)
133
+ {
134
+ uint256[] memory outfitIds = new uint256[](4);
135
+ outfitIds[0] = 10_000_000_002; // V4: 10000000001 -> V5: 10000000002
136
+ outfitIds[1] = 19_000_000_005; // V4: 19000000002 -> V5: 19000000005
137
+ outfitIds[2] = 25_000_000_004; // V4: 25000000002 -> V5: 25000000004
138
+ outfitIds[3] = 43_000_000_004; // V4: 43000000003 -> V5: 43000000004
139
+
140
+ resolver.decorateBannyWith(address(hook), 4_000_000_009, 0, outfitIds);
141
+
142
+ MigrationHelper.verifyV4AssetMatch(
143
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_009
144
+ );
145
+ }
146
+
147
+ // Dress Banny 4000000010 (Original)
148
+ {
149
+ uint256[] memory outfitIds = new uint256[](4);
150
+ outfitIds[0] = 10_000_000_003; // V4: 10000000002 -> V5: 10000000003
151
+ outfitIds[1] = 18_000_000_002; // V4: 18000000001 -> V5: 18000000002
152
+ outfitIds[2] = 20_000_000_001; // V4: 20000000001 -> V5: 20000000001
153
+ outfitIds[3] = 44_000_000_003; // V4: 44000000001 -> V5: 44000000003
154
+
155
+ resolver.decorateBannyWith(address(hook), 4_000_000_010, 5_000_000_005, outfitIds);
156
+
157
+ MigrationHelper.verifyV4AssetMatch(
158
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_010
159
+ );
160
+ }
161
+
162
+ // Dress Banny 4000000013 (Original)
163
+ {
164
+ uint256[] memory outfitIds = new uint256[](1);
165
+ outfitIds[0] = 31_000_000_003; // V4: 31000000002 -> V5: 31000000003
166
+
167
+ resolver.decorateBannyWith(address(hook), 4_000_000_013, 0, outfitIds);
168
+
169
+ MigrationHelper.verifyV4AssetMatch(
170
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_013
171
+ );
172
+ }
173
+
174
+ // Dress Banny 4000000014 (Original)
175
+ {
176
+ uint256[] memory outfitIds = new uint256[](4);
177
+ outfitIds[0] = 10_000_000_004; // V4: 10000000006 -> V5: 10000000004
178
+ outfitIds[1] = 19_000_000_006; // V4: 19000000004 -> V5: 19000000006
179
+ outfitIds[2] = 25_000_000_005; // V4: 25000000003 -> V5: 25000000005
180
+ outfitIds[3] = 49_000_000_002; // V4: 49000000001 -> V5: 49000000002
181
+
182
+ resolver.decorateBannyWith(address(hook), 4_000_000_014, 0, outfitIds);
183
+
184
+ MigrationHelper.verifyV4AssetMatch(
185
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_014
186
+ );
187
+ }
188
+
189
+ // Dress Banny 4000000015 (Original)
190
+ {
191
+ uint256[] memory outfitIds = new uint256[](2);
192
+ outfitIds[0] = 15_000_000_002; // V4: 15000000001 -> V5: 15000000002
193
+ outfitIds[1] = 26_000_000_004; // V4: 26000000002 -> V5: 26000000004
194
+
195
+ resolver.decorateBannyWith(address(hook), 4_000_000_015, 0, outfitIds);
196
+
197
+ MigrationHelper.verifyV4AssetMatch(
198
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_015
199
+ );
200
+ }
201
+
202
+ // Dress Banny 4000000016 (Original)
203
+ {
204
+ uint256[] memory outfitIds = new uint256[](1);
205
+ outfitIds[0] = 10_000_000_005; // V4: 10000000007 -> V5: 10000000005
206
+
207
+ resolver.decorateBannyWith(address(hook), 4_000_000_016, 6_000_000_006, outfitIds);
208
+
209
+ MigrationHelper.verifyV4AssetMatch(
210
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_016
211
+ );
212
+ }
213
+
214
+ // Dress Banny 4000000019 (Original)
215
+ {
216
+ uint256[] memory outfitIds = new uint256[](3);
217
+ outfitIds[0] = 19_000_000_007; // V4: 19000000005 -> V5: 19000000007
218
+ outfitIds[1] = 35_000_000_005; // V4: 35000000003 -> V5: 35000000005
219
+ outfitIds[2] = 42_000_000_002; // V4: 42000000002 -> V5: 42000000002
220
+
221
+ resolver.decorateBannyWith(address(hook), 4_000_000_019, 0, outfitIds);
222
+
223
+ MigrationHelper.verifyV4AssetMatch(
224
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_019
225
+ );
226
+ }
227
+
228
+ // Dress Banny 4000000023 (Original)
229
+ {
230
+ uint256[] memory outfitIds = new uint256[](1);
231
+ outfitIds[0] = 31_000_000_004; // V4: 31000000007 -> V5: 31000000004
232
+
233
+ resolver.decorateBannyWith(address(hook), 4_000_000_023, 0, outfitIds);
234
+
235
+ MigrationHelper.verifyV4AssetMatch(
236
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_023
237
+ );
238
+ }
239
+
240
+ // Step 3: Transfer all assets to rightful owners using constructor data
241
+ // Generate token IDs in the same order as items appear (matching mint order)
242
+ // Token ID format: UPC * 1000000000 + unitNumber
243
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
244
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
245
+ // since they're not transferred (only used in decorateBannyWith calls).
246
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
247
+
248
+ generatedTokenIds[0] = 4_000_000_008; // Token ID (V4: 4000000008)
249
+ generatedTokenIds[1] = 4_000_000_009; // Token ID (V4: 4000000009)
250
+ generatedTokenIds[2] = 4_000_000_010; // Token ID (V4: 4000000010)
251
+ generatedTokenIds[3] = 4_000_000_011; // Token ID (V4: 4000000011)
252
+ generatedTokenIds[4] = 4_000_000_012; // Token ID (V4: 4000000012)
253
+ generatedTokenIds[5] = 4_000_000_013; // Token ID (V4: 4000000013)
254
+ generatedTokenIds[6] = 4_000_000_014; // Token ID (V4: 4000000014)
255
+ generatedTokenIds[7] = 4_000_000_015; // Token ID (V4: 4000000015)
256
+ generatedTokenIds[8] = 4_000_000_016; // Token ID (V4: 4000000016)
257
+ generatedTokenIds[9] = 4_000_000_017; // Token ID (V4: 4000000017)
258
+ generatedTokenIds[10] = 4_000_000_018; // Token ID (V4: 4000000018)
259
+ generatedTokenIds[11] = 4_000_000_019; // Token ID (V4: 4000000019)
260
+ generatedTokenIds[12] = 4_000_000_020; // Token ID (V4: 4000000020)
261
+ generatedTokenIds[13] = 4_000_000_021; // Token ID (V4: 4000000021)
262
+ generatedTokenIds[14] = 4_000_000_022; // Token ID (V4: 4000000022)
263
+ generatedTokenIds[15] = 4_000_000_023; // Token ID (V4: 4000000023)
264
+ generatedTokenIds[16] = 4_000_000_024; // Token ID (V4: 4000000024)
265
+ generatedTokenIds[17] = 4_000_000_025; // Token ID (V4: 4000000025)
266
+ generatedTokenIds[18] = 4_000_000_026; // Token ID (V4: 4000000026)
267
+ generatedTokenIds[19] = 4_000_000_027; // Token ID (V4: 4000000027)
268
+
269
+ uint256 successfulTransfers = 0;
270
+ uint256 skippedResolverOwned = 0;
271
+
272
+ for (uint256 i = 0; i < transferOwners.length; i++) {
273
+ uint256 tokenId = generatedTokenIds[i];
274
+ // Verify V4 ownership before transferring V5
275
+ address v4Owner = v4Hook.ownerOf(tokenId);
276
+ require(
277
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
278
+ "V4/V5 ownership mismatch for token"
279
+ );
280
+
281
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
282
+ // resolver)
283
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
284
+ // Token is held by resolver, skip transfer
285
+ skippedResolverOwned++;
286
+ continue;
287
+ }
288
+
289
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
290
+ successfulTransfers++;
291
+ }
292
+
293
+ // Verify all expected items were processed (transferred or skipped as expected)
294
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
295
+
296
+ // Final verification: Ensure this contract no longer owns any tokens
297
+ // This ensures all transfers completed successfully and no tokens were left behind
298
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
299
+
300
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
301
+
302
+ // // Collect unique owners
303
+ // address[] memory uniqueOwners = new address[](14);
304
+
305
+ // uniqueOwners[0] = 0x0447AD1BdC0fFA06f7029c8E63F4De21E65255d2;
306
+ // uniqueOwners[1] = 0x5706d5aD7A68bf8692bD341234bE44ca7Bf2f654;
307
+ // uniqueOwners[2] = 0x679d87D8640e66778c3419D164998E720D7495f6;
308
+ // uniqueOwners[3] = 0x817738DC393d682Ca5fBb268707b99F2aAe96baE;
309
+ // uniqueOwners[4] = 0x4A290F18c35bBFE97B2557cf765De9387726dE39;
310
+ // uniqueOwners[5] = 0x25171bD3cD3231c3057c96F38E32E3bA6681497a;
311
+ // uniqueOwners[6] = 0xa7226e53F3100C093A0a5BCb6E3D0976EB3db1D6;
312
+ // uniqueOwners[7] = 0x76A6D08b82034b397E7e09dAe4377C18F132BbB8;
313
+ // uniqueOwners[8] = 0x809C9f8dd8CA93A41c3adca4972Fa234C28F7714;
314
+ // uniqueOwners[9] = 0x126eeFa566ABF5aC3EfDAeF52d79E962CFFdB448;
315
+ // uniqueOwners[10] = 0x77fb4fa1ABA92576942aD34BC47834059b84e693;
316
+ // uniqueOwners[11] = 0x08cEb8Bba685ee708C9c4c65576837cbE19B9dea;
317
+ // uniqueOwners[12] = 0x690C01b4b1389D9D9265820F77DCbD2A6Ad04e6c;
318
+ // uniqueOwners[13] = 0x7bE8c264c9DCebA3A35990c78d5C4220D8724B6e;
319
+
320
+ // // Collect unique tier IDs
321
+ // uint256[] memory uniqueTierIds = new uint256[](16);
322
+
323
+ // uniqueTierIds[0] = 4;
324
+ // uniqueTierIds[1] = 5;
325
+ // uniqueTierIds[2] = 6;
326
+ // uniqueTierIds[3] = 10;
327
+ // uniqueTierIds[4] = 15;
328
+ // uniqueTierIds[5] = 18;
329
+ // uniqueTierIds[6] = 19;
330
+ // uniqueTierIds[7] = 20;
331
+ // uniqueTierIds[8] = 25;
332
+ // uniqueTierIds[9] = 26;
333
+ // uniqueTierIds[10] = 31;
334
+ // uniqueTierIds[11] = 35;
335
+ // uniqueTierIds[12] = 42;
336
+ // uniqueTierIds[13] = 43;
337
+ // uniqueTierIds[14] = 44;
338
+ // uniqueTierIds[15] = 49;
339
+
340
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
341
+ // MigrationHelper.verifyTierBalances(
342
+ // hookAddress,
343
+ // v4HookAddress,
344
+ // fallbackV4ResolverAddress,
345
+ // uniqueOwners,
346
+ // uniqueTierIds
347
+ // );
348
+ }
349
+ }