neozip-cli 0.75.2-beta → 0.90.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.
- package/CHANGELOG.md +78 -0
- package/DOCUMENTATION.md +20 -9
- package/README.md +55 -31
- package/dist/src/commands/mintTimestampProof.js +335 -0
- package/dist/src/commands/verifyEmail.js +146 -0
- package/dist/src/config/ConfigSetup.js +50 -20
- package/dist/src/config/ConfigStore.js +36 -3
- package/dist/src/index.js +1 -1
- package/dist/src/neolist.js +25 -11
- package/dist/src/neounzip.js +324 -66
- package/dist/src/neozip/blockchain.js +5 -5
- package/dist/src/neozip/createZip.js +211 -44
- package/dist/src/neozip/upgradeZip.js +182 -0
- package/dist/src/neozip.js +160 -24
- package/env.example +10 -0
- package/package.json +97 -82
- package/dist/neozipkit-bundles/blockchain.js +0 -13725
- package/dist/neozipkit-bundles/browser.js +0 -6186
- package/dist/neozipkit-bundles/core.js +0 -3839
- package/dist/neozipkit-bundles/node.js +0 -17730
- package/dist/neozipkit-wrappers/blockchain/core/contracts.js +0 -16
- package/dist/neozipkit-wrappers/blockchain/index.js +0 -2
- package/dist/neozipkit-wrappers/core/ZipDecompress.js +0 -2
- package/dist/neozipkit-wrappers/core/components/HashCalculator.js +0 -2
- package/dist/neozipkit-wrappers/core/components/Logger.js +0 -2
- package/dist/neozipkit-wrappers/core/constants/Errors.js +0 -2
- package/dist/neozipkit-wrappers/core/constants/Headers.js +0 -2
- package/dist/neozipkit-wrappers/core/encryption/ZipCrypto.js +0 -7
- package/dist/neozipkit-wrappers/core/index.js +0 -3
- package/dist/neozipkit-wrappers/index.js +0 -13
- package/dist/neozipkit-wrappers/node/index.js +0 -2
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Mint TimestampProofNFT from a confirmed timestamped ZIP (TIMESTAMP.NZIP)
|
|
4
|
+
*
|
|
5
|
+
* Creates TOKEN.NZIP metadata and outputs a new ZIP with NFT proof.
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.runMintTimestampProof = runMintTimestampProof;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const ethers_1 = require("ethers");
|
|
49
|
+
const node_1 = __importDefault(require("neozipkit/node"));
|
|
50
|
+
const node_2 = require("neozipkit/node");
|
|
51
|
+
const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
|
|
52
|
+
const neozip_blockchain_1 = require("neozip-blockchain");
|
|
53
|
+
const blockchain_1 = require("../neozip/blockchain");
|
|
54
|
+
const RPC_BY_CHAIN = {
|
|
55
|
+
84532: 'https://sepolia.base.org',
|
|
56
|
+
421614: 'https://sepolia-rollup.arbitrum.io/rpc',
|
|
57
|
+
11155111: 'https://ethereum-sepolia-rpc.publicnode.com',
|
|
58
|
+
8453: 'https://mainnet.base.org',
|
|
59
|
+
42161: 'https://arb1.arbitrum.io/rpc',
|
|
60
|
+
1: 'https://eth.llamarpc.com',
|
|
61
|
+
};
|
|
62
|
+
/** Get a single string from an error so we can reliably detect "already minted" (ethers v6 puts reason in error.reason). */
|
|
63
|
+
function getErrorMessage(error) {
|
|
64
|
+
if (error == null)
|
|
65
|
+
return '';
|
|
66
|
+
const e = error;
|
|
67
|
+
const parts = [];
|
|
68
|
+
if (typeof e.reason === 'string')
|
|
69
|
+
parts.push(e.reason);
|
|
70
|
+
if (typeof e.message === 'string')
|
|
71
|
+
parts.push(e.message);
|
|
72
|
+
if (parts.length === 0)
|
|
73
|
+
parts.push(String(error));
|
|
74
|
+
return parts.join(' ');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Run the mint flow: load ZIP, extract TIMESTAMP.NZIP, prepare mint, mint NFT, create output ZIP with TOKEN.NZIP
|
|
78
|
+
*/
|
|
79
|
+
async function runMintTimestampProof(options) {
|
|
80
|
+
const { inputPath, outputPath, walletKey: cliWalletKey, debug = false } = options;
|
|
81
|
+
if (!fs.existsSync(inputPath)) {
|
|
82
|
+
console.error(`Error: ZIP file not found: ${inputPath}`);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const walletKey = (0, blockchain_1.getWalletPasskey)(cliWalletKey, true);
|
|
86
|
+
if (!walletKey) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const zip = new node_1.default();
|
|
90
|
+
await zip.loadZipFile(inputPath);
|
|
91
|
+
const entries = zip.getDirectory() || [];
|
|
92
|
+
const metadataResult = (0, zipstamp_server_1.findMetadataEntry)(entries);
|
|
93
|
+
if (!metadataResult) {
|
|
94
|
+
console.error('No Zipstamp timestamp found in ZIP file');
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (metadataResult.type !== 'confirmed') {
|
|
98
|
+
console.error('Archive must have confirmed timestamp (TIMESTAMP.NZIP). Run "neozip upgrade <archive>" first.');
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
let timestampMetadata = await (0, zipstamp_server_1.extractTimestampData)(zip, metadataResult.entry);
|
|
102
|
+
if (!timestampMetadata || !timestampMetadata.digest) {
|
|
103
|
+
// Fallback for file-based ZIPs where zip.extract() may not work
|
|
104
|
+
try {
|
|
105
|
+
const tmpPath = path.join(os.tmpdir(), `neozip-mint-meta-${Date.now()}.json`);
|
|
106
|
+
await zip.extractToFile(metadataResult.entry, tmpPath, { skipHashCheck: true });
|
|
107
|
+
try {
|
|
108
|
+
const buf = fs.readFileSync(tmpPath);
|
|
109
|
+
timestampMetadata = JSON.parse(buf.toString('utf-8'));
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (fs.existsSync(tmpPath))
|
|
113
|
+
fs.unlinkSync(tmpPath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* ignore */
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!timestampMetadata || !timestampMetadata.digest) {
|
|
121
|
+
console.error('Failed to extract timestamp metadata');
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const chainId = timestampMetadata.chainId ?? 84532;
|
|
125
|
+
const batchId = timestampMetadata.batchId ?? undefined;
|
|
126
|
+
console.log('Checking NFT status...');
|
|
127
|
+
const nftStatus = await (0, zipstamp_server_1.checkNFTStatus)(timestampMetadata.digest, chainId, { debug });
|
|
128
|
+
if (nftStatus.isMinted && nftStatus.tokenId) {
|
|
129
|
+
console.log(`Digest already minted as token #${nftStatus.tokenId}`);
|
|
130
|
+
console.log('Proceeding to create TOKEN.NZIP with existing token data...');
|
|
131
|
+
}
|
|
132
|
+
console.log('Preparing mint data...');
|
|
133
|
+
const prepareResult = await (0, zipstamp_server_1.prepareMint)(timestampMetadata.digest, chainId, batchId, { debug });
|
|
134
|
+
if (!prepareResult.success || !prepareResult.mintData) {
|
|
135
|
+
console.error(`Error: ${prepareResult.error || 'Failed to prepare mint data'}`);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const mintData = prepareResult.mintData;
|
|
139
|
+
let tokenId;
|
|
140
|
+
let mintTransactionHash;
|
|
141
|
+
let mintBlockNumber;
|
|
142
|
+
let ownerAddress;
|
|
143
|
+
if (nftStatus.isMinted && nftStatus.tokenId) {
|
|
144
|
+
tokenId = nftStatus.tokenId;
|
|
145
|
+
ownerAddress = nftStatus.owner || 'unknown';
|
|
146
|
+
mintTransactionHash = 'already-minted';
|
|
147
|
+
mintBlockNumber = 0;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const rpcUrl = RPC_BY_CHAIN[chainId] ?? (0, neozip_blockchain_1.getContractConfig)(chainId)?.rpcUrls?.[0];
|
|
151
|
+
if (!rpcUrl) {
|
|
152
|
+
console.error(`Error: No RPC URL for chain ID ${chainId}`);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const provider = new ethers_1.ethers.JsonRpcProvider(rpcUrl, chainId);
|
|
156
|
+
const wallet = new ethers_1.ethers.Wallet(walletKey.startsWith('0x') ? walletKey : `0x${walletKey}`, provider);
|
|
157
|
+
ownerAddress = wallet.address;
|
|
158
|
+
const balance = await provider.getBalance(wallet.address);
|
|
159
|
+
const mintFeeWei = BigInt(mintData.mintFeeWei);
|
|
160
|
+
if (balance < mintFeeWei) {
|
|
161
|
+
console.error(`Error: Insufficient balance. Required: ${mintData.mintFee} ETH`);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const nftContract = new ethers_1.ethers.Contract(mintData.nftContractAddress, neozip_blockchain_1.NZIP_CONTRACT_ABI_V250, wallet);
|
|
165
|
+
console.log('Minting NFT...');
|
|
166
|
+
try {
|
|
167
|
+
const tx = await nftContract.mintWithTimestampProof(mintData.digest, mintData.merkleProof, mintData.batchMerkleRoot, { value: mintFeeWei });
|
|
168
|
+
const receipt = await tx.wait();
|
|
169
|
+
if (!receipt || receipt.status !== 1) {
|
|
170
|
+
throw new Error('Transaction failed');
|
|
171
|
+
}
|
|
172
|
+
mintTransactionHash = receipt.hash;
|
|
173
|
+
mintBlockNumber = receipt.blockNumber;
|
|
174
|
+
tokenId = '0';
|
|
175
|
+
for (const eventLog of receipt.logs) {
|
|
176
|
+
try {
|
|
177
|
+
const parsed = nftContract.interface.parseLog({
|
|
178
|
+
topics: eventLog.topics,
|
|
179
|
+
data: eventLog.data,
|
|
180
|
+
});
|
|
181
|
+
if (parsed?.name === 'TimestampProofMinted') {
|
|
182
|
+
tokenId = parsed.args.tokenId.toString();
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
/* skip */
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
console.log(`Token minted: #${tokenId}`);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const msg = getErrorMessage(error);
|
|
194
|
+
const isAlreadyMinted = /already minted/i.test(msg);
|
|
195
|
+
if (isAlreadyMinted) {
|
|
196
|
+
console.log('Contract reports digest already minted; resolving existing token...');
|
|
197
|
+
let existingTokenId;
|
|
198
|
+
let existingOwner;
|
|
199
|
+
let existingBlockNumber;
|
|
200
|
+
const existingStatus = await (0, zipstamp_server_1.checkNFTStatus)(timestampMetadata.digest, chainId, { debug });
|
|
201
|
+
if (existingStatus.isMinted && existingStatus.tokenId) {
|
|
202
|
+
existingTokenId = existingStatus.tokenId;
|
|
203
|
+
existingOwner = existingStatus.owner;
|
|
204
|
+
existingBlockNumber = existingStatus.proofData?.batchBlockNumber;
|
|
205
|
+
}
|
|
206
|
+
if (!existingTokenId) {
|
|
207
|
+
const verifyRes = await (0, zipstamp_server_1.verifyDigest)(timestampMetadata.digest, chainId, batchId ?? undefined, undefined, { debug });
|
|
208
|
+
if (verifyRes.verified && verifyRes.tokenId) {
|
|
209
|
+
existingTokenId = verifyRes.tokenId;
|
|
210
|
+
existingOwner = verifyRes.owner;
|
|
211
|
+
existingBlockNumber = verifyRes.blockNumber;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (existingTokenId) {
|
|
215
|
+
tokenId = existingTokenId;
|
|
216
|
+
ownerAddress = existingOwner ?? 'unknown';
|
|
217
|
+
mintTransactionHash = 'already-minted';
|
|
218
|
+
mintBlockNumber = existingBlockNumber ?? 0;
|
|
219
|
+
console.log(`Using existing token #${tokenId}. Creating TOKEN.NZIP...`);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
console.error('Mint failed: digest is already minted on-chain but token details could not be retrieved. Try the Zipstamp server or block explorer for this digest.');
|
|
223
|
+
console.error(`Details: ${msg}`);
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
console.error(`Mint failed: ${msg}`);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const nftMetadata = {
|
|
234
|
+
tokenId,
|
|
235
|
+
contractAddress: mintData.nftContractAddress,
|
|
236
|
+
network: mintData.network,
|
|
237
|
+
merkleRoot: mintData.digest,
|
|
238
|
+
networkChainId: chainId,
|
|
239
|
+
contractVersion: mintData.contractVersion,
|
|
240
|
+
transactionHash: mintTransactionHash,
|
|
241
|
+
blockNumber: mintBlockNumber,
|
|
242
|
+
owner: ownerAddress,
|
|
243
|
+
mintedAt: new Date().toISOString(),
|
|
244
|
+
timestampProof: {
|
|
245
|
+
digest: mintData.digest,
|
|
246
|
+
merkleProof: mintData.merkleProof,
|
|
247
|
+
batchMerkleRoot: mintData.batchMerkleRoot,
|
|
248
|
+
batchNumber: mintData.batchNumber,
|
|
249
|
+
batchTransactionHash: mintData.batchTransactionHash,
|
|
250
|
+
batchBlockNumber: mintData.batchBlockNumber,
|
|
251
|
+
batchTimestamp: mintData.batchTimestamp,
|
|
252
|
+
registryAddress: mintData.registryAddress,
|
|
253
|
+
nftContractAddress: mintData.nftContractAddress,
|
|
254
|
+
serverUrl: (0, zipstamp_server_1.getZipStampServerUrl)(),
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
const metadataFiles = (0, zipstamp_server_1.getMetadataFileNames)();
|
|
258
|
+
const outPath = outputPath ?? inputPath.replace(/(\.nzip|\.zip)$/i, '-nft$1');
|
|
259
|
+
const tempDir = path.dirname(outPath) || '.';
|
|
260
|
+
const tempPath = path.join(tempDir, `.neozip-mint-${Date.now()}.tmp`);
|
|
261
|
+
try {
|
|
262
|
+
const zipCopy = new node_2.ZipCopyNode(new node_1.default());
|
|
263
|
+
const { destPath, dataEndOffset, copiedEntries } = await zipCopy.copyZipEntriesOnly(inputPath, tempPath, {
|
|
264
|
+
entryFilter: (entry) => !metadataFiles.includes(entry.filename || ''),
|
|
265
|
+
});
|
|
266
|
+
const tokenContent = JSON.stringify(nftMetadata, null, 2);
|
|
267
|
+
const tokenBuffer = Buffer.from(tokenContent, 'utf-8');
|
|
268
|
+
const { crc32 } = await import('neozipkit');
|
|
269
|
+
const tokenEntry = zip.createZipEntry('META-INF/TOKEN.NZIP');
|
|
270
|
+
tokenEntry.crc = crc32(tokenBuffer) >>> 0;
|
|
271
|
+
tokenEntry.compressedSize = tokenBuffer.length;
|
|
272
|
+
tokenEntry.uncompressedSize = tokenBuffer.length;
|
|
273
|
+
tokenEntry.cmpMethod = 0;
|
|
274
|
+
tokenEntry.localHdrOffset = dataEndOffset;
|
|
275
|
+
tokenEntry.fileBuffer = tokenBuffer;
|
|
276
|
+
const localHdr = tokenEntry.createLocalHdr();
|
|
277
|
+
const fd = fs.openSync(destPath, 'r+');
|
|
278
|
+
try {
|
|
279
|
+
fs.writeSync(fd, localHdr, 0, localHdr.length, dataEndOffset);
|
|
280
|
+
fs.writeSync(fd, tokenBuffer, 0, tokenBuffer.length, dataEndOffset + localHdr.length);
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
fs.closeSync(fd);
|
|
284
|
+
}
|
|
285
|
+
const allEntries = [...copiedEntries, tokenEntry];
|
|
286
|
+
zipCopy.writeCentralDirectoryAndEOCD(destPath, allEntries, { zipComment: '' });
|
|
287
|
+
fs.renameSync(destPath, outPath);
|
|
288
|
+
console.log(`Output: ${outPath}`);
|
|
289
|
+
// Summary matching Zipstamp verification style
|
|
290
|
+
console.log('\n🔗 Mint complete:');
|
|
291
|
+
console.log(' - Status: ✅ MINTED');
|
|
292
|
+
console.log(' - Stamp type: NZIP-NFT (tokenized archive)');
|
|
293
|
+
console.log(` - Token ID: #${tokenId}`);
|
|
294
|
+
console.log(` - Network: ${mintData.network}`);
|
|
295
|
+
console.log(` - Contract: ${mintData.nftContractAddress}`);
|
|
296
|
+
if (mintTransactionHash && mintTransactionHash !== 'already-minted') {
|
|
297
|
+
console.log(` - Transaction: ${mintTransactionHash}`);
|
|
298
|
+
}
|
|
299
|
+
if (mintBlockNumber > 0) {
|
|
300
|
+
console.log(` - Block: ${mintBlockNumber}`);
|
|
301
|
+
}
|
|
302
|
+
const batchTs = mintData.batchTimestamp;
|
|
303
|
+
if (batchTs != null && mintBlockNumber > 0) {
|
|
304
|
+
const ts = typeof batchTs === 'number' ? (batchTs < 1e12 ? batchTs * 1000 : batchTs) : Date.parse(String(batchTs));
|
|
305
|
+
if (!isNaN(ts)) {
|
|
306
|
+
const blockDate = new Date(ts);
|
|
307
|
+
const mintedStr = blockDate.toLocaleString(undefined, {
|
|
308
|
+
month: '2-digit',
|
|
309
|
+
day: '2-digit',
|
|
310
|
+
year: 'numeric',
|
|
311
|
+
hour: '2-digit',
|
|
312
|
+
minute: '2-digit',
|
|
313
|
+
second: '2-digit',
|
|
314
|
+
hour12: true,
|
|
315
|
+
});
|
|
316
|
+
console.log(` - Block timestamp (minted): ${mintedStr}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
if (fs.existsSync(tempPath)) {
|
|
323
|
+
try {
|
|
324
|
+
fs.unlinkSync(tempPath);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
/* ignore */
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
331
|
+
console.error(`Error creating output: ${msg}`);
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=mintTimestampProof.js.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Verify email for Zipstamp timestamping
|
|
4
|
+
*
|
|
5
|
+
* Sets a default email for Zipstamp timestamps. Checks with the server whether
|
|
6
|
+
* the email is already verified; if not, sends a magic link. Always saves as default.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.runVerifyEmail = runVerifyEmail;
|
|
43
|
+
const readline = __importStar(require("readline"));
|
|
44
|
+
const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
|
|
45
|
+
const ConfigStore_1 = require("../config/ConfigStore");
|
|
46
|
+
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
47
|
+
function isValidEmail(email) {
|
|
48
|
+
return EMAIL_REGEX.test(email.trim());
|
|
49
|
+
}
|
|
50
|
+
function isAlreadyVerifiedMessage(msg) {
|
|
51
|
+
if (!msg)
|
|
52
|
+
return false;
|
|
53
|
+
const lower = msg.toLowerCase();
|
|
54
|
+
return ((lower.includes('already') && (lower.includes('verified') || lower.includes('registered'))) ||
|
|
55
|
+
lower.includes('already verified') ||
|
|
56
|
+
lower.includes('email already'));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run the verify-email flow.
|
|
60
|
+
* Calls the server to check verification status; if already verified, sets as default.
|
|
61
|
+
* If not, sends magic link and sets as default. Always saves email to config.
|
|
62
|
+
*/
|
|
63
|
+
async function runVerifyEmail(options) {
|
|
64
|
+
const rl = readline.createInterface({
|
|
65
|
+
input: process.stdin,
|
|
66
|
+
output: process.stdout,
|
|
67
|
+
});
|
|
68
|
+
const prompt = (question, defaultValue) => {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const display = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
|
71
|
+
rl.question(display, (answer) => {
|
|
72
|
+
resolve((answer.trim() || defaultValue || '').trim());
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
console.log('\n📧 Zipstamp Email Configuration\n');
|
|
78
|
+
console.log('Zipstamp requires a verified email to create timestamped ZIP files.');
|
|
79
|
+
console.log('');
|
|
80
|
+
const config = ConfigStore_1.ConfigStore.getConfig();
|
|
81
|
+
const defaultEmail = options?.email || config.timestampEmail || '';
|
|
82
|
+
const email = await prompt('Email address', defaultEmail || undefined);
|
|
83
|
+
if (!email) {
|
|
84
|
+
console.error('❌ Email is required');
|
|
85
|
+
rl.close();
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (!isValidEmail(email)) {
|
|
89
|
+
console.error('❌ Invalid email format');
|
|
90
|
+
rl.close();
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// --set-default: skip server check, just save
|
|
94
|
+
if (options?.setDefaultOnly) {
|
|
95
|
+
const saved = ConfigStore_1.ConfigStore.set('zipstamp.timestampEmail', email);
|
|
96
|
+
if (!saved) {
|
|
97
|
+
console.error('❌ Failed to save email to configuration');
|
|
98
|
+
rl.close();
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
console.log(`\n✅ ${email} set as default for Zipstamp timestamps.`);
|
|
102
|
+
console.log('\n💡 You can now create timestamped ZIPs: neozip -ts <archive> <files>\n');
|
|
103
|
+
rl.close();
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
// Check with server: register returns status (already verified vs magic link sent)
|
|
107
|
+
console.log(`\n📤 Checking verification status for ${email}...`);
|
|
108
|
+
const registerResult = await (0, zipstamp_server_1.registerEmail)(email, {
|
|
109
|
+
debug: options?.debug,
|
|
110
|
+
});
|
|
111
|
+
if (!registerResult.success) {
|
|
112
|
+
console.error('❌ Failed to register email:', registerResult.error || 'Unknown error');
|
|
113
|
+
rl.close();
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const res = registerResult;
|
|
117
|
+
const msg = res.message || '';
|
|
118
|
+
const alreadyVerified = res.verified === true ||
|
|
119
|
+
res.alreadyVerified === true ||
|
|
120
|
+
isAlreadyVerifiedMessage(msg);
|
|
121
|
+
const saved = ConfigStore_1.ConfigStore.set('zipstamp.timestampEmail', email);
|
|
122
|
+
if (!saved) {
|
|
123
|
+
console.error('❌ Failed to save email to configuration');
|
|
124
|
+
rl.close();
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (alreadyVerified) {
|
|
128
|
+
console.log('\n✅ Email is already verified. Set as default.');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
console.log('\n✅ Verification link sent. Check your inbox (and spam folder).');
|
|
132
|
+
console.log(' Click the magic link in the email to complete verification.');
|
|
133
|
+
console.log(` ${email} has been set as default and will work once you've clicked the link.`);
|
|
134
|
+
}
|
|
135
|
+
console.log('\n💡 You can now create timestamped ZIPs: neozip -ts <archive> <files>\n');
|
|
136
|
+
rl.close();
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
141
|
+
console.error('❌ Error:', msg);
|
|
142
|
+
rl.close();
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=verifyEmail.js.map
|
|
@@ -41,7 +41,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
41
41
|
exports.ConfigSetup = void 0;
|
|
42
42
|
const readline = __importStar(require("readline"));
|
|
43
43
|
const ConfigStore_1 = require("./ConfigStore");
|
|
44
|
-
const
|
|
44
|
+
const neozip_blockchain_1 = require("neozip-blockchain");
|
|
45
|
+
function resolveScriptPath(jsPath) {
|
|
46
|
+
if (typeof __filename !== 'undefined' && __filename.endsWith('.ts')) {
|
|
47
|
+
return jsPath.replace(/\.js$/, '.ts');
|
|
48
|
+
}
|
|
49
|
+
return jsPath;
|
|
50
|
+
}
|
|
45
51
|
class ConfigSetup {
|
|
46
52
|
constructor() {
|
|
47
53
|
this.rl = readline.createInterface({
|
|
@@ -148,7 +154,7 @@ class ConfigSetup {
|
|
|
148
154
|
// Dynamically build network options from CONTRACT_CONFIGS
|
|
149
155
|
// CONTRACT_CONFIGS is already ordered correctly: Base (testnet/mainnet), Arbitrum (testnet/mainnet), Ethereum
|
|
150
156
|
const networkOptions = [];
|
|
151
|
-
const networkConfigs = Object.values(
|
|
157
|
+
const networkConfigs = Object.values(neozip_blockchain_1.CONTRACT_CONFIGS);
|
|
152
158
|
// Verify we're loading networks
|
|
153
159
|
if (networkConfigs.length === 0) {
|
|
154
160
|
console.error('❌ Error: No networks found in CONTRACT_CONFIGS');
|
|
@@ -160,7 +166,7 @@ class ConfigSetup {
|
|
|
160
166
|
// Use the primary alias (first nameAlias) or network name as value
|
|
161
167
|
const primaryAlias = config.nameAliases && config.nameAliases.length > 0
|
|
162
168
|
? config.nameAliases[0]
|
|
163
|
-
: (0,
|
|
169
|
+
: (0, neozip_blockchain_1.normalizeNetworkName)(config.network);
|
|
164
170
|
// Determine description based on network type
|
|
165
171
|
const isTestnet = config.network.toLowerCase().includes('testnet') ||
|
|
166
172
|
config.network.toLowerCase().includes('sepolia');
|
|
@@ -188,7 +194,7 @@ class ConfigSetup {
|
|
|
188
194
|
}
|
|
189
195
|
if (useCustomRpc === 'y') {
|
|
190
196
|
// Get the selected network config to show default RPC
|
|
191
|
-
const selectedNetworkConfig = (0,
|
|
197
|
+
const selectedNetworkConfig = (0, neozip_blockchain_1.getNetworkByName)(network);
|
|
192
198
|
// Prompt for RPC URL for the selected network
|
|
193
199
|
if (selectedNetworkConfig) {
|
|
194
200
|
const defaultRpc = selectedNetworkConfig.rpcUrls && selectedNetworkConfig.rpcUrls.length > 0
|
|
@@ -197,12 +203,12 @@ class ConfigSetup {
|
|
|
197
203
|
// Use primary alias as RPC key for consistency (or normalized network name as fallback)
|
|
198
204
|
const rpcKey = (selectedNetworkConfig.nameAliases && selectedNetworkConfig.nameAliases.length > 0)
|
|
199
205
|
? selectedNetworkConfig.nameAliases[0]
|
|
200
|
-
: (0,
|
|
206
|
+
: (0, neozip_blockchain_1.normalizeNetworkName)(selectedNetworkConfig.network);
|
|
201
207
|
// Check both new format and legacy format for backward compatibility
|
|
202
208
|
const existingRpc = rpcConfig[rpcKey]
|
|
203
|
-
|| rpcConfig[(0,
|
|
209
|
+
|| rpcConfig[(0, neozip_blockchain_1.normalizeNetworkName)(selectedNetworkConfig.network).replace(/\s+/g, '')]
|
|
204
210
|
|| existingConfig?.rpc?.[rpcKey]
|
|
205
|
-
|| existingConfig?.rpc?.[(0,
|
|
211
|
+
|| existingConfig?.rpc?.[(0, neozip_blockchain_1.normalizeNetworkName)(selectedNetworkConfig.network).replace(/\s+/g, '')];
|
|
206
212
|
const customRpc = await this.prompt(` ${selectedNetworkConfig.network} RPC URL`, existingRpc || defaultRpc);
|
|
207
213
|
if (customRpc) {
|
|
208
214
|
rpcConfig[rpcKey] = customRpc;
|
|
@@ -218,21 +224,21 @@ class ConfigSetup {
|
|
|
218
224
|
// Skip the already configured network
|
|
219
225
|
const configAlias = config.nameAliases && config.nameAliases.length > 0
|
|
220
226
|
? config.nameAliases[0]
|
|
221
|
-
: (0,
|
|
227
|
+
: (0, neozip_blockchain_1.normalizeNetworkName)(config.network);
|
|
222
228
|
if (configAlias === network)
|
|
223
229
|
continue;
|
|
224
230
|
// Use primary alias as RPC key for consistency (or normalized network name as fallback)
|
|
225
231
|
const rpcKey = (config.nameAliases && config.nameAliases.length > 0)
|
|
226
232
|
? config.nameAliases[0]
|
|
227
|
-
: (0,
|
|
233
|
+
: (0, neozip_blockchain_1.normalizeNetworkName)(config.network);
|
|
228
234
|
const defaultRpc = config.rpcUrls && config.rpcUrls.length > 0
|
|
229
235
|
? config.rpcUrls[0]
|
|
230
236
|
: '';
|
|
231
237
|
// Check both new format and legacy format for backward compatibility
|
|
232
238
|
const existingRpc = rpcConfig[rpcKey]
|
|
233
|
-
|| rpcConfig[(0,
|
|
239
|
+
|| rpcConfig[(0, neozip_blockchain_1.normalizeNetworkName)(config.network).replace(/\s+/g, '')]
|
|
234
240
|
|| existingConfig?.rpc?.[rpcKey]
|
|
235
|
-
|| existingConfig?.rpc?.[(0,
|
|
241
|
+
|| existingConfig?.rpc?.[(0, neozip_blockchain_1.normalizeNetworkName)(config.network).replace(/\s+/g, '')];
|
|
236
242
|
const customRpc = await this.prompt(` ${config.network} RPC URL`, existingRpc || defaultRpc);
|
|
237
243
|
if (customRpc) {
|
|
238
244
|
rpcConfig[rpcKey] = customRpc;
|
|
@@ -274,12 +280,29 @@ class ConfigSetup {
|
|
|
274
280
|
verbose = verboseStr === 'y';
|
|
275
281
|
debugEnabled = debugStr === 'y';
|
|
276
282
|
}
|
|
277
|
-
//
|
|
283
|
+
// 6. Zipstamp Email (optional - for timestamped ZIPs)
|
|
284
|
+
console.log('\n6️⃣ Zipstamp Email (optional)');
|
|
285
|
+
const configureZipstamp = await this.promptSelection('Configure email for Zipstamp timestamping?', [
|
|
286
|
+
{ value: 'n', label: 'No', description: 'Skip - run neozip verify-email later if needed' },
|
|
287
|
+
{ value: 'y', label: 'Yes', description: 'Set email (must verify with neozip verify-email)' }
|
|
288
|
+
], 'n');
|
|
289
|
+
let zipstampEmail;
|
|
290
|
+
if (configureZipstamp === 'y') {
|
|
291
|
+
zipstampEmail = await this.prompt(' Zipstamp timestamp email', existingConfig?.zipstamp?.timestampEmail || '');
|
|
292
|
+
if (zipstampEmail) {
|
|
293
|
+
console.log(' 💡 Run "neozip verify-email" to verify this email before creating timestamped ZIPs.');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Build configuration object (preserve existing zipstamp if not configuring)
|
|
297
|
+
const zipstampConfig = zipstampEmail !== undefined
|
|
298
|
+
? (zipstampEmail ? { timestampEmail: zipstampEmail } : undefined)
|
|
299
|
+
: existingConfig?.zipstamp;
|
|
278
300
|
const config = {
|
|
279
301
|
wallet: {
|
|
280
302
|
privateKey,
|
|
281
303
|
network,
|
|
282
304
|
},
|
|
305
|
+
zipstamp: zipstampConfig,
|
|
283
306
|
rpc: Object.keys(rpcConfig).length > 0 ? rpcConfig : undefined,
|
|
284
307
|
gas: gasMultiplier || maxGasPrice ? {
|
|
285
308
|
multiplier: gasMultiplier,
|
|
@@ -343,16 +366,17 @@ class ConfigSetup {
|
|
|
343
366
|
const hasPrivateKey = config.walletKey !== null;
|
|
344
367
|
console.log('What would you like to do?');
|
|
345
368
|
console.log(' 1. Modify configuration (update settings)');
|
|
369
|
+
console.log(' 2. Verify Zipstamp email (for timestamped ZIPs)');
|
|
346
370
|
if (hasPrivateKey) {
|
|
347
|
-
console.log('
|
|
348
|
-
console.log('
|
|
349
|
-
console.log('
|
|
371
|
+
console.log(' 3. Delete private key (remove wallet key from configuration)');
|
|
372
|
+
console.log(' 4. Reset to defaults (delete entire configuration)');
|
|
373
|
+
console.log(' 5. Exit');
|
|
350
374
|
}
|
|
351
375
|
else {
|
|
352
|
-
console.log('
|
|
353
|
-
console.log('
|
|
376
|
+
console.log(' 3. Reset to defaults (delete entire configuration)');
|
|
377
|
+
console.log(' 4. Exit');
|
|
354
378
|
}
|
|
355
|
-
const maxChoice = hasPrivateKey ?
|
|
379
|
+
const maxChoice = hasPrivateKey ? 5 : 4;
|
|
356
380
|
rl.question(`\nEnter choice (1-${maxChoice}): `, async (answer) => {
|
|
357
381
|
const choice = parseInt(answer);
|
|
358
382
|
switch (choice) {
|
|
@@ -363,6 +387,12 @@ class ConfigSetup {
|
|
|
363
387
|
process.exit(success ? 0 : 1);
|
|
364
388
|
break;
|
|
365
389
|
case 2:
|
|
390
|
+
rl.close();
|
|
391
|
+
const { runVerifyEmail } = await import(resolveScriptPath('../commands/verifyEmail.js'));
|
|
392
|
+
const verifySuccess = await runVerifyEmail();
|
|
393
|
+
process.exit(verifySuccess ? 0 : 1);
|
|
394
|
+
break;
|
|
395
|
+
case 3:
|
|
366
396
|
if (hasPrivateKey) {
|
|
367
397
|
// Delete private key only
|
|
368
398
|
rl.question('\n⚠️ Are you sure you want to delete your private key? (y/n): ', (confirm) => {
|
|
@@ -403,7 +433,7 @@ class ConfigSetup {
|
|
|
403
433
|
});
|
|
404
434
|
}
|
|
405
435
|
break;
|
|
406
|
-
case
|
|
436
|
+
case 4:
|
|
407
437
|
if (hasPrivateKey) {
|
|
408
438
|
// Reset entire config
|
|
409
439
|
rl.question('\n⚠️ Are you sure you want to delete your wallet configuration? (y/n): ', (confirm) => {
|
|
@@ -429,7 +459,7 @@ class ConfigSetup {
|
|
|
429
459
|
process.exit(0);
|
|
430
460
|
}
|
|
431
461
|
break;
|
|
432
|
-
case
|
|
462
|
+
case 5:
|
|
433
463
|
if (hasPrivateKey) {
|
|
434
464
|
// Exit
|
|
435
465
|
console.log('');
|