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
JavaScript
"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;
}
}