@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,309 @@
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 MigrationContractBase3 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[27] upc4;
13
+ uint256[3] upc10;
14
+ uint256[1] upc14;
15
+ uint256[1] upc19;
16
+ uint256[2] upc25;
17
+ uint256[1] upc28;
18
+ uint256[1] upc31;
19
+ uint256[1] upc38;
20
+ uint256[2] upc43;
21
+ uint256[1] upc47;
22
+ }
23
+
24
+ address[] private transferOwners;
25
+
26
+ constructor(address[] memory _transferOwners) {
27
+ transferOwners = _transferOwners;
28
+ }
29
+
30
+ function executeMigration(
31
+ address hookAddress,
32
+ address resolverAddress,
33
+ address v4HookAddress,
34
+ address v4ResolverAddress,
35
+ address fallbackV4ResolverAddress
36
+ )
37
+ external
38
+ {
39
+ // Validate addresses
40
+ require(hookAddress != address(0), "Hook address not set");
41
+ require(resolverAddress != address(0), "Resolver address not set");
42
+ require(v4HookAddress != address(0), "V4 Hook address not set");
43
+ require(v4ResolverAddress != address(0), "V4 Resolver address not set");
44
+ require(fallbackV4ResolverAddress != address(0), "V4 fallback resolver address not set");
45
+
46
+ JB721TiersHook hook = JB721TiersHook(hookAddress);
47
+ Banny721TokenUriResolver resolver = Banny721TokenUriResolver(resolverAddress);
48
+ IERC721 v4Hook = IERC721(v4HookAddress);
49
+ Banny721TokenUriResolver v4Resolver = Banny721TokenUriResolver(v4ResolverAddress);
50
+ Banny721TokenUriResolver fallbackV4Resolver = Banny721TokenUriResolver(fallbackV4ResolverAddress);
51
+
52
+ // Base migration chunk 3/4 - 40 items
53
+
54
+ // Step 1: Assets are already minted to this contract by the deployer
55
+
56
+ // Assets are already minted to this contract by the deployer
57
+
58
+ // Create and populate the struct
59
+ // Token IDs follow formula: upc * 1000000000 + unitNumber (unitNumber starts at 1 per UPC)
60
+ MintedIds memory sortedMintedIds;
61
+
62
+ // Populate UPC 4 minted tokenIds (27 items)
63
+ sortedMintedIds.upc4[0] = 4_000_000_042; // Token ID: 4 * 1000000000 + 1
64
+ sortedMintedIds.upc4[1] = 4_000_000_043; // Token ID: 4 * 1000000000 + 2
65
+ sortedMintedIds.upc4[2] = 4_000_000_044; // Token ID: 4 * 1000000000 + 3
66
+ sortedMintedIds.upc4[3] = 4_000_000_045; // Token ID: 4 * 1000000000 + 4
67
+ sortedMintedIds.upc4[4] = 4_000_000_046; // Token ID: 4 * 1000000000 + 5
68
+ sortedMintedIds.upc4[5] = 4_000_000_047; // Token ID: 4 * 1000000000 + 6
69
+ sortedMintedIds.upc4[6] = 4_000_000_048; // Token ID: 4 * 1000000000 + 7
70
+ sortedMintedIds.upc4[7] = 4_000_000_049; // Token ID: 4 * 1000000000 + 8
71
+ sortedMintedIds.upc4[8] = 4_000_000_050; // Token ID: 4 * 1000000000 + 9
72
+ sortedMintedIds.upc4[9] = 4_000_000_051; // Token ID: 4 * 1000000000 + 10
73
+ sortedMintedIds.upc4[10] = 4_000_000_052; // Token ID: 4 * 1000000000 + 11
74
+ sortedMintedIds.upc4[11] = 4_000_000_053; // Token ID: 4 * 1000000000 + 12
75
+ sortedMintedIds.upc4[12] = 4_000_000_054; // Token ID: 4 * 1000000000 + 13
76
+ sortedMintedIds.upc4[13] = 4_000_000_055; // Token ID: 4 * 1000000000 + 14
77
+ sortedMintedIds.upc4[14] = 4_000_000_056; // Token ID: 4 * 1000000000 + 15
78
+ sortedMintedIds.upc4[15] = 4_000_000_057; // Token ID: 4 * 1000000000 + 16
79
+ sortedMintedIds.upc4[16] = 4_000_000_058; // Token ID: 4 * 1000000000 + 17
80
+ sortedMintedIds.upc4[17] = 4_000_000_059; // Token ID: 4 * 1000000000 + 18
81
+ sortedMintedIds.upc4[18] = 4_000_000_060; // Token ID: 4 * 1000000000 + 19
82
+ sortedMintedIds.upc4[19] = 4_000_000_061; // Token ID: 4 * 1000000000 + 20
83
+ sortedMintedIds.upc4[20] = 4_000_000_062; // Token ID: 4 * 1000000000 + 21
84
+ sortedMintedIds.upc4[21] = 4_000_000_063; // Token ID: 4 * 1000000000 + 22
85
+ sortedMintedIds.upc4[22] = 4_000_000_064; // Token ID: 4 * 1000000000 + 23
86
+ sortedMintedIds.upc4[23] = 4_000_000_065; // Token ID: 4 * 1000000000 + 24
87
+ sortedMintedIds.upc4[24] = 4_000_000_066; // Token ID: 4 * 1000000000 + 25
88
+ sortedMintedIds.upc4[25] = 4_000_000_067; // Token ID: 4 * 1000000000 + 26
89
+ sortedMintedIds.upc4[26] = 4_000_000_068; // Token ID: 4 * 1000000000 + 27
90
+ // Populate UPC 10 minted tokenIds (3 items)
91
+ sortedMintedIds.upc10[0] = 10_000_000_002; // Token ID: 10 * 1000000000 + 1
92
+ sortedMintedIds.upc10[1] = 10_000_000_003; // Token ID: 10 * 1000000000 + 2
93
+ sortedMintedIds.upc10[2] = 10_000_000_004; // Token ID: 10 * 1000000000 + 3
94
+ // Populate UPC 14 minted tokenIds (1 items)
95
+ sortedMintedIds.upc14[0] = 14_000_000_003; // Token ID: 14 * 1000000000 + 1
96
+ // Populate UPC 19 minted tokenIds (1 items)
97
+ sortedMintedIds.upc19[0] = 19_000_000_005; // Token ID: 19 * 1000000000 + 1
98
+ // Populate UPC 25 minted tokenIds (2 items)
99
+ sortedMintedIds.upc25[0] = 25_000_000_005; // Token ID: 25 * 1000000000 + 1
100
+ sortedMintedIds.upc25[1] = 25_000_000_006; // Token ID: 25 * 1000000000 + 2
101
+ // Populate UPC 28 minted tokenIds (1 items)
102
+ sortedMintedIds.upc28[0] = 28_000_000_005; // Token ID: 28 * 1000000000 + 1
103
+ // Populate UPC 31 minted tokenIds (1 items)
104
+ sortedMintedIds.upc31[0] = 31_000_000_002; // Token ID: 31 * 1000000000 + 1
105
+ // Populate UPC 38 minted tokenIds (1 items)
106
+ sortedMintedIds.upc38[0] = 38_000_000_001; // Token ID: 38 * 1000000000 + 1
107
+ // Populate UPC 43 minted tokenIds (2 items)
108
+ sortedMintedIds.upc43[0] = 43_000_000_002; // Token ID: 43 * 1000000000 + 1
109
+ sortedMintedIds.upc43[1] = 43_000_000_003; // Token ID: 43 * 1000000000 + 2
110
+ // Populate UPC 47 minted tokenIds (1 items)
111
+ sortedMintedIds.upc47[0] = 47_000_000_003; // Token ID: 47 * 1000000000 + 1
112
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
113
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
114
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
115
+
116
+ // Step 2: Process each Banny body and dress them
117
+
118
+ // Dress Banny 4000000045 (Original)
119
+ {
120
+ uint256[] memory outfitIds = new uint256[](3);
121
+ outfitIds[0] = 10_000_000_002; // V4: 10000000001 -> V5: 10000000002
122
+ outfitIds[1] = 25_000_000_005; // V4: 25000000002 -> V5: 25000000005
123
+ outfitIds[2] = 43_000_000_002; // V4: 43000000002 -> V5: 43000000002
124
+
125
+ resolver.decorateBannyWith(address(hook), 4_000_000_045, 0, outfitIds);
126
+
127
+ MigrationHelper.verifyV4AssetMatch(
128
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_045
129
+ );
130
+ }
131
+
132
+ // Dress Banny 4000000046 (Original)
133
+ {
134
+ uint256[] memory outfitIds = new uint256[](1);
135
+ outfitIds[0] = 47_000_000_003; // V4: 47000000001 -> V5: 47000000003
136
+
137
+ resolver.decorateBannyWith(address(hook), 4_000_000_046, 0, outfitIds);
138
+
139
+ MigrationHelper.verifyV4AssetMatch(
140
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_046
141
+ );
142
+ }
143
+
144
+ // Dress Banny 4000000048 (Original)
145
+ {
146
+ uint256[] memory outfitIds = new uint256[](3);
147
+ outfitIds[0] = 10_000_000_003; // V4: 10000000003 -> V5: 10000000003
148
+ outfitIds[1] = 19_000_000_005; // V4: 19000000003 -> V5: 19000000005
149
+ outfitIds[2] = 28_000_000_005; // V4: 28000000004 -> V5: 28000000005
150
+
151
+ resolver.decorateBannyWith(address(hook), 4_000_000_048, 0, outfitIds);
152
+
153
+ MigrationHelper.verifyV4AssetMatch(
154
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_048
155
+ );
156
+ }
157
+
158
+ // Dress Banny 4000000049 (Original)
159
+ {
160
+ uint256[] memory outfitIds = new uint256[](1);
161
+ outfitIds[0] = 10_000_000_004; // V4: 10000000004 -> V5: 10000000004
162
+
163
+ resolver.decorateBannyWith(address(hook), 4_000_000_049, 0, outfitIds);
164
+
165
+ MigrationHelper.verifyV4AssetMatch(
166
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_049
167
+ );
168
+ }
169
+
170
+ // Dress Banny 4000000050 (Original)
171
+ {
172
+ uint256[] memory outfitIds = new uint256[](4);
173
+ outfitIds[0] = 14_000_000_003; // V4: 14000000001 -> V5: 14000000003
174
+ outfitIds[1] = 31_000_000_002; // V4: 31000000001 -> V5: 31000000002
175
+ outfitIds[2] = 38_000_000_001; // V4: 38000000001 -> V5: 38000000001
176
+ outfitIds[3] = 43_000_000_003; // V4: 43000000003 -> V5: 43000000003
177
+
178
+ resolver.decorateBannyWith(address(hook), 4_000_000_050, 0, outfitIds);
179
+
180
+ MigrationHelper.verifyV4AssetMatch(
181
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_050
182
+ );
183
+ }
184
+
185
+ // Dress Banny 4000000054 (Original)
186
+ {
187
+ uint256[] memory outfitIds = new uint256[](1);
188
+ outfitIds[0] = 25_000_000_006; // V4: 25000000005 -> V5: 25000000006
189
+
190
+ resolver.decorateBannyWith(address(hook), 4_000_000_054, 0, outfitIds);
191
+
192
+ MigrationHelper.verifyV4AssetMatch(
193
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_054
194
+ );
195
+ }
196
+
197
+ // Step 3: Transfer all assets to rightful owners using constructor data
198
+ // Generate token IDs in the same order as items appear (matching mint order)
199
+ // Token ID format: UPC * 1000000000 + unitNumber
200
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
201
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
202
+ // since they're not transferred (only used in decorateBannyWith calls).
203
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
204
+
205
+ generatedTokenIds[0] = 4_000_000_042; // Token ID (V4: 4000000042)
206
+ generatedTokenIds[1] = 4_000_000_043; // Token ID (V4: 4000000043)
207
+ generatedTokenIds[2] = 4_000_000_044; // Token ID (V4: 4000000044)
208
+ generatedTokenIds[3] = 4_000_000_045; // Token ID (V4: 4000000045)
209
+ generatedTokenIds[4] = 4_000_000_046; // Token ID (V4: 4000000046)
210
+ generatedTokenIds[5] = 4_000_000_047; // Token ID (V4: 4000000047)
211
+ generatedTokenIds[6] = 4_000_000_048; // Token ID (V4: 4000000048)
212
+ generatedTokenIds[7] = 4_000_000_049; // Token ID (V4: 4000000049)
213
+ generatedTokenIds[8] = 4_000_000_050; // Token ID (V4: 4000000050)
214
+ generatedTokenIds[9] = 4_000_000_051; // Token ID (V4: 4000000051)
215
+ generatedTokenIds[10] = 4_000_000_052; // Token ID (V4: 4000000052)
216
+ generatedTokenIds[11] = 4_000_000_053; // Token ID (V4: 4000000053)
217
+ generatedTokenIds[12] = 4_000_000_054; // Token ID (V4: 4000000054)
218
+ generatedTokenIds[13] = 4_000_000_055; // Token ID (V4: 4000000055)
219
+ generatedTokenIds[14] = 4_000_000_056; // Token ID (V4: 4000000056)
220
+ generatedTokenIds[15] = 4_000_000_057; // Token ID (V4: 4000000057)
221
+ generatedTokenIds[16] = 4_000_000_058; // Token ID (V4: 4000000058)
222
+ generatedTokenIds[17] = 4_000_000_059; // Token ID (V4: 4000000059)
223
+ generatedTokenIds[18] = 4_000_000_060; // Token ID (V4: 4000000060)
224
+ generatedTokenIds[19] = 4_000_000_061; // Token ID (V4: 4000000061)
225
+ generatedTokenIds[20] = 4_000_000_062; // Token ID (V4: 4000000062)
226
+ generatedTokenIds[21] = 4_000_000_063; // Token ID (V4: 4000000063)
227
+ generatedTokenIds[22] = 4_000_000_064; // Token ID (V4: 4000000064)
228
+ generatedTokenIds[23] = 4_000_000_065; // Token ID (V4: 4000000065)
229
+ generatedTokenIds[24] = 4_000_000_066; // Token ID (V4: 4000000066)
230
+ generatedTokenIds[25] = 4_000_000_067; // Token ID (V4: 4000000067)
231
+ generatedTokenIds[26] = 4_000_000_068; // Token ID (V4: 4000000068)
232
+
233
+ uint256 successfulTransfers = 0;
234
+ uint256 skippedResolverOwned = 0;
235
+
236
+ for (uint256 i = 0; i < transferOwners.length; i++) {
237
+ uint256 tokenId = generatedTokenIds[i];
238
+ // Verify V4 ownership before transferring V5
239
+ address v4Owner = v4Hook.ownerOf(tokenId);
240
+ require(
241
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
242
+ "V4/V5 ownership mismatch for token"
243
+ );
244
+
245
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
246
+ // resolver)
247
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
248
+ // Token is held by resolver, skip transfer
249
+ skippedResolverOwned++;
250
+ continue;
251
+ }
252
+
253
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
254
+ successfulTransfers++;
255
+ }
256
+
257
+ // Verify all expected items were processed (transferred or skipped as expected)
258
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
259
+
260
+ // Final verification: Ensure this contract no longer owns any tokens
261
+ // This ensures all transfers completed successfully and no tokens were left behind
262
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
263
+
264
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
265
+
266
+ // // Collect unique owners
267
+ // address[] memory uniqueOwners = new address[](16);
268
+
269
+ // uniqueOwners[0] = 0xf7253A0E87E39d2cD6365919D4a3D56D431D0041;
270
+ // uniqueOwners[1] = 0x67BcBE602e870e2286C19E4E0044E583967c9665;
271
+ // uniqueOwners[2] = 0x18deEE9699526f8C8a87004b2e4e55029Fb26b9a;
272
+ // uniqueOwners[3] = 0xFB46349c0A3F04150E8c731B3A4fC415b0850CE3;
273
+ // uniqueOwners[4] = 0xAcD59e854adf632d2322404198624F757C868C97;
274
+ // uniqueOwners[5] = 0xa13d49fCbf79EAF6A0a58cBDD3361422DB4eAfF1;
275
+ // uniqueOwners[6] = 0x1C51517d8277C9aD6d701Fb5394ceC0C18219eDb;
276
+ // uniqueOwners[7] = 0xbeC26FFa12c90217943D1b2958f60A821aE6E549;
277
+ // uniqueOwners[8] = 0x8Ec174c5d86469D1A74094E10485357eBFe2e08e;
278
+ // uniqueOwners[9] = 0xC5704f77f94087CC644d361A5A57295851d242aB;
279
+ // uniqueOwners[10] = 0x99Fa48ccEa8a38CDE6B437450fF9bBdDAFAA4Fc8;
280
+ // uniqueOwners[11] = 0xb6ECb51e3638Eb7aa0C6289ef058DCa27494Acb2;
281
+ // uniqueOwners[12] = 0x57700212B1cB7b67bD7DF3801DA43CA634513fE0;
282
+ // uniqueOwners[13] = 0x9342E2aC6dd4A907948E91E80D2734ecAC1D70eC;
283
+ // uniqueOwners[14] = 0x96D087aba8552A0e111D7fB4Feb2e7621213E244;
284
+ // uniqueOwners[15] = 0x2830e21792019CE670fBc548AacB004b08c7f71f;
285
+
286
+ // // Collect unique tier IDs
287
+ // uint256[] memory uniqueTierIds = new uint256[](10);
288
+
289
+ // uniqueTierIds[0] = 4;
290
+ // uniqueTierIds[1] = 10;
291
+ // uniqueTierIds[2] = 14;
292
+ // uniqueTierIds[3] = 19;
293
+ // uniqueTierIds[4] = 25;
294
+ // uniqueTierIds[5] = 28;
295
+ // uniqueTierIds[6] = 31;
296
+ // uniqueTierIds[7] = 38;
297
+ // uniqueTierIds[8] = 43;
298
+ // uniqueTierIds[9] = 47;
299
+
300
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
301
+ // MigrationHelper.verifyTierBalances(
302
+ // hookAddress,
303
+ // v4HookAddress,
304
+ // fallbackV4ResolverAddress,
305
+ // uniqueOwners,
306
+ // uniqueTierIds
307
+ // );
308
+ }
309
+ }
@@ -0,0 +1,350 @@
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 MigrationContractBase4 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[26] upc4;
13
+ uint256[2] upc5;
14
+ uint256[1] upc10;
15
+ uint256[1] upc13;
16
+ uint256[2] upc19;
17
+ uint256[1] upc20;
18
+ uint256[2] upc25;
19
+ uint256[1] upc27;
20
+ uint256[1] upc28;
21
+ uint256[1] upc35;
22
+ uint256[1] upc38;
23
+ uint256[1] upc39;
24
+ uint256[1] upc41;
25
+ uint256[3] upc43;
26
+ uint256[1] upc44;
27
+ uint256[1] upc48;
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
+ // Base migration chunk 4/4 - 46 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 (26 items)
69
+ sortedMintedIds.upc4[0] = 4_000_000_069; // Token ID: 4 * 1000000000 + 1
70
+ sortedMintedIds.upc4[1] = 4_000_000_070; // Token ID: 4 * 1000000000 + 2
71
+ sortedMintedIds.upc4[2] = 4_000_000_071; // Token ID: 4 * 1000000000 + 3
72
+ sortedMintedIds.upc4[3] = 4_000_000_072; // Token ID: 4 * 1000000000 + 4
73
+ sortedMintedIds.upc4[4] = 4_000_000_073; // Token ID: 4 * 1000000000 + 5
74
+ sortedMintedIds.upc4[5] = 4_000_000_074; // Token ID: 4 * 1000000000 + 6
75
+ sortedMintedIds.upc4[6] = 4_000_000_075; // Token ID: 4 * 1000000000 + 7
76
+ sortedMintedIds.upc4[7] = 4_000_000_076; // Token ID: 4 * 1000000000 + 8
77
+ sortedMintedIds.upc4[8] = 4_000_000_077; // Token ID: 4 * 1000000000 + 9
78
+ sortedMintedIds.upc4[9] = 4_000_000_078; // Token ID: 4 * 1000000000 + 10
79
+ sortedMintedIds.upc4[10] = 4_000_000_079; // Token ID: 4 * 1000000000 + 11
80
+ sortedMintedIds.upc4[11] = 4_000_000_080; // Token ID: 4 * 1000000000 + 12
81
+ sortedMintedIds.upc4[12] = 4_000_000_081; // Token ID: 4 * 1000000000 + 13
82
+ sortedMintedIds.upc4[13] = 4_000_000_082; // Token ID: 4 * 1000000000 + 14
83
+ sortedMintedIds.upc4[14] = 4_000_000_083; // Token ID: 4 * 1000000000 + 15
84
+ sortedMintedIds.upc4[15] = 4_000_000_084; // Token ID: 4 * 1000000000 + 16
85
+ sortedMintedIds.upc4[16] = 4_000_000_085; // Token ID: 4 * 1000000000 + 17
86
+ sortedMintedIds.upc4[17] = 4_000_000_086; // Token ID: 4 * 1000000000 + 18
87
+ sortedMintedIds.upc4[18] = 4_000_000_087; // Token ID: 4 * 1000000000 + 19
88
+ sortedMintedIds.upc4[19] = 4_000_000_088; // Token ID: 4 * 1000000000 + 20
89
+ sortedMintedIds.upc4[20] = 4_000_000_089; // Token ID: 4 * 1000000000 + 21
90
+ sortedMintedIds.upc4[21] = 4_000_000_090; // Token ID: 4 * 1000000000 + 22
91
+ sortedMintedIds.upc4[22] = 4_000_000_091; // Token ID: 4 * 1000000000 + 23
92
+ sortedMintedIds.upc4[23] = 4_000_000_092; // Token ID: 4 * 1000000000 + 24
93
+ sortedMintedIds.upc4[24] = 4_000_000_093; // Token ID: 4 * 1000000000 + 25
94
+ sortedMintedIds.upc4[25] = 4_000_000_094; // Token ID: 4 * 1000000000 + 26
95
+ // Populate UPC 5 minted tokenIds (2 items)
96
+ sortedMintedIds.upc5[0] = 5_000_000_002; // Token ID: 5 * 1000000000 + 1
97
+ sortedMintedIds.upc5[1] = 5_000_000_003; // Token ID: 5 * 1000000000 + 2
98
+ // Populate UPC 10 minted tokenIds (1 items)
99
+ sortedMintedIds.upc10[0] = 10_000_000_005; // Token ID: 10 * 1000000000 + 1
100
+ // Populate UPC 13 minted tokenIds (1 items)
101
+ sortedMintedIds.upc13[0] = 13_000_000_001; // Token ID: 13 * 1000000000 + 1
102
+ // Populate UPC 19 minted tokenIds (2 items)
103
+ sortedMintedIds.upc19[0] = 19_000_000_006; // Token ID: 19 * 1000000000 + 1
104
+ sortedMintedIds.upc19[1] = 19_000_000_007; // Token ID: 19 * 1000000000 + 2
105
+ // Populate UPC 20 minted tokenIds (1 items)
106
+ sortedMintedIds.upc20[0] = 20_000_000_001; // Token ID: 20 * 1000000000 + 1
107
+ // Populate UPC 25 minted tokenIds (2 items)
108
+ sortedMintedIds.upc25[0] = 25_000_000_007; // Token ID: 25 * 1000000000 + 1
109
+ sortedMintedIds.upc25[1] = 25_000_000_008; // Token ID: 25 * 1000000000 + 2
110
+ // Populate UPC 27 minted tokenIds (1 items)
111
+ sortedMintedIds.upc27[0] = 27_000_000_001; // Token ID: 27 * 1000000000 + 1
112
+ // Populate UPC 28 minted tokenIds (1 items)
113
+ sortedMintedIds.upc28[0] = 28_000_000_006; // Token ID: 28 * 1000000000 + 1
114
+ // Populate UPC 35 minted tokenIds (1 items)
115
+ sortedMintedIds.upc35[0] = 35_000_000_001; // Token ID: 35 * 1000000000 + 1
116
+ // Populate UPC 38 minted tokenIds (1 items)
117
+ sortedMintedIds.upc38[0] = 38_000_000_002; // Token ID: 38 * 1000000000 + 1
118
+ // Populate UPC 39 minted tokenIds (1 items)
119
+ sortedMintedIds.upc39[0] = 39_000_000_001; // Token ID: 39 * 1000000000 + 1
120
+ // Populate UPC 41 minted tokenIds (1 items)
121
+ sortedMintedIds.upc41[0] = 41_000_000_001; // Token ID: 41 * 1000000000 + 1
122
+ // Populate UPC 43 minted tokenIds (3 items)
123
+ sortedMintedIds.upc43[0] = 43_000_000_004; // Token ID: 43 * 1000000000 + 1
124
+ sortedMintedIds.upc43[1] = 43_000_000_005; // Token ID: 43 * 1000000000 + 2
125
+ sortedMintedIds.upc43[2] = 43_000_000_006; // Token ID: 43 * 1000000000 + 3
126
+ // Populate UPC 44 minted tokenIds (1 items)
127
+ sortedMintedIds.upc44[0] = 44_000_000_003; // Token ID: 44 * 1000000000 + 1
128
+ // Populate UPC 48 minted tokenIds (1 items)
129
+ sortedMintedIds.upc48[0] = 48_000_000_001; // Token ID: 48 * 1000000000 + 1
130
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
131
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
132
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
133
+
134
+ // Step 2: Process each Banny body and dress them
135
+
136
+ // Dress Banny 4000000073 (Original)
137
+ {
138
+ uint256[] memory outfitIds = new uint256[](4);
139
+ outfitIds[0] = 10_000_000_005; // V4: 10000000007 -> V5: 10000000005
140
+ outfitIds[1] = 19_000_000_006; // V4: 19000000006 -> V5: 19000000006
141
+ outfitIds[2] = 25_000_000_007; // V4: 25000000006 -> V5: 25000000007
142
+ outfitIds[3] = 43_000_000_004; // V4: 43000000005 -> V5: 43000000004
143
+
144
+ resolver.decorateBannyWith(address(hook), 4_000_000_073, 0, outfitIds);
145
+
146
+ MigrationHelper.verifyV4AssetMatch(
147
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_073
148
+ );
149
+ }
150
+
151
+ // Dress Banny 4000000079 (Original)
152
+ {
153
+ uint256[] memory outfitIds = new uint256[](3);
154
+ outfitIds[0] = 27_000_000_001; // V4: 27000000001 -> V5: 27000000001
155
+ outfitIds[1] = 38_000_000_002; // V4: 38000000002 -> V5: 38000000002
156
+ outfitIds[2] = 48_000_000_001; // V4: 48000000001 -> V5: 48000000001
157
+
158
+ resolver.decorateBannyWith(address(hook), 4_000_000_079, 0, outfitIds);
159
+
160
+ MigrationHelper.verifyV4AssetMatch(
161
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_079
162
+ );
163
+ }
164
+
165
+ // Dress Banny 4000000080 (Original)
166
+ {
167
+ uint256[] memory outfitIds = new uint256[](3);
168
+ outfitIds[0] = 13_000_000_001; // V4: 13000000001 -> V5: 13000000001
169
+ outfitIds[1] = 20_000_000_001; // V4: 20000000001 -> V5: 20000000001
170
+ outfitIds[2] = 44_000_000_003; // V4: 44000000004 -> V5: 44000000003
171
+
172
+ resolver.decorateBannyWith(address(hook), 4_000_000_080, 5_000_000_002, outfitIds);
173
+
174
+ MigrationHelper.verifyV4AssetMatch(
175
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_080
176
+ );
177
+ }
178
+
179
+ // Dress Banny 4000000081 (Original)
180
+ {
181
+ uint256[] memory outfitIds = new uint256[](4);
182
+ outfitIds[0] = 19_000_000_007; // V4: 19000000008 -> V5: 19000000007
183
+ outfitIds[1] = 25_000_000_008; // V4: 25000000007 -> V5: 25000000008
184
+ outfitIds[2] = 35_000_000_001; // V4: 35000000002 -> V5: 35000000001
185
+ outfitIds[3] = 43_000_000_005; // V4: 43000000006 -> V5: 43000000005
186
+
187
+ resolver.decorateBannyWith(address(hook), 4_000_000_081, 0, outfitIds);
188
+
189
+ MigrationHelper.verifyV4AssetMatch(
190
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_081
191
+ );
192
+ }
193
+
194
+ // Dress Banny 4000000082 (Original)
195
+ {
196
+ uint256[] memory outfitIds = new uint256[](1);
197
+ outfitIds[0] = 43_000_000_006; // V4: 43000000007 -> V5: 43000000006
198
+
199
+ resolver.decorateBannyWith(address(hook), 4_000_000_082, 0, outfitIds);
200
+
201
+ MigrationHelper.verifyV4AssetMatch(
202
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_082
203
+ );
204
+ }
205
+
206
+ // Dress Banny 4000000084 (Original)
207
+ {
208
+ uint256[] memory outfitIds = new uint256[](2);
209
+ outfitIds[0] = 39_000_000_001; // V4: 39000000001 -> V5: 39000000001
210
+ outfitIds[1] = 41_000_000_001; // V4: 41000000001 -> V5: 41000000001
211
+
212
+ resolver.decorateBannyWith(address(hook), 4_000_000_084, 5_000_000_003, outfitIds);
213
+
214
+ MigrationHelper.verifyV4AssetMatch(
215
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_084
216
+ );
217
+ }
218
+
219
+ // Dress Banny 4000000085 (Original)
220
+ {
221
+ uint256[] memory outfitIds = new uint256[](1);
222
+ outfitIds[0] = 28_000_000_006; // V4: 28000000008 -> V5: 28000000006
223
+
224
+ resolver.decorateBannyWith(address(hook), 4_000_000_085, 0, outfitIds);
225
+
226
+ MigrationHelper.verifyV4AssetMatch(
227
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_085
228
+ );
229
+ }
230
+
231
+ // Step 3: Transfer all assets to rightful owners using constructor data
232
+ // Generate token IDs in the same order as items appear (matching mint order)
233
+ // Token ID format: UPC * 1000000000 + unitNumber
234
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
235
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
236
+ // since they're not transferred (only used in decorateBannyWith calls).
237
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
238
+
239
+ generatedTokenIds[0] = 4_000_000_069; // Token ID (V4: 4000000069)
240
+ generatedTokenIds[1] = 4_000_000_070; // Token ID (V4: 4000000070)
241
+ generatedTokenIds[2] = 4_000_000_071; // Token ID (V4: 4000000071)
242
+ generatedTokenIds[3] = 4_000_000_072; // Token ID (V4: 4000000072)
243
+ generatedTokenIds[4] = 4_000_000_073; // Token ID (V4: 4000000073)
244
+ generatedTokenIds[5] = 4_000_000_074; // Token ID (V4: 4000000074)
245
+ generatedTokenIds[6] = 4_000_000_075; // Token ID (V4: 4000000075)
246
+ generatedTokenIds[7] = 4_000_000_076; // Token ID (V4: 4000000076)
247
+ generatedTokenIds[8] = 4_000_000_077; // Token ID (V4: 4000000077)
248
+ generatedTokenIds[9] = 4_000_000_078; // Token ID (V4: 4000000078)
249
+ generatedTokenIds[10] = 4_000_000_079; // Token ID (V4: 4000000079)
250
+ generatedTokenIds[11] = 4_000_000_080; // Token ID (V4: 4000000080)
251
+ generatedTokenIds[12] = 4_000_000_081; // Token ID (V4: 4000000081)
252
+ generatedTokenIds[13] = 4_000_000_082; // Token ID (V4: 4000000082)
253
+ generatedTokenIds[14] = 4_000_000_083; // Token ID (V4: 4000000083)
254
+ generatedTokenIds[15] = 4_000_000_084; // Token ID (V4: 4000000084)
255
+ generatedTokenIds[16] = 4_000_000_085; // Token ID (V4: 4000000085)
256
+ generatedTokenIds[17] = 4_000_000_086; // Token ID (V4: 4000000086)
257
+ generatedTokenIds[18] = 4_000_000_087; // Token ID (V4: 4000000087)
258
+ generatedTokenIds[19] = 4_000_000_088; // Token ID (V4: 4000000088)
259
+ generatedTokenIds[20] = 4_000_000_089; // Token ID (V4: 4000000089)
260
+ generatedTokenIds[21] = 4_000_000_090; // Token ID (V4: 4000000090)
261
+ generatedTokenIds[22] = 4_000_000_091; // Token ID (V4: 4000000091)
262
+ generatedTokenIds[23] = 4_000_000_092; // Token ID (V4: 4000000092)
263
+ generatedTokenIds[24] = 4_000_000_093; // Token ID (V4: 4000000093)
264
+ generatedTokenIds[25] = 4_000_000_094; // Token ID (V4: 4000000094)
265
+
266
+ uint256 successfulTransfers = 0;
267
+ uint256 skippedResolverOwned = 0;
268
+
269
+ for (uint256 i = 0; i < transferOwners.length; i++) {
270
+ uint256 tokenId = generatedTokenIds[i];
271
+ // Verify V4 ownership before transferring V5
272
+ address v4Owner = v4Hook.ownerOf(tokenId);
273
+ require(
274
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
275
+ "V4/V5 ownership mismatch for token"
276
+ );
277
+
278
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
279
+ // resolver)
280
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
281
+ // Token is held by resolver, skip transfer
282
+ skippedResolverOwned++;
283
+ continue;
284
+ }
285
+
286
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
287
+ successfulTransfers++;
288
+ }
289
+
290
+ // Verify all expected items were processed (transferred or skipped as expected)
291
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
292
+
293
+ // Final verification: Ensure this contract no longer owns any tokens
294
+ // This ensures all transfers completed successfully and no tokens were left behind
295
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
296
+
297
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
298
+
299
+ // // Collect unique owners
300
+ // address[] memory uniqueOwners = new address[](18);
301
+
302
+ // uniqueOwners[0] = 0x2830e21792019CE670fBc548AacB004b08c7f71f;
303
+ // uniqueOwners[1] = 0x46f3cC6a1c00A5cD8864d2B92f128196CAE07D15;
304
+ // uniqueOwners[2] = 0x8e2B25dF2484000B9127b2D2F8E92079dcEE3E48;
305
+ // uniqueOwners[3] = 0x817738DC393d682Ca5fBb268707b99F2aAe96baE;
306
+ // uniqueOwners[4] = 0x224aBa5D489675a7bD3CE07786FAda466b46FA0F;
307
+ // uniqueOwners[5] = 0x29f4aE3c24681940E537f72830b4Fe4076bDF9fe;
308
+ // uniqueOwners[6] = 0x8b80755C441d355405CA7571443Bb9247B77Ec16;
309
+ // uniqueOwners[7] = 0x3c2736f995535b5a755F3CE2BEb754362820671e;
310
+ // uniqueOwners[8] = 0x6877be9E00d0bc5886c28419901E8cC98C1c2739;
311
+ // uniqueOwners[9] = 0x8DFBdEEC8c5d4970BB5F481C6ec7f73fa1C65be5;
312
+ // uniqueOwners[10] = 0xaECD6D9172d602b93dBA3981991268b44af8096e;
313
+ // uniqueOwners[11] = 0xFd37f4625CA5816157D55a5b3F7Dd8DD5F8a0C2F;
314
+ // uniqueOwners[12] = 0x39a7B6fa1597BB6657Fe84e64E3B836c37d6F75d;
315
+ // uniqueOwners[13] = 0x57a482EA32c7F75A9C0734206f5BD4f9BCb38e12;
316
+ // uniqueOwners[14] = 0xDdB4938755C243a4f60a2f2f8f95dF4F894c58Cc;
317
+ // uniqueOwners[15] = 0x34aA3F359A9D614239015126635CE7732c18fDF3;
318
+ // uniqueOwners[16] = 0xF6cC71878e23c05406B35946CD9d378E0f2f4f2F;
319
+ // uniqueOwners[17] = 0xd2e44E40B5FB960A8A74dD7B9D6b7f14B805b50d;
320
+
321
+ // // Collect unique tier IDs
322
+ // uint256[] memory uniqueTierIds = new uint256[](16);
323
+
324
+ // uniqueTierIds[0] = 4;
325
+ // uniqueTierIds[1] = 5;
326
+ // uniqueTierIds[2] = 10;
327
+ // uniqueTierIds[3] = 13;
328
+ // uniqueTierIds[4] = 19;
329
+ // uniqueTierIds[5] = 20;
330
+ // uniqueTierIds[6] = 25;
331
+ // uniqueTierIds[7] = 27;
332
+ // uniqueTierIds[8] = 28;
333
+ // uniqueTierIds[9] = 35;
334
+ // uniqueTierIds[10] = 38;
335
+ // uniqueTierIds[11] = 39;
336
+ // uniqueTierIds[12] = 41;
337
+ // uniqueTierIds[13] = 43;
338
+ // uniqueTierIds[14] = 44;
339
+ // uniqueTierIds[15] = 48;
340
+
341
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
342
+ // MigrationHelper.verifyTierBalances(
343
+ // hookAddress,
344
+ // v4HookAddress,
345
+ // fallbackV4ResolverAddress,
346
+ // uniqueOwners,
347
+ // uniqueTierIds
348
+ // );
349
+ }
350
+ }