neozip-blockchain 0.5.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 (127) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +189 -0
  3. package/dist/browser/TokenVerifierBrowser.d.ts +63 -0
  4. package/dist/browser/TokenVerifierBrowser.d.ts.map +1 -0
  5. package/dist/browser/TokenVerifierBrowser.js +358 -0
  6. package/dist/browser/TokenVerifierBrowser.js.map +1 -0
  7. package/dist/browser/WalletManagerBrowser.d.ts +99 -0
  8. package/dist/browser/WalletManagerBrowser.d.ts.map +1 -0
  9. package/dist/browser/WalletManagerBrowser.js +434 -0
  10. package/dist/browser/WalletManagerBrowser.js.map +1 -0
  11. package/dist/browser/ZipkitMinterBrowser.d.ts +59 -0
  12. package/dist/browser/ZipkitMinterBrowser.d.ts.map +1 -0
  13. package/dist/browser/ZipkitMinterBrowser.js +337 -0
  14. package/dist/browser/ZipkitMinterBrowser.js.map +1 -0
  15. package/dist/browser/index.d.ts +12 -0
  16. package/dist/browser/index.d.ts.map +1 -0
  17. package/dist/browser/index.js +19 -0
  18. package/dist/browser/index.js.map +1 -0
  19. package/dist/constants/index.d.ts +5 -0
  20. package/dist/constants/index.d.ts.map +1 -0
  21. package/dist/constants/index.js +21 -0
  22. package/dist/constants/index.js.map +1 -0
  23. package/dist/constants/metadata.d.ts +18 -0
  24. package/dist/constants/metadata.d.ts.map +1 -0
  25. package/dist/constants/metadata.js +33 -0
  26. package/dist/constants/metadata.js.map +1 -0
  27. package/dist/constants/servers.d.ts +38 -0
  28. package/dist/constants/servers.d.ts.map +1 -0
  29. package/dist/constants/servers.js +54 -0
  30. package/dist/constants/servers.js.map +1 -0
  31. package/dist/core/ContractVersionRegistry.d.ts +63 -0
  32. package/dist/core/ContractVersionRegistry.d.ts.map +1 -0
  33. package/dist/core/ContractVersionRegistry.js +128 -0
  34. package/dist/core/ContractVersionRegistry.js.map +1 -0
  35. package/dist/core/WalletManager.d.ts +123 -0
  36. package/dist/core/WalletManager.d.ts.map +1 -0
  37. package/dist/core/WalletManager.js +360 -0
  38. package/dist/core/WalletManager.js.map +1 -0
  39. package/dist/core/ZipkitMinter.d.ts +155 -0
  40. package/dist/core/ZipkitMinter.d.ts.map +1 -0
  41. package/dist/core/ZipkitMinter.js +625 -0
  42. package/dist/core/ZipkitMinter.js.map +1 -0
  43. package/dist/core/ZipkitVerifier.d.ts +252 -0
  44. package/dist/core/ZipkitVerifier.d.ts.map +1 -0
  45. package/dist/core/ZipkitVerifier.js +1370 -0
  46. package/dist/core/ZipkitVerifier.js.map +1 -0
  47. package/dist/core/adapters/AdapterFactory.d.ts +22 -0
  48. package/dist/core/adapters/AdapterFactory.d.ts.map +1 -0
  49. package/dist/core/adapters/AdapterFactory.js +62 -0
  50. package/dist/core/adapters/AdapterFactory.js.map +1 -0
  51. package/dist/core/adapters/ContractVersionAdapter.d.ts +96 -0
  52. package/dist/core/adapters/ContractVersionAdapter.d.ts.map +1 -0
  53. package/dist/core/adapters/ContractVersionAdapter.js +9 -0
  54. package/dist/core/adapters/ContractVersionAdapter.js.map +1 -0
  55. package/dist/core/adapters/V2_10Adapter.d.ts +24 -0
  56. package/dist/core/adapters/V2_10Adapter.d.ts.map +1 -0
  57. package/dist/core/adapters/V2_10Adapter.js +87 -0
  58. package/dist/core/adapters/V2_10Adapter.js.map +1 -0
  59. package/dist/core/adapters/V2_11Adapter.d.ts +24 -0
  60. package/dist/core/adapters/V2_11Adapter.d.ts.map +1 -0
  61. package/dist/core/adapters/V2_11Adapter.js +87 -0
  62. package/dist/core/adapters/V2_11Adapter.js.map +1 -0
  63. package/dist/core/adapters/V2_50Adapter.d.ts +105 -0
  64. package/dist/core/adapters/V2_50Adapter.d.ts.map +1 -0
  65. package/dist/core/adapters/V2_50Adapter.js +263 -0
  66. package/dist/core/adapters/V2_50Adapter.js.map +1 -0
  67. package/dist/core/contracts.d.ts +169 -0
  68. package/dist/core/contracts.d.ts.map +1 -0
  69. package/dist/core/contracts.js +731 -0
  70. package/dist/core/contracts.js.map +1 -0
  71. package/dist/core/index.d.ts +24 -0
  72. package/dist/core/index.d.ts.map +1 -0
  73. package/dist/core/index.js +82 -0
  74. package/dist/core/index.js.map +1 -0
  75. package/dist/index.d.ts +15 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +40 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/node/WalletManagerNode.d.ts +47 -0
  80. package/dist/node/WalletManagerNode.d.ts.map +1 -0
  81. package/dist/node/WalletManagerNode.js +203 -0
  82. package/dist/node/WalletManagerNode.js.map +1 -0
  83. package/dist/node/index.d.ts +12 -0
  84. package/dist/node/index.d.ts.map +1 -0
  85. package/dist/node/index.js +21 -0
  86. package/dist/node/index.js.map +1 -0
  87. package/dist/ots/ZipkitOTS.d.ts +152 -0
  88. package/dist/ots/ZipkitOTS.d.ts.map +1 -0
  89. package/dist/ots/ZipkitOTS.js +544 -0
  90. package/dist/ots/ZipkitOTS.js.map +1 -0
  91. package/dist/ots/index.d.ts +9 -0
  92. package/dist/ots/index.d.ts.map +1 -0
  93. package/dist/ots/index.js +25 -0
  94. package/dist/ots/index.js.map +1 -0
  95. package/dist/types/index.d.ts +84 -0
  96. package/dist/types/index.d.ts.map +1 -0
  97. package/dist/types/index.js +37 -0
  98. package/dist/types/index.js.map +1 -0
  99. package/dist/utils/Logger.d.ts +73 -0
  100. package/dist/utils/Logger.d.ts.map +1 -0
  101. package/dist/utils/Logger.js +156 -0
  102. package/dist/utils/Logger.js.map +1 -0
  103. package/dist/utils/crc32.d.ts +6 -0
  104. package/dist/utils/crc32.d.ts.map +1 -0
  105. package/dist/utils/crc32.js +23 -0
  106. package/dist/utils/crc32.js.map +1 -0
  107. package/dist/zipstamp-server/CalendarManager.d.ts +238 -0
  108. package/dist/zipstamp-server/CalendarManager.d.ts.map +1 -0
  109. package/dist/zipstamp-server/CalendarManager.js +364 -0
  110. package/dist/zipstamp-server/CalendarManager.js.map +1 -0
  111. package/dist/zipstamp-server/ZipstampServerAPI.d.ts +427 -0
  112. package/dist/zipstamp-server/ZipstampServerAPI.d.ts.map +1 -0
  113. package/dist/zipstamp-server/ZipstampServerAPI.js +696 -0
  114. package/dist/zipstamp-server/ZipstampServerAPI.js.map +1 -0
  115. package/dist/zipstamp-server/ZipstampServerClient.d.ts +590 -0
  116. package/dist/zipstamp-server/ZipstampServerClient.d.ts.map +1 -0
  117. package/dist/zipstamp-server/ZipstampServerClient.js +433 -0
  118. package/dist/zipstamp-server/ZipstampServerClient.js.map +1 -0
  119. package/dist/zipstamp-server/ZipstampServerHelpers.d.ts +430 -0
  120. package/dist/zipstamp-server/ZipstampServerHelpers.d.ts.map +1 -0
  121. package/dist/zipstamp-server/ZipstampServerHelpers.js +503 -0
  122. package/dist/zipstamp-server/ZipstampServerHelpers.js.map +1 -0
  123. package/dist/zipstamp-server/index.d.ts +24 -0
  124. package/dist/zipstamp-server/index.d.ts.map +1 -0
  125. package/dist/zipstamp-server/index.js +67 -0
  126. package/dist/zipstamp-server/index.js.map +1 -0
  127. package/package.json +117 -0
