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,716 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ZipkitMinter - Handles NFT token minting for ZIP files
5
+ * Minter API for NZIP-NFT smart contract.
6
+ */
7
+
8
+ import { ethers } from 'ethers';
9
+ import { NZIP_CONTRACT_ABI, CONTRACT_CONFIGS, getContractConfig, getChainIdByName, getSupportedNetworkNames, type ContractConfig } from './contracts';
10
+ import type { TokenMetadata } from '../../types';
11
+
12
+ export interface MintingOptions {
13
+ walletPrivateKey: string;
14
+ network: string;
15
+ verbose?: boolean;
16
+ debug?: boolean;
17
+ rpcUrlIndex?: number; // RPC URL index to use (default: 0)
18
+ }
19
+
20
+ export interface MintingResult {
21
+ success: boolean;
22
+ message: string;
23
+ tokenId?: string;
24
+ transactionHash?: string;
25
+ contractAddress?: string;
26
+ blockNumber?: number;
27
+ walletAddress?: string;
28
+ gasUsed?: string;
29
+ gasCost?: string;
30
+ }
31
+
32
+ export interface TokenInfo {
33
+ tokenId: string;
34
+ owner: string;
35
+ isOwnedByUser: boolean;
36
+ tokenData?: any;
37
+ }
38
+
39
+ export interface DuplicateCheckResult {
40
+ hasExistingTokens: boolean;
41
+ allTokens: TokenInfo[];
42
+ userOwnedTokens: TokenInfo[];
43
+ othersTokens: TokenInfo[];
44
+ }
45
+
46
+ export interface WalletInfo {
47
+ address: string;
48
+ balance: string;
49
+ networkName: string;
50
+ }
51
+
52
+ /**
53
+ * Blockchain Token Minter for ZIP files
54
+ *
55
+ * SECURITY WARNING: This class handles private keys for blockchain operations.
56
+ * - Never hardcode private keys in source code
57
+ * - Always use environment variables for private keys (e.g., NEOZIP_WALLET_PASSKEY)
58
+ * - Use testnet keys for development/testing
59
+ * - Use secure key management (HSMs, KMS) for production
60
+ * - See SECURITY.md for complete security guidelines
61
+ */
62
+ export class ZipkitMinter {
63
+ protected provider: ethers.JsonRpcProvider;
64
+ protected wallet: ethers.Wallet;
65
+ protected contract: ethers.Contract;
66
+ protected merkleRoot: string;
67
+ protected networkConfig: ContractConfig;
68
+ protected debug: boolean;
69
+ protected rpcUrlIndex: number;
70
+
71
+ constructor(merkleRoot: string, options: MintingOptions) {
72
+ this.merkleRoot = merkleRoot;
73
+ this.debug = options.debug || false;
74
+ this.rpcUrlIndex = options.rpcUrlIndex ?? 0;
75
+
76
+ // Get chain ID from network name using contracts.ts (single source of truth)
77
+ const chainId = getChainIdByName(options.network);
78
+ if (!chainId) {
79
+ const supported = getSupportedNetworkNames().join(', ');
80
+ throw new Error(`Unsupported network: ${options.network}. Supported networks: ${supported}`);
81
+ }
82
+
83
+ // Get network configuration from contracts.ts
84
+ this.networkConfig = getContractConfig(chainId);
85
+
86
+ if (this.networkConfig.rpcUrls.length === 0) {
87
+ throw new Error(`No RPC URLs configured for network: ${options.network} (chainId: ${chainId})`);
88
+ }
89
+
90
+ // Validate RPC URL index
91
+ if (this.rpcUrlIndex < 0 || this.rpcUrlIndex >= this.networkConfig.rpcUrls.length) {
92
+ throw new Error(`RPC URL index ${this.rpcUrlIndex} is out of range. Available RPCs: ${this.networkConfig.rpcUrls.length}`);
93
+ }
94
+
95
+ // Use specified RPC URL index
96
+ const rpcUrl = this.networkConfig.rpcUrls[this.rpcUrlIndex];
97
+ this.provider = new ethers.JsonRpcProvider(rpcUrl);
98
+
99
+ if (this.debug) {
100
+ console.log(`[DEBUG] Using RPC URL index ${this.rpcUrlIndex}: ${rpcUrl}`);
101
+ console.log(`[DEBUG] Available RPC URLs: ${this.networkConfig.rpcUrls.length}`);
102
+ }
103
+
104
+ // Initialize wallet from private key
105
+ // SECURITY: Private key should come from environment variable, never hardcoded
106
+ this.wallet = new ethers.Wallet(options.walletPrivateKey, this.provider);
107
+
108
+ // Initialize contract instance
109
+ this.contract = new ethers.Contract(
110
+ this.networkConfig.address,
111
+ NZIP_CONTRACT_ABI,
112
+ this.wallet
113
+ );
114
+
115
+ if (this.debug) {
116
+ console.log(`[DEBUG] ZipkitMinter initialized for ${this.networkConfig.network}`);
117
+ console.log(`[DEBUG] Chain ID: ${this.networkConfig.chainId}`);
118
+ console.log(`[DEBUG] Contract Address: ${this.networkConfig.address}`);
119
+ console.log(`[DEBUG] Merkle Root: ${merkleRoot.substring(0, 10)}...`);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Get wallet information
125
+ */
126
+ async getWalletInfo(): Promise<WalletInfo> {
127
+ const balance = await this.provider.getBalance(this.wallet.address);
128
+ const balanceInEth = ethers.formatEther(balance);
129
+
130
+ if (this.debug) {
131
+ console.log(`[DEBUG] Wallet ${this.wallet.address} balance: ${balanceInEth} ETH`);
132
+ }
133
+
134
+ return {
135
+ address: this.wallet.address,
136
+ balance: balanceInEth,
137
+ networkName: this.networkConfig.network
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Check for existing tokens with same merkle root and verify ownership
143
+ */
144
+ async checkForDuplicates(): Promise<DuplicateCheckResult> {
145
+ try {
146
+ if (this.debug) {
147
+ console.log(`[DEBUG] Checking for existing tokens with merkle root: ${this.merkleRoot}`);
148
+ console.log(`[DEBUG] User wallet address: ${this.wallet.address}`);
149
+ }
150
+
151
+ // Call contract method to check if merkle root already exists
152
+ const existingTokenIds = await this.contract.getTokensByMerkleRoot(this.merkleRoot);
153
+
154
+ if (!existingTokenIds || existingTokenIds.length === 0) {
155
+ if (this.debug) {
156
+ console.log(`[DEBUG] No existing tokens found for this merkle root`);
157
+ }
158
+ return {
159
+ hasExistingTokens: false,
160
+ allTokens: [],
161
+ userOwnedTokens: [],
162
+ othersTokens: []
163
+ };
164
+ }
165
+
166
+ if (this.debug) {
167
+ console.log(`[DEBUG] Found ${existingTokenIds.length} existing token(s)`);
168
+ }
169
+
170
+ // Check ownership and get token details for each token
171
+ const allTokens: TokenInfo[] = [];
172
+ const userOwnedTokens: TokenInfo[] = [];
173
+ const othersTokens: TokenInfo[] = [];
174
+
175
+ for (const tokenId of existingTokenIds) {
176
+ const tokenIdStr = tokenId.toString();
177
+
178
+ try {
179
+ // Get token owner
180
+ const owner = await this.contract.ownerOf(tokenId);
181
+ const isOwnedByUser = owner.toLowerCase() === this.wallet.address.toLowerCase();
182
+
183
+ // Get token data
184
+ let tokenData = null;
185
+ try {
186
+ tokenData = await this.contract.getZipFileInfo(tokenId);
187
+ } catch (e) {
188
+ if (this.debug) {
189
+ console.log(`[DEBUG] Could not get token data for ${tokenIdStr}: ${e}`);
190
+ }
191
+ }
192
+
193
+ const tokenInfo: TokenInfo = {
194
+ tokenId: tokenIdStr,
195
+ owner: owner,
196
+ isOwnedByUser: isOwnedByUser,
197
+ tokenData: tokenData
198
+ };
199
+
200
+ allTokens.push(tokenInfo);
201
+
202
+ if (isOwnedByUser) {
203
+ userOwnedTokens.push(tokenInfo);
204
+ if (this.debug) {
205
+ console.log(`[DEBUG] Token ${tokenIdStr} is owned by user`);
206
+ }
207
+ } else {
208
+ othersTokens.push(tokenInfo);
209
+ if (this.debug) {
210
+ console.log(`[DEBUG] Token ${tokenIdStr} is owned by ${owner}`);
211
+ }
212
+ }
213
+
214
+ } catch (e) {
215
+ if (this.debug) {
216
+ console.log(`[DEBUG] Error checking ownership for token ${tokenIdStr}: ${e}`);
217
+ }
218
+ }
219
+ }
220
+
221
+ return {
222
+ hasExistingTokens: true,
223
+ allTokens,
224
+ userOwnedTokens,
225
+ othersTokens
226
+ };
227
+
228
+ } catch (error) {
229
+ if (this.debug) {
230
+ console.log(`[DEBUG] Error checking duplicates: ${error instanceof Error ? error.message : 'Unknown error'}`);
231
+ }
232
+
233
+ // If the contract method doesn't exist or fails, assume no duplicates
234
+ return {
235
+ hasExistingTokens: false,
236
+ allTokens: [],
237
+ userOwnedTokens: [],
238
+ othersTokens: []
239
+ };
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Estimate gas costs for minting
245
+ */
246
+ async estimateGasCosts(): Promise<{ gasLimit: bigint; gasPrice: bigint; estimatedCost: string }> {
247
+ const creationTimestamp = Math.floor(Date.now() / 1000);
248
+ const metadata = this.createTokenMetadataString();
249
+
250
+ // Estimate gas for the minting transaction
251
+ const gasLimit = await this.contract.publicMintZipFile.estimateGas(
252
+ this.merkleRoot,
253
+ creationTimestamp,
254
+ '', // ipfsHash
255
+ metadata
256
+ );
257
+
258
+ const feeData = await this.provider.getFeeData();
259
+ const gasPrice = feeData.gasPrice || BigInt(0);
260
+ const estimatedCost = ethers.formatEther(gasLimit * gasPrice);
261
+
262
+ if (this.debug) {
263
+ console.log(`[DEBUG] Gas estimate - Limit: ${gasLimit}, Price: ${gasPrice}, Cost: ${estimatedCost} ETH`);
264
+ }
265
+
266
+ return { gasLimit, gasPrice, estimatedCost };
267
+ }
268
+
269
+ /**
270
+ * Create token metadata string
271
+ */
272
+ private createTokenMetadataString(): string {
273
+ const now = new Date();
274
+ return JSON.stringify({
275
+ name: `NeoZip Archive`,
276
+ description: `Tokenized ZIP archive with cryptographic proof of integrity`,
277
+ image: '', // Could be a thumbnail or icon
278
+ merkleRoot: this.merkleRoot,
279
+ network: this.networkConfig.network,
280
+ createdAt: now.toISOString(),
281
+ timestamp: Math.floor(now.getTime() / 1000)
282
+ });
283
+ }
284
+
285
+ /**
286
+ * Create TokenMetadata object from minting result
287
+ */
288
+ private createTokenMetadata(tokenId: string, transactionHash: string, blockNumber?: number): TokenMetadata {
289
+ const now = new Date();
290
+
291
+ return {
292
+ tokenId,
293
+ network: this.networkConfig.network,
294
+ networkChainId: this.networkConfig.chainId,
295
+ contractAddress: this.networkConfig.address,
296
+ merkleRoot: this.merkleRoot,
297
+ mintDate: now.toLocaleDateString('en-US') + ' at ' + now.toLocaleTimeString('en-US'),
298
+ creationTimestamp: Math.floor(now.getTime() / 1000),
299
+ transactionHash,
300
+ blockNumber
301
+ };
302
+ }
303
+
304
+ /**
305
+ * Mint new token on blockchain
306
+ */
307
+ async mintToken(): Promise<MintingResult> {
308
+ try {
309
+ if (this.debug) {
310
+ console.log(`[DEBUG] Starting token minting process`);
311
+ }
312
+
313
+ // Generate token ID (could be random or sequential)
314
+ const tokenId = Math.floor(Math.random() * 1000000).toString();
315
+ const metadata = this.createTokenMetadataString();
316
+ const creationTimestamp = Math.floor(Date.now() / 1000);
317
+
318
+ if (this.debug) {
319
+ console.log(`[DEBUG] Generated token ID: ${tokenId}`);
320
+ console.log(`[DEBUG] Metadata: ${metadata.substring(0, 100)}...`);
321
+ }
322
+
323
+ // Estimate gas first (with timeout)
324
+ if (this.debug) {
325
+ console.log(`[DEBUG] Estimating gas for transaction...`);
326
+ }
327
+
328
+ let gasLimit: bigint;
329
+ let gasPrice: bigint;
330
+ try {
331
+ const gasEstimate = await Promise.race([
332
+ this.contract.publicMintZipFile.estimateGas(
333
+ this.merkleRoot,
334
+ creationTimestamp,
335
+ '', // ipfsHash
336
+ metadata
337
+ ),
338
+ new Promise<never>((_, reject) =>
339
+ setTimeout(() => reject(new Error('Gas estimation timeout after 20 seconds')), 20000)
340
+ )
341
+ ]);
342
+
343
+ // Add 20% buffer to gas estimate
344
+ gasLimit = (gasEstimate * BigInt(120)) / BigInt(100);
345
+
346
+ // Get current gas price
347
+ const feeData = await Promise.race([
348
+ this.provider.getFeeData(),
349
+ new Promise<never>((_, reject) =>
350
+ setTimeout(() => reject(new Error('Gas price fetch timeout after 10 seconds')), 10000)
351
+ )
352
+ ]);
353
+ gasPrice = feeData.gasPrice || feeData.maxFeePerGas || BigInt(0);
354
+
355
+ if (gasPrice === BigInt(0)) {
356
+ throw new Error('Could not get gas price from network');
357
+ }
358
+
359
+ const estimatedCost = ethers.formatEther(gasLimit * gasPrice);
360
+ const balance = await this.provider.getBalance(this.wallet.address);
361
+ const balanceEth = ethers.formatEther(balance);
362
+
363
+ if (this.debug) {
364
+ console.log(`[DEBUG] Gas estimate: ${gasLimit.toString()}`);
365
+ console.log(`[DEBUG] Gas price: ${ethers.formatUnits(gasPrice, 'gwei')} gwei`);
366
+ console.log(`[DEBUG] Estimated cost: ${estimatedCost} ETH`);
367
+ console.log(`[DEBUG] Wallet balance: ${balanceEth} ETH`);
368
+ }
369
+
370
+ // Check if we have enough balance
371
+ if (balance < gasLimit * gasPrice) {
372
+ throw new Error(`Insufficient balance. Need ${estimatedCost} ETH, have ${balanceEth} ETH`);
373
+ }
374
+
375
+ } catch (error) {
376
+ const errorMessage = error instanceof Error ? error.message : 'Gas estimation failed';
377
+ if (this.debug) {
378
+ console.log(`[DEBUG] Gas estimation failed: ${errorMessage}`);
379
+ }
380
+ throw new Error(`Gas estimation failed: ${errorMessage}`);
381
+ }
382
+
383
+ // Call the contract publicMintZipFile function with explicit gas parameters
384
+ if (this.debug) {
385
+ console.log(`[DEBUG] Submitting transaction with gas limit: ${gasLimit.toString()}, gas price: ${ethers.formatUnits(gasPrice, 'gwei')} gwei`);
386
+ }
387
+
388
+ const tx = await Promise.race([
389
+ this.contract.publicMintZipFile(
390
+ this.merkleRoot,
391
+ creationTimestamp,
392
+ '', // ipfsHash (empty since we store in ZIP)
393
+ metadata,
394
+ {
395
+ gasLimit: gasLimit,
396
+ gasPrice: gasPrice
397
+ }
398
+ ),
399
+ new Promise<never>((_, reject) =>
400
+ setTimeout(() => reject(new Error('Transaction submission timeout after 30 seconds')), 30000)
401
+ )
402
+ ]);
403
+
404
+ if (this.debug) {
405
+ console.log(`[DEBUG] Transaction submitted: ${tx.hash}`);
406
+ process.stdout.write(`[DEBUG] Waiting for confirmation (timeout: 120s)`);
407
+ } else {
408
+ // Show waiting message for non-debug mode
409
+ process.stdout.write('⏳ Waiting for confirmation');
410
+ }
411
+
412
+ // Animated waiting dots (first dot immediately, then one every 2 seconds)
413
+ let dotCount = 0;
414
+ process.stdout.write('.'); // Show first dot immediately
415
+ dotCount++;
416
+
417
+ const dotInterval = setInterval(() => {
418
+ dotCount++;
419
+ process.stdout.write('.');
420
+ if (dotCount >= 60) { // Max 60 dots (120 seconds)
421
+ clearInterval(dotInterval);
422
+ }
423
+ }, 2000);
424
+
425
+ // Wait for transaction confirmation with timeout
426
+ let receipt: any;
427
+ try {
428
+ receipt = await Promise.race([
429
+ tx.wait(),
430
+ new Promise<never>((_, reject) =>
431
+ setTimeout(() => reject(new Error('Transaction confirmation timeout after 120 seconds')), 120000)
432
+ )
433
+ ]);
434
+
435
+ clearInterval(dotInterval);
436
+ if (dotCount > 0) {
437
+ console.log(''); // New line after dots
438
+ }
439
+ } catch (error) {
440
+ clearInterval(dotInterval);
441
+ if (dotCount > 0) {
442
+ console.log(''); // New line after dots
443
+ }
444
+ throw error;
445
+ }
446
+
447
+ if (this.debug) {
448
+ console.log(`[DEBUG] Transaction confirmed in block: ${receipt.blockNumber}`);
449
+ console.log(`[DEBUG] Gas used: ${receipt.gasUsed}`);
450
+ }
451
+
452
+ // Extract actual token ID from contract events
453
+ let actualTokenId = tokenId; // fallback
454
+
455
+ if (receipt.logs && receipt.logs.length > 0) {
456
+ try {
457
+ const iface = new ethers.Interface(NZIP_CONTRACT_ABI);
458
+ for (const log of receipt.logs) {
459
+ try {
460
+ const parsed = iface.parseLog(log);
461
+ if (parsed?.name === 'ZipFileTokenized') {
462
+ actualTokenId = parsed.args.tokenId.toString();
463
+ if (this.debug) {
464
+ console.log(`[DEBUG] Actual token ID from contract event: ${actualTokenId}`);
465
+ }
466
+ break;
467
+ }
468
+ } catch (e) {
469
+ // Ignore parsing errors for non-matching logs
470
+ }
471
+ }
472
+ } catch (e) {
473
+ if (this.debug) {
474
+ console.log(`[DEBUG] Could not parse token ID from logs: ${e}`);
475
+ }
476
+ }
477
+ }
478
+
479
+ const gasCost = ethers.formatEther(receipt.gasUsed * (receipt.gasPrice || BigInt(0)));
480
+
481
+ return {
482
+ success: true,
483
+ message: `Token successfully minted`,
484
+ tokenId: actualTokenId,
485
+ transactionHash: tx.hash,
486
+ contractAddress: this.networkConfig.address,
487
+ blockNumber: receipt.blockNumber,
488
+ walletAddress: this.wallet.address,
489
+ gasUsed: receipt.gasUsed.toString(),
490
+ gasCost
491
+ };
492
+
493
+ } catch (error) {
494
+ const errorMessage = error instanceof Error ? error.message : 'Unknown minting error';
495
+
496
+ if (this.debug) {
497
+ console.log(`[DEBUG] Minting failed: ${errorMessage}`);
498
+ }
499
+
500
+ return {
501
+ success: false,
502
+ message: errorMessage
503
+ };
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Main minting process - checks for duplicates, then mints if needed
509
+ */
510
+ async processMinting(): Promise<{
511
+ walletInfo: WalletInfo;
512
+ duplicateCheck: DuplicateCheckResult;
513
+ gasCosts?: { gasLimit: bigint; gasPrice: bigint; estimatedCost: string };
514
+ mintingResult?: MintingResult;
515
+ tokenMetadata?: TokenMetadata;
516
+ }> {
517
+ try {
518
+ // Get wallet information
519
+ const walletInfo = await this.getWalletInfo();
520
+
521
+ // Check for duplicates first (free operation)
522
+ const duplicateCheck = await this.checkForDuplicates();
523
+
524
+ if (duplicateCheck.hasExistingTokens) {
525
+ if (this.debug) {
526
+ console.log(`[DEBUG] Existing tokens found, user will choose`);
527
+ }
528
+
529
+ return {
530
+ walletInfo,
531
+ duplicateCheck
532
+ };
533
+ }
534
+
535
+ // Estimate gas costs
536
+ const gasCosts = await this.estimateGasCosts();
537
+
538
+ // This is where the user would be prompted for confirmation in the calling code
539
+ // The actual minting happens in a separate call to mintToken()
540
+
541
+ return {
542
+ walletInfo,
543
+ duplicateCheck,
544
+ gasCosts
545
+ };
546
+
547
+ } catch (error) {
548
+ throw new Error(`Minting process failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
549
+ }
550
+ }
551
+
552
+ /**
553
+ * Complete the minting after user confirmation
554
+ */
555
+ async completeMinting(): Promise<{
556
+ mintingResult: MintingResult;
557
+ tokenMetadata?: TokenMetadata;
558
+ }> {
559
+ const mintingResult = await this.mintToken();
560
+
561
+ if (mintingResult.success && mintingResult.tokenId && mintingResult.transactionHash) {
562
+ const tokenMetadata = this.createTokenMetadata(
563
+ mintingResult.tokenId,
564
+ mintingResult.transactionHash,
565
+ mintingResult.blockNumber
566
+ );
567
+
568
+ return {
569
+ mintingResult,
570
+ tokenMetadata
571
+ };
572
+ }
573
+
574
+ return { mintingResult };
575
+ }
576
+
577
+ /**
578
+ * Switch to a different RPC URL by index
579
+ * This recreates the provider, wallet, and contract with the new RPC
580
+ */
581
+ switchRpcUrl(newIndex: number): void {
582
+ if (newIndex < 0 || newIndex >= this.networkConfig.rpcUrls.length) {
583
+ throw new Error(`RPC URL index ${newIndex} is out of range. Available RPCs: ${this.networkConfig.rpcUrls.length}`);
584
+ }
585
+
586
+ // Destroy old provider
587
+ if (this.provider) {
588
+ try {
589
+ this.provider.destroy();
590
+ } catch (error) {
591
+ // Ignore errors during cleanup
592
+ }
593
+ }
594
+
595
+ // Update RPC index
596
+ this.rpcUrlIndex = newIndex;
597
+ const rpcUrl = this.networkConfig.rpcUrls[this.rpcUrlIndex];
598
+
599
+ // Create new provider
600
+ this.provider = new ethers.JsonRpcProvider(rpcUrl);
601
+
602
+ // Recreate wallet with new provider
603
+ this.wallet = new ethers.Wallet(this.wallet.privateKey, this.provider);
604
+
605
+ // Recreate contract with new provider
606
+ this.contract = new ethers.Contract(
607
+ this.networkConfig.address,
608
+ NZIP_CONTRACT_ABI,
609
+ this.wallet
610
+ );
611
+
612
+ if (this.debug) {
613
+ console.log(`[DEBUG] Switched to RPC URL index ${this.rpcUrlIndex}: ${rpcUrl}`);
614
+ }
615
+ }
616
+
617
+ /**
618
+ * Destroy provider to allow process to exit
619
+ */
620
+ destroy(): void {
621
+ try {
622
+ if (this.provider) {
623
+ // Destroy the provider to close connections and allow process to exit
624
+ // Safe to call multiple times - ethers.js handles idempotency
625
+ this.provider.destroy();
626
+ if (this.debug) {
627
+ console.log('[DEBUG] Provider destroyed');
628
+ }
629
+ }
630
+ } catch (error) {
631
+ if (this.debug) {
632
+ console.log(`[DEBUG] Error destroying provider: ${error instanceof Error ? error.message : String(error)}`);
633
+ }
634
+ }
635
+ }
636
+
637
+ /**
638
+ * Mint token with retry logic and exponential backoff
639
+ * @param maxRetries Maximum number of retry attempts (default: 3)
640
+ * @returns Minting result with retry information
641
+ */
642
+ async mintTokenWithRetry(maxRetries: number = 3): Promise<MintingResult> {
643
+ let lastError: Error | null = null;
644
+
645
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
646
+ try {
647
+ console.log(`🔄 Minting attempt ${attempt}/${maxRetries}`);
648
+ const result = await this.mintToken();
649
+
650
+ if (result.success) {
651
+ console.log(`✅ Minting successful on attempt ${attempt}`);
652
+ return result;
653
+ }
654
+
655
+ lastError = new Error(result.message || 'Minting failed');
656
+
657
+ } catch (error) {
658
+ lastError = error instanceof Error ? error : new Error('Unknown error');
659
+ console.warn(`❌ Minting attempt ${attempt} failed:`, lastError.message);
660
+
661
+ if (attempt < maxRetries) {
662
+ const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
663
+ console.log(`⏳ Waiting ${delay}ms before retry...`);
664
+ await new Promise(resolve => setTimeout(resolve, delay));
665
+ }
666
+ }
667
+ }
668
+
669
+ return {
670
+ success: false,
671
+ message: `Minting failed after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}`
672
+ };
673
+ }
674
+
675
+ /**
676
+ * Batch minting for multiple archives
677
+ * @param archives Array of archive objects with name and merkleRoot
678
+ * @returns Array of minting results
679
+ */
680
+ async mintBatch(archives: Array<{ name: string; merkleRoot: string }>): Promise<MintingResult[]> {
681
+ const results: MintingResult[] = [];
682
+
683
+ for (const archive of archives) {
684
+ try {
685
+ // Update the merkle root for this archive
686
+ this.merkleRoot = archive.merkleRoot;
687
+
688
+ const result = await this.mintToken();
689
+ results.push(result);
690
+
691
+ // Add delay between mints to avoid rate limiting
692
+ await new Promise(resolve => setTimeout(resolve, 1000));
693
+
694
+ } catch (error) {
695
+ results.push({
696
+ success: false,
697
+ message: `Failed to mint ${archive.name}: ${error instanceof Error ? error.message : 'Unknown error'}`
698
+ });
699
+ }
700
+ }
701
+
702
+ return results;
703
+ }
704
+ }
705
+
706
+ /**
707
+ * Utility function to validate Ethereum private key
708
+ */
709
+ export function validatePrivateKey(privateKey: string): boolean {
710
+ try {
711
+ new ethers.Wallet(privateKey);
712
+ return true;
713
+ } catch {
714
+ return false;
715
+ }
716
+ }