neozipkit 0.2.1 → 0.3.0

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 (301) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +410 -186
  3. package/contracts/ABI.txt +889 -0
  4. package/contracts/Bytecode.txt +1 -0
  5. package/contracts/COMPILATION_NOTES.md +59 -0
  6. package/contracts/QUICK_DEPLOY.md +286 -0
  7. package/contracts/README.md +235 -131
  8. package/contracts/VERIFICATION.md +308 -0
  9. package/contracts/deploy-interactive.js +448 -0
  10. package/contracts/deployment-arbitrum-sepolia.json +12 -0
  11. package/contracts/deployment-arbitrum.json +12 -0
  12. package/contracts/deployment-eth-sepolia.json +12 -0
  13. package/contracts/hardhat.config.js +114 -0
  14. package/contracts/package.json +20 -0
  15. package/contracts/scripts/compile-and-extract.js +119 -0
  16. package/contracts/scripts/verify-contract.js +223 -0
  17. package/contracts/src/{NZIP-NFT-Private.sol → NZIP-NFT-v2.0.sol} +66 -151
  18. package/contracts/src/NZIP-NFT.sol +9 -15
  19. package/dist/blockchain/browser/TokenVerifierBrowser.d.ts +67 -0
  20. package/dist/blockchain/browser/TokenVerifierBrowser.d.ts.map +1 -0
  21. package/dist/blockchain/browser/TokenVerifierBrowser.js +313 -0
  22. package/dist/blockchain/browser/TokenVerifierBrowser.js.map +1 -0
  23. package/dist/blockchain/browser/WalletManagerBrowser.d.ts +98 -0
  24. package/dist/blockchain/browser/WalletManagerBrowser.d.ts.map +1 -0
  25. package/dist/blockchain/browser/WalletManagerBrowser.js +434 -0
  26. package/dist/blockchain/browser/WalletManagerBrowser.js.map +1 -0
  27. package/dist/blockchain/browser/ZipkitMinterBrowser.d.ts +58 -0
  28. package/dist/blockchain/browser/ZipkitMinterBrowser.d.ts.map +1 -0
  29. package/dist/blockchain/browser/ZipkitMinterBrowser.js +317 -0
  30. package/dist/blockchain/browser/ZipkitMinterBrowser.js.map +1 -0
  31. package/dist/blockchain/browser/index.d.ts +11 -0
  32. package/dist/blockchain/browser/index.d.ts.map +1 -0
  33. package/dist/blockchain/browser/index.js +16 -0
  34. package/dist/blockchain/browser/index.js.map +1 -0
  35. package/dist/blockchain/core/WalletManager.d.ts +124 -0
  36. package/dist/blockchain/core/WalletManager.d.ts.map +1 -0
  37. package/dist/blockchain/{ZipKitWallet.js → core/WalletManager.js} +157 -177
  38. package/dist/blockchain/core/WalletManager.js.map +1 -0
  39. package/dist/blockchain/{ZipKitMinter.d.ts → core/ZipkitMinter.d.ts} +46 -11
  40. package/dist/blockchain/core/ZipkitMinter.d.ts.map +1 -0
  41. package/dist/blockchain/core/ZipkitMinter.js +548 -0
  42. package/dist/blockchain/core/ZipkitMinter.js.map +1 -0
  43. package/dist/blockchain/{ZipKitOTS.d.ts → core/ZipkitOTS.d.ts} +6 -15
  44. package/dist/blockchain/core/ZipkitOTS.d.ts.map +1 -0
  45. package/dist/blockchain/{ZipKitOTS.js → core/ZipkitOTS.js} +26 -84
  46. package/dist/blockchain/core/ZipkitOTS.js.map +1 -0
  47. package/dist/blockchain/core/ZipkitVerifier.d.ts +177 -0
  48. package/dist/blockchain/core/ZipkitVerifier.d.ts.map +1 -0
  49. package/dist/blockchain/core/ZipkitVerifier.js +632 -0
  50. package/dist/blockchain/core/ZipkitVerifier.js.map +1 -0
  51. package/dist/blockchain/core/contracts.d.ts +83 -0
  52. package/dist/blockchain/core/contracts.d.ts.map +1 -0
  53. package/dist/blockchain/core/contracts.js +341 -0
  54. package/dist/blockchain/core/contracts.js.map +1 -0
  55. package/dist/blockchain/core/index.d.ts +12 -0
  56. package/dist/blockchain/core/index.d.ts.map +1 -0
  57. package/dist/blockchain/core/index.js +59 -0
  58. package/dist/blockchain/core/index.js.map +1 -0
  59. package/dist/blockchain/index.d.ts +11 -7
  60. package/dist/blockchain/index.d.ts.map +1 -0
  61. package/dist/blockchain/index.js +18 -34
  62. package/dist/blockchain/index.js.map +1 -0
  63. package/dist/blockchain/node/WalletManagerNode.d.ts +47 -0
  64. package/dist/blockchain/node/WalletManagerNode.d.ts.map +1 -0
  65. package/dist/blockchain/node/WalletManagerNode.js +203 -0
  66. package/dist/blockchain/node/WalletManagerNode.js.map +1 -0
  67. package/dist/blockchain/node/index.d.ts +12 -0
  68. package/dist/blockchain/node/index.d.ts.map +1 -0
  69. package/dist/blockchain/node/index.js +24 -0
  70. package/dist/blockchain/node/index.js.map +1 -0
  71. package/dist/browser/ZipkitBrowser.d.ts +27 -0
  72. package/dist/browser/ZipkitBrowser.d.ts.map +1 -0
  73. package/dist/browser/{ZipKitBrowser.js → ZipkitBrowser.js} +96 -43
  74. package/dist/browser/ZipkitBrowser.js.map +1 -0
  75. package/dist/browser/index.d.ts +12 -0
  76. package/dist/browser/index.d.ts.map +1 -0
  77. package/dist/browser/index.js +42 -0
  78. package/dist/browser/index.js.map +1 -0
  79. package/dist/browser-esm/index.d.ts +12 -0
  80. package/dist/browser-esm/index.js +59559 -0
  81. package/dist/browser-esm/index.js.map +7 -0
  82. package/dist/browser-umd/index.d.ts +12 -0
  83. package/dist/browser-umd/index.js +59576 -0
  84. package/dist/browser-umd/index.js.map +7 -0
  85. package/dist/browser-umd/index.min.js +63 -0
  86. package/dist/browser.d.ts +8 -14
  87. package/dist/browser.js +13 -28
  88. package/dist/core/ZipCompress.d.ts +99 -0
  89. package/dist/core/ZipCompress.d.ts.map +1 -0
  90. package/dist/core/ZipCompress.js +309 -0
  91. package/dist/core/ZipCompress.js.map +1 -0
  92. package/dist/core/ZipDecompress.d.ts +58 -0
  93. package/dist/core/ZipDecompress.d.ts.map +1 -0
  94. package/dist/core/ZipDecompress.js +172 -0
  95. package/dist/core/ZipDecompress.js.map +1 -0
  96. package/dist/{shared → core}/ZipEntry.d.ts +8 -1
  97. package/dist/core/ZipEntry.d.ts.map +1 -0
  98. package/dist/{shared → core}/ZipEntry.js +225 -82
  99. package/dist/core/ZipEntry.js.map +1 -0
  100. package/dist/core/Zipkit.d.ts +315 -0
  101. package/dist/core/Zipkit.d.ts.map +1 -0
  102. package/dist/core/Zipkit.js +647 -0
  103. package/dist/core/Zipkit.js.map +1 -0
  104. package/dist/core/components/HashCalculator.d.ts +138 -0
  105. package/dist/core/components/HashCalculator.d.ts.map +1 -0
  106. package/dist/core/components/HashCalculator.js +360 -0
  107. package/dist/core/components/HashCalculator.js.map +1 -0
  108. package/dist/core/components/Logger.d.ts +73 -0
  109. package/dist/core/components/Logger.d.ts.map +1 -0
  110. package/dist/core/components/Logger.js +156 -0
  111. package/dist/core/components/Logger.js.map +1 -0
  112. package/dist/core/components/ProgressTracker.d.ts +43 -0
  113. package/dist/core/components/ProgressTracker.d.ts.map +1 -0
  114. package/dist/core/components/ProgressTracker.js +112 -0
  115. package/dist/core/components/ProgressTracker.js.map +1 -0
  116. package/dist/{shared → core}/components/Support.d.ts +1 -0
  117. package/dist/core/components/Support.d.ts.map +1 -0
  118. package/dist/{shared → core}/components/Support.js +1 -0
  119. package/dist/core/components/Support.js.map +1 -0
  120. package/dist/{shared → core}/components/Util.d.ts +7 -2
  121. package/dist/core/components/Util.d.ts.map +1 -0
  122. package/dist/{shared → core}/components/Util.js +14 -34
  123. package/dist/core/components/Util.js.map +1 -0
  124. package/dist/{shared → core}/constants/Errors.d.ts +1 -2
  125. package/dist/core/constants/Errors.d.ts.map +1 -0
  126. package/dist/{shared → core}/constants/Errors.js +1 -3
  127. package/dist/core/constants/Errors.js.map +1 -0
  128. package/dist/{shared → core}/constants/Headers.d.ts +3 -0
  129. package/dist/core/constants/Headers.d.ts.map +1 -0
  130. package/dist/{shared → core}/constants/Headers.js +20 -17
  131. package/dist/core/constants/Headers.js.map +1 -0
  132. package/dist/core/encryption/Manager.d.ts +58 -0
  133. package/dist/core/encryption/Manager.d.ts.map +1 -0
  134. package/dist/core/encryption/Manager.js +121 -0
  135. package/dist/core/encryption/Manager.js.map +1 -0
  136. package/dist/core/encryption/ZipCrypto.d.ts +172 -0
  137. package/dist/core/encryption/ZipCrypto.d.ts.map +1 -0
  138. package/dist/core/encryption/ZipCrypto.js +554 -0
  139. package/dist/core/encryption/ZipCrypto.js.map +1 -0
  140. package/dist/core/encryption/index.d.ts +9 -0
  141. package/dist/core/encryption/index.d.ts.map +1 -0
  142. package/dist/core/encryption/index.js +17 -0
  143. package/dist/core/encryption/index.js.map +1 -0
  144. package/dist/core/encryption/types.d.ts +29 -0
  145. package/dist/core/encryption/types.d.ts.map +1 -0
  146. package/dist/core/encryption/types.js +12 -0
  147. package/dist/core/encryption/types.js.map +1 -0
  148. package/dist/core/index.d.ts +24 -0
  149. package/dist/core/index.d.ts.map +1 -0
  150. package/dist/core/index.js +57 -0
  151. package/dist/core/index.js.map +1 -0
  152. package/dist/core/version.d.ts +5 -0
  153. package/dist/core/version.d.ts.map +1 -0
  154. package/dist/core/version.js +31 -0
  155. package/dist/core/version.js.map +1 -0
  156. package/dist/index.d.ts +8 -18
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +29 -35
  159. package/dist/index.js.map +1 -0
  160. package/dist/node/ZipCompressNode.d.ts +123 -0
  161. package/dist/node/ZipCompressNode.d.ts.map +1 -0
  162. package/dist/node/ZipCompressNode.js +576 -0
  163. package/dist/node/ZipCompressNode.js.map +1 -0
  164. package/dist/node/ZipDecompressNode.d.ts +120 -0
  165. package/dist/node/ZipDecompressNode.d.ts.map +1 -0
  166. package/dist/node/ZipDecompressNode.js +461 -0
  167. package/dist/node/ZipDecompressNode.js.map +1 -0
  168. package/dist/node/ZipkitNode.d.ts +406 -0
  169. package/dist/node/ZipkitNode.d.ts.map +1 -0
  170. package/dist/node/ZipkitNode.js +1305 -0
  171. package/dist/node/ZipkitNode.js.map +1 -0
  172. package/dist/node/index.d.ts +26 -0
  173. package/dist/node/index.d.ts.map +1 -0
  174. package/dist/node/index.js +65 -0
  175. package/dist/node/index.js.map +1 -0
  176. package/dist/types/index.d.ts +59 -0
  177. package/dist/types/index.d.ts.map +1 -0
  178. package/dist/types/index.js +36 -1
  179. package/dist/types/index.js.map +1 -0
  180. package/examples/README.md +269 -0
  181. package/examples/blockchain-tokenize.ts +354 -0
  182. package/examples/create-zip.ts +172 -0
  183. package/examples/extract-zip.ts +158 -0
  184. package/examples/list-zip.ts +161 -0
  185. package/examples/test-files/data.json +116 -0
  186. package/examples/test-files/document.md +80 -0
  187. package/examples/test-files/document.txt +2 -0
  188. package/examples/test-files/file1.txt +48 -0
  189. package/examples/test-files/file2.txt +80 -0
  190. package/examples/tsconfig.json +44 -0
  191. package/examples/unzip-token.ts +491 -0
  192. package/examples/verify-tokenized-zip.ts +305 -0
  193. package/examples/zip-token.ts +604 -0
  194. package/package.json +108 -24
  195. package/src/blockchain/README.md +59 -66
  196. package/src/{browser/TokenVerifierBrowser.ts-remove → blockchain/browser/TokenVerifierBrowser.ts} +125 -79
  197. package/src/blockchain/browser/WalletManagerBrowser.ts +486 -0
  198. package/src/blockchain/browser/ZipkitMinterBrowser.ts +400 -0
  199. package/src/blockchain/browser/index.ts +13 -0
  200. package/src/blockchain/{ZipKitWallet.ts → core/WalletManager.ts} +241 -143
  201. package/src/blockchain/core/ZipkitMinter.ts +716 -0
  202. package/src/blockchain/{ZipKitOTS.ts → core/ZipkitOTS.ts} +28 -62
  203. package/src/blockchain/core/ZipkitVerifier.ts +853 -0
  204. package/src/blockchain/core/contracts.ts +370 -0
  205. package/src/blockchain/core/index.ts +71 -0
  206. package/src/blockchain/index.ts +21 -55
  207. package/src/blockchain/node/WalletManagerNode.ts +171 -0
  208. package/src/blockchain/node/index.ts +40 -0
  209. package/src/browser/{ZipKitBrowser.ts → ZipkitBrowser.ts} +104 -41
  210. package/src/browser/index.esm.ts +37 -0
  211. package/src/browser/index.ts +25 -0
  212. package/src/core/ZipCompress.ts +398 -0
  213. package/src/core/ZipDecompress.ts +212 -0
  214. package/src/{shared → core}/ZipEntry.ts +240 -90
  215. package/src/core/Zipkit.ts +794 -0
  216. package/src/core/components/HashCalculator.ts +384 -0
  217. package/src/core/components/Logger.ts +180 -0
  218. package/src/core/components/ProgressTracker.ts +134 -0
  219. package/src/{shared → core}/components/Util.ts +12 -34
  220. package/src/{shared → core}/constants/Errors.ts +0 -4
  221. package/src/{shared → core}/constants/Headers.ts +19 -17
  222. package/src/core/encryption/Manager.ts +137 -0
  223. package/src/core/encryption/ZipCrypto.ts +650 -0
  224. package/src/core/encryption/index.ts +15 -0
  225. package/src/core/encryption/types.ts +33 -0
  226. package/src/core/index.ts +34 -0
  227. package/src/core/version.ts +33 -0
  228. package/src/index.ts +54 -25
  229. package/src/node/ZipCompressNode.ts +632 -0
  230. package/src/node/ZipDecompressNode.ts +508 -0
  231. package/src/node/ZipkitNode.ts +1559 -0
  232. package/src/node/index.ts +69 -0
  233. package/src/types/index.ts +131 -1
  234. package/src/types/modules.d.ts +1 -11
  235. package/src/types/opentimestamps.d.ts +1 -23
  236. package/contracts/INTEGRATION.md +0 -338
  237. package/contracts/NETWORKS.md +0 -156
  238. package/contracts/NZIP-NFT-bytecode.txt +0 -1
  239. package/contracts/constants.ts +0 -952
  240. package/contracts/deploy-bytecode.mjs +0 -94
  241. package/dist/blockchain/CertificateToken.d.ts +0 -48
  242. package/dist/blockchain/CertificateToken.js +0 -386
  243. package/dist/blockchain/ZipKitMinter.js +0 -347
  244. package/dist/blockchain/ZipKitNFT.d.ts +0 -3
  245. package/dist/blockchain/ZipKitNFT.js +0 -7
  246. package/dist/blockchain/ZipKitVerifier.d.ts +0 -69
  247. package/dist/blockchain/ZipKitVerifier.js +0 -289
  248. package/dist/blockchain/ZipKitWallet.d.ts +0 -69
  249. package/dist/blockchain/contracts.d.ts +0 -14
  250. package/dist/blockchain/contracts.js +0 -77
  251. package/dist/blockchain/types.d.ts +0 -47
  252. package/dist/blockchain/types.js +0 -30
  253. package/dist/browser/ZipKitBrowser.d.ts +0 -18
  254. package/dist/browser/ZipKitStampBrowser.d.ts +0 -2
  255. package/dist/browser/ZipKitStampBrowser.js +0 -12
  256. package/dist/browser-index.d.ts +0 -18
  257. package/dist/browser-index.js +0 -57
  258. package/dist/server/ZipKitServer.d.ts +0 -74
  259. package/dist/server/ZipKitServer.js +0 -310
  260. package/dist/shared/ZipKit.d.ts +0 -243
  261. package/dist/shared/ZipKit.js +0 -772
  262. package/dist/shared/components/CertificateOTS.d.ts +0 -15
  263. package/dist/shared/components/CertificateOTS.js +0 -116
  264. package/dist/shared/components/FileSys.d.ts +0 -2
  265. package/dist/shared/components/FileSys.js +0 -113
  266. package/dist/shared/components/HashAccumulator.d.ts +0 -55
  267. package/dist/shared/components/HashAccumulator.js +0 -100
  268. package/dist/shared/components/ZipCrypto.d.ts +0 -3
  269. package/dist/shared/components/ZipCrypto.js +0 -78
  270. package/dist/shared/components/merkleTree.d.ts +0 -66
  271. package/dist/shared/components/merkleTree.js +0 -136
  272. package/dist/shared/types/index.d.ts +0 -21
  273. package/dist/shared/types/index.js +0 -3
  274. package/dist/shared/types.d.ts +0 -33
  275. package/dist/shared/types.js +0 -8
  276. package/dist/shared/version.d.ts +0 -2
  277. package/dist/shared/version.js +0 -6
  278. package/src/blockchain/CertificateToken.ts +0 -444
  279. package/src/blockchain/ZipKitMinter.ts +0 -470
  280. package/src/blockchain/ZipKitNFT.ts +0 -12
  281. package/src/blockchain/ZipKitVerifier.ts +0 -382
  282. package/src/blockchain/contracts.ts +0 -88
  283. package/src/blockchain/types.ts +0 -91
  284. package/src/browser/CertificateGeneratorBrowser.ts-remove +0 -498
  285. package/src/browser/ZipKitStampBrowser.ts +0 -9
  286. package/src/browser-index.ts +0 -36
  287. package/src/server/README.md +0 -25
  288. package/src/server/ZipKitServer.ts +0 -305
  289. package/src/shared/ZipKit.ts +0 -881
  290. package/src/shared/components/CertificateOTS.ts +0 -132
  291. package/src/shared/components/FileSys.ts +0 -87
  292. package/src/shared/components/HashAccumulator.ts +0 -113
  293. package/src/shared/components/ZipCrypto.ts +0 -91
  294. package/src/shared/components/merkleTree.ts +0 -162
  295. package/src/shared/types/index.ts +0 -25
  296. package/src/shared/types/modules.d.ts +0 -22
  297. package/src/shared/types/opentimestamps.d.ts +0 -23
  298. package/src/shared/types.d.ts +0 -8
  299. package/src/shared/types.ts +0 -43
  300. package/src/shared/version.ts +0 -3
  301. /package/src/{shared → core}/components/Support.ts +0 -0
