UNPKG

chaingate

Version:

Multi-chain cryptocurrency SDK for TypeScript — unified API for Bitcoin, Ethereum, Litecoin, Dogecoin, Bitcoin Cash, Polygon, Arbitrum, and any EVM-compatible chain. Create wallets, query balances, send transactions, and manage tokens and NFTs across UTXO

340 lines (339 loc) 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.detectWalletImportType = detectWalletImportType; exports.newWallet = newWallet; exports.importWallet = importWallet; exports.isValidSerialized = isValidSerialized; exports.deserializeWallet = deserializeWallet; exports.createWalletFromString = createWalletFromString; exports.isValidKeystore = isValidKeystore; exports.importFromKeystore = importFromKeystore; exports.isValidPhrase = isValidPhrase; exports.isValidSeed = isValidSeed; exports.isValidPrivateKey = isValidPrivateKey; const Phrase_1 = require("./Wallet/SigningWallet/HDWallet/PhraseWallet/Phrase"); const Seed_1 = require("./Wallet/SigningWallet/HDWallet/SeedWallet/Seed"); const PrivateKey_1 = require("./Wallet/SigningWallet/PrivateKeyWallet/PrivateKey"); const PublicKey_1 = require("./Wallet/ViewOnlyWallet/PublicKeyWallet/PublicKey"); const PhraseWallet_1 = require("./Wallet/SigningWallet/HDWallet/PhraseWallet/PhraseWallet"); const SeedWallet_1 = require("./Wallet/SigningWallet/HDWallet/SeedWallet/SeedWallet"); const XprivWallet_1 = require("./Wallet/SigningWallet/HDWallet/XprivWallet/XprivWallet"); const Xpriv_1 = require("./Wallet/SigningWallet/HDWallet/XprivWallet/Xpriv"); const PrivateKeyWallet_1 = require("./Wallet/SigningWallet/PrivateKeyWallet/PrivateKeyWallet"); const XpubWallet_1 = require("./Wallet/ViewOnlyWallet/XpubWallet/XpubWallet"); const PublicKeyWallet_1 = require("./Wallet/ViewOnlyWallet/PublicKeyWallet/PublicKeyWallet"); const WalletSerialized_1 = require("./Wallet/WalletSerialized"); const utils_1 = require("./utils"); const errors_1 = require("./errors"); const LegacyKeystore_1 = require("./Keystore/LegacyKeystore"); const Web3Keystore_1 = require("./Keystore/Web3Keystore"); /** * Detects what type of wallet input a string is (phrase, xpriv, xpub, WIF, seed, private/public key). * * @param input - The string to analyze. * @throws {@link UnrecognizedFormatError} if the format cannot be identified. */ function detectWalletImportType(input) { const trimmed = input.trim(); if (trimmed.startsWith('xpub')) { if (trimmed.length < 107 || trimmed.length > 112) { throw new errors_1.UnrecognizedFormatError(`Invalid xpub: expected 107-112 characters, got ${trimmed.length}`); } return 'xpub'; } if (trimmed.startsWith('xprv')) { if (trimmed.length < 107 || trimmed.length > 112) { throw new errors_1.UnrecognizedFormatError(`Invalid xpriv: expected 107-112 characters, got ${trimmed.length}`); } return 'xpriv'; } if (Phrase_1.Phrase.isValid(trimmed)) return 'phrase'; if ((0, utils_1.isHex)(trimmed)) { const clean = trimmed.startsWith('0x') ? trimmed.substring(2) : trimmed; if (clean.length === 128) return 'seed'; if (clean.length === 64) return 'privateKey'; if (clean.length === 66 || clean.length === 130) return 'publicKey'; throw new errors_1.UnrecognizedFormatError(`Invalid hex key: expected 64 (private key), 66 (public key), 128 (seed), or 130 (uncompressed public key) hex characters, got ${clean.length}`); } if ((0, utils_1.isBase58)(trimmed)) { if (trimmed.length < 51 || trimmed.length > 52) { throw new errors_1.UnrecognizedFormatError(`Invalid WIF: expected 51-52 characters, got ${trimmed.length}`); } return 'wif'; } throw new errors_1.UnrecognizedFormatError('Unrecognized input format'); } /** * Generates a new random mnemonic phrase and creates an HD wallet. * * @param language - Wordlist language. Defaults to `'english'`. * @param numberOfWords - Word count. Defaults to `12`. * @returns The mnemonic `phrase` and the created `wallet`. * * @example * ```ts * const { phrase, wallet } = newWallet(); * const key = await wallet.derive("m/44'/60'/0'/0/0"); * ``` */ function newWallet(language = 'english', numberOfWords = 12) { const phraseEntity = Phrase_1.Phrase.new(language, numberOfWords); const wallet = new PhraseWallet_1.PhraseWallet(phraseEntity); return { phrase: new TextDecoder().decode(phraseEntity.raw), wallet }; } function importWallet(params) { if ('phrase' in params) { return new PhraseWallet_1.PhraseWallet(new Phrase_1.Phrase(params.phrase)); } if ('seed' in params) { return new SeedWallet_1.SeedWallet(new Seed_1.Seed(params.seed)); } if ('xpriv' in params) { return new XprivWallet_1.XprivWallet(new Xpriv_1.Xpriv(params.xpriv)); } if ('privateKey' in params) { return new PrivateKeyWallet_1.PrivateKeyWallet(new PrivateKey_1.PrivateKey(params.privateKey)); } if ('xpub' in params) { return new XpubWallet_1.XpubWallet(params.xpub); } if ('publicKey' in params) { return new PublicKeyWallet_1.PublicKeyWallet(new PublicKey_1.PublicKey(params.publicKey)); } throw new errors_1.InvalidWalletParamsError('Invalid wallet params'); } // --- Deserialize from serialized data --- /** * Type guard — checks if unknown data is a valid {@link WalletSerialized}. * * @param data - The data to validate. */ function isValidSerialized(data) { if (typeof data !== 'object' || data === null) return false; const obj = data; if (typeof obj.type !== 'string') return false; if (!WalletSerialized_1.WALLET_TYPES.includes(obj.type)) return false; // Encrypted: type is a valid wallet type + ciphertext/iv/salt present if (typeof obj.ciphertext === 'string' && obj.ciphertext !== '') { return (WalletSerialized_1.SECRET_WALLET_TYPES.includes(obj.type) && typeof obj.iv === 'string' && obj.iv !== '' && typeof obj.salt === 'string' && obj.salt !== ''); } // Plaintext: the key matching the type must be a non-empty string return typeof obj[obj.type] === 'string' && obj[obj.type] !== ''; } /** * Restores a wallet from serialized data (produced by {@link Wallet.serialize}). * * @param data - The serialized wallet data. * @param askForPassword - Password prompt callback. Required for encrypted wallets. * @throws {@link InvalidWalletExportError} if the data is invalid. * * @example * ```ts * const wallet = deserializeWallet(JSON.parse(stored), async () => { * return prompt('Enter password:'); * }); * ``` */ function deserializeWallet(data, askForPassword) { if (!isValidSerialized(data)) { throw new errors_1.InvalidWalletExportError('Invalid wallet data: expected { type, phrase | seed | xpriv | privateKey | xpub | publicKey }'); } const restoreData = extractRestoreData(data); const isEncrypted = 'ciphertext' in data && data.ciphertext !== ''; if (isEncrypted) { if (!askForPassword) { throw new errors_1.InvalidWalletExportError('askForPassword is required to deserialize an encrypted wallet'); } const enc = data; const encrypted = { ciphertext: (0, utils_1.hexToBytes)(enc.ciphertext), iv: (0, utils_1.hexToBytes)(enc.iv), salt: (0, utils_1.hexToBytes)(enc.salt), askForPassword, }; switch (data.type) { case 'phrase': return new PhraseWallet_1.PhraseWallet(new Phrase_1.Phrase(encrypted), restoreData); case 'seed': return new SeedWallet_1.SeedWallet(new Seed_1.Seed(encrypted), restoreData); case 'xpriv': return new XprivWallet_1.XprivWallet(new Xpriv_1.Xpriv(encrypted), restoreData); case 'privateKey': return new PrivateKeyWallet_1.PrivateKeyWallet(new PrivateKey_1.PrivateKey(encrypted)); } } switch (data.type) { case 'phrase': return new PhraseWallet_1.PhraseWallet(new Phrase_1.Phrase(data.phrase), restoreData); case 'seed': return new SeedWallet_1.SeedWallet(new Seed_1.Seed(data.seed), restoreData); case 'xpriv': return new XprivWallet_1.XprivWallet(new Xpriv_1.Xpriv(data.xpriv), restoreData); case 'privateKey': return new PrivateKeyWallet_1.PrivateKeyWallet(new PrivateKey_1.PrivateKey(data.privateKey)); case 'xpub': return new XpubWallet_1.XpubWallet(data.xpub); case 'publicKey': return new PublicKeyWallet_1.PublicKeyWallet(new PublicKey_1.PublicKey(data.publicKey)); } } function extractRestoreData(data) { const restoreData = {}; if ('derivationIndex' in data && Array.isArray(data.derivationIndex)) { restoreData.derivationIndex = data.derivationIndex; } if ('masterPublicKey' in data && typeof data.masterPublicKey === 'string') { restoreData.masterPublicKey = data.masterPublicKey; } return Object.keys(restoreData).length > 0 ? restoreData : undefined; } // --- Auto-detect from string --- /** * Auto-detects the format of a string and creates the appropriate wallet. * * @param input - Phrase, xpriv, xpub, WIF, hex key, etc. * @throws {@link UnrecognizedFormatError} if the format cannot be identified. * * @example * ```ts * const wallet = createWalletFromString('abandon abandon ... about'); * const wallet = createWalletFromString('xpub6CUGRUo...'); * ``` */ function createWalletFromString(input) { const type = detectWalletImportType(input); const paramMap = { phrase: () => ({ phrase: input }), seed: () => ({ seed: input }), xpriv: () => ({ xpriv: input }), privateKey: () => ({ privateKey: input }), wif: () => ({ privateKey: input }), xpub: () => ({ xpub: input }), publicKey: () => ({ publicKey: input }), }; return importWallet(paramMap[type]()); } // --- Keystore import --- /** * Checks whether a JSON string is a recognized keystore format (V1 Legacy or V3 Web3). * * @param keystore - The JSON string to check. * @returns `true` if the JSON is a valid V1 or V3 keystore. * * @example * ```ts * if (isValidKeystore(jsonString)) { * const wallet = await importFromKeystore(jsonString, 'myPassword'); * } * ``` */ function isValidKeystore(keystore) { try { const obj = JSON.parse(keystore); return LegacyKeystore_1.LegacyKeystore.isKeystore(obj) || Web3Keystore_1.Web3Keystore.isKeystore(obj); } catch { return false; } } /** * Imports a wallet from an encrypted keystore JSON (V1 Legacy or V3 Web3 * format, as used by MetaMask, MyEtherWallet, Geth, etc.). * * The keystore is decrypted with the given password. The decrypted secret is * auto-detected as either a mnemonic phrase (returns {@link PhraseWallet}) or * a raw private key (returns {@link PrivateKeyWallet}). * * @param keystore - The keystore JSON string. * @param password - The decryption password. * @returns The appropriate wallet type based on the decrypted contents. * * @throws {@link InvalidKeystoreError} if the JSON is not a recognized keystore. * @throws {@link IncorrectKeystorePasswordError} if the password is wrong. * * @example * ```ts * const wallet = await importFromKeystore(keystoreJson, 'my-password'); * ``` */ async function importFromKeystore(keystore, password) { let obj; try { obj = JSON.parse(keystore); } catch { throw new errors_1.InvalidKeystoreError('Invalid JSON'); } let decrypted; if (LegacyKeystore_1.LegacyKeystore.isKeystore(obj)) { decrypted = await new LegacyKeystore_1.LegacyKeystore(obj).decrypt(password); } else if (Web3Keystore_1.Web3Keystore.isKeystore(obj)) { decrypted = await new Web3Keystore_1.Web3Keystore(obj).decrypt(password); } else { throw new errors_1.InvalidKeystoreError('Unrecognized keystore format'); } // Try to interpret the decrypted bytes as a mnemonic phrase first try { const phraseText = new TextDecoder().decode(decrypted); if (Phrase_1.Phrase.isValid(phraseText)) { return new PhraseWallet_1.PhraseWallet(new Phrase_1.Phrase(phraseText)); } } catch { // Not a valid phrase — fall through to private key } return new PrivateKeyWallet_1.PrivateKeyWallet(new PrivateKey_1.PrivateKey(decrypted)); } // --- Validation helpers --- /** * Checks whether a string is a valid BIP-39 mnemonic phrase. * * @param phrase - The mnemonic phrase to validate. * @returns `true` if valid. */ function isValidPhrase(phrase) { return Phrase_1.Phrase.isValid(phrase); } /** * Checks whether a value is a valid HD seed (hex string or bytes). * * @param seed - The seed to validate (hex string or `Uint8Array`). * @returns `true` if valid. */ function isValidSeed(seed) { try { new Seed_1.Seed(seed); return true; } catch { return false; } } /** * Checks whether a value is a valid secp256k1 private key (hex, WIF, or bytes). * * @param privateKey - The private key to validate. * @returns `true` if valid. */ function isValidPrivateKey(privateKey) { try { new PrivateKey_1.PrivateKey(privateKey); return true; } catch { return false; } }