neozip-blockchain 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +189 -0
- package/dist/browser/TokenVerifierBrowser.d.ts +63 -0
- package/dist/browser/TokenVerifierBrowser.d.ts.map +1 -0
- package/dist/browser/TokenVerifierBrowser.js +358 -0
- package/dist/browser/TokenVerifierBrowser.js.map +1 -0
- package/dist/browser/WalletManagerBrowser.d.ts +99 -0
- package/dist/browser/WalletManagerBrowser.d.ts.map +1 -0
- package/dist/browser/WalletManagerBrowser.js +434 -0
- package/dist/browser/WalletManagerBrowser.js.map +1 -0
- package/dist/browser/ZipkitMinterBrowser.d.ts +59 -0
- package/dist/browser/ZipkitMinterBrowser.d.ts.map +1 -0
- package/dist/browser/ZipkitMinterBrowser.js +337 -0
- package/dist/browser/ZipkitMinterBrowser.js.map +1 -0
- package/dist/browser/index.d.ts +12 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +19 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/constants/index.d.ts +5 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +21 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/metadata.d.ts +18 -0
- package/dist/constants/metadata.d.ts.map +1 -0
- package/dist/constants/metadata.js +33 -0
- package/dist/constants/metadata.js.map +1 -0
- package/dist/constants/servers.d.ts +38 -0
- package/dist/constants/servers.d.ts.map +1 -0
- package/dist/constants/servers.js +54 -0
- package/dist/constants/servers.js.map +1 -0
- package/dist/core/ContractVersionRegistry.d.ts +63 -0
- package/dist/core/ContractVersionRegistry.d.ts.map +1 -0
- package/dist/core/ContractVersionRegistry.js +128 -0
- package/dist/core/ContractVersionRegistry.js.map +1 -0
- package/dist/core/WalletManager.d.ts +123 -0
- package/dist/core/WalletManager.d.ts.map +1 -0
- package/dist/core/WalletManager.js +360 -0
- package/dist/core/WalletManager.js.map +1 -0
- package/dist/core/ZipkitMinter.d.ts +155 -0
- package/dist/core/ZipkitMinter.d.ts.map +1 -0
- package/dist/core/ZipkitMinter.js +625 -0
- package/dist/core/ZipkitMinter.js.map +1 -0
- package/dist/core/ZipkitVerifier.d.ts +252 -0
- package/dist/core/ZipkitVerifier.d.ts.map +1 -0
- package/dist/core/ZipkitVerifier.js +1370 -0
- package/dist/core/ZipkitVerifier.js.map +1 -0
- package/dist/core/adapters/AdapterFactory.d.ts +22 -0
- package/dist/core/adapters/AdapterFactory.d.ts.map +1 -0
- package/dist/core/adapters/AdapterFactory.js +62 -0
- package/dist/core/adapters/AdapterFactory.js.map +1 -0
- package/dist/core/adapters/ContractVersionAdapter.d.ts +96 -0
- package/dist/core/adapters/ContractVersionAdapter.d.ts.map +1 -0
- package/dist/core/adapters/ContractVersionAdapter.js +9 -0
- package/dist/core/adapters/ContractVersionAdapter.js.map +1 -0
- package/dist/core/adapters/V2_10Adapter.d.ts +24 -0
- package/dist/core/adapters/V2_10Adapter.d.ts.map +1 -0
- package/dist/core/adapters/V2_10Adapter.js +87 -0
- package/dist/core/adapters/V2_10Adapter.js.map +1 -0
- package/dist/core/adapters/V2_11Adapter.d.ts +24 -0
- package/dist/core/adapters/V2_11Adapter.d.ts.map +1 -0
- package/dist/core/adapters/V2_11Adapter.js +87 -0
- package/dist/core/adapters/V2_11Adapter.js.map +1 -0
- package/dist/core/adapters/V2_50Adapter.d.ts +105 -0
- package/dist/core/adapters/V2_50Adapter.d.ts.map +1 -0
- package/dist/core/adapters/V2_50Adapter.js +263 -0
- package/dist/core/adapters/V2_50Adapter.js.map +1 -0
- package/dist/core/contracts.d.ts +169 -0
- package/dist/core/contracts.d.ts.map +1 -0
- package/dist/core/contracts.js +731 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/index.d.ts +24 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +82 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/node/WalletManagerNode.d.ts +47 -0
- package/dist/node/WalletManagerNode.d.ts.map +1 -0
- package/dist/node/WalletManagerNode.js +203 -0
- package/dist/node/WalletManagerNode.js.map +1 -0
- package/dist/node/index.d.ts +12 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +21 -0
- package/dist/node/index.js.map +1 -0
- package/dist/ots/ZipkitOTS.d.ts +152 -0
- package/dist/ots/ZipkitOTS.d.ts.map +1 -0
- package/dist/ots/ZipkitOTS.js +544 -0
- package/dist/ots/ZipkitOTS.js.map +1 -0
- package/dist/ots/index.d.ts +9 -0
- package/dist/ots/index.d.ts.map +1 -0
- package/dist/ots/index.js +25 -0
- package/dist/ots/index.js.map +1 -0
- package/dist/types/index.d.ts +84 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +37 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/Logger.d.ts +73 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +156 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/crc32.d.ts +6 -0
- package/dist/utils/crc32.d.ts.map +1 -0
- package/dist/utils/crc32.js +23 -0
- package/dist/utils/crc32.js.map +1 -0
- package/dist/zipstamp-server/CalendarManager.d.ts +238 -0
- package/dist/zipstamp-server/CalendarManager.d.ts.map +1 -0
- package/dist/zipstamp-server/CalendarManager.js +364 -0
- package/dist/zipstamp-server/CalendarManager.js.map +1 -0
- package/dist/zipstamp-server/ZipstampServerAPI.d.ts +427 -0
- package/dist/zipstamp-server/ZipstampServerAPI.d.ts.map +1 -0
- package/dist/zipstamp-server/ZipstampServerAPI.js +696 -0
- package/dist/zipstamp-server/ZipstampServerAPI.js.map +1 -0
- package/dist/zipstamp-server/ZipstampServerClient.d.ts +590 -0
- package/dist/zipstamp-server/ZipstampServerClient.d.ts.map +1 -0
- package/dist/zipstamp-server/ZipstampServerClient.js +433 -0
- package/dist/zipstamp-server/ZipstampServerClient.js.map +1 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.d.ts +430 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.d.ts.map +1 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.js +503 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.js.map +1 -0
- package/dist/zipstamp-server/index.d.ts +24 -0
- package/dist/zipstamp-server/index.d.ts.map +1 -0
- package/dist/zipstamp-server/index.js +67 -0
- package/dist/zipstamp-server/index.js.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Contract addresses and configurations for different networks
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.isNetworkSupported = exports.getSupportedNetworks = exports.getContractConfig = exports.DEFAULT_CONTRACT_VERSION = exports.CURRENT_DEPLOYMENT = exports.DEFAULT_NETWORK = exports.NZIP_CONTRACT_ABI_WEB3 = exports.UNIFIED_NFT_VERIFY_ABI = exports.TIMESTAMP_PROOF_NFT_ABI = exports.NZIP_TIMESTAMP_REG_ABI = exports.NZIP_CONTRACT_ABI_V250 = exports.NZIP_CONTRACT_ABI = exports.CONTRACT_CONFIGS = exports.TOKENIZED_METADATA_LEGACY = exports.TOKENIZED_METADATA = void 0;
|
|
5
|
+
exports.normalizeNetworkName = normalizeNetworkName;
|
|
6
|
+
exports.getChainIdByName = getChainIdByName;
|
|
7
|
+
exports.getNetworkByName = getNetworkByName;
|
|
8
|
+
exports.getSupportedNetworkNames = getSupportedNetworkNames;
|
|
9
|
+
exports.getContractAdapter = getContractAdapter;
|
|
10
|
+
exports.getContractAdapterByVersion = getContractAdapterByVersion;
|
|
11
|
+
exports.fuzzyMatchNetworkName = fuzzyMatchNetworkName;
|
|
12
|
+
exports.validateTokenId = validateTokenId;
|
|
13
|
+
exports.validateEthereumAddress = validateEthereumAddress;
|
|
14
|
+
exports.sanitizeNetworkName = sanitizeNetworkName;
|
|
15
|
+
exports.validateMerkleRootFormat = validateMerkleRootFormat;
|
|
16
|
+
exports.validateContractAddress = validateContractAddress;
|
|
17
|
+
const metadata_1 = require("../constants/metadata");
|
|
18
|
+
// Re-export token metadata filenames (used by ZipkitVerifier, TokenVerifierBrowser)
|
|
19
|
+
// Standard: TOKEN.NZIP. Legacy NZIP.TOKEN accepted for reading only.
|
|
20
|
+
exports.TOKENIZED_METADATA = metadata_1.TOKEN_NZIP;
|
|
21
|
+
exports.TOKENIZED_METADATA_LEGACY = metadata_1.TOKEN_NZIP_LEGACY;
|
|
22
|
+
// Import ethers for address validation
|
|
23
|
+
const ethers_1 = require("ethers");
|
|
24
|
+
// NZIP NFT Contract configurations by network
|
|
25
|
+
exports.CONTRACT_CONFIGS = {
|
|
26
|
+
// Base Sepolia (Primary testnet)
|
|
27
|
+
84532: {
|
|
28
|
+
address: '0xc9527869c2526C2A88d4f03202F93149B481e2d3', // Production contract v2.50
|
|
29
|
+
network: 'Base Sepolia',
|
|
30
|
+
chainId: 84532,
|
|
31
|
+
explorerUrl: 'https://sepolia.basescan.org',
|
|
32
|
+
rpcUrls: [
|
|
33
|
+
'https://sepolia.base.org',
|
|
34
|
+
'https://base-sepolia-rpc.publicnode.com',
|
|
35
|
+
'https://base-sepolia.gateway.tenderly.co'
|
|
36
|
+
],
|
|
37
|
+
version: '2.50',
|
|
38
|
+
registryAddress: '0x3CFc4E3886839dC859f611887660783a3EE241b4', // NZIPTimestampReg v0.90
|
|
39
|
+
registryVersion: '0.90',
|
|
40
|
+
nameAliases: ['base-sepolia', 'base sepolia', 'basesepolia', 'base-sepolia-testnet']
|
|
41
|
+
},
|
|
42
|
+
// Base Mainnet (Production)
|
|
43
|
+
8453: {
|
|
44
|
+
address: '0xd871Fba59F85108aF29299786DD8243B38dD9686', // Production contract v2.10
|
|
45
|
+
network: 'Base Mainnet',
|
|
46
|
+
chainId: 8453,
|
|
47
|
+
explorerUrl: 'https://basescan.org',
|
|
48
|
+
rpcUrls: [
|
|
49
|
+
'https://mainnet.base.org',
|
|
50
|
+
'https://base.drpc.org',
|
|
51
|
+
'https://base.gateway.tenderly.co'
|
|
52
|
+
],
|
|
53
|
+
version: '2.10',
|
|
54
|
+
nameAliases: ['base-mainnet', 'base mainnet', 'basemainnet', 'base']
|
|
55
|
+
},
|
|
56
|
+
// Ethereum Sepolia (Testnet)
|
|
57
|
+
11155111: {
|
|
58
|
+
address: '0x007e8888D976b0b9B6073694Da32B7b6e393f890', // Production contract v2.11
|
|
59
|
+
network: 'Sepolia Testnet',
|
|
60
|
+
chainId: 11155111,
|
|
61
|
+
explorerUrl: 'https://sepolia.etherscan.io',
|
|
62
|
+
rpcUrls: [
|
|
63
|
+
'https://rpc.sepolia.ethpandaops.io',
|
|
64
|
+
'https://eth-sepolia.public.blastapi.io',
|
|
65
|
+
'https://ethereum-sepolia-rpc.publicnode.com',
|
|
66
|
+
'https://sepolia.drpc.org',
|
|
67
|
+
'https://1rpc.io/sepolia',
|
|
68
|
+
],
|
|
69
|
+
version: '2.11',
|
|
70
|
+
nameAliases: ['sepolia-testnet', 'sepolia testnet', 'sepoliatestnet', 'sepolia', 'ethereum-sepolia', 'ethereum sepolia']
|
|
71
|
+
},
|
|
72
|
+
// Arbitrum Sepolia (Testnet)
|
|
73
|
+
421614: {
|
|
74
|
+
address: '0x3b99a72cCAc108037741cacb0D60d5571CF6412C', // Production contract v2.11
|
|
75
|
+
network: 'Arbitrum Sepolia',
|
|
76
|
+
chainId: 421614,
|
|
77
|
+
explorerUrl: 'https://sepolia.arbiscan.io',
|
|
78
|
+
rpcUrls: [
|
|
79
|
+
'https://sepolia-rollup.arbitrum.io/rpc',
|
|
80
|
+
'https://arbitrum-sepolia-rpc.publicnode.com'
|
|
81
|
+
],
|
|
82
|
+
version: '2.11',
|
|
83
|
+
nameAliases: ['arbitrum-sepolia', 'arbitrum sepolia', 'arbitrumsepolia', 'arbitrum-sepolia-testnet']
|
|
84
|
+
},
|
|
85
|
+
// Arbitrum One (Production)
|
|
86
|
+
42161: {
|
|
87
|
+
address: '0x2716c4609fD97DaEdF429BC4B4Ec2faa81e2cC60', // Production contract v2.10
|
|
88
|
+
network: 'Arbitrum One',
|
|
89
|
+
chainId: 42161,
|
|
90
|
+
explorerUrl: 'https://arbiscan.io',
|
|
91
|
+
rpcUrls: [
|
|
92
|
+
'https://arb1.arbitrum.io/rpc',
|
|
93
|
+
'https://arbitrum.llamarpc.com',
|
|
94
|
+
'https://arbitrum.publicnode.com'
|
|
95
|
+
],
|
|
96
|
+
version: '2.10',
|
|
97
|
+
nameAliases: ['arbitrum-one', 'arbitrum one', 'arbitrumone', 'arbitrum', 'arbitrum-mainnet', 'arbitrum mainnet']
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
// Contract ABI (ethers.js human-readable format)
|
|
101
|
+
// Minimal ABI that works with v2.10, v2.11, and v2.50 contracts
|
|
102
|
+
exports.NZIP_CONTRACT_ABI = [
|
|
103
|
+
// Minting function (v2.11/v2.50 signature with encryptedHash for new mints)
|
|
104
|
+
"function publicMintZipFile(string memory merkleRootHash, string memory encryptedHash, uint256 creationTimestamp, string memory ipfsHash, string memory metadataURI) public payable returns (uint256)",
|
|
105
|
+
"function totalSupply() external view returns (uint256)",
|
|
106
|
+
"function getTokensByMerkleRoot(string memory merkleRoot) external view returns (uint256[])",
|
|
107
|
+
"function getTokensByOwner(address owner) external view returns (uint256[])",
|
|
108
|
+
"function getZipFileInfo(uint256 tokenId) external view returns (tuple(string merkleRootHash, string encryptedHash, string ipfsHash, address creator, uint256 creationTimestamp, uint256 tokenizationTime, uint256 blockNumber))",
|
|
109
|
+
"function ownerOf(uint256 tokenId) external view returns (address)",
|
|
110
|
+
"function balanceOf(address owner) external view returns (uint256)",
|
|
111
|
+
"function isZipFileTokenized(string memory merkleRootHash, uint256 creationTimestamp) external view returns (bool exists, uint256 tokenId)",
|
|
112
|
+
"function verifyZipFile(uint256 tokenId, string memory providedMerkleRoot) external view returns (bool isValid)",
|
|
113
|
+
"function getVersion() external pure returns (string memory)",
|
|
114
|
+
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
|
|
115
|
+
"event ZipFileTokenized(uint256 indexed tokenId, address indexed creator, string merkleRootHash, string encryptedHash, uint256 creationTimestamp, string ipfsHash, uint256 tokenizationTime, uint256 blockNumber)"
|
|
116
|
+
];
|
|
117
|
+
// NZIP NFT v2.50 Contract ABI (full ABI with timestamp proof support)
|
|
118
|
+
exports.NZIP_CONTRACT_ABI_V250 = [
|
|
119
|
+
// v2.11 compatible functions
|
|
120
|
+
"function publicMintZipFile(string memory merkleRootHash, string memory encryptedHash, uint256 creationTimestamp, string memory ipfsHash, string memory metadataURI) public payable returns (uint256)",
|
|
121
|
+
"function publicMintZipFile(string memory merkleRootHash, uint256 creationTimestamp, string memory ipfsHash, string memory metadataURI) public payable returns (uint256)",
|
|
122
|
+
"function getZipFileInfo(uint256 tokenId) external view returns (tuple(string merkleRootHash, string encryptedHash, string ipfsHash, address creator, uint256 creationTimestamp, uint256 tokenizationTime, uint256 blockNumber))",
|
|
123
|
+
"function getEncryptedHash(uint256 tokenId) external view returns (string memory)",
|
|
124
|
+
"function isZipFileTokenized(string memory merkleRootHash, uint256 creationTimestamp) external view returns (bool exists, uint256 tokenId)",
|
|
125
|
+
"function verifyZipFile(uint256 tokenId, string memory providedMerkleRoot) external view returns (bool isValid)",
|
|
126
|
+
"function verifyEncryptedZipFile(uint256 tokenId, string memory providedEncryptedHash) external view returns (bool isValid)",
|
|
127
|
+
"function getBlockchainMetadata(uint256 tokenId) public view returns (uint256 blockNumber, uint256 tokenizationTime)",
|
|
128
|
+
"function generateCompositeKey(string memory merkleRootHash, uint256 creationTimestamp) public pure returns (bytes32)",
|
|
129
|
+
"function totalSupply() public view returns (uint256)",
|
|
130
|
+
"function getVersion() external pure returns (string memory)",
|
|
131
|
+
"function ownerOf(uint256 tokenId) external view returns (address)",
|
|
132
|
+
"function balanceOf(address owner) external view returns (uint256)",
|
|
133
|
+
// v2.50 timestamp proof functions
|
|
134
|
+
"function mintWithTimestampProof(string memory merkleRootHash, bytes32[] calldata proof, bytes32 batchMerkleRoot) external payable returns (uint256)",
|
|
135
|
+
"function hasTimestampProof(uint256 tokenId) external view returns (bool)",
|
|
136
|
+
"function getTimestampProof(uint256 tokenId) external view returns (tuple(bool hasTimestampProof, bytes32 batchMerkleRoot, uint256 batchTimestamp, uint256 batchBlockNumber) data, bytes32[] memory merkleProof)",
|
|
137
|
+
"function getTokenData(uint256 tokenId) external view returns (bytes32 merkleRoot, uint256 mintedAt, uint256 mintBlockNumber, address originalOwner, bool hasProof, bytes32 batchMerkleRoot, uint256 batchTimestamp, uint256 batchBlockNumber)",
|
|
138
|
+
"function verifyToken(uint256 tokenId) external view returns (bool isValid)",
|
|
139
|
+
"function getRegistry() external view returns (address)",
|
|
140
|
+
// v2.50 admin functions
|
|
141
|
+
"function mintFee() public view returns (uint256)",
|
|
142
|
+
"function mintFeeRequired() public view returns (bool)",
|
|
143
|
+
"function setMintFee(uint256 newFee) external",
|
|
144
|
+
"function setMintFeeRequired(bool required) external",
|
|
145
|
+
"function withdrawFees(address payable to) external",
|
|
146
|
+
"function authorizeMinter(address minter) external",
|
|
147
|
+
"function deauthorizeMinter(address minter) external",
|
|
148
|
+
"function isAuthorizedMinter(address minter) external view returns (bool)",
|
|
149
|
+
// Events
|
|
150
|
+
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
|
|
151
|
+
"event ZipFileTokenized(uint256 indexed tokenId, address indexed creator, string merkleRootHash, string encryptedHash, uint256 creationTimestamp, string ipfsHash, uint256 tokenizationTime, uint256 blockNumber)",
|
|
152
|
+
"event TimestampProofMinted(uint256 indexed tokenId, address indexed creator, bytes32 indexed batchMerkleRoot, string merkleRootHash, uint256 batchTimestamp, uint256 tokenizationTime, uint256 blockNumber)",
|
|
153
|
+
"event FeeUpdated(uint256 oldFee, uint256 newFee)",
|
|
154
|
+
"event MinterAuthorized(address indexed minter, address indexed authorizedBy)",
|
|
155
|
+
"event MinterDeauthorized(address indexed minter, address indexed deauthorizedBy)"
|
|
156
|
+
];
|
|
157
|
+
// NZIPTimestampReg Contract ABI (for batch timestamp verification)
|
|
158
|
+
exports.NZIP_TIMESTAMP_REG_ABI = [
|
|
159
|
+
'function verifyProof(bytes32 digest, bytes32[] calldata proof, bytes32 merkleRoot) external view returns (bool isValid, uint256 batchNumber, uint256 timestamp, uint256 blockNumber)',
|
|
160
|
+
'function getBatch(bytes32 merkleRoot) external view returns (bool exists, uint256 batchNumber, uint256 timestamp, uint256 blockNumber, uint256 hashCount)',
|
|
161
|
+
'function getBatchByNumber(uint256 batchNumber) external view returns (bool exists, bytes32 merkleRoot, uint256 timestamp, uint256 blockNumber, uint256 hashCount)',
|
|
162
|
+
'function batchExists(bytes32 merkleRoot) external view returns (bool)',
|
|
163
|
+
'function totalBatches() public view returns (uint256)',
|
|
164
|
+
'function totalDigests() public view returns (uint256)',
|
|
165
|
+
'function getStats() external view returns (uint256 _totalBatches, uint256 _totalDigests)',
|
|
166
|
+
'function getVersion() external pure returns (string memory)',
|
|
167
|
+
'function submitBatch(bytes32 merkleRoot, uint64 hashCount) external',
|
|
168
|
+
'function addAuthorizedSubmitter(address submitter) external',
|
|
169
|
+
'function removeAuthorizedSubmitter(address submitter) external',
|
|
170
|
+
'function isAuthorizedSubmitter(address submitter) external view returns (bool)',
|
|
171
|
+
'event BatchSubmitted(bytes32 indexed merkleRoot, uint256 indexed batchNumber, address indexed submitter, uint256 timestamp, uint256 blockNumber, uint256 hashCount)',
|
|
172
|
+
'event SubmitterAdded(address indexed submitter, address indexed addedBy)',
|
|
173
|
+
'event SubmitterRemoved(address indexed submitter, address indexed removedBy)',
|
|
174
|
+
];
|
|
175
|
+
// TimestampProofNFT ABI (minimal - for verification of v0.90-style timestamp proof NFT)
|
|
176
|
+
// Used by verify-zip when contract exposes getProof(uint256) with legacy tuple shape.
|
|
177
|
+
exports.TIMESTAMP_PROOF_NFT_ABI = [
|
|
178
|
+
'function ownerOf(uint256 tokenId) view returns (address)',
|
|
179
|
+
'function getProof(uint256 tokenId) view returns (tuple(bytes32 digest, bytes32 merkleRoot, uint256 batchTimestamp, uint256 batchBlockNumber, uint256 mintedAt, address originalOwner), bytes32[])',
|
|
180
|
+
'function getTokenByDigest(bytes32 digest) view returns (uint256)',
|
|
181
|
+
'function isDigestMinted(bytes32 digest) view returns (bool)',
|
|
182
|
+
'function verifyToken(uint256 tokenId) view returns (bool)',
|
|
183
|
+
'function getVersion() pure returns (string)',
|
|
184
|
+
'function registry() view returns (address)',
|
|
185
|
+
];
|
|
186
|
+
// UnifiedNFT verification ABI (minimal - for verify-zip and token-create; v2.50 getTokenData/getTimestampProof)
|
|
187
|
+
// Subset of NZIP_CONTRACT_ABI_V250 used when only reading token/proof data.
|
|
188
|
+
exports.UNIFIED_NFT_VERIFY_ABI = [
|
|
189
|
+
'function ownerOf(uint256 tokenId) view returns (address)',
|
|
190
|
+
'function getTokenData(uint256 tokenId) view returns (tuple(bytes32 merkleRoot, uint256 mintedAt, uint256 mintBlockNumber, address originalOwner, bool hasTimestampProof, bytes32 batchMerkleRoot, uint256 batchTimestamp, uint256 batchBlockNumber))',
|
|
191
|
+
'function getTimestampProof(uint256 tokenId) view returns (bytes32 digest, bytes32[] proof, bytes32 batchMerkleRoot, uint256 batchTimestamp, uint256 batchBlockNumber)',
|
|
192
|
+
'function getTokenByMerkleRoot(bytes32 merkleRoot) view returns (uint256)',
|
|
193
|
+
'function isMinted(bytes32 merkleRoot) view returns (bool)',
|
|
194
|
+
'function hasTimestampProof(uint256 tokenId) view returns (bool)',
|
|
195
|
+
'function verifyToken(uint256 tokenId) view returns (bool)',
|
|
196
|
+
'function getVersion() pure returns (string)',
|
|
197
|
+
'function getRegistry() view returns (address)',
|
|
198
|
+
];
|
|
199
|
+
// Contract ABI (Web3.js JSON format)
|
|
200
|
+
exports.NZIP_CONTRACT_ABI_WEB3 = [
|
|
201
|
+
{
|
|
202
|
+
inputs: [
|
|
203
|
+
{ name: "merkleRootHash", type: "string" },
|
|
204
|
+
{ name: "encryptedHash", type: "string" },
|
|
205
|
+
{ name: "creationTimestamp", type: "uint256" },
|
|
206
|
+
{ name: "ipfsHash", type: "string" },
|
|
207
|
+
{ name: "metadataURI", type: "string" }
|
|
208
|
+
],
|
|
209
|
+
name: "publicMintZipFile",
|
|
210
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
211
|
+
stateMutability: "nonpayable",
|
|
212
|
+
type: "function"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
inputs: [],
|
|
216
|
+
name: "totalSupply",
|
|
217
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
218
|
+
stateMutability: "view",
|
|
219
|
+
type: "function"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
inputs: [{ name: "merkleRoot", type: "string" }],
|
|
223
|
+
name: "getTokensByMerkleRoot",
|
|
224
|
+
outputs: [{ name: "", type: "uint256[]" }],
|
|
225
|
+
stateMutability: "view",
|
|
226
|
+
type: "function"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
230
|
+
name: "getZipFileInfo",
|
|
231
|
+
outputs: [
|
|
232
|
+
{
|
|
233
|
+
components: [
|
|
234
|
+
{ name: "merkleRootHash", type: "string" },
|
|
235
|
+
{ name: "encryptedHash", type: "string" },
|
|
236
|
+
{ name: "ipfsHash", type: "string" },
|
|
237
|
+
{ name: "creator", type: "address" },
|
|
238
|
+
{ name: "creationTimestamp", type: "uint256" },
|
|
239
|
+
{ name: "tokenizationTime", type: "uint256" },
|
|
240
|
+
{ name: "blockNumber", type: "uint256" }
|
|
241
|
+
],
|
|
242
|
+
name: "",
|
|
243
|
+
type: "tuple"
|
|
244
|
+
}
|
|
245
|
+
],
|
|
246
|
+
stateMutability: "view",
|
|
247
|
+
type: "function"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
251
|
+
name: "ownerOf",
|
|
252
|
+
outputs: [{ name: "", type: "address" }],
|
|
253
|
+
stateMutability: "view",
|
|
254
|
+
type: "function"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
258
|
+
name: "balanceOf",
|
|
259
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
260
|
+
stateMutability: "view",
|
|
261
|
+
type: "function"
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
inputs: [
|
|
265
|
+
{ name: "merkleRootHash", type: "string" },
|
|
266
|
+
{ name: "creationTimestamp", type: "uint256" }
|
|
267
|
+
],
|
|
268
|
+
name: "isZipFileTokenized",
|
|
269
|
+
outputs: [
|
|
270
|
+
{ name: "exists", type: "bool" },
|
|
271
|
+
{ name: "tokenId", type: "uint256" }
|
|
272
|
+
],
|
|
273
|
+
stateMutability: "view",
|
|
274
|
+
type: "function"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
inputs: [
|
|
278
|
+
{ name: "tokenId", type: "uint256" },
|
|
279
|
+
{ name: "providedMerkleRoot", type: "string" }
|
|
280
|
+
],
|
|
281
|
+
name: "verifyZipFile",
|
|
282
|
+
outputs: [{ name: "isValid", type: "bool" }],
|
|
283
|
+
stateMutability: "view",
|
|
284
|
+
type: "function"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
inputs: [
|
|
288
|
+
{ name: "tokenId", type: "uint256" },
|
|
289
|
+
{ name: "providedEncryptedHash", type: "string" }
|
|
290
|
+
],
|
|
291
|
+
name: "verifyEncryptedZipFile",
|
|
292
|
+
outputs: [{ name: "isValid", type: "bool" }],
|
|
293
|
+
stateMutability: "view",
|
|
294
|
+
type: "function"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
inputs: [],
|
|
298
|
+
name: "getVersion",
|
|
299
|
+
outputs: [{ name: "", type: "string" }],
|
|
300
|
+
stateMutability: "pure",
|
|
301
|
+
type: "function"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
anonymous: false,
|
|
305
|
+
inputs: [
|
|
306
|
+
{ indexed: true, name: "from", type: "address" },
|
|
307
|
+
{ indexed: true, name: "to", type: "address" },
|
|
308
|
+
{ indexed: true, name: "tokenId", type: "uint256" }
|
|
309
|
+
],
|
|
310
|
+
name: "Transfer",
|
|
311
|
+
type: "event"
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
anonymous: false,
|
|
315
|
+
inputs: [
|
|
316
|
+
{ indexed: true, name: "tokenId", type: "uint256" },
|
|
317
|
+
{ indexed: true, name: "creator", type: "address" },
|
|
318
|
+
{ indexed: false, name: "merkleRootHash", type: "string" },
|
|
319
|
+
{ indexed: false, name: "encryptedHash", type: "string" },
|
|
320
|
+
{ indexed: false, name: "creationTimestamp", type: "uint256" },
|
|
321
|
+
{ indexed: false, name: "ipfsHash", type: "string" },
|
|
322
|
+
{ indexed: false, name: "tokenizationTime", type: "uint256" },
|
|
323
|
+
{ indexed: false, name: "blockNumber", type: "uint256" }
|
|
324
|
+
],
|
|
325
|
+
name: "ZipFileTokenized",
|
|
326
|
+
type: "event"
|
|
327
|
+
}
|
|
328
|
+
];
|
|
329
|
+
// Default configuration (Base Sepolia for development; v2.50 contract)
|
|
330
|
+
exports.DEFAULT_NETWORK = 84532;
|
|
331
|
+
exports.CURRENT_DEPLOYMENT = exports.CONTRACT_CONFIGS[exports.DEFAULT_NETWORK];
|
|
332
|
+
/** Default contract version used when version is unspecified (e.g. legacy metadata). Prefer v2.50. */
|
|
333
|
+
exports.DEFAULT_CONTRACT_VERSION = '2.50';
|
|
334
|
+
// Helper functions
|
|
335
|
+
const getContractConfig = (chainId) => {
|
|
336
|
+
return exports.CONTRACT_CONFIGS[chainId] || exports.CURRENT_DEPLOYMENT;
|
|
337
|
+
};
|
|
338
|
+
exports.getContractConfig = getContractConfig;
|
|
339
|
+
const getSupportedNetworks = () => {
|
|
340
|
+
return Object.keys(exports.CONTRACT_CONFIGS).map(Number);
|
|
341
|
+
};
|
|
342
|
+
exports.getSupportedNetworks = getSupportedNetworks;
|
|
343
|
+
const isNetworkSupported = (chainId) => {
|
|
344
|
+
return chainId in exports.CONTRACT_CONFIGS;
|
|
345
|
+
};
|
|
346
|
+
exports.isNetworkSupported = isNetworkSupported;
|
|
347
|
+
/**
|
|
348
|
+
* Normalize network name for matching (lowercase, trim, replace spaces with hyphens)
|
|
349
|
+
*/
|
|
350
|
+
function normalizeNetworkName(networkName) {
|
|
351
|
+
return networkName.toLowerCase().trim().replace(/\s+/g, '-').replace(/_/g, '-');
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get chain ID by network name (supports aliases)
|
|
355
|
+
* Returns chain ID if found, null otherwise
|
|
356
|
+
*/
|
|
357
|
+
function getChainIdByName(networkName) {
|
|
358
|
+
const normalized = normalizeNetworkName(networkName);
|
|
359
|
+
// Search through all configs
|
|
360
|
+
for (const [chainIdStr, config] of Object.entries(exports.CONTRACT_CONFIGS)) {
|
|
361
|
+
const chainId = Number(chainIdStr);
|
|
362
|
+
// Check primary network name
|
|
363
|
+
if (normalizeNetworkName(config.network) === normalized) {
|
|
364
|
+
return chainId;
|
|
365
|
+
}
|
|
366
|
+
// Check name aliases
|
|
367
|
+
if (config.nameAliases) {
|
|
368
|
+
for (const alias of config.nameAliases) {
|
|
369
|
+
if (normalizeNetworkName(alias) === normalized) {
|
|
370
|
+
return chainId;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get network configuration by network name (supports aliases)
|
|
379
|
+
* Returns ContractConfig if found, null otherwise
|
|
380
|
+
*/
|
|
381
|
+
function getNetworkByName(networkName) {
|
|
382
|
+
const chainId = getChainIdByName(networkName);
|
|
383
|
+
if (chainId === null) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return (0, exports.getContractConfig)(chainId);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Get all supported network name aliases
|
|
390
|
+
* Returns array of all supported network names (primary + aliases)
|
|
391
|
+
*/
|
|
392
|
+
function getSupportedNetworkNames() {
|
|
393
|
+
const names = [];
|
|
394
|
+
for (const config of Object.values(exports.CONTRACT_CONFIGS)) {
|
|
395
|
+
// Add primary network name
|
|
396
|
+
names.push(config.network);
|
|
397
|
+
// Add aliases
|
|
398
|
+
if (config.nameAliases) {
|
|
399
|
+
names.push(...config.nameAliases);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return names;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get contract adapter for a given chain ID
|
|
406
|
+
* Uses the version from CONTRACT_CONFIGS to select the appropriate adapter
|
|
407
|
+
* @param chainId Chain ID
|
|
408
|
+
* @returns ContractVersionAdapter instance
|
|
409
|
+
* @throws Error if chainId not found or version not supported
|
|
410
|
+
*/
|
|
411
|
+
function getContractAdapter(chainId) {
|
|
412
|
+
const config = (0, exports.getContractConfig)(chainId);
|
|
413
|
+
if (!config) {
|
|
414
|
+
throw new Error(`No contract config found for chainId: ${chainId}`);
|
|
415
|
+
}
|
|
416
|
+
if (!config.version) {
|
|
417
|
+
throw new Error(`Contract version not specified for chainId: ${chainId}`);
|
|
418
|
+
}
|
|
419
|
+
// Import here to avoid circular dependency
|
|
420
|
+
const { getAdapter } = require('./adapters/AdapterFactory');
|
|
421
|
+
return getAdapter(config.version);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get contract adapter by version string
|
|
425
|
+
* @param version Contract version (e.g., "2.11", "2.10")
|
|
426
|
+
* @returns ContractVersionAdapter instance
|
|
427
|
+
* @throws Error if version not supported
|
|
428
|
+
*/
|
|
429
|
+
function getContractAdapterByVersion(version) {
|
|
430
|
+
// Import here to avoid circular dependency
|
|
431
|
+
const { getAdapter } = require('./adapters/AdapterFactory');
|
|
432
|
+
return getAdapter(version);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Fuzzy match network name (for backward compatibility)
|
|
436
|
+
* Handles partial matches like "sepolia" matching "Base Sepolia" or "Arbitrum Sepolia"
|
|
437
|
+
* Returns best match or null
|
|
438
|
+
*/
|
|
439
|
+
function fuzzyMatchNetworkName(networkName) {
|
|
440
|
+
const normalized = normalizeNetworkName(networkName);
|
|
441
|
+
// First try exact match
|
|
442
|
+
const exactMatch = getNetworkByName(networkName);
|
|
443
|
+
if (exactMatch) {
|
|
444
|
+
return exactMatch;
|
|
445
|
+
}
|
|
446
|
+
// Try partial matching
|
|
447
|
+
const matches = [];
|
|
448
|
+
for (const config of Object.values(exports.CONTRACT_CONFIGS)) {
|
|
449
|
+
const configNormalized = normalizeNetworkName(config.network);
|
|
450
|
+
let score = 0;
|
|
451
|
+
// Check if normalized input is contained in config name or vice versa
|
|
452
|
+
if (configNormalized.includes(normalized) || normalized.includes(configNormalized)) {
|
|
453
|
+
score = Math.max(configNormalized.length, normalized.length) / Math.min(configNormalized.length, normalized.length);
|
|
454
|
+
matches.push({ config, score });
|
|
455
|
+
}
|
|
456
|
+
// Check aliases
|
|
457
|
+
if (config.nameAliases) {
|
|
458
|
+
for (const alias of config.nameAliases) {
|
|
459
|
+
const aliasNormalized = normalizeNetworkName(alias);
|
|
460
|
+
if (aliasNormalized.includes(normalized) || normalized.includes(aliasNormalized)) {
|
|
461
|
+
score = Math.max(aliasNormalized.length, normalized.length) / Math.min(aliasNormalized.length, normalized.length);
|
|
462
|
+
matches.push({ config, score });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// Return best match (highest score)
|
|
468
|
+
if (matches.length > 0) {
|
|
469
|
+
matches.sort((a, b) => b.score - a.score);
|
|
470
|
+
return matches[0].config;
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Validate that a token ID is a valid uint256 format
|
|
476
|
+
* Token IDs must be numeric strings that can be converted to BigInt and be within uint256 bounds
|
|
477
|
+
*
|
|
478
|
+
* @param tokenId Token ID string to validate
|
|
479
|
+
* @returns Validation result with success status and error message if failed
|
|
480
|
+
*/
|
|
481
|
+
function validateTokenId(tokenId) {
|
|
482
|
+
if (!tokenId || typeof tokenId !== 'string') {
|
|
483
|
+
return {
|
|
484
|
+
success: false,
|
|
485
|
+
error: 'Token ID must be a non-empty string'
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
// Trim whitespace
|
|
489
|
+
const trimmed = tokenId.trim();
|
|
490
|
+
if (trimmed.length === 0) {
|
|
491
|
+
return {
|
|
492
|
+
success: false,
|
|
493
|
+
error: 'Token ID cannot be empty or whitespace only'
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
// Check if it's a valid numeric string (allows decimal, hex with 0x prefix, or plain numeric)
|
|
497
|
+
// Remove 0x prefix if present for validation
|
|
498
|
+
const normalized = trimmed.startsWith('0x') ? trimmed.slice(2) : trimmed;
|
|
499
|
+
// Must contain only hexadecimal digits (0-9, a-f, A-F)
|
|
500
|
+
if (!/^[0-9a-fA-F]+$/.test(normalized)) {
|
|
501
|
+
return {
|
|
502
|
+
success: false,
|
|
503
|
+
error: `Token ID must be a valid numeric string (hexadecimal). Got: ${tokenId}`
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
// Check if it can be converted to BigInt (uint256)
|
|
507
|
+
try {
|
|
508
|
+
const bigIntValue = BigInt(trimmed.startsWith('0x') ? trimmed : `0x${normalized}`);
|
|
509
|
+
// uint256 max value: 2^256 - 1
|
|
510
|
+
const maxUint256 = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
|
511
|
+
if (bigIntValue < 0n || bigIntValue > maxUint256) {
|
|
512
|
+
return {
|
|
513
|
+
success: false,
|
|
514
|
+
error: `Token ID out of uint256 bounds (0 to 2^256-1). Got: ${tokenId}`
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
520
|
+
return {
|
|
521
|
+
success: false,
|
|
522
|
+
error: `Token ID validation failed: ${errorMessage}. Token ID: ${tokenId}`
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
return { success: true };
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Validate that an Ethereum address is in the correct format
|
|
529
|
+
* Uses ethers.js validation to ensure proper format and checksum
|
|
530
|
+
*
|
|
531
|
+
* @param address Ethereum address to validate
|
|
532
|
+
* @returns Validation result with success status and error message if failed
|
|
533
|
+
*/
|
|
534
|
+
function validateEthereumAddress(address) {
|
|
535
|
+
if (!address || typeof address !== 'string') {
|
|
536
|
+
return {
|
|
537
|
+
success: false,
|
|
538
|
+
error: 'Address must be a non-empty string'
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
// Trim whitespace
|
|
542
|
+
const trimmed = address.trim();
|
|
543
|
+
if (trimmed.length === 0) {
|
|
544
|
+
return {
|
|
545
|
+
success: false,
|
|
546
|
+
error: 'Address cannot be empty or whitespace only'
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
// Validate address format using ethers.js
|
|
550
|
+
if (!ethers_1.ethers.isAddress(trimmed)) {
|
|
551
|
+
return {
|
|
552
|
+
success: false,
|
|
553
|
+
error: `Invalid Ethereum address format: ${trimmed}`
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
// Normalize to checksum format
|
|
557
|
+
try {
|
|
558
|
+
const normalized = ethers_1.ethers.getAddress(trimmed);
|
|
559
|
+
return {
|
|
560
|
+
success: true,
|
|
561
|
+
normalizedAddress: normalized
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown address normalization error';
|
|
566
|
+
return {
|
|
567
|
+
success: false,
|
|
568
|
+
error: `Address normalization failed: ${errorMessage}`
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Sanitize and validate network name
|
|
574
|
+
* Removes leading/trailing whitespace, validates length, and removes potentially dangerous characters
|
|
575
|
+
*
|
|
576
|
+
* @param networkName Network name to sanitize
|
|
577
|
+
* @returns Validation result with success status, sanitized name, and error message if failed
|
|
578
|
+
*/
|
|
579
|
+
function sanitizeNetworkName(networkName) {
|
|
580
|
+
if (!networkName || typeof networkName !== 'string') {
|
|
581
|
+
return {
|
|
582
|
+
success: false,
|
|
583
|
+
error: 'Network name must be a non-empty string'
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
// Trim whitespace
|
|
587
|
+
const trimmed = networkName.trim();
|
|
588
|
+
if (trimmed.length === 0) {
|
|
589
|
+
return {
|
|
590
|
+
success: false,
|
|
591
|
+
error: 'Network name cannot be empty or whitespace only'
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
// Validate length (reasonable limit to prevent DoS)
|
|
595
|
+
const MAX_NETWORK_NAME_LENGTH = 100;
|
|
596
|
+
if (trimmed.length > MAX_NETWORK_NAME_LENGTH) {
|
|
597
|
+
return {
|
|
598
|
+
success: false,
|
|
599
|
+
error: `Network name too long (max ${MAX_NETWORK_NAME_LENGTH} characters). Got: ${trimmed.length}`
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
// Remove control characters and validate basic character set
|
|
603
|
+
// Allow alphanumeric, spaces, hyphens, underscores, and common punctuation
|
|
604
|
+
const sanitized = trimmed.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); // Remove control characters
|
|
605
|
+
// Check for potentially dangerous patterns (script tags, etc.)
|
|
606
|
+
const dangerousPatterns = [
|
|
607
|
+
/<script/i,
|
|
608
|
+
/javascript:/i,
|
|
609
|
+
/on\w+\s*=/i, // Event handlers like onclick=
|
|
610
|
+
/data:text\/html/i
|
|
611
|
+
];
|
|
612
|
+
for (const pattern of dangerousPatterns) {
|
|
613
|
+
if (pattern.test(sanitized)) {
|
|
614
|
+
return {
|
|
615
|
+
success: false,
|
|
616
|
+
error: `Network name contains potentially dangerous content: ${sanitized}`
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
success: true,
|
|
622
|
+
sanitized
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Validate that a merkle root is in the correct format
|
|
627
|
+
* Merkle roots should be valid hexadecimal strings of 64 characters (SHA-256 hash)
|
|
628
|
+
*
|
|
629
|
+
* @param merkleRoot Merkle root string to validate
|
|
630
|
+
* @returns Validation result with success status and error message if failed
|
|
631
|
+
*/
|
|
632
|
+
function validateMerkleRootFormat(merkleRoot) {
|
|
633
|
+
if (!merkleRoot || typeof merkleRoot !== 'string') {
|
|
634
|
+
return {
|
|
635
|
+
success: false,
|
|
636
|
+
error: 'Merkle root must be a non-empty string'
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
// Trim whitespace
|
|
640
|
+
const trimmed = merkleRoot.trim();
|
|
641
|
+
if (trimmed.length === 0) {
|
|
642
|
+
return {
|
|
643
|
+
success: false,
|
|
644
|
+
error: 'Merkle root cannot be empty or whitespace only'
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
// Remove 0x prefix if present for validation
|
|
648
|
+
const normalized = trimmed.startsWith('0x') ? trimmed.slice(2) : trimmed;
|
|
649
|
+
// Must be exactly 64 hexadecimal characters (SHA-256 hash)
|
|
650
|
+
if (normalized.length !== 64) {
|
|
651
|
+
return {
|
|
652
|
+
success: false,
|
|
653
|
+
error: `Merkle root must be exactly 64 hexadecimal characters (SHA-256). Got: ${normalized.length} characters`
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
// Must contain only hexadecimal digits (0-9, a-f, A-F)
|
|
657
|
+
if (!/^[0-9a-fA-F]{64}$/.test(normalized)) {
|
|
658
|
+
return {
|
|
659
|
+
success: false,
|
|
660
|
+
error: `Merkle root must be a valid hexadecimal string. Got: ${trimmed}`
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
// Return normalized version (with 0x prefix for consistency)
|
|
664
|
+
return {
|
|
665
|
+
success: true,
|
|
666
|
+
normalized: `0x${normalized.toLowerCase()}`
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Validate that a contract address matches the expected contract address for a network
|
|
671
|
+
* This prevents attackers from creating fake metadata pointing to malicious contracts
|
|
672
|
+
*
|
|
673
|
+
* @param contractAddress Contract address to validate
|
|
674
|
+
* @param networkChainId Chain ID of the network
|
|
675
|
+
* @param debug Optional debug flag for logging
|
|
676
|
+
* @returns Validation result with success status and error message if failed
|
|
677
|
+
*/
|
|
678
|
+
function validateContractAddress(contractAddress, networkChainId, debug = false) {
|
|
679
|
+
if (debug) {
|
|
680
|
+
console.log(`[DEBUG] Validating contract address: ${contractAddress} for chainId: ${networkChainId}`);
|
|
681
|
+
}
|
|
682
|
+
// Get expected contract address for this network
|
|
683
|
+
const networkConfig = (0, exports.getContractConfig)(networkChainId);
|
|
684
|
+
if (!networkConfig) {
|
|
685
|
+
return {
|
|
686
|
+
success: false,
|
|
687
|
+
error: `Network configuration not found for chainId: ${networkChainId}`
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
const expectedAddress = networkConfig.address;
|
|
691
|
+
// Normalize addresses for comparison (Ethereum addresses are case-insensitive)
|
|
692
|
+
// Use ethers.js getAddress() to normalize both addresses to checksum format
|
|
693
|
+
let normalizedProvided;
|
|
694
|
+
let normalizedExpected;
|
|
695
|
+
try {
|
|
696
|
+
// Validate and normalize the provided address
|
|
697
|
+
if (!ethers_1.ethers.isAddress(contractAddress)) {
|
|
698
|
+
return {
|
|
699
|
+
success: false,
|
|
700
|
+
error: `Invalid contract address format: ${contractAddress}`
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
normalizedProvided = ethers_1.ethers.getAddress(contractAddress);
|
|
704
|
+
// Normalize the expected address
|
|
705
|
+
normalizedExpected = ethers_1.ethers.getAddress(expectedAddress);
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
708
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown address validation error';
|
|
709
|
+
return {
|
|
710
|
+
success: false,
|
|
711
|
+
error: `Address validation failed: ${errorMessage}`
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
// Compare normalized addresses
|
|
715
|
+
if (normalizedProvided.toLowerCase() !== normalizedExpected.toLowerCase()) {
|
|
716
|
+
if (debug) {
|
|
717
|
+
console.log(`[DEBUG] Contract address mismatch:`);
|
|
718
|
+
console.log(`[DEBUG] Provided: ${normalizedProvided}`);
|
|
719
|
+
console.log(`[DEBUG] Expected: ${normalizedExpected}`);
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
success: false,
|
|
723
|
+
error: `Contract address mismatch: provided address ${normalizedProvided} does not match expected address ${normalizedExpected} for network (chainId: ${networkChainId})`
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
if (debug) {
|
|
727
|
+
console.log(`[DEBUG] Contract address validation passed: ${normalizedProvided} matches expected address for network`);
|
|
728
|
+
}
|
|
729
|
+
return { success: true };
|
|
730
|
+
}
|
|
731
|
+
//# sourceMappingURL=contracts.js.map
|