@@ -0,0 +1,696 @@
1
+ "use strict";
2
+ /**
3
+ * ======================================
4
+ * ZipstampServerAPI.ts - Zipstamp Server API Integration
5
+ * ======================================
6
+ *
7
+ * This module provides timestamping functionality using the Zipstamp server API,
8
+ * similar to OpenTimestamps but using Ethereum blockchain.
9
+ *
10
+ * Key Features:
11
+ * - Ethereum blockchain timestamping via Zipstamp server
12
+ * - Merkle root calculation and verification
13
+ * - ZIP file integration for timestamp metadata
14
+ * - Support for immediate, batched, and transferable modes
15
+ *
16
+ * @fileoverview Zipstamp server API utilities for blockchain timestamping
17
+ * @author NeoWare Inc.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.CMP_METHOD = void 0;
21
+ exports.getEthTimestampEntry = getEthTimestampEntry;
22
+ exports.extractTimestampData = extractTimestampData;
23
+ exports.createTimestamp = createTimestamp;
24
+ exports.verifyTimestamp = verifyTimestamp;
25
+ exports.verifyTimestampedZip = verifyTimestampedZip;
26
+ exports.createTimestampMetadataEntry = createTimestampMetadataEntry;
27
+ exports.createTimestampedZip = createTimestampedZip;
28
+ exports.findMetadataEntry = findMetadataEntry;
29
+ exports.getMetadataType = getMetadataType;
30
+ exports.shouldUpgrade = shouldUpgrade;
31
+ exports.getMetadataFileNames = getMetadataFileNames;
32
+ exports.verifyMerkleProofLocal = verifyMerkleProofLocal;
33
+ const contracts_1 = require("../core/contracts");
34
+ const servers_1 = require("../constants/servers");
35
+ const metadata_1 = require("../constants/metadata");
36
+ const crc32_1 = require("../utils/crc32");
37
+ const ethers_1 = require("ethers");
38
+ const ZipstampServerHelpers_1 = require("./ZipstampServerHelpers");
39
+ exports.CMP_METHOD = { STORED: 0, DEFLATED: 8 };
40
+ /**
41
+ * Safely extracts merkle root from a ZIP file.
42
+ */
43
+ function getMerkleRootSafe(zip) {
44
+ try {
45
+ const mr = zip.getMerkleRoot?.();
46
+ return (typeof mr === 'string' && mr.length > 0) ? mr : null;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Finds the timestamp metadata entry in a ZIP file.
54
+ *
55
+ * Searches for timestamp metadata entries in the ZIP directory, preferring confirmed
56
+ * timestamps (TIMESTAMP.NZIP) over pending submissions (TS-SUBMIT.NZIP).
57
+ *
58
+ * @param zip - A ZipkitLike instance representing the ZIP file to search
59
+ * @returns The ZIP entry containing timestamp metadata, or `null` if not found
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const entry = getEthTimestampEntry(zipkit);
64
+ * if (entry) {
65
+ * const metadata = await extractTimestampData(zipkit, entry);
66
+ * console.log('Timestamp status:', metadata?.status);
67
+ * }
68
+ * ```
69
+ */
70
+ function getEthTimestampEntry(zip) {
71
+ try {
72
+ const entries = zip.getDirectory?.(true) || [];
73
+ // Prefer confirmed metadata entry first
74
+ let entry = entries.find((e) => e && e.filename === metadata_1.TIMESTAMP_NZIP);
75
+ if (entry)
76
+ return entry;
77
+ // Fallback to submitted proof entry
78
+ entry = entries.find((e) => e && e.filename === metadata_1.TS_SUBMIT_NZIP);
79
+ if (entry)
80
+ return entry;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ return null;
86
+ }
87
+ /**
88
+ * Extracts timestamp metadata from a ZIP entry.
89
+ *
90
+ * Reads and parses the JSON metadata from a timestamp entry (either TIMESTAMP.NZIP
91
+ * or TS-SUBMIT.NZIP) in the ZIP file.
92
+ *
93
+ * @param zip - A ZipkitLike instance representing the ZIP file
94
+ * @param entry - The ZIP entry containing the timestamp metadata (from `getEthTimestampEntry`)
95
+ * @returns Parsed timestamp metadata object, or `null` if extraction fails
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const entry = getEthTimestampEntry(zipkit);
100
+ * if (entry) {
101
+ * const metadata = await extractTimestampData(zipkit, entry);
102
+ * if (metadata) {
103
+ * console.log('Digest:', metadata.digest);
104
+ * console.log('Batch ID:', metadata.batchId);
105
+ * }
106
+ * }
107
+ * ```
108
+ */
109
+ async function extractTimestampData(zip, entry) {
110
+ try {
111
+ const data = await zip.extract?.(entry, true);
112
+ if (!data)
113
+ return null;
114
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
115
+ const jsonString = buffer.toString('utf-8');
116
+ const metadata = JSON.parse(jsonString);
117
+ return metadata;
118
+ }
119
+ catch (error) {
120
+ return null;
121
+ }
122
+ }
123
+ /**
124
+ * Creates a timestamp by submitting a merkle root to the Zipstamp server.
125
+ *
126
+ * Submits a SHA-256 digest (merkle root) to the Zipstamp server API for blockchain
127
+ * timestamping. The digest is typically the merkle root of a ZIP file's contents.
128
+ *
129
+ * The function uses `submitDigest` internally and converts the response to a
130
+ * `TimestampMetadata` format suitable for storage in ZIP files.
131
+ *
132
+ * @param merkleRoot - A 64-character hexadecimal string representing the SHA-256 hash
133
+ * @param options - Configuration options for timestamp creation
134
+ * @param options.recipientEmail - Optional email address for notifications
135
+ * @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
136
+ * @param options.debug - Enable debug logging (defaults to `false`)
137
+ * @param options.mode - **Deprecated**: For backward compatibility only, not sent to API
138
+ * @param options.recipientAddress - **Deprecated**: For backward compatibility only, not sent to API
139
+ * @param options.metadata - **Deprecated**: For backward compatibility only, not sent to API
140
+ * @returns Promise resolving to timestamp metadata, or `null` if creation fails
141
+ * @throws {Error} If merkle root format is invalid or API request fails
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const merkleRoot = 'a1b2c3d4e5f6...'; // 64-char hex string
146
+ * const metadata = await createTimestamp(merkleRoot, {
147
+ * recipientEmail: 'user@example.com',
148
+ * debug: true
149
+ * });
150
+ *
151
+ * if (metadata) {
152
+ * console.log('Timestamp created:', metadata.batchId);
153
+ * console.log('Status:', metadata.status); // 'pending' or 'confirmed'
154
+ * }
155
+ * ```
156
+ */
157
+ async function createTimestamp(merkleRoot, options = {}) {
158
+ if (!merkleRoot || !/^[a-f0-9]{64}$/i.test(merkleRoot)) {
159
+ throw new Error('Invalid merkle root format. Must be a 64-character hex string (SHA-256)');
160
+ }
161
+ try {
162
+ const serverUrl = (0, servers_1.getZipStampServerUrl)(options);
163
+ if (options.debug) {
164
+ console.log('[Zipstamp Server API] ========================================');
165
+ console.log('[Zipstamp Server API] Creating Timestamp');
166
+ console.log('[Zipstamp Server API] ========================================');
167
+ console.log('[Zipstamp Server API] Server URL:', serverUrl);
168
+ console.log('[Zipstamp Server API] Merkle Root:', merkleRoot);
169
+ console.log('[Zipstamp Server API] ----------------------------------------');
170
+ }
171
+ // Use submitDigest helper (only digest, email, and chainId are sent to API)
172
+ // Note: mode, recipientAddress, and metadata options are for backward compatibility
173
+ // but are not actually sent to the Zipstamp server API
174
+ const response = await (0, ZipstampServerHelpers_1.submitDigest)(merkleRoot, options.recipientEmail, undefined, // chainId - let server determine
175
+ { serverUrl, debug: options.debug });
176
+ if (!response.success) {
177
+ const errorMsg = response.error || 'Failed to create timestamp';
178
+ if (options.debug) {
179
+ console.error('[Zipstamp Server API] Error in response:', errorMsg);
180
+ }
181
+ throw new Error(errorMsg);
182
+ }
183
+ // Get network chain ID from network name
184
+ const networkChainId = response.network
185
+ ? ((0, contracts_1.getChainIdByName)(response.network) || 0)
186
+ : 0;
187
+ // Get contract address from network config if available
188
+ let contractAddress = '';
189
+ if (networkChainId) {
190
+ const config = (0, contracts_1.getContractConfig)(networkChainId);
191
+ contractAddress = config?.address || '';
192
+ }
193
+ const timestampMetadata = {
194
+ digest: response.digest,
195
+ status: response.status === 'confirmed' ? 'confirmed' : 'pending',
196
+ tokenId: undefined, // Not in SubmitDigestResponse
197
+ transactionHash: undefined, // Not in SubmitDigestResponse
198
+ contractAddress,
199
+ network: response.network || 'unknown',
200
+ chainId: networkChainId,
201
+ batchId: response.batchId,
202
+ batchNumber: response.batchNumber,
203
+ serverUrl,
204
+ submittedAt: new Date().toISOString(),
205
+ };
206
+ if (options.debug) {
207
+ console.log('[Zipstamp Server API] Processed Timestamp Metadata:');
208
+ console.log(JSON.stringify(timestampMetadata, null, 2));
209
+ console.log('[Zipstamp Server API] ========================================\n');
210
+ }
211
+ return timestampMetadata;
212
+ }
213
+ catch (error) {
214
+ if (options.debug) {
215
+ console.error('[Zipstamp Server API] ========================================');
216
+ console.error('[Zipstamp Server API] Error creating timestamp:');
217
+ console.error('[Zipstamp Server API]', error instanceof Error ? error.message : String(error));
218
+ if (error instanceof Error && error.stack) {
219
+ console.error('[Zipstamp Server API] Stack trace:');
220
+ console.error(error.stack);
221
+ }
222
+ console.error('[Zipstamp Server API] ========================================\n');
223
+ }
224
+ throw error;
225
+ }
226
+ }
227
+ /**
228
+ * Verifies a timestamp against the Zipstamp server.
229
+ *
230
+ * Verifies that a timestamp has been confirmed on the blockchain by checking
231
+ * with the Zipstamp server API. Uses `verifyDigest` internally and converts the
232
+ * response to an `EthTimestampVerifyResult` format.
233
+ *
234
+ * @param merkleRoot - The SHA-256 digest (merkle root) to verify
235
+ * @param timestampData - The timestamp metadata from the ZIP file (contains batchId, chainId, etc.)
236
+ * @param options - Configuration options for verification
237
+ * @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
238
+ * @param options.debug - Enable debug logging (defaults to `false`)
239
+ * @returns Promise resolving to verification result with status and blockchain details
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * const result = await verifyTimestamp(merkleRoot, timestampMetadata, {
244
+ * debug: true
245
+ * });
246
+ *
247
+ * if (result.status === 'valid') {
248
+ * console.log('Verified on blockchain!');
249
+ * console.log('Transaction:', result.transactionHash);
250
+ * console.log('Block:', result.blockNumber);
251
+ * } else if (result.status === 'pending') {
252
+ * console.log('Timestamp is pending confirmation');
253
+ * }
254
+ * ```
255
+ */
256
+ async function verifyTimestamp(merkleRoot, timestampData, options = {}) {
257
+ try {
258
+ const serverUrl = (0, servers_1.getZipStampServerUrl)(options);
259
+ if (options.debug) {
260
+ console.log('[Zipstamp Server API] ========================================');
261
+ console.log('[Zipstamp Server API] Verifying Timestamp');
262
+ console.log('[Zipstamp Server API] ========================================');
263
+ console.log('[Zipstamp Server API] Server URL:', serverUrl);
264
+ console.log('[Zipstamp Server API] Merkle Root:', merkleRoot);
265
+ console.log('[Zipstamp Server API] Timestamp Data:');
266
+ console.log(JSON.stringify(timestampData, null, 2));
267
+ console.log('[Zipstamp Server API] ----------------------------------------');
268
+ }
269
+ // Use verifyDigest helper
270
+ const response = await (0, ZipstampServerHelpers_1.verifyDigest)(merkleRoot, timestampData.chainId, timestampData.batchId || undefined, undefined, // client - use default
271
+ { serverUrl, debug: options.debug });
272
+ if (!response.success) {
273
+ const errorMsg = response.error || 'Verification failed';
274
+ if (options.debug) {
275
+ console.error('[Zipstamp Server API] Error in response:', errorMsg);
276
+ console.log('[Zipstamp Server API] ========================================\n');
277
+ }
278
+ return {
279
+ status: 'error',
280
+ message: errorMsg,
281
+ };
282
+ }
283
+ if (response.verified) {
284
+ const result = {
285
+ status: 'valid',
286
+ verified: true,
287
+ digest: response.digest,
288
+ tokenId: response.tokenId,
289
+ transactionHash: response.transactionHash,
290
+ contractAddress: response.contractAddress,
291
+ network: response.network,
292
+ blockNumber: response.blockNumber,
293
+ timestamp: response.timestamp,
294
+ };
295
+ if (options.debug) {
296
+ console.log('[Zipstamp Server API] Verification Result (VALID):');
297
+ console.log(JSON.stringify(result, null, 2));
298
+ console.log('[Zipstamp Server API] ========================================\n');
299
+ }
300
+ return result;
301
+ }
302
+ const pendingResult = {
303
+ status: 'pending',
304
+ verified: false,
305
+ digest: response.digest,
306
+ message: 'Timestamp is pending confirmation',
307
+ };
308
+ if (options.debug) {
309
+ console.log('[Zipstamp Server API] Verification Result (PENDING):');
310
+ console.log(JSON.stringify(pendingResult, null, 2));
311
+ console.log('[Zipstamp Server API] ========================================\n');
312
+ }
313
+ return pendingResult;
314
+ }
315
+ catch (error) {
316
+ const errorResult = {
317
+ status: 'error',
318
+ message: error instanceof Error ? error.message : 'Verification error',
319
+ };
320
+ if (options.debug) {
321
+ console.error('[Zipstamp Server API] ========================================');
322
+ console.error('[Zipstamp Server API] Error during verification:');
323
+ console.error('[Zipstamp Server API]', error instanceof Error ? error.message : String(error));
324
+ if (error instanceof Error && error.stack) {
325
+ console.error('[Zipstamp Server API] Stack trace:');
326
+ console.error(error.stack);
327
+ }
328
+ console.error('[Zipstamp Server API] ========================================\n');
329
+ }
330
+ return errorResult;
331
+ }
332
+ }
333
+ /**
334
+ * Verifies a timestamp within a ZIP file.
335
+ *
336
+ * Extracts the merkle root and timestamp metadata from a ZIP file, then verifies
337
+ * the timestamp against the Zipstamp server API. This is a convenience function that
338
+ * combines `getEthTimestampEntry`, `extractTimestampData`, and `verifyTimestamp`.
339
+ *
340
+ * @param zip - A ZipkitLike instance representing the timestamped ZIP file
341
+ * @param options - Configuration options for verification
342
+ * @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
343
+ * @param options.debug - Enable debug logging (defaults to `false`)
344
+ * @returns Promise resolving to verification result
345
+ *
346
+ * @example
347
+ * ```typescript
348
+ * const result = await verifyTimestampedZip(zipkit, { debug: true });
349
+ *
350
+ * switch (result.status) {
351
+ * case 'valid':
352
+ * console.log('✅ Timestamp verified on blockchain');
353
+ * break;
354
+ * case 'pending':
355
+ * console.log('⏳ Timestamp pending confirmation');
356
+ * break;
357
+ * case 'none':
358
+ * console.log('❌ No timestamp found in ZIP file');
359
+ * break;
360
+ * case 'error':
361
+ * console.log('❌ Verification error:', result.message);
362
+ * break;
363
+ * }
364
+ * ```
365
+ */
366
+ async function verifyTimestampedZip(zip, options = {}) {
367
+ const merkleRoot = getMerkleRootSafe(zip);
368
+ if (!merkleRoot) {
369
+ return { status: 'error', message: 'Merkle root not found in ZIP file' };
370
+ }
371
+ const entry = getEthTimestampEntry(zip);
372
+ if (!entry) {
373
+ return { status: 'none', message: 'No timestamp entry found in ZIP file' };
374
+ }
375
+ const timestampData = await extractTimestampData(zip, entry);
376
+ if (!timestampData) {
377
+ return { status: 'error', message: 'Failed to extract timestamp metadata' };
378
+ }
379
+ // Verify that the digest matches the merkle root
380
+ if (timestampData.digest.toLowerCase() !== merkleRoot.toLowerCase()) {
381
+ return {
382
+ status: 'error',
383
+ message: 'Timestamp digest does not match ZIP merkle root',
384
+ };
385
+ }
386
+ return await verifyTimestamp(merkleRoot, timestampData, options);
387
+ }
388
+ /**
389
+ * Creates a ZIP entry containing timestamp metadata.
390
+ *
391
+ * Creates a ZIP entry object that can be added to a ZIP file. The entry contains
392
+ * the timestamp metadata as JSON. The filename is automatically chosen based on
393
+ * the timestamp status:
394
+ * - `TIMESTAMP.NZIP` for confirmed timestamps
395
+ * - `TS-SUBMIT.NZIP` for pending timestamps
396
+ *
397
+ * @param zipkit - A ZipkitLike instance for creating ZIP entries
398
+ * @param timestampMetadata - The timestamp metadata to embed in the ZIP entry
399
+ * @returns A ZIP entry object ready to be added to a ZIP file, or `null` if creation fails
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * // Create timestamp
404
+ * const metadata = await createTimestamp(merkleRoot);
405
+ *
406
+ * // Create ZIP entry
407
+ * const entry = createTimestampMetadataEntry(zipkit, metadata);
408
+ * if (entry) {
409
+ * // Add entry to ZIP file using zipkit methods
410
+ * zipkit.addEntry(entry);
411
+ * }
412
+ * ```
413
+ */
414
+ function createTimestampMetadataEntry(zipkit, timestampMetadata) {
415
+ if (!zipkit)
416
+ return null;
417
+ try {
418
+ const jsonString = JSON.stringify(timestampMetadata, null, 2);
419
+ const buffer = Buffer.from(jsonString, 'utf-8');
420
+ // Determine which filename to use based on status
421
+ const filename = timestampMetadata.status === 'confirmed'
422
+ ? metadata_1.TIMESTAMP_NZIP
423
+ : metadata_1.TS_SUBMIT_NZIP;
424
+ const entry = zipkit.createZipEntry?.(filename);
425
+ if (!entry)
426
+ return null;
427
+ entry.crc = (0, crc32_1.crc32)(buffer);
428
+ entry.uncompressedSize = buffer.length;
429
+ entry.compressedSize = buffer.length;
430
+ entry.cmpMethod = exports.CMP_METHOD.STORED;
431
+ entry.fileBuffer = buffer;
432
+ entry.isMetaData = true;
433
+ return entry;
434
+ }
435
+ catch (error) {
436
+ return null;
437
+ }
438
+ }
439
+ /**
440
+ * Creates timestamp metadata for a ZIP file.
441
+ *
442
+ * Extracts the merkle root from a ZIP file and submits it to the Zipstamp server
443
+ * for timestamping. Returns the timestamp metadata that can then be added to
444
+ * the ZIP file using `createTimestampMetadataEntry`.
445
+ *
446
+ * **Note**: This function does NOT modify the ZIP file. It only creates the
447
+ * timestamp metadata. You must use `createTimestampMetadataEntry` to create
448
+ * a ZIP entry and add it to your ZIP file.
449
+ *
450
+ * @param zipkit - A ZipkitLike instance representing the ZIP file to timestamp
451
+ * @param options - Configuration options for timestamp creation
452
+ * @param options.recipientEmail - Optional email address for notifications
453
+ * @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
454
+ * @param options.debug - Enable debug logging (defaults to `false`)
455
+ * @returns Promise resolving to timestamp metadata, or `null` if creation fails
456
+ * @throws {Error} If merkle root is not found in ZIP or timestamp creation fails
457
+ *
458
+ * @example
459
+ * ```typescript
460
+ * // Create timestamp metadata
461
+ * const metadata = await createTimestampedZip(zipkit, {
462
+ * recipientEmail: 'user@example.com'
463
+ * });
464
+ *
465
+ * if (metadata) {
466
+ * // Create ZIP entry from metadata
467
+ * const entry = createTimestampMetadataEntry(zipkit, metadata);
468
+ * if (entry) {
469
+ * // Add entry to ZIP file
470
+ * zipkit.addEntry(entry);
471
+ * await zipkit.save('timestamped-file.nzip');
472
+ * }
473
+ * }
474
+ * ```
475
+ */
476
+ async function createTimestampedZip(zipkit, options = {}) {
477
+ const merkleRoot = getMerkleRootSafe(zipkit);
478
+ if (!merkleRoot) {
479
+ throw new Error('Merkle root not found in ZIP file');
480
+ }
481
+ // Create timestamp
482
+ const timestampMetadata = await createTimestamp(merkleRoot, options);
483
+ if (!timestampMetadata) {
484
+ throw new Error('Failed to create timestamp');
485
+ }
486
+ // Return metadata - caller should use createTimestampMetadataEntry to add to ZIP
487
+ return timestampMetadata;
488
+ }
489
+ /**
490
+ * Finds timestamp metadata entry in an array of ZIP directory entries.
491
+ *
492
+ * Searches through ZIP directory entries to find timestamp metadata files.
493
+ * Prefers confirmed timestamps (TIMESTAMP.NZIP) over pending submissions (TS-SUBMIT.NZIP).
494
+ * Returns both the entry and its type for easier handling.
495
+ *
496
+ * @param entries - Array of ZIP directory entries to search
497
+ * @returns Object containing the metadata entry and its type ('pending' | 'confirmed'), or `null` if not found
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * const entries = zipkit.getDirectory(true);
502
+ * const result = findMetadataEntry(entries);
503
+ *
504
+ * if (result) {
505
+ * console.log('Found', result.type, 'timestamp');
506
+ * const metadata = await extractTimestampData(zipkit, result.entry);
507
+ * }
508
+ * ```
509
+ */
510
+ function findMetadataEntry(entries) {
511
+ // Check for confirmed timestamp first
512
+ const timestampEntry = entries.find((e) => e.filename === metadata_1.TIMESTAMP_NZIP);
513
+ if (timestampEntry) {
514
+ return {
515
+ entry: timestampEntry,
516
+ type: 'confirmed'
517
+ };
518
+ }
519
+ // Fall back to submission metadata
520
+ const submitEntry = entries.find((e) => e.filename === metadata_1.TS_SUBMIT_NZIP);
521
+ if (submitEntry) {
522
+ return {
523
+ entry: submitEntry,
524
+ type: 'pending'
525
+ };
526
+ }
527
+ return null;
528
+ }
529
+ /**
530
+ * Determines the metadata type from a ZIP entry's filename.
531
+ *
532
+ * Examines the filename of a ZIP entry to determine if it's a timestamp metadata
533
+ * file and whether it represents a pending or confirmed timestamp.
534
+ *
535
+ * @param entry - The ZIP entry to examine (must have a `filename` property)
536
+ * @returns The metadata type: `'pending'` for TS-SUBMIT.NZIP, `'confirmed'` for TIMESTAMP.NZIP, or `null` if not a timestamp metadata file
537
+ *
538
+ * @example
539
+ * ```typescript
540
+ * const entry = getEthTimestampEntry(zipkit);
541
+ * const type = getMetadataType(entry);
542
+ *
543
+ * if (type === 'pending') {
544
+ * console.log('Timestamp is pending confirmation');
545
+ * } else if (type === 'confirmed') {
546
+ * console.log('Timestamp is confirmed on blockchain');
547
+ * }
548
+ * ```
549
+ */
550
+ function getMetadataType(entry) {
551
+ if (!entry)
552
+ return null;
553
+ if (entry.filename === metadata_1.TIMESTAMP_NZIP) {
554
+ return 'confirmed';
555
+ }
556
+ if (entry.filename === metadata_1.TS_SUBMIT_NZIP) {
557
+ return 'pending';
558
+ }
559
+ return null;
560
+ }
561
+ /**
562
+ * Determines if a timestamp metadata needs to be upgraded.
563
+ *
564
+ * Checks whether a timestamp should be upgraded from pending (TS-SUBMIT.NZIP) to
565
+ * confirmed (TIMESTAMP.NZIP) status. An upgrade is needed if:
566
+ * - The metadata file type is 'pending' (TS-SUBMIT.NZIP)
567
+ * - OR the metadata status is 'pending' (for backward compatibility)
568
+ * - OR the metadata lacks a transactionHash (not yet confirmed on blockchain)
569
+ *
570
+ * @param metadata - The timestamp metadata object to check
571
+ * @param metadataType - The type of metadata file ('pending' | 'confirmed' | null)
572
+ * @returns `true` if the timestamp should be upgraded, `false` otherwise
573
+ *
574
+ * @example
575
+ * ```typescript
576
+ * const result = findMetadataEntry(entries);
577
+ * if (result && shouldUpgrade(metadata, result.type)) {
578
+ * console.log('Timestamp needs upgrade - batch may be confirmed now');
579
+ * // Call upgrade-zip script or pollForConfirmation
580
+ * }
581
+ * ```
582
+ */
583
+ function shouldUpgrade(metadata, metadataType) {
584
+ // If we have confirmed metadata file, no upgrade needed
585
+ if (metadataType === 'confirmed') {
586
+ return false;
587
+ }
588
+ // If we have pending metadata file, upgrade is needed
589
+ if (metadataType === 'pending') {
590
+ return true;
591
+ }
592
+ // Fallback: check metadata content
593
+ // If no transactionHash, it's not confirmed
594
+ if (!metadata.transactionHash) {
595
+ return true;
596
+ }
597
+ // If status is explicitly pending, upgrade needed
598
+ if (metadata.status === 'pending') {
599
+ return true;
600
+ }
601
+ return false;
602
+ }
603
+ /**
604
+ * Returns all timestamp metadata file names.
605
+ *
606
+ * Returns an array of all possible timestamp metadata filenames. Useful for
607
+ * error messages, validation, or filtering operations.
608
+ *
609
+ * @returns Array containing `['META-INF/TIMESTAMP.NZIP', 'META-INF/TS-SUBMIT.NZIP']`
610
+ *
611
+ * @example
612
+ * ```typescript
613
+ * const metadataFiles = getMetadataFileNames();
614
+ * console.log('Looking for:', metadataFiles.join(', '));
615
+ *
616
+ * // Filter entries to exclude metadata files
617
+ * const dataEntries = entries.filter(
618
+ * entry => !metadataFiles.includes(entry.filename)
619
+ * );
620
+ * ```
621
+ */
622
+ function getMetadataFileNames() {
623
+ return [metadata_1.TIMESTAMP_NZIP, metadata_1.TS_SUBMIT_NZIP];
624
+ }
625
+ // ============================================================================
626
+ // Proof Verification Utilities
627
+ // ============================================================================
628
+ /**
629
+ * Proof verification utilities for Zipstamp server timestamps.
630
+ *
631
+ * The Zipstamp server provides merkle proofs as an array of bytes32 hex strings.
632
+ * This module provides local proof verification in the same style as
633
+ * OpenZeppelin's MerkleProof (sorted pair hashing).
634
+ */
635
+ function normalizeBytes32(hex) {
636
+ const with0x = hex.startsWith('0x') ? hex : `0x${hex}`;
637
+ const bytes = ethers_1.ethers.getBytes(with0x);
638
+ if (bytes.length !== 32) {
639
+ throw new Error(`Expected bytes32, got ${bytes.length} bytes`);
640
+ }
641
+ return ethers_1.ethers.hexlify(bytes);
642
+ }
643
+ function hashPair(a, b) {
644
+ // OpenZeppelin MerkleProof uses a sorted pair hash
645
+ const aBytes = ethers_1.ethers.getBytes(a);
646
+ const bBytes = ethers_1.ethers.getBytes(b);
647
+ const [left, right] = Buffer.compare(Buffer.from(aBytes), Buffer.from(bBytes)) <= 0 ? [aBytes, bBytes] : [bBytes, aBytes];
648
+ return ethers_1.ethers.keccak256(ethers_1.ethers.concat([left, right]));
649
+ }
650
+ /**
651
+ * Verifies a Merkle proof locally using sorted pair hashing (keccak256).
652
+ *
653
+ * Performs local verification of a Merkle proof without requiring access to
654
+ * the Zipstamp server or blockchain. Uses the same sorted pair hashing algorithm
655
+ * as OpenZeppelin's MerkleProof library, ensuring compatibility with on-chain
656
+ * verification.
657
+ *
658
+ * This is useful for:
659
+ * - Verifying timestamps offline
660
+ * - Validating proofs before submitting to blockchain
661
+ * - Confirming proof integrity without API calls
662
+ *
663
+ * @param digest - The leaf digest (64-character hex string, bytes32) - typically the ZIP file's merkle root
664
+ * @param batchMerkleRoot - The root of the batch Merkle tree (64-character hex string, bytes32) stored on-chain
665
+ * @param merkleProof - Array of sibling hashes (each 64-character hex string, bytes32) forming the proof path from leaf to root
666
+ * @returns `true` if the proof is valid (digest is proven to be in the batch tree), `false` otherwise
667
+ *
668
+ * @example
669
+ * ```typescript
670
+ * const isValid = verifyMerkleProofLocal(
671
+ * zipMerkleRoot, // Your ZIP file's merkle root
672
+ * batchMerkleRoot, // From TIMESTAMP.NZIP metadata
673
+ * merkleProof // From TIMESTAMP.NZIP metadata
674
+ * );
675
+ *
676
+ * if (isValid) {
677
+ * console.log('✅ Proof is valid - ZIP is in the confirmed batch');
678
+ * } else {
679
+ * console.log('❌ Proof is invalid');
680
+ * }
681
+ * ```
682
+ */
683
+ function verifyMerkleProofLocal(digest, batchMerkleRoot, merkleProof) {
684
+ try {
685
+ let computed = normalizeBytes32(digest);
686
+ for (const p of merkleProof || []) {
687
+ computed = hashPair(computed, normalizeBytes32(p));
688
+ }
689
+ const root = normalizeBytes32(batchMerkleRoot);
690
+ return computed.toLowerCase() === root.toLowerCase();
691
+ }
692
+ catch {
693
+ return false;
694
+ }
695
+ }
696
+ //# sourceMappingURL=ZipstampServerAPI.js.map