neozipkit 0.1.1 → 0.2.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 (115) hide show
  1. package/README.md +52 -105
  2. package/contracts/INTEGRATION.md +338 -0
  3. package/contracts/NETWORKS.md +156 -0
  4. package/contracts/README.md +152 -0
  5. package/{src/server/blockchain → contracts}/constants.ts +5 -22
  6. package/contracts/deploy-bytecode.mjs +94 -0
  7. package/dist/blockchain/ZipKitMinter.d.ts +106 -0
  8. package/dist/blockchain/ZipKitMinter.js +347 -0
  9. package/dist/blockchain/ZipKitNFT.d.ts +3 -0
  10. package/dist/blockchain/ZipKitNFT.js +7 -0
  11. package/dist/blockchain/ZipKitOTS.d.ts +135 -0
  12. package/dist/blockchain/ZipKitOTS.js +564 -0
  13. package/dist/blockchain/ZipKitVerifier.d.ts +69 -0
  14. package/dist/blockchain/ZipKitVerifier.js +289 -0
  15. package/dist/{server/blockchain/WalletAnalyzer.d.ts → blockchain/ZipKitWallet.d.ts} +31 -43
  16. package/dist/{server/blockchain/WalletAnalyzer.js → blockchain/ZipKitWallet.js} +193 -161
  17. package/dist/blockchain/contracts.d.ts +14 -0
  18. package/dist/blockchain/contracts.js +77 -0
  19. package/dist/blockchain/index.d.ts +7 -0
  20. package/dist/blockchain/index.js +50 -0
  21. package/dist/{server/blockchain → blockchain}/types.d.ts +12 -34
  22. package/dist/{server/blockchain → blockchain}/types.js +1 -22
  23. package/dist/browser-index.d.ts +0 -3
  24. package/dist/browser-index.js +4 -5
  25. package/dist/browser.d.ts +0 -3
  26. package/dist/browser.js +4 -5
  27. package/dist/index.d.ts +8 -15
  28. package/dist/index.js +12 -24
  29. package/dist/server/{platform/server/ZipKitServer.d.ts → ZipKitServer.d.ts} +4 -35
  30. package/dist/server/{platform/server/ZipKitServer.js → ZipKitServer.js} +55 -182
  31. package/dist/shared/ZipEntry.js +1 -1
  32. package/dist/shared/ZipKit.d.ts +0 -6
  33. package/dist/shared/ZipKit.js +1 -18
  34. package/dist/shared/types.js +2 -2
  35. package/dist/shared/version.d.ts +2 -4
  36. package/dist/shared/version.js +4 -5
  37. package/package.json +13 -3
  38. package/src/blockchain/README.md +229 -0
  39. package/src/blockchain/ZipKitMinter.ts +470 -0
  40. package/src/blockchain/ZipKitNFT.ts +12 -0
  41. package/src/blockchain/ZipKitOTS.ts +622 -0
  42. package/src/blockchain/ZipKitVerifier.ts +382 -0
  43. package/src/blockchain/ZipKitWallet.ts +338 -0
  44. package/src/blockchain/contracts.ts +88 -0
  45. package/src/blockchain/index.ts +61 -0
  46. package/src/blockchain/types.ts +91 -0
  47. package/src/browser/{TokenVerifierBrowser.ts → TokenVerifierBrowser.ts-remove} +1 -1
  48. package/src/browser-index.ts +3 -3
  49. package/src/index.ts +10 -17
  50. package/src/server/README.md +25 -0
  51. package/src/server/ZipKitServer.ts +305 -0
  52. package/src/shared/ZipEntry.ts +1 -1
  53. package/src/shared/ZipKit.ts +1 -19
  54. package/src/shared/types.ts +3 -3
  55. package/src/shared/version.ts +3 -4
  56. package/src/types/modules.d.ts +11 -1
  57. package/contracts/Lock.sol +0 -34
  58. package/contracts/compile-contract.js +0 -168
  59. package/contracts/deploy/deploy-both-contracts.js +0 -286
  60. package/contracts/deploy/deploy-enhanced-contract.js +0 -197
  61. package/contracts/deploy/deploy-private-contract.js +0 -190
  62. package/contracts/deploy/deploy-public-clean.js +0 -1063
  63. package/contracts/deploy/deploy-public-contract.js +0 -1107
  64. package/contracts/deployment.config.example.js +0 -25
  65. package/contracts/remix-compile.js +0 -222
  66. package/contracts/setup-deployment.js +0 -51
  67. package/contracts/src/ZipFileNFT-DEPRECATED-6field.sol +0 -260
  68. package/contracts/src/ZipFileNFT-DEPRECATED-Enhanced.sol +0 -289
  69. package/dist/browser/CertificateGeneratorBrowser.d.ts +0 -75
  70. package/dist/browser/CertificateGeneratorBrowser.js +0 -433
  71. package/dist/browser/TokenVerifierBrowser.d.ts +0 -76
  72. package/dist/browser/TokenVerifierBrowser.js +0 -299
  73. package/dist/server/blockchain/NFTMinter.d.ts +0 -41
  74. package/dist/server/blockchain/NFTMinter.js +0 -323
  75. package/dist/server/blockchain/TokenQuerier.d.ts +0 -31
  76. package/dist/server/blockchain/TokenQuerier.js +0 -259
  77. package/dist/server/blockchain/TokenVerifier.d.ts +0 -20
  78. package/dist/server/blockchain/TokenVerifier.js +0 -395
  79. package/dist/server/blockchain/WalletFormatter.d.ts +0 -39
  80. package/dist/server/blockchain/WalletFormatter.js +0 -196
  81. package/dist/server/blockchain/WalletManager.d.ts +0 -46
  82. package/dist/server/blockchain/WalletManager.js +0 -294
  83. package/dist/server/blockchain/ZipMetadata.d.ts +0 -27
  84. package/dist/server/blockchain/ZipMetadata.js +0 -131
  85. package/dist/server/blockchain/ZipTokenProcessor.d.ts +0 -44
  86. package/dist/server/blockchain/ZipTokenProcessor.js +0 -204
  87. package/dist/server/blockchain/constants.d.ts +0 -102
  88. package/dist/server/blockchain/constants.js +0 -968
  89. package/dist/server/blockchain/index.d.ts +0 -9
  90. package/dist/server/blockchain/index.js +0 -44
  91. package/dist/server/blockchain/test-certificate.d.ts +0 -8
  92. package/dist/server/blockchain/test-certificate.js +0 -100
  93. package/dist/server/platform/servercomponents/ZipKitOTS.d.ts +0 -91
  94. package/dist/server/platform/servercomponents/ZipKitOTS.js +0 -271
  95. package/examples/unzip-token.ts +0 -488
  96. package/examples/zip-token.ts +0 -604
  97. package/src/server/blockchain/NFTMinter.ts +0 -357
  98. package/src/server/blockchain/TokenQuerier.ts +0 -287
  99. package/src/server/blockchain/TokenVerifier.ts +0 -453
  100. package/src/server/blockchain/WalletAnalyzer.ts +0 -397
  101. package/src/server/blockchain/WalletFormatter.ts +0 -206
  102. package/src/server/blockchain/WalletManager.ts +0 -294
  103. package/src/server/blockchain/ZipMetadata.ts +0 -117
  104. package/src/server/blockchain/ZipTokenProcessor.ts +0 -234
  105. package/src/server/blockchain/index.ts +0 -42
  106. package/src/server/blockchain/test-certificate.ts +0 -137
  107. package/src/server/blockchain/types.ts +0 -134
  108. package/src/server/platform/server/ZipKitServer.ts +0 -459
  109. package/src/server/platform/servercomponents/ZipKitOTS.ts +0 -313
  110. package/src/server/platform/servercomponents/opentimestamps.d.ts +0 -23
  111. /package/contracts/{bytecode.txt → NZIP-NFT-bytecode.txt} +0 -0
  112. /package/dist/{server/blockchain → blockchain}/CertificateToken.d.ts +0 -0
  113. /package/dist/{server/blockchain → blockchain}/CertificateToken.js +0 -0
  114. /package/src/{server/blockchain → blockchain}/CertificateToken.ts +0 -0
  115. /package/src/browser/{CertificateGeneratorBrowser.ts → CertificateGeneratorBrowser.ts-remove} +0 -0