@@ -0,0 +1,853 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ZipkitVerifier - Handles NFT token verification for ZIP files
5
+ * Verifier API for NZIP-NFT smart contract.
6
+ */
7
+
8
+ import { ethers } from 'ethers';
9
+ import { NZIP_CONTRACT_ABI, CONTRACT_CONFIGS, getContractConfig, getNetworkByName, fuzzyMatchNetworkName, type ContractConfig } from './contracts';
10
+ import type { TokenMetadata } from '../../types';
11
+
12
+ export interface VerificationOptions {
13
+ debug?: boolean;
14
+ skipHash?: boolean;
15
+ }
16
+
17
+ export interface VerificationResult {
18
+ success: boolean;
19
+ message: string;
20
+ tokenMetadata?: TokenMetadata;
21
+ verificationDetails?: {
22
+ tokenId: string;
23
+ network: string;
24
+ contractAddress: string;
25
+ merkleRoot: string;
26
+ mintDate: string;
27
+ calculatedMerkleRoot?: string;
28
+ declaredMerkleRoot?: string;
29
+ merkleRootMatch?: boolean;
30
+ onChainValid?: boolean;
31
+ currentWallet?: string;
32
+ };
33
+ errorDetails?: {
34
+ errorType: 'METADATA_ERROR' | 'MERKLE_ERROR' | 'NETWORK_ERROR' | 'CONTRACT_ERROR';
35
+ networkName?: string;
36
+ rpcUrl?: string;
37
+ contractAddress?: string;
38
+ tokenId?: string;
39
+ merkleRoot?: string;
40
+ calculatedMerkleRoot?: string;
41
+ onChainMerkleRoot?: string;
42
+ };
43
+ }
44
+
45
+ export interface EnhancedVerificationResult extends VerificationResult {
46
+ retryAttempts?: number;
47
+ processingTime?: number;
48
+ timestamp?: number;
49
+ }
50
+
51
+ export interface BatchVerificationResult {
52
+ success: boolean;
53
+ totalTokens: number;
54
+ successfulVerifications: number;
55
+ failedVerifications: number;
56
+ results: EnhancedVerificationResult[];
57
+ processingTime: number;
58
+ }
59
+
60
+ export interface VerificationJob {
61
+ tokenId: string;
62
+ merkleRoot?: string;
63
+ priority?: 'low' | 'normal' | 'high';
64
+ retryCount?: number;
65
+ }
66
+
67
+ /**
68
+ * Blockchain Token Verifier for ZIP files
69
+ */
70
+ export class ZipkitVerifier {
71
+ private debug: boolean;
72
+ protected maxRetries: number;
73
+ protected retryDelay: number;
74
+ protected batchDelay: number;
75
+
76
+ constructor(options: VerificationOptions & {
77
+ maxRetries?: number;
78
+ retryDelay?: number;
79
+ batchDelay?: number;
80
+ } = {}) {
81
+ this.debug = options.debug || false;
82
+ this.maxRetries = options.maxRetries || 3;
83
+ this.retryDelay = options.retryDelay || 1000;
84
+ this.batchDelay = options.batchDelay || 500;
85
+
86
+ if (this.debug) {
87
+ console.log(`[DEBUG] ZipkitVerifier initialized`);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Extract and validate token metadata from ZIP
93
+ */
94
+ async extractTokenMetadata(tokenBuffer: Buffer): Promise<{ success: boolean; metadata?: TokenMetadata; error?: string }> {
95
+ try {
96
+ if (this.debug) {
97
+ console.log(`[DEBUG] Extracting token metadata from buffer (${tokenBuffer.length} bytes)`);
98
+ }
99
+
100
+ const tokenData = JSON.parse(tokenBuffer.toString('utf8'));
101
+
102
+ // Validate required fields
103
+ const requiredFields = ['tokenId', 'contractAddress', 'network'];
104
+ for (const field of requiredFields) {
105
+ if (!tokenData[field]) {
106
+ if (this.debug) {
107
+ console.log(`[DEBUG] Missing required field: ${field}`);
108
+ }
109
+ return {
110
+ success: false,
111
+ error: `Missing required field: ${field}`
112
+ };
113
+ }
114
+ }
115
+
116
+ if (this.debug) {
117
+ console.log(`[DEBUG] Token metadata extracted successfully`);
118
+ console.log(`[DEBUG] Token ID: ${tokenData.tokenId}, Network: ${tokenData.network}`);
119
+ }
120
+
121
+ return {
122
+ success: true,
123
+ metadata: tokenData as TokenMetadata
124
+ };
125
+
126
+ } catch (error) {
127
+ const errorMessage = error instanceof Error ? error.message : 'Invalid JSON format';
128
+
129
+ if (this.debug) {
130
+ console.log(`[DEBUG] Failed to parse token metadata: ${errorMessage}`);
131
+ }
132
+
133
+ return {
134
+ success: false,
135
+ error: `Invalid token metadata format: ${errorMessage}`
136
+ };
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Map network name to RPC configuration using contracts.ts (single source of truth)
142
+ * Supports both old metadata (without networkChainId) and new metadata (with networkChainId)
143
+ */
144
+ private getNetworkConfig(networkName: string, chainId?: number): { success: boolean; config?: ContractConfig; error?: string } {
145
+ if (this.debug) {
146
+ if (chainId) {
147
+ console.log(`[DEBUG] Looking up network config by chainId: ${chainId} (network: ${networkName})`);
148
+ } else {
149
+ console.log(`[DEBUG] Looking up network config by name: ${networkName} (chainId not provided - using fallback)`);
150
+ }
151
+ }
152
+
153
+ // Priority 1: If chainId is provided, use it directly (most reliable - new metadata format)
154
+ if (chainId) {
155
+ const config = getContractConfig(chainId);
156
+ if (config && config.chainId === chainId) {
157
+ if (this.debug) {
158
+ console.log(`[DEBUG] Found config by chainId: ${config.network} (${config.chainId})`);
159
+ }
160
+ return { success: true, config };
161
+ } else {
162
+ if (this.debug) {
163
+ console.log(`[DEBUG] ChainId ${chainId} not found in contract configs, falling back to network name`);
164
+ }
165
+ }
166
+ }
167
+
168
+ // Priority 2: Try exact name match using contracts.ts helpers
169
+ const exactMatch = getNetworkByName(networkName);
170
+ if (exactMatch) {
171
+ if (this.debug) {
172
+ console.log(`[DEBUG] Matched to ${exactMatch.network} (${exactMatch.chainId}) by network name`);
173
+ }
174
+ return { success: true, config: exactMatch };
175
+ }
176
+
177
+ // Priority 3: Try fuzzy matching (for backward compatibility with partial names)
178
+ const fuzzyMatch = fuzzyMatchNetworkName(networkName);
179
+ if (fuzzyMatch) {
180
+ if (this.debug) {
181
+ console.log(`[DEBUG] Fuzzy matched to ${fuzzyMatch.network} (${fuzzyMatch.chainId}) by network name`);
182
+ }
183
+ return { success: true, config: fuzzyMatch };
184
+ }
185
+
186
+ if (this.debug) {
187
+ console.log(`[DEBUG] Unsupported network: ${networkName}`);
188
+ }
189
+
190
+ return {
191
+ success: false,
192
+ error: `Unsupported network: ${networkName}`
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Perform merkle root comparison
198
+ */
199
+ private compareMerkleRoots(calculated: string | null, declared: string | null): {
200
+ success: boolean;
201
+ match: boolean;
202
+ calculated?: string;
203
+ declared?: string;
204
+ error?: string;
205
+ } {
206
+ if (this.debug) {
207
+ console.log(`[DEBUG] Comparing merkle roots`);
208
+ console.log(`[DEBUG] Calculated: ${calculated || 'null'}`);
209
+ console.log(`[DEBUG] Declared: ${declared || 'null'}`);
210
+ }
211
+
212
+ if (!calculated) {
213
+ return {
214
+ success: false,
215
+ match: false,
216
+ error: 'Cannot calculate merkle root from archive contents'
217
+ };
218
+ }
219
+
220
+ if (!declared) {
221
+ return {
222
+ success: false,
223
+ match: false,
224
+ calculated,
225
+ error: 'No merkle root found in token metadata'
226
+ };
227
+ }
228
+
229
+ const match = calculated === declared;
230
+
231
+ if (this.debug) {
232
+ console.log(`[DEBUG] Merkle root match: ${match}`);
233
+ }
234
+
235
+ return {
236
+ success: true,
237
+ match,
238
+ calculated,
239
+ declared
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Verify token against blockchain
245
+ * @param rpcUrlIndex Optional index of RPC URL to use (default: 0)
246
+ */
247
+ async verifyOnChain(
248
+ tokenId: string,
249
+ contractAddress: string,
250
+ networkConfig: ContractConfig,
251
+ merkleRoot: string,
252
+ rpcUrlIndex: number = 0
253
+ ): Promise<{
254
+ success: boolean;
255
+ isValid?: boolean;
256
+ onChainMerkleRoot?: string;
257
+ onChainTokenizationTime?: number;
258
+ onChainCreator?: string;
259
+ onChainBlockNumber?: number;
260
+ error?: string;
261
+ rpcUrl?: string
262
+ }> {
263
+ // Use specified RPC URL index
264
+ const rpcUrls = networkConfig.rpcUrls.length > 0 ? networkConfig.rpcUrls : [];
265
+ if (rpcUrls.length === 0) {
266
+ return {
267
+ success: false,
268
+ error: 'No RPC URLs configured for this network'
269
+ };
270
+ }
271
+
272
+ if (rpcUrlIndex >= rpcUrls.length) {
273
+ return {
274
+ success: false,
275
+ error: `RPC URL index ${rpcUrlIndex} is out of range (${rpcUrls.length} RPCs available)`
276
+ };
277
+ }
278
+
279
+ const rpcUrl = rpcUrls[rpcUrlIndex];
280
+ let provider: ethers.JsonRpcProvider | null = null;
281
+
282
+ try {
283
+
284
+ if (this.debug) {
285
+ console.log(`[DEBUG] Starting on-chain verification`);
286
+ console.log(`[DEBUG] Token ID: ${tokenId}, Contract: ${contractAddress}`);
287
+ console.log(`[DEBUG] Network: ${networkConfig.network}, Chain ID: ${networkConfig.chainId}`);
288
+ console.log(`[DEBUG] Using RPC: ${rpcUrl}`);
289
+ }
290
+
291
+ provider = new ethers.JsonRpcProvider(rpcUrl);
292
+ const contract = new ethers.Contract(contractAddress, NZIP_CONTRACT_ABI, provider);
293
+
294
+ if (this.debug) {
295
+ console.log(`[DEBUG] Calling contract.verifyZipFile(${tokenId}, ${merkleRoot.substring(0, 10)}...)`);
296
+ }
297
+
298
+ // First check if token exists and get ALL actual on-chain data (not from metadata)
299
+ let onChainMerkleRoot: string | undefined;
300
+ let onChainTokenizationTime: number | undefined;
301
+ let onChainCreator: string | undefined;
302
+ let onChainBlockNumber: number | undefined;
303
+
304
+ try {
305
+ // Get token info including ALL data stored on-chain (merkle root, tokenization time, creator, block number)
306
+ // CRITICAL: We trust ONLY blockchain data, not metadata file which can be tampered with
307
+ const zipFileInfo = await Promise.race([
308
+ contract.getZipFileInfo(tokenId),
309
+ new Promise<never>((_, reject) =>
310
+ setTimeout(() => reject(new Error('getZipFileInfo timeout after 10 seconds')), 10000)
311
+ )
312
+ ]);
313
+
314
+ // Extract all on-chain data
315
+ onChainMerkleRoot = zipFileInfo.merkleRootHash;
316
+ onChainTokenizationTime = Number(zipFileInfo.tokenizationTime);
317
+ onChainCreator = zipFileInfo.creator;
318
+ onChainBlockNumber = Number(zipFileInfo.blockNumber);
319
+
320
+ if (this.debug) {
321
+ console.log(`[DEBUG] Token ${tokenId} exists`);
322
+ console.log(`[DEBUG] On-chain merkle root: ${onChainMerkleRoot}`);
323
+ console.log(`[DEBUG] On-chain tokenization time: ${onChainTokenizationTime} (${new Date(onChainTokenizationTime * 1000).toLocaleString()})`);
324
+ console.log(`[DEBUG] On-chain creator: ${onChainCreator}`);
325
+ console.log(`[DEBUG] On-chain block number: ${onChainBlockNumber}`);
326
+ console.log(`[DEBUG] Calculated merkle root: ${merkleRoot}`);
327
+ }
328
+ } catch (infoError: any) {
329
+ const errorMsg = infoError.message || String(infoError);
330
+ if (this.debug) {
331
+ console.log(`[DEBUG] Token info check failed: ${errorMsg}`);
332
+ }
333
+ // If token doesn't exist, return error
334
+ if (errorMsg.includes('nonexistent token') || errorMsg.includes('ERC721: invalid token ID')) {
335
+ return {
336
+ success: false,
337
+ error: `Token ${tokenId} does not exist on contract ${contractAddress}`
338
+ };
339
+ }
340
+ // For other errors, fail immediately
341
+ return {
342
+ success: false,
343
+ error: `Failed to get token info: ${errorMsg}`
344
+ };
345
+ }
346
+
347
+ // Compare the calculated merkle root with the on-chain merkle root
348
+ // This is the critical security check - we trust the blockchain, not the metadata file
349
+ const merkleRootMatch = onChainMerkleRoot && onChainMerkleRoot.toLowerCase() === merkleRoot.toLowerCase();
350
+
351
+ if (this.debug) {
352
+ console.log(`[DEBUG] Merkle root match: ${merkleRootMatch}`);
353
+ }
354
+
355
+ // Also call verifyZipFile for additional validation (though we already have the root)
356
+ let verifyResult: boolean = false;
357
+ try {
358
+ verifyResult = await Promise.race([
359
+ contract.verifyZipFile(tokenId, merkleRoot),
360
+ new Promise<never>((_, reject) =>
361
+ setTimeout(() => reject(new Error('Contract call timeout after 30 seconds')), 30000)
362
+ )
363
+ ]);
364
+ } catch (verifyError: any) {
365
+ // If verifyZipFile fails but we have the root, we can still validate
366
+ if (this.debug) {
367
+ console.log(`[DEBUG] verifyZipFile call failed: ${verifyError.message}`);
368
+ }
369
+ }
370
+
371
+ // The merkle root comparison is the primary check - if roots match, verification passes
372
+ // verifyZipFile is a secondary check for additional validation
373
+ const isValid: boolean = merkleRootMatch === true;
374
+
375
+ if (this.debug) {
376
+ console.log(`[DEBUG] On-chain verification result: ${isValid} (merkle root match: ${merkleRootMatch}, verifyZipFile: ${verifyResult})`);
377
+ }
378
+
379
+ // Clean up provider to allow process to exit
380
+ try {
381
+ if (provider) {
382
+ provider.destroy();
383
+ }
384
+ } catch (cleanupError) {
385
+ // Ignore cleanup errors
386
+ if (this.debug) {
387
+ console.log(`[DEBUG] Error destroying provider: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
388
+ }
389
+ }
390
+
391
+ return {
392
+ success: true,
393
+ isValid,
394
+ onChainMerkleRoot,
395
+ onChainTokenizationTime,
396
+ onChainCreator,
397
+ onChainBlockNumber
398
+ };
399
+
400
+ } catch (error) {
401
+ const errorMessage = error instanceof Error ? error.message : 'Network error';
402
+
403
+ if (this.debug) {
404
+ console.log(`[DEBUG] On-chain verification failed: ${errorMessage}`);
405
+ }
406
+
407
+ // Clean up provider on error
408
+ try {
409
+ if (provider) {
410
+ provider.destroy();
411
+ }
412
+ } catch (cleanupError) {
413
+ // Ignore cleanup errors
414
+ }
415
+
416
+ return {
417
+ success: false,
418
+ error: errorMessage,
419
+ rpcUrl: rpcUrl
420
+ };
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Complete verification process
426
+ * @param rpcUrlIndex Optional index of RPC URL to use (default: 0)
427
+ */
428
+ async verifyToken(
429
+ tokenMetadata: TokenMetadata,
430
+ calculatedMerkleRoot: string | null,
431
+ options: VerificationOptions = {},
432
+ rpcUrlIndex: number = 0
433
+ ): Promise<VerificationResult> {
434
+ try {
435
+ if (this.debug) {
436
+ console.log(`[DEBUG] Starting complete token verification`);
437
+ }
438
+
439
+ // NOTE: tokenMetadata (tokenId, contractAddress, network) is used ONLY to locate the token on blockchain
440
+ // All verification data (merkle root, tokenization time, creator, block number) comes from blockchain contract
441
+ // If metadata is tampered with, the blockchain query will fail or return mismatched data, causing verification to fail
442
+
443
+ // Get network configuration - use chainId if available (most reliable)
444
+ const networkResult = this.getNetworkConfig(tokenMetadata.network, tokenMetadata.networkChainId);
445
+ if (!networkResult.success || !networkResult.config) {
446
+ return {
447
+ success: false,
448
+ message: networkResult.error || 'Unknown network error',
449
+ errorDetails: {
450
+ errorType: 'NETWORK_ERROR',
451
+ networkName: tokenMetadata.network
452
+ }
453
+ };
454
+ }
455
+
456
+ const networkConfig = networkResult.config;
457
+
458
+ // CRITICAL: We must calculate the merkle root from the actual files and compare with on-chain value
459
+ // We do NOT trust the merkle root stored in the metadata file as it could be tampered with
460
+ if (!calculatedMerkleRoot) {
461
+ return {
462
+ success: false,
463
+ message: 'No merkle root calculated from archive contents - cannot verify',
464
+ errorDetails: {
465
+ errorType: 'MERKLE_ERROR'
466
+ }
467
+ };
468
+ }
469
+
470
+ // Perform on-chain verification - this will fetch the actual merkle root from the blockchain
471
+ const onChainResult = await this.verifyOnChain(
472
+ tokenMetadata.tokenId,
473
+ tokenMetadata.contractAddress,
474
+ networkConfig,
475
+ calculatedMerkleRoot,
476
+ rpcUrlIndex
477
+ );
478
+
479
+ if (!onChainResult.success) {
480
+ return {
481
+ success: false,
482
+ message: `Blockchain verification failed: ${onChainResult.error}`,
483
+ errorDetails: {
484
+ errorType: 'CONTRACT_ERROR',
485
+ networkName: tokenMetadata.network,
486
+ rpcUrl: onChainResult.rpcUrl || networkConfig.rpcUrls[rpcUrlIndex] || 'unknown',
487
+ contractAddress: tokenMetadata.contractAddress,
488
+ tokenId: tokenMetadata.tokenId
489
+ }
490
+ };
491
+ }
492
+
493
+ // Compare calculated merkle root with on-chain merkle root
494
+ if (!onChainResult.onChainMerkleRoot) {
495
+ return {
496
+ success: false,
497
+ message: 'Blockchain verification failed - could not retrieve on-chain merkle root',
498
+ errorDetails: {
499
+ errorType: 'CONTRACT_ERROR',
500
+ tokenId: tokenMetadata.tokenId,
501
+ contractAddress: tokenMetadata.contractAddress
502
+ }
503
+ };
504
+ }
505
+
506
+ // This is the critical security check: calculated root must match on-chain root
507
+ const onChainRoot = onChainResult.onChainMerkleRoot.toLowerCase();
508
+ const calculatedRoot = calculatedMerkleRoot.toLowerCase();
509
+
510
+ if (onChainRoot !== calculatedRoot) {
511
+ return {
512
+ success: false,
513
+ message: 'Archive integrity verification FAILED - merkle root mismatch',
514
+ errorDetails: {
515
+ errorType: 'MERKLE_ERROR',
516
+ calculatedMerkleRoot: calculatedMerkleRoot,
517
+ onChainMerkleRoot: onChainResult.onChainMerkleRoot,
518
+ merkleRoot: `calculated: ${calculatedMerkleRoot}, on-chain: ${onChainResult.onChainMerkleRoot}` // Keep for backward compatibility
519
+ }
520
+ };
521
+ }
522
+
523
+ if (!onChainResult.isValid) {
524
+ return {
525
+ success: false,
526
+ message: 'Blockchain verification FAILED - contract verification returned false',
527
+ errorDetails: {
528
+ errorType: 'CONTRACT_ERROR',
529
+ tokenId: tokenMetadata.tokenId,
530
+ contractAddress: tokenMetadata.contractAddress,
531
+ merkleRoot: calculatedMerkleRoot
532
+ }
533
+ };
534
+ }
535
+
536
+ // Success - create verification details
537
+ // CRITICAL: Use ONLY blockchain data for display, not metadata which can be tampered with
538
+ const mintDate = onChainResult.onChainTokenizationTime
539
+ ? new Date(onChainResult.onChainTokenizationTime * 1000).toLocaleString()
540
+ : (tokenMetadata.mintDate ||
541
+ (tokenMetadata.creationTimestamp ? new Date(tokenMetadata.creationTimestamp * 1000).toLocaleString() : '') ||
542
+ 'Unknown'); // Use on-chain tokenization time as source of truth
543
+
544
+ return {
545
+ success: true,
546
+ message: 'Archive integrity VERIFIED against blockchain records',
547
+ tokenMetadata,
548
+ verificationDetails: {
549
+ // These identifiers come from metadata (needed to locate token), but are validated by blockchain query
550
+ tokenId: tokenMetadata.tokenId,
551
+ network: tokenMetadata.network,
552
+ contractAddress: tokenMetadata.contractAddress,
553
+ // CRITICAL: All verification data comes from blockchain, not metadata
554
+ merkleRoot: onChainResult.onChainMerkleRoot || calculatedMerkleRoot, // On-chain value (source of truth)
555
+ mintDate, // On-chain tokenizationTime converted to date string (source of truth)
556
+ calculatedMerkleRoot: calculatedMerkleRoot || undefined,
557
+ declaredMerkleRoot: onChainResult.onChainMerkleRoot || undefined, // On-chain value, not metadata
558
+ merkleRootMatch: true, // Verified: calculated root matches on-chain root
559
+ onChainValid: true
560
+ }
561
+ };
562
+
563
+ } catch (error) {
564
+ const errorMessage = error instanceof Error ? error.message : 'Unknown verification error';
565
+
566
+ if (this.debug) {
567
+ console.log(`[DEBUG] Verification process failed: ${errorMessage}`);
568
+ }
569
+
570
+ return {
571
+ success: false,
572
+ message: `Tokenization verification failed: ${errorMessage}`,
573
+ errorDetails: {
574
+ errorType: 'METADATA_ERROR'
575
+ }
576
+ };
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Verify token with retry logic and exponential backoff
582
+ * @param tokenMetadata Token metadata to verify
583
+ * @param calculatedMerkleRoot Calculated merkle root from archive
584
+ * @param maxRetries Maximum number of retry attempts (default: uses instance maxRetries)
585
+ * @returns Enhanced verification result with retry information
586
+ */
587
+ async verifyTokenWithRetry(
588
+ tokenMetadata: TokenMetadata,
589
+ calculatedMerkleRoot: string | null,
590
+ maxRetries?: number
591
+ ): Promise<EnhancedVerificationResult> {
592
+ const startTime = Date.now();
593
+ const retries = maxRetries || this.maxRetries;
594
+ let lastError: Error | null = null;
595
+
596
+ for (let attempt = 1; attempt <= retries; attempt++) {
597
+ try {
598
+ console.log(`🔄 Verification attempt ${attempt}/${retries} for token ${tokenMetadata.tokenId}`);
599
+
600
+ const result = await this.verifyToken(tokenMetadata, calculatedMerkleRoot);
601
+
602
+ if (result.success) {
603
+ console.log(`✅ Verification successful on attempt ${attempt} for token ${tokenMetadata.tokenId}`);
604
+ return {
605
+ ...result,
606
+ retryAttempts: attempt,
607
+ processingTime: Date.now() - startTime,
608
+ timestamp: Date.now()
609
+ };
610
+ }
611
+
612
+ lastError = new Error(result.message || 'Verification failed');
613
+
614
+ } catch (error) {
615
+ lastError = error instanceof Error ? error : new Error('Unknown verification error');
616
+ console.warn(`❌ Verification attempt ${attempt} failed for token ${tokenMetadata.tokenId}:`, lastError.message);
617
+
618
+ if (attempt < retries) {
619
+ const delay = this.calculateRetryDelay(attempt);
620
+ console.log(`⏳ Waiting ${delay}ms before retry...`);
621
+ await this.delay(delay);
622
+ }
623
+ }
624
+ }
625
+
626
+ return {
627
+ success: false,
628
+ message: `Verification failed after ${retries} attempts: ${lastError?.message || 'Unknown error'}`,
629
+ retryAttempts: retries,
630
+ processingTime: Date.now() - startTime,
631
+ timestamp: Date.now()
632
+ };
633
+ }
634
+
635
+ /**
636
+ * Batch verification for multiple tokens with rate limiting
637
+ * @param tokens Array of token metadata and merkle roots to verify
638
+ * @param options Batch processing options
639
+ * @returns Batch verification result
640
+ */
641
+ async verifyBatch(
642
+ tokens: Array<{ tokenMetadata: TokenMetadata; calculatedMerkleRoot: string | null }>,
643
+ options: {
644
+ parallel?: boolean;
645
+ maxConcurrent?: number;
646
+ } = {}
647
+ ): Promise<BatchVerificationResult> {
648
+ const startTime = Date.now();
649
+ const { parallel = false, maxConcurrent = 3 } = options;
650
+
651
+ console.log(`🔄 Starting batch verification for ${tokens.length} tokens (parallel: ${parallel})`);
652
+
653
+ let results: EnhancedVerificationResult[] = [];
654
+
655
+ if (parallel) {
656
+ // Parallel processing with concurrency limit
657
+ results = await this.verifyBatchParallel(tokens, maxConcurrent);
658
+ } else {
659
+ // Sequential processing with rate limiting
660
+ results = await this.verifyBatchSequential(tokens);
661
+ }
662
+
663
+ const successfulVerifications = results.filter(r => r.success).length;
664
+ const failedVerifications = results.length - successfulVerifications;
665
+
666
+ console.log(`✅ Batch verification completed: ${successfulVerifications}/${tokens.length} successful`);
667
+
668
+ return {
669
+ success: failedVerifications === 0,
670
+ totalTokens: tokens.length,
671
+ successfulVerifications,
672
+ failedVerifications,
673
+ results,
674
+ processingTime: Date.now() - startTime
675
+ };
676
+ }
677
+
678
+ /**
679
+ * Verify tokens in parallel with concurrency control
680
+ */
681
+ private async verifyBatchParallel(
682
+ tokens: Array<{ tokenMetadata: TokenMetadata; calculatedMerkleRoot: string | null }>,
683
+ maxConcurrent: number
684
+ ): Promise<EnhancedVerificationResult[]> {
685
+ const results: EnhancedVerificationResult[] = [];
686
+ const chunks: Array<{ tokenMetadata: TokenMetadata; calculatedMerkleRoot: string | null }[]> = [];
687
+
688
+ // Split tokens into chunks for parallel processing
689
+ for (let i = 0; i < tokens.length; i += maxConcurrent) {
690
+ chunks.push(tokens.slice(i, i + maxConcurrent));
691
+ }
692
+
693
+ // Process each chunk in parallel
694
+ for (const chunk of chunks) {
695
+ const chunkPromises = chunk.map(token =>
696
+ this.verifyTokenWithRetry(token.tokenMetadata, token.calculatedMerkleRoot)
697
+ );
698
+
699
+ const chunkResults = await Promise.all(chunkPromises);
700
+ results.push(...chunkResults);
701
+
702
+ // Add delay between chunks to avoid overwhelming the network
703
+ if (chunks.indexOf(chunk) < chunks.length - 1) {
704
+ await this.delay(this.batchDelay);
705
+ }
706
+ }
707
+
708
+ return results;
709
+ }
710
+
711
+ /**
712
+ * Verify tokens sequentially with rate limiting
713
+ */
714
+ private async verifyBatchSequential(
715
+ tokens: Array<{ tokenMetadata: TokenMetadata; calculatedMerkleRoot: string | null }>
716
+ ): Promise<EnhancedVerificationResult[]> {
717
+ const results: EnhancedVerificationResult[] = [];
718
+
719
+ for (const token of tokens) {
720
+ const result = await this.verifyTokenWithRetry(token.tokenMetadata, token.calculatedMerkleRoot);
721
+ results.push(result);
722
+
723
+ // Add delay between verifications to avoid rate limiting
724
+ if (tokens.indexOf(token) < tokens.length - 1) {
725
+ await this.delay(this.batchDelay);
726
+ }
727
+ }
728
+
729
+ return results;
730
+ }
731
+
732
+ /**
733
+ * Process verification jobs with priority queue
734
+ * @param jobs Array of verification jobs
735
+ * @param options Processing options
736
+ * @returns Batch verification result
737
+ */
738
+ async processVerificationJobs(
739
+ jobs: VerificationJob[],
740
+ options: {
741
+ maxConcurrent?: number;
742
+ priorityOrder?: boolean;
743
+ getTokenMetadata?: (tokenId: string) => Promise<TokenMetadata | null>;
744
+ getCalculatedMerkleRoot?: (tokenId: string) => Promise<string | null>;
745
+ } = {}
746
+ ): Promise<BatchVerificationResult> {
747
+ const { maxConcurrent = 3, priorityOrder = true, getTokenMetadata, getCalculatedMerkleRoot } = options;
748
+
749
+ if (!getTokenMetadata || !getCalculatedMerkleRoot) {
750
+ return {
751
+ success: false,
752
+ totalTokens: jobs.length,
753
+ successfulVerifications: 0,
754
+ failedVerifications: jobs.length,
755
+ results: [],
756
+ processingTime: 0
757
+ };
758
+ }
759
+
760
+ // Sort jobs by priority if requested
761
+ const sortedJobs = priorityOrder
762
+ ? this.sortJobsByPriority(jobs)
763
+ : jobs;
764
+
765
+ // Convert jobs to token format
766
+ const tokens: Array<{ tokenMetadata: TokenMetadata; calculatedMerkleRoot: string | null }> = [];
767
+
768
+ for (const job of sortedJobs) {
769
+ const tokenMetadata = await getTokenMetadata(job.tokenId);
770
+ const calculatedMerkleRoot = await getCalculatedMerkleRoot(job.tokenId);
771
+
772
+ if (tokenMetadata) {
773
+ tokens.push({
774
+ tokenMetadata,
775
+ calculatedMerkleRoot: calculatedMerkleRoot || null
776
+ });
777
+ }
778
+ }
779
+
780
+ return this.verifyBatch(tokens, { parallel: true, maxConcurrent });
781
+ }
782
+
783
+ /**
784
+ * Sort verification jobs by priority
785
+ */
786
+ private sortJobsByPriority(jobs: VerificationJob[]): VerificationJob[] {
787
+ const priorityOrder = { high: 0, normal: 1, low: 2 };
788
+
789
+ return jobs.sort((a, b) => {
790
+ const aPriority = priorityOrder[a.priority || 'normal'];
791
+ const bPriority = priorityOrder[b.priority || 'normal'];
792
+
793
+ if (aPriority !== bPriority) {
794
+ return aPriority - bPriority;
795
+ }
796
+
797
+ // If same priority, sort by retry count (fewer retries first)
798
+ return (a.retryCount || 0) - (b.retryCount || 0);
799
+ });
800
+ }
801
+
802
+ /**
803
+ * Calculate retry delay with exponential backoff
804
+ */
805
+ protected calculateRetryDelay(attempt: number): number {
806
+ return Math.min(this.retryDelay * Math.pow(2, attempt - 1), 30000); // Max 30 seconds
807
+ }
808
+
809
+ /**
810
+ * Utility method for delays
811
+ */
812
+ protected async delay(ms: number): Promise<void> {
813
+ return new Promise(resolve => setTimeout(resolve, ms));
814
+ }
815
+
816
+ /**
817
+ * Get verification statistics
818
+ */
819
+ getVerificationStats(): {
820
+ maxRetries: number;
821
+ retryDelay: number;
822
+ batchDelay: number;
823
+ } {
824
+ return {
825
+ maxRetries: this.maxRetries,
826
+ retryDelay: this.retryDelay,
827
+ batchDelay: this.batchDelay
828
+ };
829
+ }
830
+
831
+ /**
832
+ * Update verification configuration
833
+ */
834
+ updateConfig(config: {
835
+ maxRetries?: number;
836
+ retryDelay?: number;
837
+ batchDelay?: number;
838
+ }): void {
839
+ if (config.maxRetries !== undefined) {
840
+ this.maxRetries = config.maxRetries;
841
+ }
842
+ if (config.retryDelay !== undefined) {
843
+ this.retryDelay = config.retryDelay;
844
+ }
845
+ if (config.batchDelay !== undefined) {
846
+ this.batchDelay = config.batchDelay;
847
+ }
848
+
849
+ if (this.debug) {
850
+ console.log('🔧 ZipkitVerifier configuration updated:', this.getVerificationStats());
851
+ }
852
+ }
853
+ }