@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,444 @@
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 MigrationContractBase1 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[3] upc2;
13
+ uint256[10] upc3;
14
+ uint256[14] upc4;
15
+ uint256[1] upc5;
16
+ uint256[4] upc6;
17
+ uint256[1] upc10;
18
+ uint256[1] upc11;
19
+ uint256[2] upc14;
20
+ uint256[2] upc15;
21
+ uint256[4] upc19;
22
+ uint256[4] upc25;
23
+ uint256[4] upc28;
24
+ uint256[1] upc31;
25
+ uint256[1] upc32;
26
+ uint256[1] upc33;
27
+ uint256[2] upc37;
28
+ uint256[1] upc40;
29
+ uint256[1] upc43;
30
+ uint256[2] upc44;
31
+ uint256[1] upc45;
32
+ uint256[2] upc47;
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
+ // Base migration chunk 1/4 - 62 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 2 minted tokenIds (3 items)
74
+ sortedMintedIds.upc2[0] = 2_000_000_001; // Token ID: 2 * 1000000000 + 1
75
+ sortedMintedIds.upc2[1] = 2_000_000_002; // Token ID: 2 * 1000000000 + 2
76
+ sortedMintedIds.upc2[2] = 2_000_000_003; // Token ID: 2 * 1000000000 + 3
77
+ // Populate UPC 3 minted tokenIds (10 items)
78
+ sortedMintedIds.upc3[0] = 3_000_000_001; // Token ID: 3 * 1000000000 + 1
79
+ sortedMintedIds.upc3[1] = 3_000_000_002; // Token ID: 3 * 1000000000 + 2
80
+ sortedMintedIds.upc3[2] = 3_000_000_003; // Token ID: 3 * 1000000000 + 3
81
+ sortedMintedIds.upc3[3] = 3_000_000_004; // Token ID: 3 * 1000000000 + 4
82
+ sortedMintedIds.upc3[4] = 3_000_000_005; // Token ID: 3 * 1000000000 + 5
83
+ sortedMintedIds.upc3[5] = 3_000_000_006; // Token ID: 3 * 1000000000 + 6
84
+ sortedMintedIds.upc3[6] = 3_000_000_007; // Token ID: 3 * 1000000000 + 7
85
+ sortedMintedIds.upc3[7] = 3_000_000_008; // Token ID: 3 * 1000000000 + 8
86
+ sortedMintedIds.upc3[8] = 3_000_000_009; // Token ID: 3 * 1000000000 + 9
87
+ sortedMintedIds.upc3[9] = 3_000_000_010; // Token ID: 3 * 1000000000 + 10
88
+ // Populate UPC 4 minted tokenIds (14 items)
89
+ sortedMintedIds.upc4[0] = 4_000_000_001; // Token ID: 4 * 1000000000 + 1
90
+ sortedMintedIds.upc4[1] = 4_000_000_002; // Token ID: 4 * 1000000000 + 2
91
+ sortedMintedIds.upc4[2] = 4_000_000_003; // Token ID: 4 * 1000000000 + 3
92
+ sortedMintedIds.upc4[3] = 4_000_000_004; // Token ID: 4 * 1000000000 + 4
93
+ sortedMintedIds.upc4[4] = 4_000_000_005; // Token ID: 4 * 1000000000 + 5
94
+ sortedMintedIds.upc4[5] = 4_000_000_006; // Token ID: 4 * 1000000000 + 6
95
+ sortedMintedIds.upc4[6] = 4_000_000_007; // Token ID: 4 * 1000000000 + 7
96
+ sortedMintedIds.upc4[7] = 4_000_000_008; // Token ID: 4 * 1000000000 + 8
97
+ sortedMintedIds.upc4[8] = 4_000_000_009; // Token ID: 4 * 1000000000 + 9
98
+ sortedMintedIds.upc4[9] = 4_000_000_010; // Token ID: 4 * 1000000000 + 10
99
+ sortedMintedIds.upc4[10] = 4_000_000_011; // Token ID: 4 * 1000000000 + 11
100
+ sortedMintedIds.upc4[11] = 4_000_000_012; // Token ID: 4 * 1000000000 + 12
101
+ sortedMintedIds.upc4[12] = 4_000_000_013; // Token ID: 4 * 1000000000 + 13
102
+ sortedMintedIds.upc4[13] = 4_000_000_014; // Token ID: 4 * 1000000000 + 14
103
+ // Populate UPC 5 minted tokenIds (1 items)
104
+ sortedMintedIds.upc5[0] = 5_000_000_001; // Token ID: 5 * 1000000000 + 1
105
+ // Populate UPC 6 minted tokenIds (4 items)
106
+ sortedMintedIds.upc6[0] = 6_000_000_001; // Token ID: 6 * 1000000000 + 1
107
+ sortedMintedIds.upc6[1] = 6_000_000_002; // Token ID: 6 * 1000000000 + 2
108
+ sortedMintedIds.upc6[2] = 6_000_000_003; // Token ID: 6 * 1000000000 + 3
109
+ sortedMintedIds.upc6[3] = 6_000_000_004; // Token ID: 6 * 1000000000 + 4
110
+ // Populate UPC 10 minted tokenIds (1 items)
111
+ sortedMintedIds.upc10[0] = 10_000_000_001; // Token ID: 10 * 1000000000 + 1
112
+ // Populate UPC 11 minted tokenIds (1 items)
113
+ sortedMintedIds.upc11[0] = 11_000_000_001; // Token ID: 11 * 1000000000 + 1
114
+ // Populate UPC 14 minted tokenIds (2 items)
115
+ sortedMintedIds.upc14[0] = 14_000_000_001; // Token ID: 14 * 1000000000 + 1
116
+ sortedMintedIds.upc14[1] = 14_000_000_002; // Token ID: 14 * 1000000000 + 2
117
+ // Populate UPC 15 minted tokenIds (2 items)
118
+ sortedMintedIds.upc15[0] = 15_000_000_001; // Token ID: 15 * 1000000000 + 1
119
+ sortedMintedIds.upc15[1] = 15_000_000_002; // Token ID: 15 * 1000000000 + 2
120
+ // Populate UPC 19 minted tokenIds (4 items)
121
+ sortedMintedIds.upc19[0] = 19_000_000_001; // Token ID: 19 * 1000000000 + 1
122
+ sortedMintedIds.upc19[1] = 19_000_000_002; // Token ID: 19 * 1000000000 + 2
123
+ sortedMintedIds.upc19[2] = 19_000_000_003; // Token ID: 19 * 1000000000 + 3
124
+ sortedMintedIds.upc19[3] = 19_000_000_004; // Token ID: 19 * 1000000000 + 4
125
+ // Populate UPC 25 minted tokenIds (4 items)
126
+ sortedMintedIds.upc25[0] = 25_000_000_001; // Token ID: 25 * 1000000000 + 1
127
+ sortedMintedIds.upc25[1] = 25_000_000_002; // Token ID: 25 * 1000000000 + 2
128
+ sortedMintedIds.upc25[2] = 25_000_000_003; // Token ID: 25 * 1000000000 + 3
129
+ sortedMintedIds.upc25[3] = 25_000_000_004; // Token ID: 25 * 1000000000 + 4
130
+ // Populate UPC 28 minted tokenIds (4 items)
131
+ sortedMintedIds.upc28[0] = 28_000_000_001; // Token ID: 28 * 1000000000 + 1
132
+ sortedMintedIds.upc28[1] = 28_000_000_002; // Token ID: 28 * 1000000000 + 2
133
+ sortedMintedIds.upc28[2] = 28_000_000_003; // Token ID: 28 * 1000000000 + 3
134
+ sortedMintedIds.upc28[3] = 28_000_000_004; // Token ID: 28 * 1000000000 + 4
135
+ // Populate UPC 31 minted tokenIds (1 items)
136
+ sortedMintedIds.upc31[0] = 31_000_000_001; // Token ID: 31 * 1000000000 + 1
137
+ // Populate UPC 32 minted tokenIds (1 items)
138
+ sortedMintedIds.upc32[0] = 32_000_000_001; // Token ID: 32 * 1000000000 + 1
139
+ // Populate UPC 33 minted tokenIds (1 items)
140
+ sortedMintedIds.upc33[0] = 33_000_000_001; // Token ID: 33 * 1000000000 + 1
141
+ // Populate UPC 37 minted tokenIds (2 items)
142
+ sortedMintedIds.upc37[0] = 37_000_000_001; // Token ID: 37 * 1000000000 + 1
143
+ sortedMintedIds.upc37[1] = 37_000_000_002; // Token ID: 37 * 1000000000 + 2
144
+ // Populate UPC 40 minted tokenIds (1 items)
145
+ sortedMintedIds.upc40[0] = 40_000_000_001; // Token ID: 40 * 1000000000 + 1
146
+ // Populate UPC 43 minted tokenIds (1 items)
147
+ sortedMintedIds.upc43[0] = 43_000_000_001; // Token ID: 43 * 1000000000 + 1
148
+ // Populate UPC 44 minted tokenIds (2 items)
149
+ sortedMintedIds.upc44[0] = 44_000_000_001; // Token ID: 44 * 1000000000 + 1
150
+ sortedMintedIds.upc44[1] = 44_000_000_002; // Token ID: 44 * 1000000000 + 2
151
+ // Populate UPC 45 minted tokenIds (1 items)
152
+ sortedMintedIds.upc45[0] = 45_000_000_001; // Token ID: 45 * 1000000000 + 1
153
+ // Populate UPC 47 minted tokenIds (2 items)
154
+ sortedMintedIds.upc47[0] = 47_000_000_001; // Token ID: 47 * 1000000000 + 1
155
+ sortedMintedIds.upc47[1] = 47_000_000_002; // Token ID: 47 * 1000000000 + 2
156
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
157
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
158
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
159
+
160
+ // Step 2: Process each Banny body and dress them
161
+
162
+ // Dress Banny 2000000001 (Pink)
163
+ {
164
+ uint256[] memory outfitIds = new uint256[](2);
165
+ outfitIds[0] = 28_000_000_002; // V4: 28000000002 -> V5: 28000000002
166
+ outfitIds[1] = 37_000_000_001; // V4: 37000000001 -> V5: 37000000001
167
+
168
+ resolver.decorateBannyWith(address(hook), 2_000_000_001, 0, outfitIds);
169
+
170
+ MigrationHelper.verifyV4AssetMatch(
171
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 2_000_000_001
172
+ );
173
+ }
174
+
175
+ // Dress Banny 2000000002 (Pink)
176
+ {
177
+ uint256[] memory outfitIds = new uint256[](2);
178
+ outfitIds[0] = 14_000_000_001; // V4: 14000000002 -> V5: 14000000001
179
+ outfitIds[1] = 32_000_000_001; // V4: 32000000001 -> V5: 32000000001
180
+
181
+ resolver.decorateBannyWith(address(hook), 2_000_000_002, 6_000_000_001, outfitIds);
182
+
183
+ MigrationHelper.verifyV4AssetMatch(
184
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 2_000_000_002
185
+ );
186
+ }
187
+
188
+ // Dress Banny 2000000003 (Pink)
189
+ {
190
+ uint256[] memory outfitIds = new uint256[](3);
191
+ outfitIds[0] = 25_000_000_003; // V4: 25000000008 -> V5: 25000000003
192
+ outfitIds[1] = 37_000_000_002; // V4: 37000000002 -> V5: 37000000002
193
+ outfitIds[2] = 45_000_000_001; // V4: 45000000001 -> V5: 45000000001
194
+
195
+ resolver.decorateBannyWith(address(hook), 2_000_000_003, 6_000_000_003, outfitIds);
196
+
197
+ MigrationHelper.verifyV4AssetMatch(
198
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 2_000_000_003
199
+ );
200
+ }
201
+
202
+ // Dress Banny 3000000001 (Orange)
203
+ {
204
+ uint256[] memory outfitIds = new uint256[](2);
205
+ outfitIds[0] = 25_000_000_002; // V4: 25000000004 -> V5: 25000000002
206
+ outfitIds[1] = 47_000_000_001; // V4: 47000000003 -> V5: 47000000001
207
+
208
+ resolver.decorateBannyWith(address(hook), 3_000_000_001, 0, outfitIds);
209
+
210
+ MigrationHelper.verifyV4AssetMatch(
211
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_001
212
+ );
213
+ }
214
+
215
+ // Dress Banny 3000000002 (Orange)
216
+ {
217
+ uint256[] memory outfitIds = new uint256[](1);
218
+ outfitIds[0] = 31_000_000_001; // V4: 31000000002 -> V5: 31000000001
219
+
220
+ resolver.decorateBannyWith(address(hook), 3_000_000_002, 0, outfitIds);
221
+
222
+ MigrationHelper.verifyV4AssetMatch(
223
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_002
224
+ );
225
+ }
226
+
227
+ // Dress Banny 3000000003 (Orange)
228
+ {
229
+ uint256[] memory outfitIds = new uint256[](4);
230
+ outfitIds[0] = 10_000_000_001; // V4: 10000000005 -> V5: 10000000001
231
+ outfitIds[1] = 19_000_000_002; // V4: 19000000005 -> V5: 19000000002
232
+ outfitIds[2] = 28_000_000_003; // V4: 28000000005 -> V5: 28000000003
233
+ outfitIds[3] = 47_000_000_002; // V4: 47000000005 -> V5: 47000000002
234
+
235
+ resolver.decorateBannyWith(address(hook), 3_000_000_003, 0, outfitIds);
236
+
237
+ MigrationHelper.verifyV4AssetMatch(
238
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_003
239
+ );
240
+ }
241
+
242
+ // Dress Banny 3000000006 (Orange)
243
+ {
244
+ uint256[] memory outfitIds = new uint256[](3);
245
+ outfitIds[0] = 14_000_000_002; // V4: 14000000003 -> V5: 14000000002
246
+ outfitIds[1] = 19_000_000_003; // V4: 19000000007 -> V5: 19000000003
247
+ outfitIds[2] = 28_000_000_001; // V4: 28000000001 -> V5: 28000000001
248
+
249
+ resolver.decorateBannyWith(address(hook), 3_000_000_006, 6_000_000_002, outfitIds);
250
+
251
+ MigrationHelper.verifyV4AssetMatch(
252
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_006
253
+ );
254
+ }
255
+
256
+ // Dress Banny 3000000007 (Orange)
257
+ {
258
+ uint256[] memory outfitIds = new uint256[](3);
259
+ outfitIds[0] = 19_000_000_004; // V4: 19000000009 -> V5: 19000000004
260
+ outfitIds[1] = 28_000_000_004; // V4: 28000000007 -> V5: 28000000004
261
+ outfitIds[2] = 44_000_000_002; // V4: 44000000005 -> V5: 44000000002
262
+
263
+ resolver.decorateBannyWith(address(hook), 3_000_000_007, 0, outfitIds);
264
+
265
+ MigrationHelper.verifyV4AssetMatch(
266
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_007
267
+ );
268
+ }
269
+
270
+ // Dress Banny 3000000008 (Orange)
271
+ {
272
+ uint256[] memory outfitIds = new uint256[](2);
273
+ outfitIds[0] = 15_000_000_002; // V4: 15000000002 -> V5: 15000000002
274
+ outfitIds[1] = 40_000_000_001; // V4: 40000000001 -> V5: 40000000001
275
+
276
+ resolver.decorateBannyWith(address(hook), 3_000_000_008, 0, outfitIds);
277
+
278
+ MigrationHelper.verifyV4AssetMatch(
279
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_008
280
+ );
281
+ }
282
+
283
+ // Dress Banny 3000000010 (Orange)
284
+ {
285
+ uint256[] memory outfitIds = new uint256[](2);
286
+ outfitIds[0] = 25_000_000_004; // V4: 25000000009 -> V5: 25000000004
287
+ outfitIds[1] = 43_000_000_001; // V4: 43000000008 -> V5: 43000000001
288
+
289
+ resolver.decorateBannyWith(address(hook), 3_000_000_010, 5_000_000_001, outfitIds);
290
+
291
+ MigrationHelper.verifyV4AssetMatch(
292
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 3_000_000_010
293
+ );
294
+ }
295
+
296
+ // Dress Banny 4000000001 (Original)
297
+ {
298
+ uint256[] memory outfitIds = new uint256[](1);
299
+ outfitIds[0] = 15_000_000_001; // V4: 15000000001 -> V5: 15000000001
300
+
301
+ resolver.decorateBannyWith(address(hook), 4_000_000_001, 0, outfitIds);
302
+
303
+ MigrationHelper.verifyV4AssetMatch(
304
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_001
305
+ );
306
+ }
307
+
308
+ // Dress Banny 4000000003 (Original)
309
+ {
310
+ uint256[] memory outfitIds = new uint256[](5);
311
+ outfitIds[0] = 11_000_000_001; // V4: 11000000001 -> V5: 11000000001
312
+ outfitIds[1] = 19_000_000_001; // V4: 19000000001 -> V5: 19000000001
313
+ outfitIds[2] = 25_000_000_001; // V4: 25000000001 -> V5: 25000000001
314
+ outfitIds[3] = 33_000_000_001; // V4: 33000000001 -> V5: 33000000001
315
+ outfitIds[4] = 44_000_000_001; // V4: 44000000001 -> V5: 44000000001
316
+
317
+ resolver.decorateBannyWith(address(hook), 4_000_000_003, 6_000_000_004, outfitIds);
318
+
319
+ MigrationHelper.verifyV4AssetMatch(
320
+ resolver, v4Resolver, fallbackV4Resolver, address(hook), v4HookAddress, 4_000_000_003
321
+ );
322
+ }
323
+
324
+ // Step 3: Transfer all assets to rightful owners using constructor data
325
+ // Generate token IDs in the same order as items appear (matching mint order)
326
+ // Token ID format: UPC * 1000000000 + unitNumber
327
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
328
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
329
+ // since they're not transferred (only used in decorateBannyWith calls).
330
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
331
+
332
+ generatedTokenIds[0] = 2_000_000_001; // Token ID (V4: 2000000001)
333
+ generatedTokenIds[1] = 2_000_000_002; // Token ID (V4: 2000000002)
334
+ generatedTokenIds[2] = 2_000_000_003; // Token ID (V4: 2000000003)
335
+ generatedTokenIds[3] = 3_000_000_001; // Token ID (V4: 3000000001)
336
+ generatedTokenIds[4] = 3_000_000_002; // Token ID (V4: 3000000002)
337
+ generatedTokenIds[5] = 3_000_000_003; // Token ID (V4: 3000000003)
338
+ generatedTokenIds[6] = 3_000_000_004; // Token ID (V4: 3000000004)
339
+ generatedTokenIds[7] = 3_000_000_005; // Token ID (V4: 3000000005)
340
+ generatedTokenIds[8] = 3_000_000_006; // Token ID (V4: 3000000006)
341
+ generatedTokenIds[9] = 3_000_000_007; // Token ID (V4: 3000000007)
342
+ generatedTokenIds[10] = 3_000_000_008; // Token ID (V4: 3000000008)
343
+ generatedTokenIds[11] = 3_000_000_009; // Token ID (V4: 3000000009)
344
+ generatedTokenIds[12] = 3_000_000_010; // Token ID (V4: 3000000010)
345
+ generatedTokenIds[13] = 4_000_000_001; // Token ID (V4: 4000000001)
346
+ generatedTokenIds[14] = 4_000_000_002; // Token ID (V4: 4000000002)
347
+ generatedTokenIds[15] = 4_000_000_003; // Token ID (V4: 4000000003)
348
+ generatedTokenIds[16] = 4_000_000_004; // Token ID (V4: 4000000004)
349
+ generatedTokenIds[17] = 4_000_000_005; // Token ID (V4: 4000000005)
350
+ generatedTokenIds[18] = 4_000_000_006; // Token ID (V4: 4000000006)
351
+ generatedTokenIds[19] = 4_000_000_007; // Token ID (V4: 4000000007)
352
+ generatedTokenIds[20] = 4_000_000_008; // Token ID (V4: 4000000008)
353
+ generatedTokenIds[21] = 4_000_000_009; // Token ID (V4: 4000000009)
354
+ generatedTokenIds[22] = 4_000_000_010; // Token ID (V4: 4000000010)
355
+ generatedTokenIds[23] = 4_000_000_011; // Token ID (V4: 4000000011)
356
+ generatedTokenIds[24] = 4_000_000_012; // Token ID (V4: 4000000012)
357
+ generatedTokenIds[25] = 4_000_000_013; // Token ID (V4: 4000000013)
358
+ generatedTokenIds[26] = 4_000_000_014; // Token ID (V4: 4000000014)
359
+
360
+ uint256 successfulTransfers = 0;
361
+ uint256 skippedResolverOwned = 0;
362
+
363
+ for (uint256 i = 0; i < transferOwners.length; i++) {
364
+ uint256 tokenId = generatedTokenIds[i];
365
+ // Verify V4 ownership before transferring V5
366
+ address v4Owner = v4Hook.ownerOf(tokenId);
367
+ require(
368
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
369
+ "V4/V5 ownership mismatch for token"
370
+ );
371
+
372
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
373
+ // resolver)
374
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
375
+ // Token is held by resolver, skip transfer
376
+ skippedResolverOwned++;
377
+ continue;
378
+ }
379
+
380
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
381
+ successfulTransfers++;
382
+ }
383
+
384
+ // Verify all expected items were processed (transferred or skipped as expected)
385
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
386
+
387
+ // Final verification: Ensure this contract no longer owns any tokens
388
+ // This ensures all transfers completed successfully and no tokens were left behind
389
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
390
+
391
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
392
+
393
+ // // Collect unique owners
394
+ // address[] memory uniqueOwners = new address[](13);
395
+
396
+ // uniqueOwners[0] = 0xf7253A0E87E39d2cD6365919D4a3D56D431D0041;
397
+ // uniqueOwners[1] = 0x565B93a15d38aCD79c120b15432D21E21eD274d6;
398
+ // uniqueOwners[2] = 0xFd37f4625CA5816157D55a5b3F7Dd8DD5F8a0C2F;
399
+ // uniqueOwners[3] = 0x25171bD3cD3231c3057c96F38E32E3bA6681497a;
400
+ // uniqueOwners[4] = 0x4718ce007293bCe1E514887E6F55ea71d9A992d6;
401
+ // uniqueOwners[5] = 0x96D087aba8552A0e111D7fB4Feb2e7621213E244;
402
+ // uniqueOwners[6] = 0x2830e21792019CE670fBc548AacB004b08c7f71f;
403
+ // uniqueOwners[7] = 0x328809A567b87b6123462c3062e8438BBB75c1c5;
404
+ // uniqueOwners[8] = 0xAAeD9fFF9858d48925904E391B77892BA5Fda824;
405
+ // uniqueOwners[9] = 0xDf087B724174A3E4eD2338C0798193932E851F1b;
406
+ // uniqueOwners[10] = 0x28C173B8F20488eEF1b0f48Df8453A2f59C38337;
407
+ // uniqueOwners[11] = 0x57a482EA32c7F75A9C0734206f5BD4f9BCb38e12;
408
+ // uniqueOwners[12] = 0x817738DC393d682Ca5fBb268707b99F2aAe96baE;
409
+
410
+ // // Collect unique tier IDs
411
+ // uint256[] memory uniqueTierIds = new uint256[](21);
412
+
413
+ // uniqueTierIds[0] = 2;
414
+ // uniqueTierIds[1] = 3;
415
+ // uniqueTierIds[2] = 4;
416
+ // uniqueTierIds[3] = 5;
417
+ // uniqueTierIds[4] = 6;
418
+ // uniqueTierIds[5] = 10;
419
+ // uniqueTierIds[6] = 11;
420
+ // uniqueTierIds[7] = 14;
421
+ // uniqueTierIds[8] = 15;
422
+ // uniqueTierIds[9] = 19;
423
+ // uniqueTierIds[10] = 25;
424
+ // uniqueTierIds[11] = 28;
425
+ // uniqueTierIds[12] = 31;
426
+ // uniqueTierIds[13] = 32;
427
+ // uniqueTierIds[14] = 33;
428
+ // uniqueTierIds[15] = 37;
429
+ // uniqueTierIds[16] = 40;
430
+ // uniqueTierIds[17] = 43;
431
+ // uniqueTierIds[18] = 44;
432
+ // uniqueTierIds[19] = 45;
433
+ // uniqueTierIds[20] = 47;
434
+
435
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
436
+ // MigrationHelper.verifyTierBalances(
437
+ // hookAddress,
438
+ // v4HookAddress,
439
+ // fallbackV4ResolverAddress,
440
+ // uniqueOwners,
441
+ // uniqueTierIds
442
+ // );
443
+ }
444
+ }
@@ -0,0 +1,175 @@
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 MigrationContractBase2 {
10
+ // Define struct to hold all UPC minted tokenIds
11
+ struct MintedIds {
12
+ uint256[27] upc4;
13
+ }
14
+
15
+ address[] private transferOwners;
16
+
17
+ constructor(address[] memory _transferOwners) {
18
+ transferOwners = _transferOwners;
19
+ }
20
+
21
+ function executeMigration(
22
+ address hookAddress,
23
+ address resolverAddress,
24
+ address v4HookAddress,
25
+ address v4ResolverAddress,
26
+ address fallbackV4ResolverAddress
27
+ )
28
+ external
29
+ {
30
+ // Validate addresses
31
+ require(hookAddress != address(0), "Hook address not set");
32
+ require(resolverAddress != address(0), "Resolver address not set");
33
+ require(v4HookAddress != address(0), "V4 Hook address not set");
34
+ require(v4ResolverAddress != address(0), "V4 Resolver address not set");
35
+ require(fallbackV4ResolverAddress != address(0), "V4 fallback resolver address not set");
36
+
37
+ JB721TiersHook hook = JB721TiersHook(hookAddress);
38
+ Banny721TokenUriResolver resolver = Banny721TokenUriResolver(resolverAddress);
39
+ IERC721 v4Hook = IERC721(v4HookAddress);
40
+ Banny721TokenUriResolver v4Resolver = Banny721TokenUriResolver(v4ResolverAddress);
41
+ Banny721TokenUriResolver fallbackV4Resolver = Banny721TokenUriResolver(fallbackV4ResolverAddress);
42
+
43
+ // Base migration chunk 2/4 - 27 items
44
+
45
+ // Step 1: Assets are already minted to this contract by the deployer
46
+
47
+ // Assets are already minted to this contract by the deployer
48
+
49
+ // Create and populate the struct
50
+ // Token IDs follow formula: upc * 1000000000 + unitNumber (unitNumber starts at 1 per UPC)
51
+ MintedIds memory sortedMintedIds;
52
+
53
+ // Populate UPC 4 minted tokenIds (27 items)
54
+ sortedMintedIds.upc4[0] = 4_000_000_015; // Token ID: 4 * 1000000000 + 1
55
+ sortedMintedIds.upc4[1] = 4_000_000_016; // Token ID: 4 * 1000000000 + 2
56
+ sortedMintedIds.upc4[2] = 4_000_000_017; // Token ID: 4 * 1000000000 + 3
57
+ sortedMintedIds.upc4[3] = 4_000_000_018; // Token ID: 4 * 1000000000 + 4
58
+ sortedMintedIds.upc4[4] = 4_000_000_019; // Token ID: 4 * 1000000000 + 5
59
+ sortedMintedIds.upc4[5] = 4_000_000_020; // Token ID: 4 * 1000000000 + 6
60
+ sortedMintedIds.upc4[6] = 4_000_000_021; // Token ID: 4 * 1000000000 + 7
61
+ sortedMintedIds.upc4[7] = 4_000_000_022; // Token ID: 4 * 1000000000 + 8
62
+ sortedMintedIds.upc4[8] = 4_000_000_023; // Token ID: 4 * 1000000000 + 9
63
+ sortedMintedIds.upc4[9] = 4_000_000_024; // Token ID: 4 * 1000000000 + 10
64
+ sortedMintedIds.upc4[10] = 4_000_000_025; // Token ID: 4 * 1000000000 + 11
65
+ sortedMintedIds.upc4[11] = 4_000_000_026; // Token ID: 4 * 1000000000 + 12
66
+ sortedMintedIds.upc4[12] = 4_000_000_027; // Token ID: 4 * 1000000000 + 13
67
+ sortedMintedIds.upc4[13] = 4_000_000_028; // Token ID: 4 * 1000000000 + 14
68
+ sortedMintedIds.upc4[14] = 4_000_000_029; // Token ID: 4 * 1000000000 + 15
69
+ sortedMintedIds.upc4[15] = 4_000_000_030; // Token ID: 4 * 1000000000 + 16
70
+ sortedMintedIds.upc4[16] = 4_000_000_031; // Token ID: 4 * 1000000000 + 17
71
+ sortedMintedIds.upc4[17] = 4_000_000_032; // Token ID: 4 * 1000000000 + 18
72
+ sortedMintedIds.upc4[18] = 4_000_000_033; // Token ID: 4 * 1000000000 + 19
73
+ sortedMintedIds.upc4[19] = 4_000_000_034; // Token ID: 4 * 1000000000 + 20
74
+ sortedMintedIds.upc4[20] = 4_000_000_035; // Token ID: 4 * 1000000000 + 21
75
+ sortedMintedIds.upc4[21] = 4_000_000_036; // Token ID: 4 * 1000000000 + 22
76
+ sortedMintedIds.upc4[22] = 4_000_000_037; // Token ID: 4 * 1000000000 + 23
77
+ sortedMintedIds.upc4[23] = 4_000_000_038; // Token ID: 4 * 1000000000 + 24
78
+ sortedMintedIds.upc4[24] = 4_000_000_039; // Token ID: 4 * 1000000000 + 25
79
+ sortedMintedIds.upc4[25] = 4_000_000_040; // Token ID: 4 * 1000000000 + 26
80
+ sortedMintedIds.upc4[26] = 4_000_000_041; // Token ID: 4 * 1000000000 + 27
81
+ // Step 1.5: Approve resolver to transfer all tokens owned by this contract
82
+ // The resolver needs approval to transfer outfits and backgrounds to itself during decoration
83
+ IERC721(address(hook)).setApprovalForAll(address(resolver), true);
84
+
85
+ // Step 2: Process each Banny body and dress them
86
+
87
+ // Step 3: Transfer all assets to rightful owners using constructor data
88
+ // Generate token IDs in the same order as items appear (matching mint order)
89
+ // Token ID format: UPC * 1000000000 + unitNumber
90
+ // Note: Only banny body token IDs are guaranteed to match between V4 and V5.
91
+ // Outfits/backgrounds being worn by bannys may have different IDs, but that's OK
92
+ // since they're not transferred (only used in decorateBannyWith calls).
93
+ uint256[] memory generatedTokenIds = new uint256[](transferOwners.length);
94
+
95
+ generatedTokenIds[0] = 4_000_000_015; // Token ID (V4: 4000000015)
96
+ generatedTokenIds[1] = 4_000_000_016; // Token ID (V4: 4000000016)
97
+ generatedTokenIds[2] = 4_000_000_017; // Token ID (V4: 4000000017)
98
+ generatedTokenIds[3] = 4_000_000_018; // Token ID (V4: 4000000018)
99
+ generatedTokenIds[4] = 4_000_000_019; // Token ID (V4: 4000000019)
100
+ generatedTokenIds[5] = 4_000_000_020; // Token ID (V4: 4000000020)
101
+ generatedTokenIds[6] = 4_000_000_021; // Token ID (V4: 4000000021)
102
+ generatedTokenIds[7] = 4_000_000_022; // Token ID (V4: 4000000022)
103
+ generatedTokenIds[8] = 4_000_000_023; // Token ID (V4: 4000000023)
104
+ generatedTokenIds[9] = 4_000_000_024; // Token ID (V4: 4000000024)
105
+ generatedTokenIds[10] = 4_000_000_025; // Token ID (V4: 4000000025)
106
+ generatedTokenIds[11] = 4_000_000_026; // Token ID (V4: 4000000026)
107
+ generatedTokenIds[12] = 4_000_000_027; // Token ID (V4: 4000000027)
108
+ generatedTokenIds[13] = 4_000_000_028; // Token ID (V4: 4000000028)
109
+ generatedTokenIds[14] = 4_000_000_029; // Token ID (V4: 4000000029)
110
+ generatedTokenIds[15] = 4_000_000_030; // Token ID (V4: 4000000030)
111
+ generatedTokenIds[16] = 4_000_000_031; // Token ID (V4: 4000000031)
112
+ generatedTokenIds[17] = 4_000_000_032; // Token ID (V4: 4000000032)
113
+ generatedTokenIds[18] = 4_000_000_033; // Token ID (V4: 4000000033)
114
+ generatedTokenIds[19] = 4_000_000_034; // Token ID (V4: 4000000034)
115
+ generatedTokenIds[20] = 4_000_000_035; // Token ID (V4: 4000000035)
116
+ generatedTokenIds[21] = 4_000_000_036; // Token ID (V4: 4000000036)
117
+ generatedTokenIds[22] = 4_000_000_037; // Token ID (V4: 4000000037)
118
+ generatedTokenIds[23] = 4_000_000_038; // Token ID (V4: 4000000038)
119
+ generatedTokenIds[24] = 4_000_000_039; // Token ID (V4: 4000000039)
120
+ generatedTokenIds[25] = 4_000_000_040; // Token ID (V4: 4000000040)
121
+ generatedTokenIds[26] = 4_000_000_041; // Token ID (V4: 4000000041)
122
+
123
+ uint256 successfulTransfers = 0;
124
+ uint256 skippedResolverOwned = 0;
125
+
126
+ for (uint256 i = 0; i < transferOwners.length; i++) {
127
+ uint256 tokenId = generatedTokenIds[i];
128
+ // Verify V4 ownership before transferring V5
129
+ address v4Owner = v4Hook.ownerOf(tokenId);
130
+ require(
131
+ v4Owner == transferOwners[i] || v4Owner == address(fallbackV4ResolverAddress),
132
+ "V4/V5 ownership mismatch for token"
133
+ );
134
+
135
+ // Skip transfer if V4 owner is the resolver (resolver holds these tokens, we shouldn't transfer to
136
+ // resolver)
137
+ if (v4Owner == address(v4ResolverAddress) || v4Owner == address(fallbackV4ResolverAddress)) {
138
+ // Token is held by resolver, skip transfer
139
+ skippedResolverOwned++;
140
+ continue;
141
+ }
142
+
143
+ IERC721(address(hook)).safeTransferFrom(address(this), transferOwners[i], tokenId);
144
+ successfulTransfers++;
145
+ }
146
+
147
+ // Verify all expected items were processed (transferred or skipped as expected)
148
+ require(successfulTransfers + skippedResolverOwned == transferOwners.length, "Not all items were processed");
149
+
150
+ // Final verification: Ensure this contract no longer owns any tokens
151
+ // This ensures all transfers completed successfully and no tokens were left behind
152
+ require(hook.balanceOf(address(this)) == 0, "Contract still owns tokens after migration");
153
+
154
+ // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
155
+
156
+ // // Collect unique owners
157
+ // address[] memory uniqueOwners = new address[](1);
158
+
159
+ // uniqueOwners[0] = 0xf7253A0E87E39d2cD6365919D4a3D56D431D0041;
160
+
161
+ // // Collect unique tier IDs
162
+ // uint256[] memory uniqueTierIds = new uint256[](1);
163
+
164
+ // uniqueTierIds[0] = 4;
165
+
166
+ // // Verify tier balances: V5 should never exceed V4 (except for tiers owned by fallback resolver in V4)
167
+ // MigrationHelper.verifyTierBalances(
168
+ // hookAddress,
169
+ // v4HookAddress,
170
+ // fallbackV4ResolverAddress,
171
+ // uniqueOwners,
172
+ // uniqueTierIds
173
+ // );
174
+ }
175
+ }