@@ -1,299 +0,0 @@
1
- "use strict";
2
- // ======================================
3
- // TokenVerifierBrowser.ts
4
- // Browser-compatible blockchain token verification utilities for ZIP archives
5
- // Copyright (c) 2025 NeoWare, Inc. All rights reserved.
6
- // ======================================
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
- Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.TokenVerifierBrowser = void 0;
42
- exports.createTokenVerifier = createTokenVerifier;
43
- exports.checkForTokenization = checkForTokenization;
44
- const Headers_1 = require("../shared/constants/Headers");
45
- const constants_1 = require("../server/blockchain/constants");
46
- // Network configuration for blockchain queries
47
- const NETWORK_CONFIG = {
48
- 'Base Sepolia': {
49
- chainId: 84532,
50
- rpcUrls: [
51
- 'https://sepolia.base.org',
52
- 'https://base-sepolia.g.alchemy.com/v2/demo',
53
- 'https://base-sepolia-rpc.publicnode.com',
54
- 'https://base-sepolia.blockpi.network/v1/rpc/public'
55
- ]
56
- },
57
- 'Base Mainnet': {
58
- chainId: 8453,
59
- rpcUrls: [
60
- 'https://mainnet.base.org',
61
- 'https://base-mainnet.g.alchemy.com/v2/demo',
62
- 'https://base-rpc.publicnode.com',
63
- 'https://base.blockpi.network/v1/rpc/public'
64
- ]
65
- }
66
- };
67
- // Use the complete contract ABI from constants
68
- const CONTRACT_ABI = constants_1.NZIP_CONTRACT_ABI;
69
- /**
70
- * Browser-compatible token verification class
71
- */
72
- class TokenVerifierBrowser {
73
- constructor(zipKit) {
74
- this.zipKit = zipKit;
75
- }
76
- /**
77
- * Checks if the ZIP file contains NFT token metadata and verifies the merkle root
78
- */
79
- async checkForTokenization(zipEntries, options = {}) {
80
- // Look for the tokenized metadata file
81
- const tokenEntry = zipEntries.find(entry => entry.filename === Headers_1.TOKENIZED_METADATA);
82
- if (!tokenEntry) {
83
- return {
84
- isTokenized: false,
85
- tokenMetadata: null,
86
- calculatedMerkleRoot: null,
87
- blockchainMerkleRoot: null,
88
- verificationPassed: false
89
- };
90
- }
91
- try {
92
- // Extract the token metadata
93
- const tokenData = await this.zipKit.extractBlob(tokenEntry);
94
- if (!tokenData) {
95
- return {
96
- isTokenized: false,
97
- tokenMetadata: null,
98
- calculatedMerkleRoot: null,
99
- blockchainMerkleRoot: null,
100
- verificationPassed: false,
101
- errorMessage: 'Could not read token metadata'
102
- };
103
- }
104
- const tokenContent = await this.blobToString(tokenData);
105
- const tokenMetadata = JSON.parse(tokenContent);
106
- // Validate token metadata structure
107
- if (!this.validateTokenInfo(tokenMetadata)) {
108
- return {
109
- isTokenized: false,
110
- tokenMetadata: null,
111
- calculatedMerkleRoot: null,
112
- blockchainMerkleRoot: null,
113
- verificationPassed: false,
114
- errorMessage: 'Invalid token metadata structure'
115
- };
116
- }
117
- // Skip verification if requested
118
- if (options.skipHash || options.skipToken) {
119
- return {
120
- isTokenized: true,
121
- tokenMetadata,
122
- calculatedMerkleRoot: null,
123
- blockchainMerkleRoot: null,
124
- verificationPassed: false,
125
- errorMessage: options.skipHash ? 'Hash verification skipped' : 'Token verification skipped'
126
- };
127
- }
128
- // Calculate merkle root excluding token file
129
- const calculatedMerkleRoot = await this.calculateMerkleRootExcludingToken(zipEntries);
130
- if (!calculatedMerkleRoot) {
131
- return {
132
- isTokenized: true,
133
- tokenMetadata,
134
- calculatedMerkleRoot: null,
135
- blockchainMerkleRoot: null,
136
- verificationPassed: false,
137
- errorMessage: 'Could not calculate merkle root'
138
- };
139
- }
140
- // Query blockchain for the actual merkle root and timestamp
141
- const blockchainData = await this.queryBlockchainData(tokenMetadata);
142
- if (!blockchainData.merkleRoot) {
143
- return {
144
- isTokenized: true,
145
- tokenMetadata,
146
- calculatedMerkleRoot,
147
- blockchainMerkleRoot: null,
148
- blockchainTimestamp: blockchainData.timestamp ?? undefined,
149
- verificationPassed: false,
150
- errorMessage: 'Could not retrieve merkle root from blockchain'
151
- };
152
- }
153
- const verificationPassed = blockchainData.merkleRoot === calculatedMerkleRoot;
154
- return {
155
- isTokenized: true,
156
- tokenMetadata,
157
- calculatedMerkleRoot,
158
- blockchainMerkleRoot: blockchainData.merkleRoot,
159
- blockchainTimestamp: blockchainData.timestamp ?? undefined,
160
- verificationPassed,
161
- errorMessage: verificationPassed ? undefined : 'Merkle root mismatch - archive may be tampered'
162
- };
163
- }
164
- catch (error) {
165
- return {
166
- isTokenized: false,
167
- tokenMetadata: null,
168
- calculatedMerkleRoot: null,
169
- blockchainMerkleRoot: null,
170
- verificationPassed: false,
171
- errorMessage: `Error processing token: ${error instanceof Error ? error.message : String(error)}`
172
- };
173
- }
174
- }
175
- /**
176
- * Validates token metadata structure
177
- */
178
- validateTokenInfo(tokenMetadata) {
179
- return tokenMetadata &&
180
- typeof tokenMetadata.version === 'string' &&
181
- typeof tokenMetadata.tokenId === 'string' &&
182
- typeof tokenMetadata.contractAddress === 'string' &&
183
- typeof tokenMetadata.network === 'string' &&
184
- typeof tokenMetadata.networkChainId === 'number' &&
185
- typeof tokenMetadata.transactionHash === 'string' &&
186
- typeof tokenMetadata.merkleRoot === 'string' &&
187
- typeof tokenMetadata.fileName === 'string' &&
188
- typeof tokenMetadata.mintedAt === 'string';
189
- }
190
- /**
191
- * Calculates the merkle root excluding the token metadata file
192
- */
193
- async calculateMerkleRootExcludingToken(zipEntries) {
194
- try {
195
- // Get all entries except the token file
196
- const contentEntries = zipEntries.filter(entry => entry.filename !== Headers_1.TOKENIZED_METADATA);
197
- if (contentEntries.length === 0) {
198
- return null;
199
- }
200
- // For each content entry, ensure we have SHA-256 hash calculated
201
- for (const entry of contentEntries) {
202
- if (!entry.sha256) {
203
- // Extract the file to calculate its SHA-256 hash
204
- await this.zipKit.extractBlob(entry);
205
- }
206
- }
207
- // Calculate merkle root from content files
208
- // This would need to be implemented based on the ZipKit's merkle tree functionality
209
- // For now, we'll use a placeholder that would need to be implemented
210
- return await this.zipKit.getMerkleRoot?.() || null;
211
- }
212
- catch (error) {
213
- console.error('Error calculating merkle root:', error);
214
- return null;
215
- }
216
- }
217
- /**
218
- * Queries the blockchain to get the actual merkle root and timestamp stored in the smart contract
219
- */
220
- async queryBlockchainData(tokenMetadata) {
221
- try {
222
- // Dynamic import of ethers to avoid SSR issues
223
- const { ethers } = await Promise.resolve().then(() => __importStar(require('ethers')));
224
- const networkConfig = NETWORK_CONFIG[tokenMetadata.network];
225
- if (!networkConfig) {
226
- console.warn(`Unsupported network: ${tokenMetadata.network}`);
227
- return { merkleRoot: null, timestamp: null };
228
- }
229
- // Try multiple RPC endpoints
230
- for (const rpcUrl of networkConfig.rpcUrls) {
231
- try {
232
- const provider = new ethers.JsonRpcProvider(rpcUrl);
233
- // Test connection with a timeout
234
- const blockNumber = await Promise.race([
235
- provider.getBlockNumber(),
236
- new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000))
237
- ]);
238
- // Create contract instance
239
- const contract = new ethers.Contract(tokenMetadata.contractAddress, CONTRACT_ABI, provider);
240
- // Call getZipFileInfo to get the blockchain-stored data
241
- const zipInfo = await contract.getZipFileInfo(tokenMetadata.tokenId);
242
- return {
243
- merkleRoot: zipInfo.merkleRootHash,
244
- timestamp: Number(zipInfo.tokenizationTime)
245
- };
246
- }
247
- catch (error) {
248
- console.warn(`RPC ${rpcUrl} failed:`, error);
249
- continue;
250
- }
251
- }
252
- console.warn(`All RPC endpoints failed for network: ${tokenMetadata.network}`);
253
- return { merkleRoot: null, timestamp: null };
254
- }
255
- catch (error) {
256
- console.error('Blockchain query error:', error);
257
- return { merkleRoot: null, timestamp: null };
258
- }
259
- }
260
- /**
261
- * Helper to convert Blob to string
262
- */
263
- async blobToString(blob) {
264
- return new Promise((resolve, reject) => {
265
- const reader = new FileReader();
266
- reader.onload = () => resolve(reader.result);
267
- reader.onerror = reject;
268
- reader.readAsText(blob);
269
- });
270
- }
271
- /**
272
- * Creates a formatted summary of token information
273
- */
274
- createTokenSummary(tokenMetadata) {
275
- return `
276
- 🎫 NFT Token Information:
277
- Token ID: ${tokenMetadata.tokenId}
278
- Network: ${tokenMetadata.network}
279
- Contract: ${tokenMetadata.contractAddress}
280
- Transaction: ${tokenMetadata.transactionHash}
281
- File: ${tokenMetadata.fileName}
282
- Minted: ${new Date(tokenMetadata.mintedAt).toLocaleString()}
283
- `;
284
- }
285
- }
286
- exports.TokenVerifierBrowser = TokenVerifierBrowser;
287
- /**
288
- * Creates a new TokenVerifierBrowser instance
289
- */
290
- function createTokenVerifier(zipKit) {
291
- return new TokenVerifierBrowser(zipKit);
292
- }
293
- /**
294
- * Convenience function to check for tokenization
295
- */
296
- async function checkForTokenization(zipKit, zipEntries, options = {}) {
297
- const verifier = new TokenVerifierBrowser(zipKit);
298
- return verifier.checkForTokenization(zipEntries, options);
299
- }
@@ -1,41 +0,0 @@
1
- import { ethers } from 'ethers';
2
- import { MintingResult, ZipTokenData, NFTMetadata } from './types';
3
- export interface ExistingTokenInfo {
4
- tokenId: string;
5
- fileName: string;
6
- merkleRootHash: string;
7
- creationTimestamp: number;
8
- tokenizationTime: number;
9
- creator: string;
10
- isOwnedByWallet: boolean;
11
- }
12
- export declare class NFTMinter {
13
- private wallet;
14
- private provider;
15
- constructor(wallet: ethers.Wallet | null, provider: ethers.JsonRpcProvider | null);
16
- /**
17
- * Creates NFT metadata for the ZIP archive
18
- */
19
- createNFTMetadata(zipTokenData: ZipTokenData): NFTMetadata;
20
- /**
21
- * Loads contract deployment information
22
- */
23
- private loadContractInfo;
24
- /**
25
- * Check for existing tokens with the same merkle root
26
- * This is efficient because it uses the contract's getTokensByMerkleRoot function
27
- */
28
- findExistingTokensByMerkleRoot(merkleRoot: string): Promise<ExistingTokenInfo[]>;
29
- /**
30
- * Prompts user to choose between reusing an existing timestamp or creating a new token
31
- */
32
- promptUserChoice(existingTokens: ExistingTokenInfo[], fileName: string): Promise<{
33
- useExisting: boolean;
34
- selectedTimestamp?: number;
35
- selectedTokenId?: string;
36
- }>;
37
- /**
38
- * Mints an NFT token for the ZIP archive using the timestamped contract
39
- */
40
- mintZipFileNFT(zipTokenData: ZipTokenData): Promise<MintingResult>;
41
- }
@@ -1,323 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.NFTMinter = void 0;
37
- const fs = __importStar(require("fs"));
38
- const path = __importStar(require("path"));
39
- const ethers_1 = require("ethers");
40
- const types_1 = require("./types");
41
- class NFTMinter {
42
- constructor(wallet, provider) {
43
- this.wallet = wallet;
44
- this.provider = provider;
45
- }
46
- /**
47
- * Creates NFT metadata for the ZIP archive
48
- */
49
- createNFTMetadata(zipTokenData) {
50
- const metadata = { ...types_1.NFT_METADATA_TEMPLATE };
51
- metadata.name = `${zipTokenData.fileName} Archive`;
52
- metadata.description = `Verified archive created on ${zipTokenData.timestamp}`;
53
- metadata.attributes[0].value = zipTokenData.merkleRoot;
54
- metadata.attributes[1].value = zipTokenData.timestamp;
55
- return metadata;
56
- }
57
- /**
58
- * Loads contract deployment information
59
- */
60
- loadContractInfo() {
61
- try {
62
- const contractInfoPath = path.join(process.cwd(), 'zipfile-nft-deployment.json');
63
- if (!fs.existsSync(contractInfoPath)) {
64
- return null;
65
- }
66
- const contractInfo = JSON.parse(fs.readFileSync(contractInfoPath, 'utf8'));
67
- return contractInfo;
68
- }
69
- catch (error) {
70
- console.error('Error loading contract info:', error);
71
- return null;
72
- }
73
- }
74
- /**
75
- * Check for existing tokens with the same merkle root
76
- * This is efficient because it uses the contract's getTokensByMerkleRoot function
77
- */
78
- async findExistingTokensByMerkleRoot(merkleRoot) {
79
- if (!this.provider || !this.wallet) {
80
- return [];
81
- }
82
- try {
83
- const contractInfo = this.loadContractInfo();
84
- if (!contractInfo) {
85
- return [];
86
- }
87
- console.log(`🔍 Checking for existing tokens with merkle root: ${merkleRoot}`);
88
- const contract = new ethers_1.ethers.Contract(contractInfo.address, contractInfo.abi, this.provider);
89
- // Use the efficient contract function to get tokens by merkle root
90
- const tokenIds = await contract.getTokensByMerkleRoot(merkleRoot);
91
- if (tokenIds.length === 0) {
92
- console.log(` ✅ No existing tokens found for this content`);
93
- return [];
94
- }
95
- console.log(` 📋 Found ${tokenIds.length} existing token(s) for this content:`);
96
- const existingTokens = [];
97
- for (const tokenId of tokenIds) {
98
- try {
99
- // Get token info
100
- const zipInfo = await contract.getZipFileInfo(tokenId);
101
- const owner = await contract.ownerOf(tokenId);
102
- const tokenInfo = {
103
- tokenId: tokenId.toString(),
104
- fileName: zipInfo.fileName,
105
- merkleRootHash: zipInfo.merkleRootHash,
106
- creationTimestamp: Number(zipInfo.creationTimestamp),
107
- tokenizationTime: Number(zipInfo.tokenizationTime),
108
- creator: zipInfo.creator,
109
- isOwnedByWallet: owner.toLowerCase() === this.wallet.address.toLowerCase()
110
- };
111
- existingTokens.push(tokenInfo);
112
- const ownershipStatus = tokenInfo.isOwnedByWallet ? "👤 (owned by you)" : `👥 (owned by ${owner})`;
113
- const createdDate = new Date(tokenInfo.creationTimestamp * 1000).toISOString();
114
- const mintedDate = new Date(tokenInfo.tokenizationTime * 1000).toISOString();
115
- console.log(` 📄 Token ID ${tokenInfo.tokenId}: ${tokenInfo.fileName}`);
116
- console.log(` Created: ${createdDate}`);
117
- console.log(` Minted: ${mintedDate}`);
118
- console.log(` Owner: ${ownershipStatus}`);
119
- }
120
- catch (error) {
121
- console.warn(` ⚠️ Could not retrieve info for token ${tokenId}: ${error}`);
122
- }
123
- }
124
- return existingTokens;
125
- }
126
- catch (error) {
127
- console.warn(`Error checking for existing tokens: ${error}`);
128
- return [];
129
- }
130
- }
131
- /**
132
- * Prompts user to choose between reusing an existing timestamp or creating a new token
133
- */
134
- async promptUserChoice(existingTokens, fileName) {
135
- return new Promise((resolve) => {
136
- const readline = require('readline');
137
- const rl = readline.createInterface({
138
- input: process.stdin,
139
- output: process.stdout
140
- });
141
- console.log('\n🤔 What would you like to do?');
142
- console.log('═'.repeat(60));
143
- console.log('1. Create a new token with current timestamp (recommended)');
144
- // Show existing tokens as options
145
- existingTokens.forEach((token, index) => {
146
- const createdDate = new Date(token.creationTimestamp * 1000).toLocaleString();
147
- const ownershipNote = token.isOwnedByWallet ? ' (you own this)' : ` (owned by ${token.creator.slice(0, 8)}...)`;
148
- console.log(`${index + 2}. Reuse timestamp from Token ${token.tokenId}: ${createdDate}${ownershipNote}`);
149
- });
150
- console.log('═'.repeat(60));
151
- rl.question('Enter your choice (1-' + (existingTokens.length + 1) + '): ', (answer) => {
152
- rl.close();
153
- const choice = parseInt(answer.trim());
154
- if (choice === 1) {
155
- resolve({ useExisting: false });
156
- }
157
- else if (choice >= 2 && choice <= existingTokens.length + 1) {
158
- const selectedToken = existingTokens[choice - 2];
159
- resolve({
160
- useExisting: true,
161
- selectedTimestamp: selectedToken.creationTimestamp,
162
- selectedTokenId: selectedToken.tokenId
163
- });
164
- }
165
- else {
166
- console.log('❌ Invalid choice. Creating new token with current timestamp.');
167
- resolve({ useExisting: false });
168
- }
169
- });
170
- });
171
- }
172
- /**
173
- * Mints an NFT token for the ZIP archive using the timestamped contract
174
- */
175
- async mintZipFileNFT(zipTokenData) {
176
- if (!this.wallet) {
177
- return {
178
- success: false,
179
- message: 'Wallet not set up. Cannot mint token.'
180
- };
181
- }
182
- if (!this.provider) {
183
- return {
184
- success: false,
185
- message: 'Network connection failed. Cannot mint token without blockchain connectivity.'
186
- };
187
- }
188
- try {
189
- console.log(`\nArchive Merkle root: ${zipTokenData.merkleRoot}`);
190
- // Check for existing tokens with the same merkle root
191
- const existingTokens = await this.findExistingTokensByMerkleRoot(zipTokenData.merkleRoot);
192
- let creationTimestamp;
193
- let reusingExistingToken = false;
194
- let selectedTokenId;
195
- if (existingTokens.length > 0) {
196
- console.log(`\n💡 Found ${existingTokens.length} existing token(s) with the same content.`);
197
- console.log(' You can create a new token or reuse an existing timestamp.');
198
- const userChoice = await this.promptUserChoice(existingTokens, zipTokenData.fileName);
199
- if (userChoice.useExisting && userChoice.selectedTimestamp) {
200
- creationTimestamp = userChoice.selectedTimestamp;
201
- selectedTokenId = userChoice.selectedTokenId;
202
- reusingExistingToken = true;
203
- console.log(`\n✅ Reusing timestamp ${creationTimestamp} from Token ID ${selectedTokenId}`);
204
- // Check if this exact combination already exists
205
- const contractInfo = this.loadContractInfo();
206
- if (contractInfo) {
207
- const contract = new ethers_1.ethers.Contract(contractInfo.address, contractInfo.abi, this.provider);
208
- const [exists, existingTokenId] = await contract.isZipFileTokenized(zipTokenData.merkleRoot, creationTimestamp);
209
- if (exists) {
210
- return {
211
- success: false,
212
- message: `A token with this exact merkle root and timestamp already exists (Token ID: ${existingTokenId}). Cannot create duplicate.`
213
- };
214
- }
215
- }
216
- }
217
- else {
218
- creationTimestamp = Math.floor(Date.now() / 1000);
219
- console.log(`\n✅ Creating new token with current timestamp: ${creationTimestamp}`);
220
- }
221
- }
222
- else {
223
- // No existing tokens, use current timestamp
224
- creationTimestamp = Math.floor(Date.now() / 1000);
225
- console.log('Creating new NFT token for this ZIP archive...');
226
- }
227
- // Create token metadata
228
- const metadata = this.createNFTMetadata(zipTokenData);
229
- console.log('Token metadata created:');
230
- console.log(JSON.stringify(metadata, null, 2));
231
- // Load contract information
232
- const contractInfo = this.loadContractInfo();
233
- if (!contractInfo) {
234
- return {
235
- success: false,
236
- message: 'No deployed contract found. Deploy the contract using: npm run deploy-nft'
237
- };
238
- }
239
- console.log(`Found deployed contract at ${contractInfo.address}`);
240
- try {
241
- // Create contract instance
242
- const contract = new ethers_1.ethers.Contract(contractInfo.address, contractInfo.abi, this.wallet);
243
- console.log(`\nMinting NFT token...`);
244
- console.log(`Merkle Root: ${zipTokenData.merkleRoot}`);
245
- console.log(`Creation Timestamp: ${creationTimestamp}${reusingExistingToken ? ' (reused)' : ' (new)'}`);
246
- // Use the timestamped contract (5 parameters)
247
- console.log(`Using ZipFileNFT contract`);
248
- console.log(`contract.publicMintZipFile(`);
249
- console.log(` fileName: ${zipTokenData.fileName},`);
250
- console.log(` merkleRootHash: ${zipTokenData.merkleRoot},`);
251
- console.log(` creationTimestamp: ${creationTimestamp},`);
252
- console.log(` ipfsHash: "",`);
253
- console.log(` tokenURI: ""`);
254
- console.log(`)`);
255
- const tx = await contract.publicMintZipFile(zipTokenData.fileName, zipTokenData.merkleRoot, // Use original merkle root
256
- creationTimestamp, // Use selected timestamp (new or reused)
257
- "", // Empty ipfsHash - not implemented yet
258
- "" // Empty tokenURI - not implemented yet
259
- );
260
- console.log(`Transaction submitted: ${tx.hash}`);
261
- console.log('Waiting for confirmation...');
262
- const receipt = await tx.wait();
263
- console.log(`Transaction confirmed in block: ${receipt.blockNumber}`);
264
- // Extract token ID from the transfer event
265
- let tokenId;
266
- for (const log of receipt.logs) {
267
- try {
268
- const parsedLog = contract.interface.parseLog(log);
269
- if (parsedLog && parsedLog.name === 'Transfer' && parsedLog.args.from === ethers_1.ethers.ZeroAddress) {
270
- tokenId = parsedLog.args.tokenId.toString();
271
- break;
272
- }
273
- }
274
- catch (error) {
275
- // Skip logs that can't be parsed
276
- }
277
- }
278
- if (!tokenId) {
279
- // Try to get token ID from ZipFileTokenized event
280
- for (const log of receipt.logs) {
281
- try {
282
- const parsedLog = contract.interface.parseLog(log);
283
- if (parsedLog && parsedLog.name === 'ZipFileTokenized') {
284
- tokenId = parsedLog.args.tokenId.toString();
285
- break;
286
- }
287
- }
288
- catch (error) {
289
- // Skip logs that can't be parsed
290
- }
291
- }
292
- }
293
- const successMessage = reusingExistingToken
294
- ? `NFT minted successfully! Token ID: ${tokenId || 'Unknown'} (reused timestamp from Token ${selectedTokenId})`
295
- : `NFT minted successfully! Token ID: ${tokenId || 'Unknown'} (new timestamp)`;
296
- return {
297
- success: true,
298
- txHash: tx.hash,
299
- tokenId: tokenId,
300
- message: successMessage,
301
- creationTimestamp: creationTimestamp
302
- };
303
- }
304
- catch (contractError) {
305
- const errorMessage = contractError instanceof Error ? contractError.message : String(contractError);
306
- console.error(`Contract interaction failed: ${errorMessage}`);
307
- return {
308
- success: false,
309
- message: `Contract interaction failed: ${errorMessage}`
310
- };
311
- }
312
- }
313
- catch (error) {
314
- const errorMessage = error instanceof Error ? error.message : String(error);
315
- console.error(`Error minting NFT: ${errorMessage}`);
316
- return {
317
- success: false,
318
- message: `Error minting NFT: ${errorMessage}`
319
- };
320
- }
321
- }
322
- }
323
- exports.NFTMinter = NFTMinter;