UNPKG

p-sdk-wallet

Version:

A comprehensive wallet SDK for React Native (pwc), supporting multi-chain and multi-account features.

157 lines (156 loc) 6.28 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HDKeyring = void 0; const EncryptionService_1 = require("../crypto/EncryptionService"); const chains_1 = require("../config/chains"); const ChainService_1 = require("../chain/ChainService"); /** * HD (Hierarchical Deterministic) keyring for managing multiple accounts * derived from a single mnemonic phrase using BIP-44 derivation paths. * Supports EVM-compatible chains. */ class HDKeyring { /** * Creates a new HDKeyring instance from a mnemonic phrase. * @param mnemonic - The mnemonic phrase (12, 15, 18, 21, or 24 words) * @throws Error if the mnemonic is invalid */ constructor(mnemonic) { this.type = 'HD'; this.seed = null; // For now, we manage one account, but this can be extended. this.accounts = []; if (!EncryptionService_1.EncryptionService.validateMnemonic(mnemonic)) { throw new Error('Invalid mnemonic'); } this.mnemonic = mnemonic; } /** * Initializes the keyring by generating the seed and creating the first account. * This method must be called before using the keyring. * @returns Promise that resolves when initialization is complete * @throws Error if seed generation or account derivation fails */ async initialize() { // Check cache first if (HDKeyring.seedCache.has(this.mnemonic)) { this.seed = HDKeyring.seedCache.get(this.mnemonic); } else { this.seed = await EncryptionService_1.EncryptionService.mnemonicToSeed(this.mnemonic); // Cache the seed HDKeyring.seedCache.set(this.mnemonic, this.seed); } const firstAccount = await this.deriveAccount(0); this.accounts.push(firstAccount.address); } /** * Derives an account at a specific index using BIP-44 derivation. * @param index - The account index to derive (0-based) * @returns Promise resolving to an object containing the derived address and private key * @throws Error if the keyring is not initialized or derivation fails */ async deriveAccount(index) { if (!this.seed) { throw new Error('Keyring not initialized. Call initialize() first.'); } // This can be extended to support custom paths per index. const path = `${chains_1.DERIVATION_PATHS.EVM.slice(0, -1)}${index}`; const privateKeyBuffer = await EncryptionService_1.EncryptionService.derivePrivateKey(this.seed, path); const privateKeyHex = privateKeyBuffer.toString('hex'); const address = ChainService_1.ChainService.getAddress(privateKeyHex); return { address, privateKey: privateKeyHex }; } /** * Adds a new account derived from the mnemonic at the next available index. * @returns Promise resolving to the address of the newly created account * @throws Error if account derivation fails or duplicate address is generated */ async addNewAccount() { const newIndex = this.accounts.length; const newAccount = await this.deriveAccount(newIndex); if (this.accounts.includes(newAccount.address)) { throw new Error('Duplicate account derived. Please check derivation path logic.'); } this.accounts.push(newAccount.address); return newAccount.address; } /** * Gets the private key for a given address managed by this keyring. * @param address - The account address to get the private key for * @returns Promise resolving to the private key as a hex string * @throws Error if the address is not found in this keyring */ async getPrivateKeyForAddress(address) { const index = this.accounts.indexOf(address); if (index === -1) { throw new Error('Address not found in this keyring.'); } const account = await this.deriveAccount(index); return account.privateKey; } /** * Returns the mnemonic phrase. USE WITH CAUTION - this exposes sensitive data. * @returns The mnemonic phrase as a string */ getMnemonic() { return this.mnemonic; } /** * Serializes the keyring data for encryption and storage. * @returns An object containing the keyring type, mnemonic, and account addresses */ serialize() { return { type: this.type, mnemonic: this.mnemonic, accounts: this.accounts }; } /** * Deserializes data into an HDKeyring instance. * @param data - The serialized keyring data * @returns Promise resolving to a new HDKeyring instance * @throws Error if the data is invalid or missing required fields */ static async deserialize(data) { if (data.type !== 'HD' || !data.mnemonic) { throw new Error('Invalid data for HDKeyring deserialization.'); } const keyring = new HDKeyring(data.mnemonic); // If accounts were persisted, restore them. Otherwise, initialize fresh. if (data.accounts && data.accounts.length > 0) { keyring.accounts = data.accounts; // Cache the seed to avoid regenerating it on every load if (HDKeyring.seedCache.has(keyring.mnemonic)) { keyring.seed = HDKeyring.seedCache.get(keyring.mnemonic); } else { keyring.seed = await EncryptionService_1.EncryptionService.mnemonicToSeed(keyring.mnemonic); // Cache the seed HDKeyring.seedCache.set(keyring.mnemonic, keyring.seed); } } else { await keyring.initialize(); } return keyring; } /** * Clears the seed cache to free up memory. * Call this when you want to clear sensitive data from memory. */ static clearSeedCache() { HDKeyring.seedCache.clear(); } /** * Gets the cache size for debugging purposes. * @returns Number of cached seeds */ static getSeedCacheSize() { return HDKeyring.seedCache.size; } } exports.HDKeyring = HDKeyring; // Cache để tránh tạo lại seed HDKeyring.seedCache = new Map();