neozipkit 0.1.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.
- package/README.md +400 -0
- package/contracts/Lock.sol +34 -0
- package/contracts/bytecode.txt +1 -0
- package/contracts/compile-contract.js +168 -0
- package/contracts/deploy/deploy-both-contracts.js +286 -0
- package/contracts/deploy/deploy-enhanced-contract.js +197 -0
- package/contracts/deploy/deploy-private-contract.js +190 -0
- package/contracts/deploy/deploy-public-clean.js +1063 -0
- package/contracts/deploy/deploy-public-contract.js +1107 -0
- package/contracts/deployment.config.example.js +25 -0
- package/contracts/remix-compile.js +222 -0
- package/contracts/setup-deployment.js +51 -0
- package/contracts/src/NZIP-NFT-Private.sol +350 -0
- package/contracts/src/NZIP-NFT.sol +265 -0
- package/contracts/src/ZipFileNFT-DEPRECATED-6field.sol +260 -0
- package/contracts/src/ZipFileNFT-DEPRECATED-Enhanced.sol +289 -0
- package/dist/browser/CertificateGeneratorBrowser.d.ts +75 -0
- package/dist/browser/CertificateGeneratorBrowser.js +433 -0
- package/dist/browser/TokenVerifierBrowser.d.ts +76 -0
- package/dist/browser/TokenVerifierBrowser.js +299 -0
- package/dist/browser/ZipKitBrowser.d.ts +18 -0
- package/dist/browser/ZipKitBrowser.js +250 -0
- package/dist/browser/ZipKitStampBrowser.d.ts +2 -0
- package/dist/browser/ZipKitStampBrowser.js +12 -0
- package/dist/browser-index.d.ts +21 -0
- package/dist/browser-index.js +58 -0
- package/dist/browser.d.ts +21 -0
- package/dist/browser.js +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +76 -0
- package/dist/server/blockchain/CertificateToken.d.ts +48 -0
- package/dist/server/blockchain/CertificateToken.js +386 -0
- package/dist/server/blockchain/NFTMinter.d.ts +41 -0
- package/dist/server/blockchain/NFTMinter.js +323 -0
- package/dist/server/blockchain/TokenQuerier.d.ts +31 -0
- package/dist/server/blockchain/TokenQuerier.js +259 -0
- package/dist/server/blockchain/TokenVerifier.d.ts +20 -0
- package/dist/server/blockchain/TokenVerifier.js +395 -0
- package/dist/server/blockchain/WalletAnalyzer.d.ts +81 -0
- package/dist/server/blockchain/WalletAnalyzer.js +356 -0
- package/dist/server/blockchain/WalletFormatter.d.ts +39 -0
- package/dist/server/blockchain/WalletFormatter.js +196 -0
- package/dist/server/blockchain/WalletManager.d.ts +46 -0
- package/dist/server/blockchain/WalletManager.js +294 -0
- package/dist/server/blockchain/ZipMetadata.d.ts +27 -0
- package/dist/server/blockchain/ZipMetadata.js +131 -0
- package/dist/server/blockchain/ZipTokenProcessor.d.ts +44 -0
- package/dist/server/blockchain/ZipTokenProcessor.js +204 -0
- package/dist/server/blockchain/constants.d.ts +102 -0
- package/dist/server/blockchain/constants.js +968 -0
- package/dist/server/blockchain/index.d.ts +9 -0
- package/dist/server/blockchain/index.js +44 -0
- package/dist/server/blockchain/test-certificate.d.ts +8 -0
- package/dist/server/blockchain/test-certificate.js +100 -0
- package/dist/server/blockchain/types.d.ts +69 -0
- package/dist/server/blockchain/types.js +51 -0
- package/dist/server/platform/server/ZipKitServer.d.ts +105 -0
- package/dist/server/platform/server/ZipKitServer.js +437 -0
- package/dist/server/platform/servercomponents/ZipKitOTS.d.ts +91 -0
- package/dist/server/platform/servercomponents/ZipKitOTS.js +271 -0
- package/dist/shared/ZipEntry.d.ts +131 -0
- package/dist/shared/ZipEntry.js +686 -0
- package/dist/shared/ZipKit.d.ts +249 -0
- package/dist/shared/ZipKit.js +789 -0
- package/dist/shared/components/CertificateOTS.d.ts +15 -0
- package/dist/shared/components/CertificateOTS.js +116 -0
- package/dist/shared/components/FileSys.d.ts +2 -0
- package/dist/shared/components/FileSys.js +113 -0
- package/dist/shared/components/HashAccumulator.d.ts +55 -0
- package/dist/shared/components/HashAccumulator.js +100 -0
- package/dist/shared/components/Support.d.ts +63 -0
- package/dist/shared/components/Support.js +70 -0
- package/dist/shared/components/Util.d.ts +21 -0
- package/dist/shared/components/Util.js +115 -0
- package/dist/shared/components/ZipCrypto.d.ts +3 -0
- package/dist/shared/components/ZipCrypto.js +78 -0
- package/dist/shared/components/merkleTree.d.ts +66 -0
- package/dist/shared/components/merkleTree.js +136 -0
- package/dist/shared/constants/Errors.d.ts +53 -0
- package/dist/shared/constants/Errors.js +69 -0
- package/dist/shared/constants/Headers.d.ts +167 -0
- package/dist/shared/constants/Headers.js +191 -0
- package/dist/shared/types/index.d.ts +21 -0
- package/dist/shared/types/index.js +3 -0
- package/dist/shared/types.d.ts +33 -0
- package/dist/shared/types.js +8 -0
- package/dist/shared/version.d.ts +4 -0
- package/dist/shared/version.js +7 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.js +3 -0
- package/examples/unzip-token.ts +488 -0
- package/examples/zip-token.ts +604 -0
- package/package.json +117 -0
- package/src/browser/CertificateGeneratorBrowser.ts +498 -0
- package/src/browser/TokenVerifierBrowser.ts +344 -0
- package/src/browser/ZipKitBrowser.ts +242 -0
- package/src/browser/ZipKitStampBrowser.ts +9 -0
- package/src/browser-index.ts +36 -0
- package/src/index.ts +48 -0
- package/src/server/blockchain/CertificateToken.ts +444 -0
- package/src/server/blockchain/NFTMinter.ts +357 -0
- package/src/server/blockchain/TokenQuerier.ts +287 -0
- package/src/server/blockchain/TokenVerifier.ts +453 -0
- package/src/server/blockchain/WalletAnalyzer.ts +397 -0
- package/src/server/blockchain/WalletFormatter.ts +206 -0
- package/src/server/blockchain/WalletManager.ts +294 -0
- package/src/server/blockchain/ZipMetadata.ts +117 -0
- package/src/server/blockchain/ZipTokenProcessor.ts +234 -0
- package/src/server/blockchain/constants.ts +969 -0
- package/src/server/blockchain/index.ts +42 -0
- package/src/server/blockchain/test-certificate.ts +137 -0
- package/src/server/blockchain/types.ts +134 -0
- package/src/server/platform/server/ZipKitServer.ts +459 -0
- package/src/server/platform/servercomponents/ZipKitOTS.ts +313 -0
- package/src/server/platform/servercomponents/opentimestamps.d.ts +23 -0
- package/src/shared/ZipEntry.ts +767 -0
- package/src/shared/ZipKit.ts +899 -0
- package/src/shared/components/CertificateOTS.ts +132 -0
- package/src/shared/components/FileSys.ts +87 -0
- package/src/shared/components/HashAccumulator.ts +113 -0
- package/src/shared/components/Support.ts +77 -0
- package/src/shared/components/Util.ts +113 -0
- package/src/shared/components/ZipCrypto.ts +91 -0
- package/src/shared/components/merkleTree.ts +162 -0
- package/src/shared/constants/Errors.ts +82 -0
- package/src/shared/constants/Headers.ts +203 -0
- package/src/shared/types/index.ts +25 -0
- package/src/shared/types/modules.d.ts +22 -0
- package/src/shared/types/opentimestamps.d.ts +23 -0
- package/src/shared/types.d.ts +8 -0
- package/src/shared/types.ts +43 -0
- package/src/shared/version.ts +4 -0
- package/src/types/index.ts +25 -0
- package/src/types/modules.d.ts +22 -0
- package/src/types/opentimestamps.d.ts +23 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
// Contract ABI for NZIP tokens
|
|
6
|
+
const NZIP_CONTRACT_ABI = [
|
|
7
|
+
"function getZipFileInfo(uint256 tokenId) view returns (tuple(string fileName, string merkleRootHash, uint256 creationTimestamp, string ipfsHash, uint256 tokenizationTime, address creator))",
|
|
8
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
9
|
+
"function name() view returns (string)",
|
|
10
|
+
"function symbol() view returns (string)",
|
|
11
|
+
"function totalSupply() view returns (uint256)",
|
|
12
|
+
"function getTokensByMerkleRoot(string merkleRootHash) view returns (uint256[])",
|
|
13
|
+
"event ZipFileTokenized(uint256 indexed tokenId, address indexed creator, string fileName, string merkleRootHash, uint256 creationTimestamp, string ipfsHash)"
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// ERC-20 token ABI for checking other tokens
|
|
17
|
+
const ERC20_ABI = [
|
|
18
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
19
|
+
"function name() view returns (string)",
|
|
20
|
+
"function symbol() view returns (string)",
|
|
21
|
+
"function decimals() view returns (uint8)"
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// ERC-721 NFT ABI
|
|
25
|
+
const ERC721_ABI = [
|
|
26
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
27
|
+
"function name() view returns (string)",
|
|
28
|
+
"function symbol() view returns (string)",
|
|
29
|
+
"function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)"
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export interface TokenInfo {
|
|
33
|
+
address: string;
|
|
34
|
+
name: string;
|
|
35
|
+
symbol: string;
|
|
36
|
+
balance: string;
|
|
37
|
+
decimals?: number;
|
|
38
|
+
type: 'ERC20' | 'ERC721' | 'NZIP';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface NZipTokenDetails {
|
|
42
|
+
tokenId: string;
|
|
43
|
+
merkleRootHash: string;
|
|
44
|
+
fileName: string;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
tokenURI?: string;
|
|
47
|
+
creator: string;
|
|
48
|
+
creationTimestamp: number;
|
|
49
|
+
tokenizationTime: number;
|
|
50
|
+
ipfsHash?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface WalletBasicInfo {
|
|
54
|
+
ethBalance: string;
|
|
55
|
+
transactionCount: number;
|
|
56
|
+
networkName: string;
|
|
57
|
+
chainId: bigint;
|
|
58
|
+
latestBlock: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TokenScanResult {
|
|
62
|
+
foundTokens: number[];
|
|
63
|
+
searchMethod: 'recent-events' | 'iteration' | 'common-ids' | 'none';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CommonTokenConfig {
|
|
67
|
+
address: string;
|
|
68
|
+
name: string;
|
|
69
|
+
symbol: string;
|
|
70
|
+
type: 'ERC20' | 'ERC721';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class WalletAnalyzer {
|
|
74
|
+
private provider: ethers.JsonRpcProvider;
|
|
75
|
+
|
|
76
|
+
constructor(provider: ethers.JsonRpcProvider) {
|
|
77
|
+
this.provider = provider;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get basic wallet information (balance, transaction count, etc.)
|
|
82
|
+
*/
|
|
83
|
+
async getWalletBasics(address: string): Promise<WalletBasicInfo> {
|
|
84
|
+
try {
|
|
85
|
+
// Get ETH balance
|
|
86
|
+
const balance = await this.provider.getBalance(address);
|
|
87
|
+
const ethBalance = ethers.formatEther(balance);
|
|
88
|
+
|
|
89
|
+
// Get transaction count (nonce)
|
|
90
|
+
const txCount = await this.provider.getTransactionCount(address);
|
|
91
|
+
|
|
92
|
+
// Get network info
|
|
93
|
+
const network = await this.provider.getNetwork();
|
|
94
|
+
|
|
95
|
+
// Get latest block
|
|
96
|
+
const latestBlock = await this.provider.getBlockNumber();
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
ethBalance,
|
|
100
|
+
transactionCount: txCount,
|
|
101
|
+
networkName: network.name,
|
|
102
|
+
chainId: network.chainId,
|
|
103
|
+
latestBlock
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(`Failed to get wallet basics: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Scan for NZIP tokens owned by an address using multiple methods
|
|
112
|
+
*/
|
|
113
|
+
async scanForNZipTokens(contractAddress: string, ownerAddress: string): Promise<TokenScanResult> {
|
|
114
|
+
const contract = new ethers.Contract(contractAddress, NZIP_CONTRACT_ABI, this.provider);
|
|
115
|
+
let foundTokens: number[] = [];
|
|
116
|
+
let searchMethod: TokenScanResult['searchMethod'] = 'none';
|
|
117
|
+
|
|
118
|
+
// Method 1: Try events with recent blocks only
|
|
119
|
+
try {
|
|
120
|
+
const latestBlock = await this.provider.getBlockNumber();
|
|
121
|
+
const startBlock = Math.max(0, latestBlock - 50000);
|
|
122
|
+
|
|
123
|
+
const filter = contract.filters.ZipFileTokenized(null, ownerAddress);
|
|
124
|
+
const events = await contract.queryFilter(filter, startBlock, 'latest');
|
|
125
|
+
|
|
126
|
+
for (const event of events) {
|
|
127
|
+
if ('args' in event && event.args && event.args.tokenId) {
|
|
128
|
+
const tokenId = Number(event.args.tokenId);
|
|
129
|
+
if (!foundTokens.includes(tokenId)) {
|
|
130
|
+
foundTokens.push(tokenId);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (foundTokens.length > 0) {
|
|
136
|
+
searchMethod = 'recent-events';
|
|
137
|
+
return { foundTokens, searchMethod };
|
|
138
|
+
}
|
|
139
|
+
} catch (eventError) {
|
|
140
|
+
// Continue to next method
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Method 2: Direct token ID checking (brute force for small supply)
|
|
144
|
+
try {
|
|
145
|
+
const totalSupply = await contract.totalSupply();
|
|
146
|
+
const maxCheck = Math.min(Number(totalSupply), 50); // Limit to 50 tokens max
|
|
147
|
+
|
|
148
|
+
for (let tokenId = 0; tokenId < maxCheck; tokenId++) {
|
|
149
|
+
try {
|
|
150
|
+
const owner = await contract.ownerOf(tokenId);
|
|
151
|
+
if (owner.toLowerCase() === ownerAddress.toLowerCase()) {
|
|
152
|
+
foundTokens.push(tokenId);
|
|
153
|
+
}
|
|
154
|
+
} catch (ownerError) {
|
|
155
|
+
// Token doesn't exist or other error, skip
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (foundTokens.length > 0) {
|
|
160
|
+
searchMethod = 'iteration';
|
|
161
|
+
return { foundTokens, searchMethod };
|
|
162
|
+
}
|
|
163
|
+
} catch (iterationError) {
|
|
164
|
+
// Continue to next method
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Method 3: Check specific token IDs that commonly exist
|
|
168
|
+
const commonTokenIds = Array.from({length: 20}, (_, i) => i); // 0-19
|
|
169
|
+
|
|
170
|
+
for (const tokenId of commonTokenIds) {
|
|
171
|
+
try {
|
|
172
|
+
const owner = await contract.ownerOf(tokenId);
|
|
173
|
+
if (owner.toLowerCase() === ownerAddress.toLowerCase()) {
|
|
174
|
+
foundTokens.push(tokenId);
|
|
175
|
+
}
|
|
176
|
+
} catch (ownerError) {
|
|
177
|
+
// Token doesn't exist, skip
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (foundTokens.length > 0) {
|
|
182
|
+
searchMethod = 'common-ids';
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return { foundTokens, searchMethod };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get detailed information about NZIP tokens
|
|
190
|
+
*/
|
|
191
|
+
async getNZipTokenDetails(contractAddress: string, tokenIds: number[]): Promise<NZipTokenDetails[]> {
|
|
192
|
+
const contract = new ethers.Contract(contractAddress, NZIP_CONTRACT_ABI, this.provider);
|
|
193
|
+
const tokenDetails: NZipTokenDetails[] = [];
|
|
194
|
+
|
|
195
|
+
for (const tokenId of tokenIds) {
|
|
196
|
+
try {
|
|
197
|
+
const zipInfo = await contract.getZipFileInfo(tokenId);
|
|
198
|
+
|
|
199
|
+
tokenDetails.push({
|
|
200
|
+
tokenId: tokenId.toString(),
|
|
201
|
+
merkleRootHash: zipInfo.merkleRootHash,
|
|
202
|
+
fileName: zipInfo.fileName,
|
|
203
|
+
timestamp: Number(zipInfo.creationTimestamp),
|
|
204
|
+
creator: zipInfo.creator,
|
|
205
|
+
creationTimestamp: Number(zipInfo.creationTimestamp),
|
|
206
|
+
tokenizationTime: Number(zipInfo.tokenizationTime),
|
|
207
|
+
ipfsHash: zipInfo.ipfsHash || undefined
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(`Error fetching details for token ${tokenId}:`, error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return tokenDetails;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get contract information (name, symbol, total supply)
|
|
219
|
+
*/
|
|
220
|
+
async getContractInfo(contractAddress: string): Promise<{name: string, symbol: string, totalSupply: string} | null> {
|
|
221
|
+
try {
|
|
222
|
+
const contract = new ethers.Contract(contractAddress, NZIP_CONTRACT_ABI, this.provider);
|
|
223
|
+
|
|
224
|
+
const [name, symbol, totalSupply] = await Promise.all([
|
|
225
|
+
contract.name(),
|
|
226
|
+
contract.symbol(),
|
|
227
|
+
contract.totalSupply()
|
|
228
|
+
]);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
name,
|
|
232
|
+
symbol,
|
|
233
|
+
totalSupply: totalSupply.toString()
|
|
234
|
+
};
|
|
235
|
+
} catch (error) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Check ERC-20 token balances for common tokens
|
|
242
|
+
*/
|
|
243
|
+
async checkERC20Tokens(ownerAddress: string, tokenConfigs: CommonTokenConfig[]): Promise<TokenInfo[]> {
|
|
244
|
+
const foundTokens: TokenInfo[] = [];
|
|
245
|
+
|
|
246
|
+
for (const tokenConfig of tokenConfigs.filter(t => t.type === 'ERC20')) {
|
|
247
|
+
try {
|
|
248
|
+
const contract = new ethers.Contract(tokenConfig.address, ERC20_ABI, this.provider);
|
|
249
|
+
|
|
250
|
+
const balance = await contract.balanceOf(ownerAddress);
|
|
251
|
+
if (balance > 0) {
|
|
252
|
+
let name, symbol, decimals;
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
[name, symbol, decimals] = await Promise.all([
|
|
256
|
+
contract.name(),
|
|
257
|
+
contract.symbol(),
|
|
258
|
+
contract.decimals()
|
|
259
|
+
]);
|
|
260
|
+
} catch (metadataError) {
|
|
261
|
+
// Use fallback info if contract calls fail
|
|
262
|
+
name = tokenConfig.name;
|
|
263
|
+
symbol = tokenConfig.symbol;
|
|
264
|
+
decimals = 18; // Default decimals
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const formattedBalance = ethers.formatUnits(balance, decimals);
|
|
268
|
+
|
|
269
|
+
foundTokens.push({
|
|
270
|
+
address: tokenConfig.address,
|
|
271
|
+
name,
|
|
272
|
+
symbol,
|
|
273
|
+
balance: formattedBalance,
|
|
274
|
+
decimals,
|
|
275
|
+
type: 'ERC20'
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
// Token might not exist or might not be ERC20, continue silently
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return foundTokens;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Check NFT collection balances
|
|
288
|
+
*/
|
|
289
|
+
async checkNFTCollections(ownerAddress: string, nftConfigs: CommonTokenConfig[]): Promise<TokenInfo[]> {
|
|
290
|
+
const foundNFTs: TokenInfo[] = [];
|
|
291
|
+
|
|
292
|
+
for (const nftConfig of nftConfigs) {
|
|
293
|
+
try {
|
|
294
|
+
const contract = new ethers.Contract(nftConfig.address, ERC721_ABI, this.provider);
|
|
295
|
+
|
|
296
|
+
const balance = await contract.balanceOf(ownerAddress);
|
|
297
|
+
if (balance > 0) {
|
|
298
|
+
let name, symbol;
|
|
299
|
+
try {
|
|
300
|
+
[name, symbol] = await Promise.all([
|
|
301
|
+
contract.name(),
|
|
302
|
+
contract.symbol()
|
|
303
|
+
]);
|
|
304
|
+
} catch (metadataError) {
|
|
305
|
+
name = nftConfig.name;
|
|
306
|
+
symbol = nftConfig.symbol;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Try to get token IDs if balance is small
|
|
310
|
+
let tokenIds: string[] = [];
|
|
311
|
+
if (Number(balance) <= 5) {
|
|
312
|
+
try {
|
|
313
|
+
for (let i = 0; i < Number(balance); i++) {
|
|
314
|
+
const tokenId = await contract.tokenOfOwnerByIndex(ownerAddress, i);
|
|
315
|
+
tokenIds.push(tokenId.toString());
|
|
316
|
+
}
|
|
317
|
+
} catch (enumError) {
|
|
318
|
+
// Unable to enumerate, that's okay
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
foundNFTs.push({
|
|
323
|
+
address: nftConfig.address,
|
|
324
|
+
name,
|
|
325
|
+
symbol,
|
|
326
|
+
balance: balance.toString(),
|
|
327
|
+
type: 'ERC721'
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
} catch (error) {
|
|
331
|
+
// Contract might not exist or might not be ERC-721, continue silently
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return foundNFTs;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Detect NZIP contract from deployment file
|
|
340
|
+
*/
|
|
341
|
+
static detectNZipContract(): string | undefined {
|
|
342
|
+
const deploymentPath = path.join(process.cwd(), 'zipfile-nft-deployment.json');
|
|
343
|
+
|
|
344
|
+
if (fs.existsSync(deploymentPath)) {
|
|
345
|
+
try {
|
|
346
|
+
const deploymentInfo = JSON.parse(fs.readFileSync(deploymentPath, 'utf8'));
|
|
347
|
+
if (deploymentInfo.address) {
|
|
348
|
+
return deploymentInfo.address;
|
|
349
|
+
}
|
|
350
|
+
} catch (error) {
|
|
351
|
+
// Could not read deployment file
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get common token configurations for a network
|
|
360
|
+
*/
|
|
361
|
+
static getCommonTokens(network: string): CommonTokenConfig[] {
|
|
362
|
+
const commonTokens: Record<string, CommonTokenConfig[]> = {
|
|
363
|
+
'base-sepolia': [
|
|
364
|
+
{ address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', name: 'USD Coin', symbol: 'USDC', type: 'ERC20' },
|
|
365
|
+
{ address: '0x4200000000000000000000000000000000000006', name: 'Wrapped Ether', symbol: 'WETH', type: 'ERC20' },
|
|
366
|
+
{ address: '0xE4aB69C077896252FAFBD49EFD26B5D171A32410', name: 'Coinbase Wrapped Staked ETH', symbol: 'cbETH', type: 'ERC20' },
|
|
367
|
+
],
|
|
368
|
+
'base': [
|
|
369
|
+
{ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', name: 'USD Coin', symbol: 'USDC', type: 'ERC20' },
|
|
370
|
+
{ address: '0x4200000000000000000000000000000000000006', name: 'Wrapped Ether', symbol: 'WETH', type: 'ERC20' },
|
|
371
|
+
{ address: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', name: 'Dai Stablecoin', symbol: 'DAI', type: 'ERC20' },
|
|
372
|
+
{ address: '0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22', name: 'Coinbase Wrapped Staked ETH', symbol: 'cbETH', type: 'ERC20' },
|
|
373
|
+
{ address: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', name: 'USD Base Coin', symbol: 'USDbC', type: 'ERC20' },
|
|
374
|
+
]
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
return commonTokens[network] || [];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get common NFT configurations for a network
|
|
382
|
+
*/
|
|
383
|
+
static getCommonNFTs(network: string): CommonTokenConfig[] {
|
|
384
|
+
const commonNFTs: Record<string, CommonTokenConfig[]> = {
|
|
385
|
+
'base-sepolia': [
|
|
386
|
+
// Add known testnet NFTs here when they become available
|
|
387
|
+
],
|
|
388
|
+
'base': [
|
|
389
|
+
// Base mainnet NFTs - these are examples, real addresses would be different
|
|
390
|
+
{ address: '0x1234567890123456789012345678901234567890', name: 'Base Punks', symbol: 'BPUNK', type: 'ERC721' },
|
|
391
|
+
{ address: '0x0987654321098765432109876543210987654321', name: 'Base Bears', symbol: 'BBEAR', type: 'ERC721' },
|
|
392
|
+
]
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
return commonNFTs[network] || [];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { WalletBasicInfo, NZipTokenDetails, TokenInfo, TokenScanResult } from './WalletAnalyzer';
|
|
2
|
+
|
|
3
|
+
export class WalletFormatter {
|
|
4
|
+
/**
|
|
5
|
+
* Display basic wallet information
|
|
6
|
+
*/
|
|
7
|
+
static displayWalletBasics(walletInfo: WalletBasicInfo): void {
|
|
8
|
+
console.log('\nš° Basic Wallet Information');
|
|
9
|
+
console.log('ā'.repeat(50));
|
|
10
|
+
|
|
11
|
+
console.log(`ETH Balance: ${walletInfo.ethBalance} ETH`);
|
|
12
|
+
console.log(`Transaction Count: ${walletInfo.transactionCount}`);
|
|
13
|
+
console.log(`Network Name: ${walletInfo.networkName}`);
|
|
14
|
+
console.log(`Chain ID: ${walletInfo.chainId}`);
|
|
15
|
+
console.log(`Latest Block: ${walletInfo.latestBlock}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Display contract information
|
|
20
|
+
*/
|
|
21
|
+
static displayContractInfo(contractInfo: {name: string, symbol: string, totalSupply: string} | null, isLegacy: boolean = false): void {
|
|
22
|
+
if (contractInfo) {
|
|
23
|
+
console.log(`Contract Name: ${contractInfo.name}`);
|
|
24
|
+
console.log(`Contract Symbol: ${contractInfo.symbol}`);
|
|
25
|
+
console.log(`Total Supply: ${contractInfo.totalSupply}`);
|
|
26
|
+
} else {
|
|
27
|
+
console.log('Contract Info: Unable to fetch');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Display token scan progress
|
|
33
|
+
*/
|
|
34
|
+
static displayTokenScanProgress(scanResult: TokenScanResult): void {
|
|
35
|
+
console.log('Searching for tokens owned by this address...');
|
|
36
|
+
|
|
37
|
+
if (scanResult.searchMethod === 'recent-events') {
|
|
38
|
+
console.log(' š Trying recent events (last 50k blocks)...');
|
|
39
|
+
console.log(` ā
Found ${scanResult.foundTokens.length} tokens from recent events`);
|
|
40
|
+
} else if (scanResult.searchMethod === 'iteration') {
|
|
41
|
+
console.log(' š Trying recent events (last 50k blocks)...');
|
|
42
|
+
console.log(' ā Recent events failed, trying alternative methods...');
|
|
43
|
+
console.log(' š Checking token ownership by iteration...');
|
|
44
|
+
console.log(` ā
Found ${scanResult.foundTokens.length} tokens by iteration`);
|
|
45
|
+
} else if (scanResult.searchMethod === 'common-ids') {
|
|
46
|
+
console.log(' š Trying recent events (last 50k blocks)...');
|
|
47
|
+
console.log(' ā Recent events failed, trying alternative methods...');
|
|
48
|
+
console.log(' š Checking token ownership by iteration...');
|
|
49
|
+
console.log(' ā ļø No tokens found by iteration, checking common IDs...');
|
|
50
|
+
console.log(' š Checking common token IDs (0-19)...');
|
|
51
|
+
console.log(` ā
Found ${scanResult.foundTokens.length} tokens from common IDs`);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(' š Trying recent events (last 50k blocks)...');
|
|
54
|
+
console.log(' ā Recent events failed, trying alternative methods...');
|
|
55
|
+
console.log(' š Checking token ownership by iteration...');
|
|
56
|
+
console.log(' ā ļø No tokens found by iteration, checking common IDs...');
|
|
57
|
+
console.log(' š Checking common token IDs (0-19)...');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Display detailed NZIP token information
|
|
63
|
+
*/
|
|
64
|
+
static displayNZipTokenDetails(tokenDetails: NZipTokenDetails[], scanResult: TokenScanResult, contractAddress: string): void {
|
|
65
|
+
if (tokenDetails.length > 0) {
|
|
66
|
+
console.log(`\nā
Found ${tokenDetails.length} NZIP token(s) using ${scanResult.searchMethod}:`);
|
|
67
|
+
console.log('š Your NZIP Token Details:');
|
|
68
|
+
console.log('ā'.repeat(80));
|
|
69
|
+
|
|
70
|
+
for (const token of tokenDetails.sort((a, b) => parseInt(a.tokenId) - parseInt(b.tokenId))) {
|
|
71
|
+
console.log(`\nšÆ Token ID ${token.tokenId}:`);
|
|
72
|
+
console.log(` File Name: ${token.fileName}`);
|
|
73
|
+
console.log(` Merkle Root: ${token.merkleRootHash}`);
|
|
74
|
+
|
|
75
|
+
// Convert timestamps to dates
|
|
76
|
+
const creationDate = new Date(token.creationTimestamp * 1000);
|
|
77
|
+
const tokenizationDate = new Date(token.tokenizationTime * 1000);
|
|
78
|
+
|
|
79
|
+
// Format dates in a more readable format
|
|
80
|
+
const formatOptions: Intl.DateTimeFormatOptions = {
|
|
81
|
+
year: 'numeric',
|
|
82
|
+
month: 'short',
|
|
83
|
+
day: '2-digit',
|
|
84
|
+
hour: '2-digit',
|
|
85
|
+
minute: '2-digit',
|
|
86
|
+
second: '2-digit',
|
|
87
|
+
timeZoneName: 'short'
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
console.log(` Created: ${creationDate.toLocaleDateString('en-US', formatOptions)}`);
|
|
91
|
+
console.log(` Tokenized: ${tokenizationDate.toLocaleDateString('en-US', formatOptions)}`);
|
|
92
|
+
|
|
93
|
+
// Show time difference between creation and tokenization
|
|
94
|
+
const timeDiffSeconds = token.tokenizationTime - token.creationTimestamp;
|
|
95
|
+
if (timeDiffSeconds > 0) {
|
|
96
|
+
if (timeDiffSeconds < 60) {
|
|
97
|
+
console.log(` Tokenization Delay: ${timeDiffSeconds} seconds`);
|
|
98
|
+
} else if (timeDiffSeconds < 3600) {
|
|
99
|
+
console.log(` Tokenization Delay: ${Math.round(timeDiffSeconds / 60)} minutes`);
|
|
100
|
+
} else {
|
|
101
|
+
console.log(` Tokenization Delay: ${Math.round(timeDiffSeconds / 3600)} hours`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (token.ipfsHash && token.ipfsHash !== '') {
|
|
106
|
+
console.log(` IPFS Hash: ${token.ipfsHash}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(` Creator: ${token.creator}`);
|
|
110
|
+
|
|
111
|
+
// Calculate age since creation (using current time)
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
const ageMs = now - (token.creationTimestamp * 1000);
|
|
114
|
+
const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
|
|
115
|
+
const ageHours = Math.floor((ageMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
116
|
+
const ageMinutes = Math.floor((ageMs % (1000 * 60 * 60)) / (1000 * 60));
|
|
117
|
+
|
|
118
|
+
if (ageDays > 0) {
|
|
119
|
+
console.log(` Age: ${ageDays} days, ${ageHours} hours`);
|
|
120
|
+
} else if (ageHours > 0) {
|
|
121
|
+
console.log(` Age: ${ageHours} hours, ${ageMinutes} minutes`);
|
|
122
|
+
} else {
|
|
123
|
+
console.log(` Age: ${ageMinutes} minutes`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
console.log('\nā No NZIP tokens found for this address');
|
|
128
|
+
console.log('\nš” This could mean:');
|
|
129
|
+
console.log(' ⢠You don\'t own any NZIP tokens on this network');
|
|
130
|
+
console.log(' ⢠The tokens are on a different contract or network');
|
|
131
|
+
console.log(' ⢠There are temporary RPC connectivity issues');
|
|
132
|
+
console.log('\nš§ Try these troubleshooting steps:');
|
|
133
|
+
console.log(' ⢠Verify you\'re on the correct network (--network flag)');
|
|
134
|
+
console.log(' ⢠Check if you have tokens on a different contract (--contract flag)');
|
|
135
|
+
console.log(' ⢠Try again in a few minutes if RPC is experiencing issues');
|
|
136
|
+
console.log(` ⢠Verify the contract address: ${contractAddress}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Display ERC-20 token information
|
|
142
|
+
*/
|
|
143
|
+
static displayERC20Tokens(tokens: TokenInfo[]): void {
|
|
144
|
+
if (tokens.length > 0) {
|
|
145
|
+
console.log(`\nš° ERC-20 Token Balances:`);
|
|
146
|
+
|
|
147
|
+
for (const token of tokens) {
|
|
148
|
+
console.log(`\n š ${token.symbol} (${token.name}):`);
|
|
149
|
+
console.log(` Contract: ${token.address}`);
|
|
150
|
+
console.log(` Balance: ${token.balance} ${token.symbol}`);
|
|
151
|
+
console.log(` Decimals: ${token.decimals}`);
|
|
152
|
+
|
|
153
|
+
// Add USD value estimation for major stablecoins
|
|
154
|
+
if (['USDC', 'USDT', 'DAI', 'USDbC'].includes(token.symbol)) {
|
|
155
|
+
console.log(` Est. USD Value: ~$${token.balance}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Display NFT collection information
|
|
163
|
+
*/
|
|
164
|
+
static displayNFTCollections(nfts: TokenInfo[]): void {
|
|
165
|
+
console.log(`\nš¼ļø Other NFT Collections:`);
|
|
166
|
+
|
|
167
|
+
if (nfts.length > 0) {
|
|
168
|
+
for (const nft of nfts) {
|
|
169
|
+
console.log(`\n š¼ļø ${nft.symbol} (${nft.name}):`);
|
|
170
|
+
console.log(` Contract: ${nft.address}`);
|
|
171
|
+
console.log(` NFTs Owned: ${nft.balance}`);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
console.log(` No other NFT collections found`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Display token scan summary
|
|
180
|
+
*/
|
|
181
|
+
static displayTokenScanSummary(erc20Count: number, totalScanned: number): void {
|
|
182
|
+
console.log(`\nš Token Scan Summary:`);
|
|
183
|
+
console.log(` ERC-20 Tokens Found: ${erc20Count}`);
|
|
184
|
+
console.log(` Total Tokens Scanned: ${totalScanned}`);
|
|
185
|
+
|
|
186
|
+
if (erc20Count === 0) {
|
|
187
|
+
console.log(`\nš” No tokens found. This could mean:`);
|
|
188
|
+
console.log(` ⢠This is a new wallet with no token activity`);
|
|
189
|
+
console.log(` ⢠Tokens are on a different network`);
|
|
190
|
+
console.log(` ⢠You have tokens not in our common token list`);
|
|
191
|
+
console.log(` ⢠There are temporary RPC connectivity issues`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Display error messages for contract connection issues
|
|
197
|
+
*/
|
|
198
|
+
static displayContractError(error: any, contractAddress: string): void {
|
|
199
|
+
console.error('ā Error connecting to NZIP contract:', error);
|
|
200
|
+
console.log('\nš§ Troubleshooting suggestions:');
|
|
201
|
+
console.log(' ⢠Check if the contract address is correct');
|
|
202
|
+
console.log(' ⢠Verify you\'re on the right network');
|
|
203
|
+
console.log(' ⢠Try again later if RPC is experiencing issues');
|
|
204
|
+
console.log(' ⢠Use --contract flag to specify a different contract address');
|
|
205
|
+
}
|
|
206
|
+
}
|