UNPKG

@unisat/keyring-service

Version:

Keyring service for managing Bitcoin wallets - supports HD, simple, and hardware wallets

1 lines 106 kB
{"version":3,"sources":["../src/keyring-service.ts","../src/types/keyring.ts","../src/types/cosmos.ts","../src/types/keystone.ts","../src/encryptor/browser-encryptor.ts","../src/keyrings/simple-keyring.ts","../src/keyrings/hd-keyring.ts","../src/keyrings/keystone-keyring.ts","../src/keyrings/cold-wallet-keyring.ts","../src/constant.ts","../src/keyrings/empty-keyring.ts","../src/adapters/memory.ts","../src/adapters/extensionPersist.ts","../src/encryptor/simple-encryptor.ts"],"sourcesContent":["import { EventEmitter } from 'events'\nimport * as bip39 from 'bip39'\nimport { ObservableStore } from '@metamask/obs-store'\n\nimport {\n KeyringServiceConfig,\n StorageAdapter,\n MemStoreState,\n DisplayedKeyring,\n ToSignInput,\n KeyringType,\n Encryptor,\n} from './types'\nimport { AddressType } from '@unisat/wallet-types'\nimport { BrowserPassworderEncryptor } from './encryptor/browser-encryptor'\nimport { Keyring } from './types/keyring'\nimport { ColdWalletKeyring, HdKeyring, KeystoneKeyring, SimpleKeyring } from './keyrings'\nimport { ADDRESS_TYPES } from './constant'\nimport { EmptyKeyring } from './keyrings/empty-keyring'\nimport { bitcoin } from '@unisat/wallet-bitcoin'\n\nconst EVENTS = {\n broadcastToUI: 'broadcastToUI',\n broadcastToBackground: 'broadcastToBackground',\n SIGN_FINISHED: 'SIGN_FINISHED',\n WALLETCONNECT: {\n STATUS_CHANGED: 'WALLETCONNECT_STATUS_CHANGED',\n INIT: 'WALLETCONNECT_INIT',\n INITED: 'WALLETCONNECT_INITED',\n },\n}\n\nconst KEYRING_SDK_TYPES = {\n SimpleKeyring,\n HdKeyring,\n KeystoneKeyring,\n ColdWalletKeyring,\n}\n\nclass DisplayKeyring {\n accounts: string[] = []\n type = ''\n hdPath = ''\n\n constructor(keyring: Keyring) {\n this.accounts = keyring.accounts || []\n this.type = keyring.type\n this.hdPath = (keyring as any).hdPath\n }\n}\n\n/**\n * KeyringService - Core service for managing multiple keyrings (wallets)\n * Aligned with unisat-extension keyring implementation\n */\nexport class KeyringService extends EventEmitter {\n private storage: StorageAdapter\n private logger: any\n private encryptor: Encryptor\n private t: any\n private eventBus: any\n\n // Core state - aligned with unisat-extension\n public keyringTypes: any[] = []\n public store!: ObservableStore<any>\n public memStore: ObservableStore<MemStoreState>\n public keyrings: Keyring[] = []\n public addressTypes: AddressType[] = []\n public password: string | null = null\n private isUnlocking = false\n private cachedDisplayedKeyring: DisplayedKeyring[] | null = null\n\n constructor(config: KeyringServiceConfig) {\n super()\n this.storage = config.storage\n this.logger = config.logger || console\n this.encryptor = config.encryptor || new BrowserPassworderEncryptor()\n this.t = config.t || ((key: string) => key) // Default t function\n this.eventBus = config.eventBus\n\n // Initialize supported keyring types\n this.keyringTypes = Object.values(KEYRING_SDK_TYPES)\n\n // Initialize memory store - aligned with unisat-extension\n this.memStore = new ObservableStore<MemStoreState>({\n isUnlocked: false,\n keyringTypes: this.keyringTypes.map(krt => krt.type || krt.name),\n keyrings: [],\n preMnemonics: '',\n addressTypes: [],\n keystone: null,\n })\n\n this.keyrings = []\n this.addressTypes = []\n }\n\n /**\n * Initialize the service - must be called before use\n */\n async init(): Promise<void> {\n await this.storage.init()\n\n // Initialize store if not already initialized\n if (!this.store) {\n const persistedState = await this.getStore()\n this.loadStore(persistedState)\n }\n\n this.logger.debug('[KeyringService] Initialized')\n }\n\n private async getStore(): Promise<any> {\n const store = (await this.storage.get('keyring')) || {}\n return store\n }\n\n private async updateStore(updates: any): Promise<void> {\n const currentStore = await this.getStore()\n const newStore = { ...currentStore, ...updates }\n await this.storage.set('keyring', newStore)\n }\n\n loadStore = (initState: MemStoreState) => {\n this.store = new ObservableStore(initState)\n }\n\n boot = async (password: string) => {\n this.password = password\n const encryptBooted = await this.encryptor.encrypt(password, 'true')\n\n // Initialize store if not already initialized\n if (!this.store) {\n this.loadStore({\n isUnlocked: false,\n keyrings: [],\n keyringTypes: [],\n preMnemonics: '',\n addressTypes: [],\n })\n }\n\n // Update both in-memory and persistent storage\n this.store.updateState({ booted: encryptBooted })\n await this.updateStore({ booted: encryptBooted })\n this.setUnlocked()\n this.fullUpdate()\n }\n\n isBooted = () => {\n return !!this.store.getState().booted\n }\n\n hasVault = () => {\n return !!this.store.getState().vault\n }\n\n /**\n * Get current memory store state\n */\n getMemStore(): MemStoreState {\n return this.memStore.getState()\n }\n\n /**\n * Full Update\n *\n * Emits the `update` event and @returns a Promise that resolves to\n * the current state.\n *\n * Frequently used to end asynchronous chains in this class,\n * indicating consumers can often either listen for updates,\n * or accept a state-resolving promise to consume their results.\n *\n * @returns {Object} The controller state.\n */\n fullUpdate = (): MemStoreState => {\n this.emit('update', this.memStore.getState())\n return this.memStore.getState()\n }\n\n /**\n * Import Keychain using Private key\n *\n * @emits KeyringController#unlock\n * @param privateKey - The privateKey to generate address\n * @returns A Promise that resolves to the state.\n */\n importPrivateKey = async (privateKey: string, addressType: AddressType) => {\n // await this.persistAllKeyrings();\n const keyring = await this.addNewKeyring('Simple Key Pair', [privateKey], addressType)\n // await this.persistAllKeyrings();\n this.setUnlocked()\n this.fullUpdate()\n return keyring\n }\n\n importPublicKeyOnly = async (pubkey: string, addressType: AddressType) => {\n // await this.persistAllKeyrings()\n const keyring = await this.addNewKeyring('Readonly', [pubkey], addressType)\n // await this.persistAllKeyrings()\n this.setUnlocked()\n this.fullUpdate()\n return keyring\n }\n\n generateMnemonic = (): string => {\n return bip39.generateMnemonic(128)\n }\n\n generatePreMnemonic = async (): Promise<string> => {\n if (!this.password) {\n throw new Error(this.t('you_need_to_unlock_wallet_first'))\n }\n const mnemonic = this.generateMnemonic()\n const preMnemonics = await this.encryptor.encrypt(this.password, mnemonic)\n this.memStore.updateState({ preMnemonics })\n\n return mnemonic\n }\n\n getKeyringByType = (type: string) => {\n const keyring = this.keyrings.find(keyring => keyring.type === type)\n\n return keyring\n }\n\n removePreMnemonics = () => {\n this.memStore.updateState({ preMnemonics: '' })\n }\n\n getPreMnemonics = async (): Promise<any> => {\n if (!this.memStore.getState().preMnemonics) {\n return ''\n }\n\n if (!this.password) {\n throw new Error(this.t('you_need_to_unlock_wallet_first'))\n }\n\n return await this.encryptor.decrypt(this.password, this.memStore.getState().preMnemonics)\n }\n\n // Alias for compatibility\n getPreMnemonic = this.getPreMnemonics\n\n /**\n * CreateNewVaultAndRestore Mnenoic\n *\n * Destroys any old encrypted storage,\n * creates a new HD wallet from the given seed with 1 account.\n *\n * @emits KeyringController#unlock\n * @param seed - The BIP44-compliant seed phrase.\n * @returns A Promise that resolves to the state.\n */\n createKeyringWithMnemonics = async (\n seed: string,\n hdPath: string,\n passphrase: string,\n addressType: AddressType,\n accountCount: number\n ) => {\n if (accountCount < 1) {\n throw new Error(this.t('account_count_must_be_greater_than_0'))\n }\n if (!bip39.validateMnemonic(seed)) {\n return Promise.reject(new Error(this.t('mnemonic_phrase_is_invalid')))\n }\n\n // await this.persistAllKeyrings();\n const activeIndexes: number[] = []\n for (let i = 0; i < accountCount; i++) {\n activeIndexes.push(i)\n }\n const keyring = await this.addNewKeyring(\n 'HD Key Tree',\n {\n mnemonic: seed,\n activeIndexes,\n hdPath,\n passphrase,\n },\n addressType\n )\n const accounts = await keyring.getAccounts()\n if (!accounts[0]) {\n throw new Error(this.t('first_account_not_found'))\n }\n // this.persistAllKeyrings();\n this.setUnlocked()\n this.fullUpdate()\n return keyring\n }\n\n createKeyringWithKeystone = async (\n urType: string,\n urCbor: string,\n addressType: AddressType,\n hdPath: string,\n accountCount: number,\n connectionType: 'USB' | 'QR' = 'USB'\n ) => {\n if (accountCount < 1) {\n throw new Error(this.t('account_count_must_be_greater_than_0'))\n }\n // await this.persistAllKeyrings();\n const tmpKeyring = new KeystoneKeyring()\n await tmpKeyring.initFromUR(urType, urCbor, connectionType)\n if (hdPath.length >= 13) {\n tmpKeyring.changeChangeAddressHdPath(hdPath)\n tmpKeyring.addAccounts(accountCount)\n } else {\n const typeConfig = ADDRESS_TYPES[addressType]\n tmpKeyring.changeHdPath(typeConfig ? typeConfig.hdPath : '')\n tmpKeyring.addAccounts(accountCount)\n }\n\n const opts = await tmpKeyring.serialize()\n const keyring = await this.addNewKeyring(KeyringType.KeystoneKeyring, opts, addressType)\n const accounts = await keyring.getAccounts()\n\n if (!accounts[0]) {\n throw new Error(this.t('keyringcontroller_first_account_not_found'))\n }\n this.setUnlocked()\n return keyring\n }\n\n addKeyring = async (keyring: Keyring, addressType: AddressType) => {\n const accounts = await keyring.getAccounts()\n await this.checkForDuplicate(keyring.type, accounts)\n\n this.keyrings.push(keyring)\n this.addressTypes.push(addressType)\n this.cachedDisplayedKeyring = null\n\n await this.persistAllKeyrings()\n await this._updateMemStoreKeyrings()\n await this.fullUpdate()\n return keyring\n }\n\n changeAddressType = async (keyringIndex: number, addressType: AddressType) => {\n const keyring: Keyring = this.keyrings[keyringIndex] as Keyring\n\n if (keyring.type === KeyringType.HdKeyring || keyring.type === KeyringType.KeystoneKeyring) {\n const hdPath = ADDRESS_TYPES[addressType]?.hdPath\n if ((keyring as any).hdPath !== hdPath && keyring.changeHdPath) {\n keyring.changeHdPath(hdPath || '')\n }\n }\n this.addressTypes[keyringIndex] = addressType\n this.cachedDisplayedKeyring = null\n\n await this.persistAllKeyrings()\n await this._updateMemStoreKeyrings()\n await this.fullUpdate()\n return keyring\n }\n\n /**\n * Set Locked\n * This method deallocates all secrets, and effectively locks MetaMask.\n *\n * @emits KeyringController#lock\n * @returns {Promise<Object>} A Promise that resolves to the state.\n */\n setLocked = async (): Promise<MemStoreState> => {\n // set locked\n this.password = null\n this.memStore.updateState({ isUnlocked: false })\n\n // remove keyrings\n this.keyrings = []\n this.addressTypes = []\n this.cachedDisplayedKeyring = null\n\n await this._updateMemStoreKeyrings()\n this.emit('lock')\n return this.fullUpdate()\n }\n\n /**\n * Submit Password\n *\n * Attempts to decrypt the current vault and load its keyrings\n * into memory.\n *\n * Temporarily also migrates any old-style vaults first, as well.\n * (Pre MetaMask 3.0.0)\n *\n * @emits KeyringController#unlock\n * @param {string} password - The keyring controller password.\n * @returns {Promise<Object>} A Promise that resolves to the state.\n */\n submitPassword = async (password: string): Promise<MemStoreState> => {\n if (this.isUnlocking) {\n throw new Error(this.t('unlock_already_in_progress'))\n }\n\n this.isUnlocking = true\n\n try {\n const isValidPassword = await this.verifyPassword(password)\n if (!isValidPassword) {\n throw new Error(this.t('invalid_password'))\n }\n\n this.password = password\n\n this.keyrings = await this.unlockKeyrings(password)\n this.cachedDisplayedKeyring = null\n\n this.setUnlocked()\n return this.fullUpdate()\n } catch (e) {\n throw e\n } finally {\n this.isUnlocking = false\n }\n }\n\n changePassword = async (oldPassword: string, newPassword: string) => {\n try {\n if (this.isUnlocking) {\n throw new Error(this.t('change_password_already_in_progress'))\n }\n this.isUnlocking = true\n\n const isValidPassword = await this.verifyPassword(oldPassword)\n if (!isValidPassword) {\n throw new Error(this.t('invalid_password'))\n }\n await this.unlockKeyrings(oldPassword)\n this.password = newPassword\n\n const encryptBooted = await this.encryptor.encrypt(newPassword, 'true')\n this.store.updateState({ booted: encryptBooted })\n\n if (this.memStore.getState().preMnemonics) {\n const mnemonic = await this.encryptor.decrypt(\n oldPassword,\n this.memStore.getState().preMnemonics\n )\n const preMnemonics = await this.encryptor.encrypt(newPassword, mnemonic)\n this.memStore.updateState({ preMnemonics })\n }\n\n await this.persistAllKeyrings()\n await this._updateMemStoreKeyrings()\n await this.fullUpdate()\n } catch (e) {\n throw new Error(this.t('change_password_failed'))\n } finally {\n this.isUnlocking = false\n }\n }\n\n /**\n * Verify Password\n *\n * Attempts to decrypt the current vault with a given password\n * to verify its validity.\n *\n * @param {string} password\n */\n verifyPassword = async (password: string): Promise<boolean> => {\n const encryptedBooted = this.store.getState().booted\n if (!encryptedBooted) {\n throw new Error(this.t('cannot_unlock_without_a_previous_vault'))\n }\n try {\n await this.encryptor.decrypt(password, encryptedBooted)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Add New Keyring\n *\n * Adds a new Keyring of the given `type` to the vault\n * and the current decrypted Keyrings array.\n *\n * All Keyring classes implement a unique `type` string,\n * and this is used to retrieve them from the keyringTypes array.\n *\n * @param type - The type of keyring to add.\n * @param opts - The constructor options for the keyring.\n * @returns The new keyring.\n */\n addNewKeyring = async (\n type: string,\n opts: unknown,\n addressType: AddressType\n ): Promise<Keyring> => {\n const Keyring = this.getKeyringClassForType(type)\n const keyring = new Keyring(opts)\n return await this.addKeyring(keyring, addressType)\n }\n\n createTmpKeyring = (type: string, opts: unknown) => {\n if (type === KeyringType.ColdWalletKeyring) {\n return new ColdWalletKeyring(opts as any)\n }\n\n const Keyring = this.getKeyringClassForType(type)\n if (!Keyring) {\n throw new Error(`Unknown keyring type: ${type}`)\n }\n const keyring = new Keyring(opts)\n return keyring\n }\n\n /**\n * Checks for duplicate keypairs, using the the first account in the given\n * array. Rejects if a duplicate is found.\n *\n * Only supports 'Simple Key Pair'.\n *\n * @param {string} type - The key pair type to check for.\n * @param {Array<string>} newAccountArray - Array of new accounts.\n * @returns {Promise<Array<string>>} The account, if no duplicate is found.\n */\n checkForDuplicate = async (type: string, newAccountArray: string[]): Promise<string[]> => {\n const keyrings = this.getKeyringsByType(type)\n const _accounts = await Promise.all(keyrings.map(keyring => keyring.getAccounts()))\n\n const accounts: string[] = _accounts.reduce((m, n) => m.concat(n), [] as string[])\n\n const isIncluded = newAccountArray.some(account => {\n return accounts.find(key => key === account)\n })\n\n return isIncluded\n ? Promise.reject(new Error(this.t('wallet_existed')))\n : Promise.resolve(newAccountArray)\n }\n\n /**\n * Add New Account\n *\n * Calls the `addAccounts` method on the given keyring,\n * and then saves those changes.\n *\n * @param {Keyring} selectedKeyring - The currently selected keyring.\n * @returns {Promise<Object>} A Promise that resolves to the state.\n */\n addNewAccount = async (selectedKeyring: Keyring): Promise<string[]> => {\n const accounts = await selectedKeyring.addAccounts(1)\n this.cachedDisplayedKeyring = null\n\n accounts.forEach(hexAccount => {\n this.emit('newAccount', hexAccount)\n })\n await this.persistAllKeyrings()\n await this._updateMemStoreKeyrings()\n await this.fullUpdate()\n return accounts\n }\n\n /**\n * Export Account\n *\n * Requests the private key from the keyring controlling\n * the specified address.\n *\n * Returns a Promise that may resolve with the private key string.\n *\n * @param {string} address - The address of the account to export.\n * @returns {Promise<string>} The private key of the account.\n */\n exportAccount = async (address: string): Promise<string> => {\n const keyring = await this.getKeyringForAccount(address)\n const privkey = await keyring.exportAccount(address)\n return privkey\n }\n\n /**\n *\n * Remove Account\n *\n * Removes a specific account from a keyring\n * If the account is the last/only one then it also removes the keyring.\n *\n * @param {string} address - The address of the account to remove.\n * @returns {Promise<void>} A Promise that resolves if the operation was successful.\n */\n removeAccount = async (address: string, type: string, brand?: string): Promise<any> => {\n const keyring = await this.getKeyringForAccount(address, type)\n\n // Not all the keyrings support this, so we have to check\n if (typeof keyring.removeAccount != 'function') {\n throw new Error(\n `Keyring ${keyring.type} ${this.t('does_not_support_account_removal_operations')}`\n )\n }\n keyring.removeAccount(address)\n this.cachedDisplayedKeyring = null\n\n this.emit('removedAccount', address)\n await this.persistAllKeyrings()\n await this._updateMemStoreKeyrings()\n await this.fullUpdate()\n }\n\n removeKeyring = async (keyringIndex: number): Promise<any> => {\n delete this.keyrings[keyringIndex]\n this.keyrings[keyringIndex] = new EmptyKeyring()\n this.cachedDisplayedKeyring = null\n\n await this.persistAllKeyrings()\n await this._updateMemStoreKeyrings()\n await this.fullUpdate()\n }\n\n //\n // SIGNING METHODS\n //\n\n /**\n * Sign BTC Transaction\n *\n * Signs an BTC transaction object.\n *\n * @param btcTx - The transaction to sign.\n * @param fromAddress - The transaction 'from' address.\n * @returns The signed transactio object.\n */\n signTransaction = (keyring: Keyring, psbt: bitcoin.Psbt, inputs: ToSignInput[]) => {\n return keyring.signTransaction(psbt, inputs)\n }\n\n /**\n * Sign Message\n *\n * Attempts to sign the provided message parameters.\n */\n signMessage = async (address: string, keyringType: string, data: string) => {\n const keyring = await this.getKeyringForAccount(address, keyringType)\n const sig = await keyring.signMessage(address, data)\n return sig\n }\n\n /**\n * Decrypt Message\n *\n * Attempts to verify the provided message parameters.\n */\n verifyMessage = async (address: string, data: string, sig: string) => {\n const keyring = await this.getKeyringForAccount(address)\n const result = await keyring.verifyMessage(address, data, sig)\n return result\n }\n\n /**\n * Sign Data\n *\n * Sign any content, but note that the content signed by this method is unreadable, so use it with caution.\n *\n */\n signData = async (address: string, data: string, type: string) => {\n const keyring = await this.getKeyringForAccount(address)\n const result = await keyring.signData(address, data, type)\n return result\n }\n\n //\n // PRIVATE METHODS\n //\n\n /**\n * Persist All Keyrings\n *\n * Iterates the current `keyrings` array,\n * serializes each one into a serialized array,\n * encrypts that array with the provided `password`,\n * and persists that encrypted string to storage.\n *\n * @param {string} password - The keyring controller password.\n * @returns {Promise<boolean>} Resolves to true once keyrings are persisted.\n */\n persistAllKeyrings = (): Promise<boolean> => {\n if (!this.password || typeof this.password !== 'string') {\n return Promise.reject(new Error(this.t('keyringcontroller_password_is_not_a_string')))\n }\n return Promise.all(\n this.keyrings.map((keyring, index) => {\n return Promise.all([keyring.type, keyring.serialize()]).then(serializedKeyringArray => {\n // Label the output values on each serialized Keyring:\n return {\n type: serializedKeyringArray[0],\n data: serializedKeyringArray[1],\n addressType: this.addressTypes[index],\n }\n })\n })\n )\n .then(serializedKeyrings => {\n return this.encryptor.encrypt(\n this.password as string,\n serializedKeyrings as unknown as Buffer\n )\n })\n .then(encryptedString => {\n this.store.updateState({ vault: encryptedString })\n return true\n })\n }\n\n /**\n * Unlock Keyrings\n *\n * Attempts to unlock the persisted encrypted storage,\n * initializing the persisted keyrings to RAM.\n *\n * @param {string} password - The keyring controller password.\n * @returns {Promise<Array<Keyring>>} The keyrings.\n */\n unlockKeyrings = async (password: string): Promise<any[]> => {\n const encryptedVault = this.store.getState().vault\n if (!encryptedVault) {\n throw new Error(this.t('cannot_unlock_without_a_previous_vault'))\n }\n\n await this.clearKeyrings()\n const vault = await this.encryptor.decrypt(password, encryptedVault)\n\n const arr = Array.from(vault)\n for (let i = 0; i < arr.length; i++) {\n const { keyring, addressType } = await this._restoreKeyring(arr[i])\n this.keyrings.push(keyring)\n this.addressTypes.push(addressType)\n }\n this.cachedDisplayedKeyring = null\n\n await this._updateMemStoreKeyrings()\n return this.keyrings\n }\n\n /**\n * Restore Keyring\n *\n * Attempts to initialize a new keyring from the provided serialized payload.\n * On success, updates the memStore keyrings and returns the resulting\n * keyring instance.\n *\n * @param {Object} serialized - The serialized keyring.\n * @returns {Promise<Keyring>} The deserialized keyring.\n */\n restoreKeyring = async (serialized: any) => {\n const { keyring } = await this._restoreKeyring(serialized)\n await this._updateMemStoreKeyrings()\n return keyring\n }\n\n /**\n * Restore Keyring Helper\n *\n * Attempts to initialize a new keyring from the provided serialized payload.\n * On success, returns the resulting keyring instance.\n *\n * @param {Object} serialized - The serialized keyring.\n * @returns {Promise<Keyring>} The deserialized keyring.\n */\n _restoreKeyring = async (\n serialized: any\n ): Promise<{ keyring: Keyring; addressType: AddressType }> => {\n const { type, data, addressType } = serialized\n if (type === KeyringType.Empty) {\n const keyring = new EmptyKeyring()\n return {\n keyring,\n addressType: addressType,\n }\n }\n\n if (type === KeyringType.ColdWalletKeyring) {\n const keyring = new ColdWalletKeyring()\n await keyring.deserialize(data)\n await keyring.getAccounts()\n return {\n keyring,\n addressType: addressType,\n }\n }\n\n const Keyring = this.getKeyringClassForType(type)\n if (!Keyring) {\n throw new Error(`Unknown keyring type: ${type}`)\n }\n const keyring = new Keyring()\n await keyring.deserialize(data)\n\n // getAccounts also validates the accounts for some keyrings\n await keyring.getAccounts()\n return {\n keyring,\n addressType: addressType,\n }\n }\n\n /**\n * Get Keyring Class For Type\n *\n * Searches the current `keyringTypes` array\n * for a Keyring class whose unique `type` property\n * matches the provided `type`,\n * returning it if it exists.\n *\n * @param {string} type - The type whose class to get.\n * @returns {Keyring|undefined} The class, if it exists.\n */\n getKeyringClassForType = (type: string) => {\n if (type === KeyringType.ColdWalletKeyring) {\n return ColdWalletKeyring\n }\n\n return this.keyringTypes.find(kr => kr.type === type)\n }\n\n /**\n * Get Keyrings by Type\n *\n * Gets all keyrings of the given type.\n *\n * @param {string} type - The keyring types to retrieve.\n * @returns {Array<Keyring>} The keyrings.\n */\n getKeyringsByType = (type: string): Keyring[] => {\n return this.keyrings.filter(keyring => keyring.type === type)\n }\n\n /**\n * Get Accounts\n *\n * Returns the public addresses of all current accounts\n * managed by all currently unlocked keyrings.\n *\n * @returns {Promise<Array<string>>} The array of accounts.\n */\n getAccounts = async (): Promise<string[]> => {\n const keyrings = this.keyrings || []\n let addrs: string[] = []\n for (let i = 0; i < keyrings.length; i++) {\n const keyring = keyrings[i]\n if (!keyring) {\n continue\n }\n const accounts = await keyring.getAccounts()\n addrs = addrs.concat(accounts)\n }\n return addrs\n }\n\n /**\n * Get Keyring For Account\n *\n * Returns the currently initialized keyring that manages\n * the specified `address` if one exists.\n *\n * @param {string} address - An account address.\n * @returns {Promise<Keyring>} The keyring of the account, if it exists.\n */\n getKeyringForAccount = async (\n address: string,\n type?: string,\n start?: number,\n end?: number,\n includeWatchKeyring = true\n ): Promise<Keyring> => {\n this.logger.debug(`KeyringController - getKeyringForAccount: ${address}`)\n const keyrings = type ? this.keyrings.filter(keyring => keyring.type === type) : this.keyrings\n for (let i = 0; i < keyrings.length; i++) {\n const keyring = keyrings[i]\n if (!keyring) {\n continue\n }\n const accounts = await keyring.getAccounts()\n if (accounts.includes(address)) {\n return keyring\n }\n }\n throw new Error(this.t('no_keyring_found_for_the_requested_account'))\n }\n\n /**\n * Display For Keyring\n *\n * Is used for adding the current keyrings to the state object.\n * @param {Keyring} keyring\n * @returns {Promise<Object>} A keyring display object, with type and accounts properties.\n */\n displayForKeyring = async (\n keyring: Keyring,\n addressType: AddressType,\n index: number\n ): Promise<DisplayedKeyring> => {\n const accounts = await keyring.getAccounts()\n const all_accounts: { pubkey: string; brandName: string }[] = []\n for (let i = 0; i < accounts.length; i++) {\n const pubkey = accounts[i]\n if (!pubkey) continue\n all_accounts.push({\n pubkey,\n brandName: keyring.type,\n })\n }\n return {\n type: keyring.type,\n accounts: all_accounts,\n keyring: new DisplayKeyring(keyring),\n addressType,\n index,\n }\n }\n\n getAllDisplayedKeyrings = async (resetCache?: boolean): Promise<DisplayedKeyring[]> => {\n if (resetCache || !this.cachedDisplayedKeyring) {\n this.cachedDisplayedKeyring = await Promise.all(\n this.keyrings.map((keyring, index) =>\n this.displayForKeyring(keyring, this.addressTypes[index]!, index)\n )\n )\n }\n return this.cachedDisplayedKeyring\n }\n\n getAllVisibleAccountsArray = async () => {\n const typedAccounts = await this.getAllDisplayedKeyrings()\n const result: { pubkey: string; type: string; brandName: string }[] = []\n typedAccounts.forEach(accountGroup => {\n result.push(\n ...accountGroup.accounts.map(account => ({\n pubkey: account.pubkey,\n brandName: account.brandName,\n type: accountGroup.type,\n }))\n )\n })\n\n return result\n }\n\n getAllPubkeys = async () => {\n const keyrings = await this.getAllDisplayedKeyrings()\n const result: { pubkey: string; type: string; brandName: string }[] = []\n keyrings.forEach(accountGroup => {\n result.push(\n ...accountGroup.accounts.map(account => ({\n pubkey: account.pubkey,\n brandName: account.brandName,\n type: accountGroup.type,\n }))\n )\n })\n\n return result\n }\n\n hasPubkey = async (pubkey: string) => {\n const addresses = await this.getAllPubkeys()\n return !!addresses.find(item => item.pubkey === pubkey)\n }\n\n /**\n * Clear Keyrings\n *\n * Deallocates all currently managed keyrings and accounts.\n * Used before initializing a new vault.\n */\n /* eslint-disable require-await */\n clearKeyrings = async (): Promise<void> => {\n // clear keyrings from memory\n\n this.keyrings = []\n this.addressTypes = []\n this.cachedDisplayedKeyring = null\n\n this.memStore.updateState({\n keyrings: [],\n })\n }\n\n /**\n * Update Memstore Keyrings\n *\n * Updates the in-memory keyrings, without persisting.\n */\n _updateMemStoreKeyrings = async (): Promise<void> => {\n const keyrings = await Promise.all(\n this.keyrings.map((keyring, index) =>\n this.displayForKeyring(keyring, this.addressTypes[index] || AddressType.P2WPKH, index)\n )\n )\n return this.memStore.updateState({ keyrings })\n }\n\n /**\n * Unlock Keyrings\n *\n * Unlocks the keyrings.\n *\n * @emits KeyringController#unlock\n */\n setUnlocked = () => {\n this.memStore.updateState({ isUnlocked: true })\n this.emit('unlock')\n if (this.eventBus) {\n this.eventBus.emit(EVENTS.broadcastToUI, {\n method: 'unlock',\n params: {},\n })\n }\n }\n}\n","import { bitcoin } from '@unisat/wallet-bitcoin'\nimport { CosmosSignDataType } from './cosmos'\n\n// ToSignInput interface defined here to avoid circular imports\nexport interface ToSignInput {\n index: number\n publicKey: string\n useTweakedSigner?: boolean\n disableTweakSigner?: boolean\n tapLeafHashToSign?: Buffer\n sighashTypes?: number[] | undefined\n}\n\nexport interface Keyring {\n type: string\n mfp?: string\n accounts?: string[]\n\n serialize(): Promise<any>\n deserialize(opts: any): Promise<void>\n addAccounts(n: number): Promise<string[]>\n getAccounts(): Promise<string[]>\n signTransaction(psbt: bitcoin.Psbt, inputs: ToSignInput[]): Promise<bitcoin.Psbt>\n signMessage(address: string, message: string): Promise<string>\n signData(address: string, data: string, type: string): Promise<string>\n verifyMessage(address: string, message: string, sig: string): Promise<boolean>\n exportAccount(address: string): Promise<string>\n removeAccount(address: string): void\n\n // Optional methods for different keyring types\n unlock?(): Promise<void>\n getFirstPage?(): Promise<{ address: string; index: number }[]>\n getNextPage?(): Promise<{ address: string; index: number }[]>\n getPreviousPage?(): Promise<{ address: string; index: number }[]>\n getAddresses?(start: number, end: number): { address: string; index: number }[]\n getIndexByAddress?(address: string): number\n\n getAccountsWithBrand?(): { address: string; index: number }[]\n activeAccounts?(indexes: number[]): string[]\n\n changeHdPath?(hdPath: string): void\n getAccountByHdPath?(hdPath: string, index: number): string\n\n // Keystone specific methods\n genSignPsbtUr?(psbtHex: string): Promise<{ type: string; cbor: string }>\n parseSignPsbtUr?(type: string, cbor: string): Promise<string>\n genSignMsgUr?(\n publicKey: string,\n text: string\n ): Promise<{ type: string; cbor: string; requestId: string }>\n parseSignMsgUr?(\n type: string,\n cbor: string\n ): Promise<{ requestId: string; publicKey: string; signature: string }>\n getConnectionType?(): 'USB' | 'QR'\n genSignCosmosUr?(cosmosSignRequest: {\n requestId?: string\n signData: string\n dataType: CosmosSignDataType\n path: string\n chainId?: string\n accountNumber?: string\n address?: string\n }): Promise<{ type: string; cbor: string; requestId: string }>\n parseSignCosmosUr?(type: string, cbor: string): Promise<any>\n}\n\n// Keyring types\nexport enum KeyringType {\n HdKeyring = 'HD Key Tree',\n SimpleKeyring = 'Simple Key Pair',\n KeystoneKeyring = 'Keystone',\n ColdWalletKeyring = 'Cold Wallet',\n ReadonlyKeyring = 'Readonly',\n Empty = 'Empty',\n}\n","// Cosmos signing types\nexport enum CosmosSignDataType {\n amino = 'amino',\n direct = 'direct',\n}\nexport interface CosmosSignRequest {\n signData: string\n dataType: CosmosSignDataType\n path: string\n extra: {\n chainId?: string\n accountNumber?: string\n address?: string\n }\n}\n","export enum KeystoneSignEnum {\n MSG = 'msg',\n PSBT = 'psbt',\n BIP322_SIMPLE = 'bip322-simple',\n COSMOS_DIRECT = 'cosmos-direct',\n COSMOS_ARBITRARY = 'cosmos-arbitrary',\n}\n","import { Encryptor } from '../types'\n\n// @ts-ignore\nimport * as browserPassworder from 'browser-passworder'\n\n// Production-ready encryptor using browser-passworder\nexport class BrowserPassworderEncryptor implements Encryptor {\n async encrypt(password: string, data: any): Promise<string> {\n return await browserPassworder.encrypt(password, data)\n }\n\n async decrypt(password: string, encryptedData: string): Promise<any> {\n return await browserPassworder.decrypt(password, encryptedData)\n }\n}\n","import { isTaprootInput } from 'bitcoinjs-lib/src/psbt/bip371.js'\nimport { decode } from 'bs58check'\nimport { EventEmitter } from 'events'\nimport {\n ECPair,\n ECPairInterface,\n bitcoin,\n signMessageOfDeterministicECDSA,\n verifyMessageOfECDSA,\n tweakSigner,\n} from '@unisat/wallet-bitcoin'\nimport { ToSignInput } from '../types'\n\nconst type = 'Simple Key Pair'\n\nexport class SimpleKeyring extends EventEmitter {\n static type = type\n type = type\n network: bitcoin.Network = bitcoin.networks.bitcoin\n wallets: ECPairInterface[] = []\n\n constructor(opts?: any) {\n super()\n if (opts) {\n this.deserialize(opts)\n }\n }\n\n async serialize(): Promise<any> {\n return this.wallets.map(wallet => wallet.privateKey?.toString('hex'))\n }\n\n async deserialize(opts: any) {\n const privateKeys = opts as string[]\n\n this.wallets = privateKeys.map(key => {\n let buf: Buffer\n if (key.length === 64) {\n // privateKey\n buf = Buffer.from(key, 'hex')\n } else {\n // base58\n buf = Buffer.from(decode(key).slice(1, 33))\n }\n\n return ECPair.fromPrivateKey(buf as any)\n })\n }\n\n async addAccounts(n = 1) {\n const newWallets: ECPairInterface[] = []\n for (let i = 0; i < n; i++) {\n newWallets.push(ECPair.makeRandom())\n }\n this.wallets = this.wallets.concat(newWallets)\n const hexWallets = newWallets.map(({ publicKey }) => publicKey.toString('hex'))\n return hexWallets\n }\n\n async getAccounts() {\n return this.wallets.map(({ publicKey }) => publicKey.toString('hex'))\n }\n\n async signTransaction(psbt: bitcoin.Psbt, inputs: ToSignInput[], opts?: any) {\n inputs.forEach(input => {\n const keyPair = this._getPrivateKeyFor(input.publicKey)\n if (isTaprootInput(psbt.data.inputs[input.index] as any)) {\n let signer: bitcoin.Signer = keyPair\n let tweak = true // default to use tweaked signer\n if (typeof input.useTweakedSigner === 'boolean') {\n tweak = input.useTweakedSigner\n } else if (typeof input.disableTweakSigner === 'boolean') {\n tweak = !input.disableTweakSigner\n }\n\n if (tweak) {\n signer = tweakSigner(keyPair, opts)\n }\n psbt.signTaprootInput(\n input.index,\n signer,\n input.tapLeafHashToSign as any,\n input.sighashTypes\n )\n } else {\n let signer: bitcoin.Signer = keyPair\n let tweak = false // default not to use tweaked signer\n if (typeof input.useTweakedSigner === 'boolean') {\n tweak = input.useTweakedSigner\n }\n if (tweak) {\n signer = tweakSigner(keyPair, opts)\n }\n psbt.signInput(input.index, signer, input.sighashTypes)\n }\n })\n return psbt\n }\n\n async signMessage(publicKey: string, text: string) {\n const keyPair = this._getPrivateKeyFor(publicKey)\n return signMessageOfDeterministicECDSA(keyPair, text)\n }\n\n async verifyMessage(publicKey: string, text: string, sig: string) {\n return verifyMessageOfECDSA(publicKey, text, sig)\n }\n\n // Sign any content, but note that the content signed by this method is unreadable, so use it with caution.\n async signData(publicKey: string, data: string, type: 'ecdsa' | 'schnorr' = 'ecdsa') {\n const keyPair = this._getPrivateKeyFor(publicKey)\n if (type === 'ecdsa') {\n return keyPair.sign(Buffer.from(data, 'hex')).toString('hex')\n } else if (type === 'schnorr') {\n return keyPair.signSchnorr(Buffer.from(data, 'hex')).toString('hex')\n } else {\n throw new Error('Not support type')\n }\n }\n\n private _getPrivateKeyFor(publicKey: string) {\n if (!publicKey) {\n throw new Error('Must specify publicKey.')\n }\n const wallet = this._getWalletForAccount(publicKey)\n return wallet\n }\n\n async exportAccount(publicKey: string) {\n const wallet = this._getWalletForAccount(publicKey)\n return wallet.privateKey?.toString('hex')\n }\n\n removeAccount(publicKey: string) {\n if (!this.wallets.map(wallet => wallet.publicKey.toString('hex')).includes(publicKey)) {\n throw new Error(`PublicKey ${publicKey} not found in this keyring`)\n }\n\n this.wallets = this.wallets.filter(wallet => wallet.publicKey.toString('hex') !== publicKey)\n }\n\n private _getWalletForAccount(publicKey: string) {\n let wallet = this.wallets.find(wallet => wallet.publicKey.toString('hex') == publicKey)\n if (!wallet) {\n throw new Error('Simple Keyring - Unable to find matching publicKey.')\n }\n return wallet\n }\n}\n","import * as bip39 from 'bip39'\n//@ts-ignore\nimport bitcore from 'bitcore-lib'\n//@ts-ignore\nimport * as hdkey from 'hdkey'\nimport { ECPair, ECPairInterface, bitcoin } from '@unisat/wallet-bitcoin'\nimport { SimpleKeyring } from './simple-keyring'\n\nconst hdPathString = \"m/44'/0'/0'/0\"\nconst type = 'HD Key Tree'\n\ninterface DeserializeOption {\n hdPath?: string\n mnemonic?: string\n xpriv?: string\n activeIndexes?: number[]\n passphrase?: string\n}\n\nexport class HdKeyring extends SimpleKeyring {\n static override type = type\n\n override type = type\n mnemonic: string = ''\n xpriv: string = ''\n passphrase: string = ''\n override network: bitcoin.Network = bitcoin.networks.bitcoin\n\n hdPath = hdPathString\n root: bitcore.HDPrivateKey = null\n hdWallet?: any\n override wallets: ECPairInterface[] = []\n private _index2wallet: Record<number, [string, ECPairInterface]> = {}\n activeIndexes: number[] = []\n page = 0\n perPage = 5\n\n constructor(opts?: DeserializeOption) {\n super(null)\n if (opts) {\n this.deserialize(opts)\n }\n }\n\n override async serialize(): Promise<DeserializeOption> {\n return {\n mnemonic: this.mnemonic,\n xpriv: this.xpriv,\n activeIndexes: this.activeIndexes,\n hdPath: this.hdPath,\n passphrase: this.passphrase,\n }\n }\n\n override async deserialize(_opts: DeserializeOption = {}) {\n if (this.root) {\n throw new Error('Btc-Hd-Keyring: Secret recovery phrase already provided')\n }\n let opts = _opts as DeserializeOption\n this.wallets = []\n this.mnemonic = ''\n this.xpriv = ''\n this.root = null\n this.hdPath = opts.hdPath || hdPathString\n if (opts.passphrase) {\n this.passphrase = opts.passphrase\n }\n\n if (opts.mnemonic) {\n this.initFromMnemonic(opts.mnemonic)\n } else if (opts.xpriv) {\n this.initFromXpriv(opts.xpriv)\n }\n\n if (opts.activeIndexes) {\n this.activeAccounts(opts.activeIndexes)\n }\n }\n\n initFromXpriv(xpriv: string) {\n if (this.root) {\n throw new Error('Btc-Hd-Keyring: Secret recovery phrase already provided')\n }\n\n this.xpriv = xpriv\n this._index2wallet = {}\n\n this.hdWallet = hdkey.fromJSON({ xpriv })\n this.root = this.hdWallet\n }\n\n initFromMnemonic(mnemonic: string) {\n if (this.root) {\n throw new Error('Btc-Hd-Keyring: Secret recovery phrase already provided')\n }\n\n this.mnemonic = mnemonic\n this._index2wallet = {}\n\n const seed = bip39.mnemonicToSeedSync(mnemonic, this.passphrase)\n this.hdWallet = hdkey.fromMasterSeed(seed)\n this.root = this.hdWallet.derive(this.hdPath)\n }\n\n changeHdPath(hdPath: string) {\n if (!this.mnemonic) {\n throw new Error('Btc-Hd-Keyring: Not support')\n }\n\n this.hdPath = hdPath\n\n this.root = this.hdWallet.derive(this.hdPath)\n\n const indexes = this.activeIndexes\n this._index2wallet = {}\n this.activeIndexes = []\n this.wallets = []\n this.activeAccounts(indexes)\n }\n\n getAccountByHdPath(hdPath: string, index: number) {\n if (!this.mnemonic) {\n throw new Error('Btc-Hd-Keyring: Not support')\n }\n const root = this.hdWallet.derive(hdPath)\n const child = root.deriveChild(index)\n const ecpair = ECPair.fromPrivateKey(child.privateKey, {\n network: this.network,\n })\n const address = ecpair.publicKey.toString('hex')\n return address\n }\n\n override addAccounts(numberOfAccounts = 1) {\n let count = numberOfAccounts\n let currentIdx = 0\n const newWallets: ECPairInterface[] = []\n\n while (count) {\n const [, wallet] = this._addressFromIndex(currentIdx)\n if (this.wallets.includes(wallet)) {\n currentIdx++\n } else {\n this.wallets.push(wallet)\n newWallets.push(wallet)\n this.activeIndexes.push(currentIdx)\n count--\n }\n }\n\n const hexWallets = newWallets.map(w => {\n return w.publicKey.toString('hex')\n })\n\n return Promise.resolve(hexWallets)\n }\n\n activeAccounts(indexes: number[]) {\n const accounts: string[] = []\n for (const index of indexes) {\n const [address, wallet] = this._addressFromIndex(index)\n this.wallets.push(wallet)\n this.activeIndexes.push(index)\n\n accounts.push(address)\n }\n\n return accounts\n }\n\n getFirstPage() {\n this.page = 0\n return this.__getPage(1)\n }\n\n getNextPage() {\n return this.__getPage(1)\n }\n\n getPreviousPage() {\n return this.__getPage(-1)\n }\n\n getAddresses(start: number, end: number) {\n const from = start\n const to = end\n const accounts: { address: string; index: number }[] = []\n for (let i = from; i < to; i++) {\n const [address] = this._addressFromIndex(i)\n accounts.push({\n address,\n index: i + 1,\n })\n }\n return accounts\n }\n\n async __getPage(increment: number) {\n this.page += increment\n\n if (!this.page || this.page <= 0) {\n this.page = 1\n }\n\n const from = (this.page - 1) * this.perPage\n const to = from + this.perPage\n\n const accounts: { address: string; index: number }[] = []\n\n for (let i = from; i < to; i++) {\n const [address] = this._addressFromIndex(i)\n accounts.push({\n address,\n index: i + 1,\n })\n }\n\n return accounts\n }\n\n override async getAccounts() {\n return this.wallets.map(w => {\n return w.publicKey.toString('hex')\n })\n }\n\n getIndexByAddress(address: string) {\n for (const key in this._index2wallet) {\n if (this._index2wallet[key]?.[0] === address) {\n return Number(key)\n }\n }\n return null\n }\n\n private _addressFromIndex(i: number) {\n if (!this._index2wallet[i]) {\n const child = this.root.deriveChild(i)\n const ecpair = ECPair.fromPrivateKey(child.privateKey, {\n network: this.network,\n })\n const address = ecpair.publicKey.toString('hex')\n this._index2wallet[i] = [address, ecpair]\n }\n\n return this._index2wallet[i] as [string, ECPairInterface]\n }\n}\n","import { KeystoneSDK, KeystoneBitcoinSDK, UR } from '@keystonehq/keystone-sdk'\nimport { uuid } from '@keystonehq/keystone-sdk/dist/utils/stringHelper.js'\nimport { Psbt } from 'bitcoinjs-lib'\n//@ts-ignore\nimport bitcore from 'bitcore-lib'\nimport { EventEmitter } from 'events'\nimport { verifyMessageOfECDSA } from '@unisat/wallet-bitcoin'\n\ninterface KeystoneKey {\n path: string\n extendedPublicKey: string\n}\n\ninterface DeserializeOption {\n mfp: string\n keys: KeystoneKey[]\n hdPath?: string\n activeIndexes?: number[]\n connectionType?: 'USB' | 'QR'\n}\n\ninterface Wallet {\n index: number\n publicKey: string\n path: string\n}\n\nconst type = 'Keystone'\n\nconst DEFAULT_CONNECTION_TYPE = 'QR'\n\nexport class KeystoneKeyring extends EventEmitter {\n static type = type\n type = type\n mfp = ''\n keys: KeystoneKey[] = []\n hdPath?: string\n activeIndexes: number[] = []\n root: bitcore.HDPublicKey = null\n connectionType?: 'USB' | 'QR' = 'QR'\n\n page = 0\n perPage = 5\n\n origin = 'UniSat Wallet'\n\n constructor(opts?: DeserializeOption) {\n super()\n if (opts) {\n this.deserialize(opts)\n }\n }\n\n async initFromUR(type: string, cbor: string, connectionType?: 'USB' | 'QR') {\n const keystoneSDK = new KeystoneSDK({\n origin: this.origin,\n })\n const account = keystoneSDK.parseAccount(new UR(Buffer.from(cbor, 'hex'), type))\n await this.deserialize({\n mfp: account.masterFingerprint,\n keys: account.keys.map(k => ({\n path: k.path,\n extendedPublicKey: k.extendedPublicKey || '',\n })),\n connectionType: connectionType ?? DEFAULT_CONNECTION_TYPE,\n })\n }\n\n getHardenedPath(hdPath: string) {\n const paths = hdPath.split('/')\n return paths.slice(0, 4).join('/')\n }\n\n getHDPublicKey(hdPath: string) {\n const path = this.getHardenedPath(hdPath)\n const key = this.keys.find(v => v.path === path)\n if (!key) {\n throw new Error('Invalid path')\n }\n return new bitcore.HDPublicKey(key.extendedPublicKey)\n }\n\n getDefaultHdPath() {\n return \"m/44'/0'/0'/0\"\n }\n\n getConnectionType() {\n return this.connectionType ?? DEFAULT_CONNECTION_TYPE\n }\n\n initRoot() {\n this.root = this.getHDPublicKey(this.hdPath ?? this.getDefaultHdPath())\n }\n\n async deserialize(opts: DeserializeOption) {\n this.mfp = opts.mfp\n this.keys = opts.keys\n this.hdPath = opts.hdPath ?? this.getDefaultHdPath()\n this.activeIndexes = opts.activeIndexes ? [...opts.activeIndexes] : []\n this.connectionType = opts.connectionType ?? DEFAULT_CONNECTION_TYPE\n this.initRoot()\n if (\n opts.hdPath !== null &&\n opts.hdPath !== undefined &&\n opts.hdPath.length >= 13 &&\n opts.hdPath[opts.hdPath.length - 1] === '1'\n ) {\n this.root = this.root.derive(`m/1`)\n }\n }\n\n async serialize(): Promise<DeserializeOption> {\n return {\n mfp: this.mfp,\n keys: this.keys,\n hdPath: this.hdPath || '',\n activeIndexes: this.activeIndexes,\n connectionType: this.connectionType || 'QR',\n }\n }\n\n async addAccounts(numberOfAccounts = 1) {\n let count = numberOfAccounts\n let i = 0\n const pubkeys = []\n\n while (count) {\n if (this.activeIndexes.includes(i)) {\n i++\n } else {\n const w = this.getWalletByIndex(i)\n pubkeys.push(w.publicKey)\n this.activeIndexes.push(i)\n count--\n }\n }\n\n return Promise.resolve(pubkeys)\n }\n\n async addChangeAddressAccounts(numberOfAccounts = 1) {\n let count = numberOfAccounts\n let i = 0\n const pubkeys = []\n\n while (count) {\n if (this.activeIndexes.includes(i)) {\n i++\n } else {\n const w = this.getChangeAddressWalletByIndex(i)\n pubkeys.push(w.publicKey)\n this.activeIndexes.push(i)\n count--\n }\n }\n\n return Promise.resolve(pubkeys)\n }\n\n async getAccounts() {\n if (\n this.hdPath !== null &&\n this.hdPath !== undefined &&\n this.hdPath.length >= 13 &&\n this.hdPath[this.hdPath.length - 1] === '1'\n ) {\n return this.activeIndexes.map(index => {\n const child = this.root.derive(`m/${index}`)\n return child.publicKey.toString('hex')\n })\n }\n return this.activeIndexes.map(i => this.getWalletByIndex(i).publicKey)\n }\n\n async getAccounts2() {\n return this.activeIndexes.map(index => {\n const child = th