@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,354 @@
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 MigrationContractEthereum5 {
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[1] upc10;
16
+ uint256[1] upc13;
17
+ uint256[2] upc15;
18
+ uint256[1] upc18;
19
+ uint256[3] upc19;
20
+ uint256[1] upc20;
21
+ uint256[1] upc23;
22
+ uint256[1] upc26;
23
+ uint256[1] upc28;
24
+ uint256[1] upc31;
25
+ uint256[1] upc35;
26
+ uint256[1] upc38;
27
+ uint256[1] upc39;
28
+ uint256[1] upc40;
29
+ uint256[2] upc44;
30
+ uint256[1] upc47;
31
+ uint256[1] upc48;
32
+ uint256[1] upc49;
33
+ }
34
+
35
+ address[] private transferOwners;
36
+
37
+ constructor(address[] memory _transferOwners) {
38
+ transferOwners = _transferOwners;
39
+ }
40
+
41
+ function executeMigration(
42
+ address hookAddress,
43
+ address resolverAddress,
44
+ address v4HookAddress,
45
+ address v4ResolverAddress,
46
+ address fallbackV4ResolverAddress
47
+ )
48
+ external
49
+ {
50
+ // Validate addresses
51
+ require(hookAddress != address(0), "Hook address not set");
52
+ require(resolverAddress != address(0), "Resolver address not set");
53
+ require(v4HookAddress != address(0), "V4 Hook address not set");
54
+ require(v4ResolverAddress != address(0), "V4 Resolver address not set");
55
+ require(fallbackV4ResolverAddress != address(0), "V4 fallback resolver address not set");
56
+
57
+ JB721TiersHook hook = JB721TiersHook(hookAddress);
58
+ Banny721TokenUriResolver resolver = Banny721TokenUriResolver(resolverAddress);
59
+ IERC721 v4Hook = IERC721(v4HookAddress);
60
+ Banny721TokenUriResolver v4Resolver = Banny721TokenUriResolver(v4ResolverAddress);
61
+ Banny721TokenUriResolver fallbackV4Resolver = Banny721TokenUriResolver(fallbackV4ResolverAddress);
62
+
63
+ // Ethereum migration chunk 5/6 - 44 items
64
+
65
+ // Step 1: Assets are already minted to this contract by the deployer
66
+
67
+ // Assets are already minted to this contract by the deployer
68
+
69
+ // Create and populate the struct
70
+ // Token IDs follow formula: upc * 1000000000 + unitNumber (unitNumber starts at 1 per UPC)
71
+ MintedIds memory sortedMintedIds;
72
+
73
+ // Populate UPC 4 minted tokenIds (20 items)
74
+ sortedMintedIds.upc4[0] = 4_000_000_048; // Token ID: 4 * 1000000000 + 1
75
+ sortedMintedIds.upc4[1] = 4_000_000_049; // Token ID: 4 * 1000000000 + 2
76
+ sortedMintedIds.upc4[2] = 4_000_000_050; // Token ID: 4 * 1000000000 + 3
77
+ sortedMintedIds.upc4[3] = 4_000_000_051; // Token ID: 4 * 1000000000 + 4
78
+ sortedMintedIds.upc4[4] = 4_000_000_052; // Token ID: 4 * 1000000000 + 5
79
+ sortedMintedIds.upc4[5] = 4_000_000_053; // Token ID: 4 * 1000000000 + 6
80
+ sortedMintedIds.upc4[6] = 4_000_000_054; // Token ID: 4 * 1000000000 + 7
81
+ sortedMintedIds.upc4[7] = 4_000_000_055; // Token ID: 4 * 1000000000 + 8
82
+ sortedMintedIds.upc4[8] = 4_000_000_056; // Token ID: 4 * 1000000000 + 9
83
+ sortedMintedIds.upc4[9] = 4_000_000_057; // Token ID: 4 * 1000000000 + 10
84
+ sortedMintedIds.upc4[10] = 4_000_000_058; // Token ID: 4 * 1000000000 + 11
85
+ sortedMintedIds.upc4[11] = 4_000_000_059; // Token ID: 4 * 1000000000 + 12
86
+ sortedMintedIds.upc4[12] = 4_000_000_060; // Token ID: 4 * 1000000000 + 13
87
+ sortedMintedIds.upc4[13] = 4_000_000_061; // Token ID: 4 * 1000000000 + 14
88
+ sortedMintedIds.upc4[14] = 4_000_000_062; // Token ID: 4 * 1000000000 + 15
89
+ sortedMintedIds.upc4[15] = 4_000_000_063; // Token ID: 4 * 1000000000 + 16
90
+ sortedMintedIds.upc4[16] = 4_000_000_064; // Token ID: 4 * 1000000000 + 17
91
+ sortedMintedIds.upc4[17] = 4_000_000_065; // Token ID: 4 * 1000000000 + 18
92
+ sortedMintedIds.upc4[18] = 4_000_000_066; // Token ID: 4 * 1000000000 + 19
93
+ sortedMintedIds.upc4[19] = 4_000_000_067; // Token ID: 4 * 1000000000 + 20
94
+ // Populate UPC 5 minted tokenIds (1 items)
95
+ sortedMintedIds.upc5[0] = 5_000_000_006; // Token ID: 5 * 1000000000 + 1
96
+ // Populate UPC 6 minted tokenIds (1 items)
97
+ sortedMintedIds.upc6[0] = 6_000_000_007; // Token ID: 6 * 1000000000 + 1
98
+ // Populate UPC 10 minted tokenIds (1 items)
99
+ sortedMintedIds.upc10[0] = 10_000_000_006; // Token ID: 10 * 1000000000 + 1
100
+ // Populate UPC 13 minted tokenIds (1 items)
101
+ sortedMintedIds.upc13[0] = 13_000_000_002; // Token ID: 13 * 1000000000 + 1
102
+ // Populate UPC 15 minted tokenIds (2 items)
103
+ sortedMintedIds.upc15[0] = 15_000_000_003; // Token ID: 15 * 1000000000 + 1
104
+ sortedMintedIds.upc15[1] = 15_000_000_004; // Token ID: 15 * 1000000000 + 2
105
+ // Populate UPC 18 minted tokenIds (1 items)
106
+ sortedMintedIds.upc18[0] = 18_000_000_003; // Token ID: 18 * 1000000000 + 1
107
+ // Populate UPC 19 minted tokenIds (3 items)
108
+ sortedMintedIds.upc19[0] = 19_000_000_010; // Token ID: 19 * 1000000000 + 1
109
+ sortedMintedIds.upc19[1] = 19_000_000_011; // Token ID: 19 * 1000000000 + 2
110
+ sortedMintedIds.upc19[2] = 19_000_000_012; // Token ID: 19 * 1000000000 + 3
111
+ // Populate UPC 20 minted tokenIds (1 items)
112
+ sortedMintedIds.upc20[0] = 20_000_000_002; // Token ID: 20 * 1000000000 + 1
113
+ // Populate UPC 23 minted tokenIds (1 items)
114
+ sortedMintedIds.upc23[0] = 23_000_000_006; // Token ID: 23 * 1000000000 + 1
115
+ // Populate UPC 26 minted tokenIds (1 items)
116
+ sortedMintedIds.upc26[0] = 26_000_000_005; // Token ID: 26 * 1000000000 + 1
117
+ // Populate UPC 28 minted tokenIds (1 items)
118
+ sortedMintedIds.upc28[0] = 28_000_000_002; // Token ID: 28 * 1000000000 + 1
119
+ // Populate UPC 31 minted tokenIds (1 items)
120
+ sortedMintedIds.upc31[0] = 31_000_000_006; // Token ID: 31 * 1000000000 + 1
121
+ // Populate UPC 35 minted tokenIds (1 items)
122
+ sortedMintedIds.upc35[0] = 35_000_000_006; // Token ID: 35 * 1000000000 + 1
123
+ // Populate UPC 38 minted tokenIds (1 items)
124
+ sortedMintedIds.upc38[0] = 38_000_000_002; // Token ID: 38 * 1000000000 + 1
125
+ // Populate UPC 39 minted tokenIds (1 items)
126
+ sortedMintedIds.upc39[0] = 39_000_000_003; // Token ID: 39 * 1000000000 + 1
127
+ // Populate UPC 40 minted tokenIds (1 items)
128
+ sortedMintedIds.upc40[0] = 40_000_000_001; // Token ID: 40 * 1000000000 + 1
129
+ // Populate UPC 44 minted tokenIds (2 items)
130
+ sortedMintedIds.upc44[0] = 44_000_000_004; // Token ID: 44 * 1000000000 + 1
131
+ sortedMintedIds.upc44[1] = 44_000_000_005; // Token ID: 44 * 1000000000 + 2
132
+ // Populate UPC 47 minted tokenIds (1 items)
133
+ sortedMintedIds.upc47[0] = 47_000_000_002; // Token ID: 47 * 1000000000 + 1
134
+ // Populate UPC 48 minted tokenIds (1 items)
135
+ sortedMintedIds.upc48[0] = 48_000_000_005; // Token ID: 48 * 1000000000 + 1
136
+ // Populate UPC 49 minted tokenIds (1 items)
137
+ sortedMintedIds.upc49[0] = 49_000_000_003; // Token ID: 49 * 1000000000 + 1
138
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
139
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
140
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
141
+
142
+ // Step 2: Process each Banny body and dress them
143
+
144
+ // Dress Banny 4000000048 (Original)
145
+ {
146
+ uint256[] memory outfitIds = new uint256[](4);
147
+ outfitIds[0] = 19_000_000_010; // V4: 19000000013 -> V5: 19000000010
148
+ outfitIds[1] = 31_000_000_006; // V4: 31000000010 -> V5: 31000000006
149
+ outfitIds[2] = 35_000_000_006; // V4: 35000000007 -> V5: 35000000006
150
+ outfitIds[3] = 47_000_000_002; // V4: 47000000005 -> V5: 47000000002
151
+
152
+ resolver.decorateBannyWith(address(hook), 4_000_000_048, 0, outfitIds);
153
+
154
+ MigrationHelper.verifyV4AssetMatch(
155
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_048
156
+ );
157
+ }
158
+
159
+ // Dress Banny 4000000052 (Original)
160
+ {
161
+ uint256[] memory outfitIds = new uint256[](3);
162
+ outfitIds[0] = 10_000_000_006; // V4: 10000000012 -> V5: 10000000006
163
+ outfitIds[1] = 18_000_000_003; // V4: 18000000003 -> V5: 18000000003
164
+ outfitIds[2] = 20_000_000_002; // V4: 20000000008 -> V5: 20000000002
165
+
166
+ resolver.decorateBannyWith(address(hook), 4_000_000_052, 5_000_000_006, outfitIds);
167
+
168
+ MigrationHelper.verifyV4AssetMatch(
169
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_052
170
+ );
171
+ }
172
+
173
+ // Dress Banny 4000000054 (Original)
174
+ {
175
+ uint256[] memory outfitIds = new uint256[](3);
176
+ outfitIds[0] = 15_000_000_003; // V4: 15000000002 -> V5: 15000000003
177
+ outfitIds[1] = 26_000_000_005; // V4: 26000000005 -> V5: 26000000005
178
+ outfitIds[2] = 44_000_000_004; // V4: 44000000008 -> V5: 44000000004
179
+
180
+ resolver.decorateBannyWith(address(hook), 4_000_000_054, 6_000_000_007, outfitIds);
181
+
182
+ MigrationHelper.verifyV4AssetMatch(
183
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_054
184
+ );
185
+ }
186
+
187
+ // Dress Banny 4000000055 (Original)
188
+ {
189
+ uint256[] memory outfitIds = new uint256[](3);
190
+ outfitIds[0] = 19_000_000_011; // V4: 19000000016 -> V5: 19000000011
191
+ outfitIds[1] = 39_000_000_003; // V4: 39000000002 -> V5: 39000000003
192
+ outfitIds[2] = 44_000_000_005; // V4: 44000000009 -> V5: 44000000005
193
+
194
+ resolver.decorateBannyWith(address(hook), 4_000_000_055, 0, outfitIds);
195
+
196
+ MigrationHelper.verifyV4AssetMatch(
197
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_055
198
+ );
199
+ }
200
+
201
+ // Dress Banny 4000000056 (Original)
202
+ {
203
+ uint256[] memory outfitIds = new uint256[](4);
204
+ outfitIds[0] = 15_000_000_004; // V4: 15000000003 -> V5: 15000000004
205
+ outfitIds[1] = 23_000_000_006; // V4: 23000000007 -> V5: 23000000006
206
+ outfitIds[2] = 40_000_000_001; // V4: 40000000001 -> V5: 40000000001
207
+ outfitIds[3] = 49_000_000_003; // V4: 49000000004 -> V5: 49000000003
208
+
209
+ resolver.decorateBannyWith(address(hook), 4_000_000_056, 0, outfitIds);
210
+
211
+ MigrationHelper.verifyV4AssetMatch(
212
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_056
213
+ );
214
+ }
215
+
216
+ // Dress Banny 4000000057 (Original)
217
+ {
218
+ uint256[] memory outfitIds = new uint256[](3);
219
+ outfitIds[0] = 19_000_000_012; // V4: 19000000017 -> V5: 19000000012
220
+ outfitIds[1] = 28_000_000_002; // V4: 28000000008 -> V5: 28000000002
221
+ outfitIds[2] = 38_000_000_002; // V4: 38000000003 -> V5: 38000000002
222
+
223
+ resolver.decorateBannyWith(address(hook), 4_000_000_057, 0, outfitIds);
224
+
225
+ MigrationHelper.verifyV4AssetMatch(
226
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_057
227
+ );
228
+ }
229
+
230
+ // Dress Banny 4000000060 (Original)
231
+ {
232
+ uint256[] memory outfitIds = new uint256[](2);
233
+ outfitIds[0] = 13_000_000_002; // V4: 13000000003 -> V5: 13000000002
234
+ outfitIds[1] = 48_000_000_005; // V4: 48000000006 -> V5: 48000000005
235
+
236
+ resolver.decorateBannyWith(address(hook), 4_000_000_060, 0, outfitIds);
237
+
238
+ MigrationHelper.verifyV4AssetMatch(
239
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_060
240
+ );
241
+ }
242
+
243
+ // Step 3: Transfer all assets to rightful owners using constructor data
244
+ // Generate token IDs in the same order as items appear (matching mint order)
245
+ // Token ID format: UPC * 1000000000 + unitNumber
246
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
247
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
248
+ // since they're not transferred (only used in decorateBannyWith calls).
249
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
250
+
251
+ generatedTokenIds[0] = 4_000_000_048; // Token ID (V4: 4000000048)
252
+ generatedTokenIds[1] = 4_000_000_049; // Token ID (V4: 4000000049)
253
+ generatedTokenIds[2] = 4_000_000_050; // Token ID (V4: 4000000050)
254
+ generatedTokenIds[3] = 4_000_000_051; // Token ID (V4: 4000000051)
255
+ generatedTokenIds[4] = 4_000_000_052; // Token ID (V4: 4000000052)
256
+ generatedTokenIds[5] = 4_000_000_053; // Token ID (V4: 4000000053)
257
+ generatedTokenIds[6] = 4_000_000_054; // Token ID (V4: 4000000054)
258
+ generatedTokenIds[7] = 4_000_000_055; // Token ID (V4: 4000000055)
259
+ generatedTokenIds[8] = 4_000_000_056; // Token ID (V4: 4000000056)
260
+ generatedTokenIds[9] = 4_000_000_057; // Token ID (V4: 4000000057)
261
+ generatedTokenIds[10] = 4_000_000_058; // Token ID (V4: 4000000058)
262
+ generatedTokenIds[11] = 4_000_000_059; // Token ID (V4: 4000000059)
263
+ generatedTokenIds[12] = 4_000_000_060; // Token ID (V4: 4000000060)
264
+ generatedTokenIds[13] = 4_000_000_061; // Token ID (V4: 4000000061)
265
+ generatedTokenIds[14] = 4_000_000_062; // Token ID (V4: 4000000062)
266
+ generatedTokenIds[15] = 4_000_000_063; // Token ID (V4: 4000000063)
267
+ generatedTokenIds[16] = 4_000_000_064; // Token ID (V4: 4000000064)
268
+ generatedTokenIds[17] = 4_000_000_065; // Token ID (V4: 4000000065)
269
+ generatedTokenIds[18] = 4_000_000_066; // Token ID (V4: 4000000066)
270
+ generatedTokenIds[19] = 4_000_000_067; // Token ID (V4: 4000000067)
271
+
272
+ uint256 successfulTransfers = 0;
273
+ uint256 skippedResolverOwned = 0;
274
+
275
+ for (uint256 i = 0; i < transferOwners.length; i++) {
276
+ uint256 tokenId = generatedTokenIds[i];
277
+ // Verify V4 ownership before transferring V5
278
+ address v4Owner = v4Hook.ownerOf(tokenId);
279
+ require(
280
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
281
+ "V4/V5 ownership mismatch for token"
282
+ );
283
+
284
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
285
+ // resolver)
286
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
287
+ // Token is held by resolver, skip transfer
288
+ skippedResolverOwned++;
289
+ continue;
290
+ }
291
+
292
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
293
+ successfulTransfers++;
294
+ }
295
+
296
+ // Verify all expected items were processed (transferred or skipped as expected)
297
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
298
+
299
+ // Final verification: Ensure this contract no longer owns any tokens
300
+ // This ensures all transfers completed successfully and no tokens were left behind
301
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
302
+
303
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
304
+
305
+ // // Collect unique owners
306
+ // address[] memory uniqueOwners = new address[](11);
307
+
308
+ // uniqueOwners[0] = 0xf32dd1Bd55bD14d929218499a2E7D106F72f79c7;
309
+ // uniqueOwners[1] = 0xaECD6D9172d602b93dBA3981991268b44af8096e;
310
+ // uniqueOwners[2] = 0xe21A272c4D22eD40678a0168b4acd006bca8A482;
311
+ // uniqueOwners[3] = 0x80581C6e88Ce00095F85cdf24bB760f16d6eC0D6;
312
+ // uniqueOwners[4] = 0x4A8a9147ab0DF5A8949f964bDBA22dc4583280E2;
313
+ // uniqueOwners[5] = 0x30670D81E487c80b9EDc54370e6EaF943B6EAB39;
314
+ // uniqueOwners[6] = 0x60535A6605958fFf6cEC5B1e92892601EFb3473b;
315
+ // uniqueOwners[7] = 0x34724D71cE674FcD4d06e60Dd1BaA88c14D36b75;
316
+ // uniqueOwners[8] = 0xA99c384f43e72B65BB51fE33b85CE12A32C09526;
317
+ // uniqueOwners[9] = 0x898e24EBC9dAf5a9930f10def8B6a373F859C101;
318
+ // uniqueOwners[10] = 0x823b92d6a4b2AED4b15675c7917c9f922ea8ADAD;
319
+
320
+ // // Collect unique tier IDs
321
+ // uint256[] memory uniqueTierIds = new uint256[](21);
322
+
323
+ // uniqueTierIds[0] = 4;
324
+ // uniqueTierIds[1] = 5;
325
+ // uniqueTierIds[2] = 6;
326
+ // uniqueTierIds[3] = 10;
327
+ // uniqueTierIds[4] = 13;
328
+ // uniqueTierIds[5] = 15;
329
+ // uniqueTierIds[6] = 18;
330
+ // uniqueTierIds[7] = 19;
331
+ // uniqueTierIds[8] = 20;
332
+ // uniqueTierIds[9] = 23;
333
+ // uniqueTierIds[10] = 26;
334
+ // uniqueTierIds[11] = 28;
335
+ // uniqueTierIds[12] = 31;
336
+ // uniqueTierIds[13] = 35;
337
+ // uniqueTierIds[14] = 38;
338
+ // uniqueTierIds[15] = 39;
339
+ // uniqueTierIds[16] = 40;
340
+ // uniqueTierIds[17] = 44;
341
+ // uniqueTierIds[18] = 47;
342
+ // uniqueTierIds[19] = 48;
343
+ // uniqueTierIds[20] = 49;
344
+
345
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
346
+ // MigrationHelper.verifyTierBalances(
347
+ // hookAddress,
348
+ // v4HookAddress,
349
+ // fallbackV4ResolverAddress,
350
+ // uniqueOwners,
351
+ // uniqueTierIds
352
+ // );
353
+ }
354
+ }
@@ -0,0 +1,270 @@
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 MigrationContractEthereum6 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[18] upc4;
13
+ uint256[1] upc6;
14
+ uint256[1] upc15;
15
+ uint256[3] upc19;
16
+ uint256[2] upc25;
17
+ uint256[1] upc29;
18
+ uint256[2] upc31;
19
+ uint256[1] upc38;
20
+ uint256[2] upc43;
21
+ }
22
+
23
+ address[] private transferOwners;
24
+
25
+ constructor(address[] memory _transferOwners) {
26
+ transferOwners = _transferOwners;
27
+ }
28
+
29
+ function executeMigration(
30
+ address hookAddress,
31
+ address resolverAddress,
32
+ address v4HookAddress,
33
+ address v4ResolverAddress,
34
+ address fallbackV4ResolverAddress
35
+ )
36
+ external
37
+ {
38
+ // Validate addresses
39
+ require(hookAddress != address(0), "Hook address not set");
40
+ require(resolverAddress != address(0), "Resolver address not set");
41
+ require(v4HookAddress != address(0), "V4 Hook address not set");
42
+ require(v4ResolverAddress != address(0), "V4 Resolver address not set");
43
+ require(fallbackV4ResolverAddress != address(0), "V4 fallback resolver address not set");
44
+
45
+ JB721TiersHook hook = JB721TiersHook(hookAddress);
46
+ Banny721TokenUriResolver resolver = Banny721TokenUriResolver(resolverAddress);
47
+ IERC721 v4Hook = IERC721(v4HookAddress);
48
+ Banny721TokenUriResolver v4Resolver = Banny721TokenUriResolver(v4ResolverAddress);
49
+ Banny721TokenUriResolver fallbackV4Resolver = Banny721TokenUriResolver(fallbackV4ResolverAddress);
50
+
51
+ // Ethereum migration chunk 6/6 - 31 items
52
+
53
+ // Step 1: Assets are already minted to this contract by the deployer
54
+
55
+ // Assets are already minted to this contract by the deployer
56
+
57
+ // Create and populate the struct
58
+ // Token IDs follow formula: upc * 1000000000 + unitNumber (unitNumber starts at 1 per UPC)
59
+ MintedIds memory sortedMintedIds;
60
+
61
+ // Populate UPC 4 minted tokenIds (18 items)
62
+ sortedMintedIds.upc4[0] = 4_000_000_068; // Token ID: 4 * 1000000000 + 1
63
+ sortedMintedIds.upc4[1] = 4_000_000_069; // Token ID: 4 * 1000000000 + 2
64
+ sortedMintedIds.upc4[2] = 4_000_000_070; // Token ID: 4 * 1000000000 + 3
65
+ sortedMintedIds.upc4[3] = 4_000_000_071; // Token ID: 4 * 1000000000 + 4
66
+ sortedMintedIds.upc4[4] = 4_000_000_072; // Token ID: 4 * 1000000000 + 5
67
+ sortedMintedIds.upc4[5] = 4_000_000_073; // Token ID: 4 * 1000000000 + 6
68
+ sortedMintedIds.upc4[6] = 4_000_000_074; // Token ID: 4 * 1000000000 + 7
69
+ sortedMintedIds.upc4[7] = 4_000_000_075; // Token ID: 4 * 1000000000 + 8
70
+ sortedMintedIds.upc4[8] = 4_000_000_076; // Token ID: 4 * 1000000000 + 9
71
+ sortedMintedIds.upc4[9] = 4_000_000_077; // Token ID: 4 * 1000000000 + 10
72
+ sortedMintedIds.upc4[10] = 4_000_000_078; // Token ID: 4 * 1000000000 + 11
73
+ sortedMintedIds.upc4[11] = 4_000_000_079; // Token ID: 4 * 1000000000 + 12
74
+ sortedMintedIds.upc4[12] = 4_000_000_080; // Token ID: 4 * 1000000000 + 13
75
+ sortedMintedIds.upc4[13] = 4_000_000_081; // Token ID: 4 * 1000000000 + 14
76
+ sortedMintedIds.upc4[14] = 4_000_000_082; // Token ID: 4 * 1000000000 + 15
77
+ sortedMintedIds.upc4[15] = 4_000_000_083; // Token ID: 4 * 1000000000 + 16
78
+ sortedMintedIds.upc4[16] = 4_000_000_084; // Token ID: 4 * 1000000000 + 17
79
+ sortedMintedIds.upc4[17] = 4_000_000_085; // Token ID: 4 * 1000000000 + 18
80
+ // Populate UPC 6 minted tokenIds (1 items)
81
+ sortedMintedIds.upc6[0] = 6_000_000_008; // Token ID: 6 * 1000000000 + 1
82
+ // Populate UPC 15 minted tokenIds (1 items)
83
+ sortedMintedIds.upc15[0] = 15_000_000_005; // Token ID: 15 * 1000000000 + 1
84
+ // Populate UPC 19 minted tokenIds (3 items)
85
+ sortedMintedIds.upc19[0] = 19_000_000_013; // Token ID: 19 * 1000000000 + 1
86
+ sortedMintedIds.upc19[1] = 19_000_000_014; // Token ID: 19 * 1000000000 + 2
87
+ sortedMintedIds.upc19[2] = 19_000_000_015; // Token ID: 19 * 1000000000 + 3
88
+ // Populate UPC 25 minted tokenIds (2 items)
89
+ sortedMintedIds.upc25[0] = 25_000_000_008; // Token ID: 25 * 1000000000 + 1
90
+ sortedMintedIds.upc25[1] = 25_000_000_009; // Token ID: 25 * 1000000000 + 2
91
+ // Populate UPC 29 minted tokenIds (1 items)
92
+ sortedMintedIds.upc29[0] = 29_000_000_002; // Token ID: 29 * 1000000000 + 1
93
+ // Populate UPC 31 minted tokenIds (2 items)
94
+ sortedMintedIds.upc31[0] = 31_000_000_007; // Token ID: 31 * 1000000000 + 1
95
+ sortedMintedIds.upc31[1] = 31_000_000_008; // Token ID: 31 * 1000000000 + 2
96
+ // Populate UPC 38 minted tokenIds (1 items)
97
+ sortedMintedIds.upc38[0] = 38_000_000_003; // Token ID: 38 * 1000000000 + 1
98
+ // Populate UPC 43 minted tokenIds (2 items)
99
+ sortedMintedIds.upc43[0] = 43_000_000_006; // Token ID: 43 * 1000000000 + 1
100
+ sortedMintedIds.upc43[1] = 43_000_000_007; // Token ID: 43 * 1000000000 + 2
101
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
102
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
103
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
104
+
105
+ // Step 2: Process each Banny body and dress them
106
+
107
+ // Dress Banny 4000000076 (Original)
108
+ {
109
+ uint256[] memory outfitIds = new uint256[](3);
110
+ outfitIds[0] = 19_000_000_013; // V4: 19000000018 -> V5: 19000000013
111
+ outfitIds[1] = 29_000_000_002; // V4: 29000000002 -> V5: 29000000002
112
+ outfitIds[2] = 38_000_000_003; // V4: 38000000001 -> V5: 38000000003
113
+
114
+ resolver.decorateBannyWith(address(hook), 4_000_000_076, 0, outfitIds);
115
+
116
+ MigrationHelper.verifyV4AssetMatch(
117
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_076
118
+ );
119
+ }
120
+
121
+ // Dress Banny 4000000078 (Original)
122
+ {
123
+ uint256[] memory outfitIds = new uint256[](2);
124
+ outfitIds[0] = 31_000_000_007; // V4: 31000000011 -> V5: 31000000007
125
+ outfitIds[1] = 43_000_000_006; // V4: 43000000017 -> V5: 43000000006
126
+
127
+ resolver.decorateBannyWith(address(hook), 4_000_000_078, 0, outfitIds);
128
+
129
+ MigrationHelper.verifyV4AssetMatch(
130
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_078
131
+ );
132
+ }
133
+
134
+ // Dress Banny 4000000079 (Original)
135
+ {
136
+ uint256[] memory outfitIds = new uint256[](3);
137
+ outfitIds[0] = 19_000_000_014; // V4: 19000000020 -> V5: 19000000014
138
+ outfitIds[1] = 25_000_000_008; // V4: 25000000010 -> V5: 25000000008
139
+ outfitIds[2] = 43_000_000_007; // V4: 43000000018 -> V5: 43000000007
140
+
141
+ resolver.decorateBannyWith(address(hook), 4_000_000_079, 0, outfitIds);
142
+
143
+ MigrationHelper.verifyV4AssetMatch(
144
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_079
145
+ );
146
+ }
147
+
148
+ // Dress Banny 4000000080 (Original)
149
+ {
150
+ uint256[] memory outfitIds = new uint256[](3);
151
+ outfitIds[0] = 15_000_000_005; // V4: 15000000005 -> V5: 15000000005
152
+ outfitIds[1] = 19_000_000_015; // V4: 19000000021 -> V5: 19000000015
153
+ outfitIds[2] = 25_000_000_009; // V4: 25000000011 -> V5: 25000000009
154
+
155
+ resolver.decorateBannyWith(address(hook), 4_000_000_080, 6_000_000_008, outfitIds);
156
+
157
+ MigrationHelper.verifyV4AssetMatch(
158
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_080
159
+ );
160
+ }
161
+
162
+ // Dress Banny 4000000085 (Original)
163
+ {
164
+ uint256[] memory outfitIds = new uint256[](1);
165
+ outfitIds[0] = 31_000_000_008; // V4: 31000000013 -> V5: 31000000008
166
+
167
+ resolver.decorateBannyWith(address(hook), 4_000_000_085, 0, outfitIds);
168
+
169
+ MigrationHelper.verifyV4AssetMatch(
170
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_085
171
+ );
172
+ }
173
+
174
+ // Step 3: Transfer all assets to rightful owners using constructor data
175
+ // Generate token IDs in the same order as items appear (matching mint order)
176
+ // Token ID format: UPC * 1000000000 + unitNumber
177
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
178
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
179
+ // since they're not transferred (only used in decorateBannyWith calls).
180
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
181
+
182
+ generatedTokenIds[0] = 4_000_000_068; // Token ID (V4: 4000000068)
183
+ generatedTokenIds[1] = 4_000_000_069; // Token ID (V4: 4000000069)
184
+ generatedTokenIds[2] = 4_000_000_070; // Token ID (V4: 4000000070)
185
+ generatedTokenIds[3] = 4_000_000_071; // Token ID (V4: 4000000071)
186
+ generatedTokenIds[4] = 4_000_000_072; // Token ID (V4: 4000000072)
187
+ generatedTokenIds[5] = 4_000_000_073; // Token ID (V4: 4000000073)
188
+ generatedTokenIds[6] = 4_000_000_074; // Token ID (V4: 4000000074)
189
+ generatedTokenIds[7] = 4_000_000_075; // Token ID (V4: 4000000075)
190
+ generatedTokenIds[8] = 4_000_000_076; // Token ID (V4: 4000000076)
191
+ generatedTokenIds[9] = 4_000_000_077; // Token ID (V4: 4000000077)
192
+ generatedTokenIds[10] = 4_000_000_078; // Token ID (V4: 4000000078)
193
+ generatedTokenIds[11] = 4_000_000_079; // Token ID (V4: 4000000079)
194
+ generatedTokenIds[12] = 4_000_000_080; // Token ID (V4: 4000000080)
195
+ generatedTokenIds[13] = 4_000_000_081; // Token ID (V4: 4000000081)
196
+ generatedTokenIds[14] = 4_000_000_082; // Token ID (V4: 4000000082)
197
+ generatedTokenIds[15] = 4_000_000_083; // Token ID (V4: 4000000083)
198
+ generatedTokenIds[16] = 4_000_000_084; // Token ID (V4: 4000000084)
199
+ generatedTokenIds[17] = 4_000_000_085; // Token ID (V4: 4000000085)
200
+
201
+ uint256 successfulTransfers = 0;
202
+ uint256 skippedResolverOwned = 0;
203
+
204
+ for (uint256 i = 0; i < transferOwners.length; i++) {
205
+ uint256 tokenId = generatedTokenIds[i];
206
+ // Verify V4 ownership before transferring V5
207
+ address v4Owner = v4Hook.ownerOf(tokenId);
208
+ require(
209
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
210
+ "V4/V5 ownership mismatch for token"
211
+ );
212
+
213
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
214
+ // resolver)
215
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
216
+ // Token is held by resolver, skip transfer
217
+ skippedResolverOwned++;
218
+ continue;
219
+ }
220
+
221
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
222
+ successfulTransfers++;
223
+ }
224
+
225
+ // Verify all expected items were processed (transferred or skipped as expected)
226
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
227
+
228
+ // Final verification: Ensure this contract no longer owns any tokens
229
+ // This ensures all transfers completed successfully and no tokens were left behind
230
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
231
+
232
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
233
+
234
+ // // Collect unique owners
235
+ // address[] memory uniqueOwners = new address[](10);
236
+
237
+ // uniqueOwners[0] = 0x823b92d6a4b2AED4b15675c7917c9f922ea8ADAD;
238
+ // uniqueOwners[1] = 0xaECD6D9172d602b93dBA3981991268b44af8096e;
239
+ // uniqueOwners[2] = 0xAAeD9fFF9858d48925904E391B77892BA5Fda824;
240
+ // uniqueOwners[3] = 0x2feb329b9289b60064904Fa61Fc347157a5AEd6a;
241
+ // uniqueOwners[4] = 0xDfd60a8E1e17FBb78E8CA332906A822D862f3D57;
242
+ // uniqueOwners[5] = 0xBD07B7Ab42cE411A752DB600604ECA7fE5501947;
243
+ // uniqueOwners[6] = 0x9f729294b308f79243285348A7Be3f58ae5ED31A;
244
+ // uniqueOwners[7] = 0x35a21F7c053Aed0Dcf9E24BfB100acA163aeDdB2;
245
+ // uniqueOwners[8] = 0x1c6d61F3d8976A8aCDd311ecdFa533B8ECd0AC61;
246
+ // uniqueOwners[9] = 0x5138a42C3D5065debE950deBDa10C1f38150a908;
247
+
248
+ // // Collect unique tier IDs
249
+ // uint256[] memory uniqueTierIds = new uint256[](9);
250
+
251
+ // uniqueTierIds[0] = 4;
252
+ // uniqueTierIds[1] = 6;
253
+ // uniqueTierIds[2] = 15;
254
+ // uniqueTierIds[3] = 19;
255
+ // uniqueTierIds[4] = 25;
256
+ // uniqueTierIds[5] = 29;
257
+ // uniqueTierIds[6] = 31;
258
+ // uniqueTierIds[7] = 38;
259
+ // uniqueTierIds[8] = 43;
260
+
261
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
262
+ // MigrationHelper.verifyTierBalances(
263
+ // hookAddress,
264
+ // v4HookAddress,
265
+ // fallbackV4ResolverAddress,
266
+ // uniqueOwners,
267
+ // uniqueTierIds
268
+ // );
269
+ }
270
+ }