]> git.codecow.com Git - libnemo.git/commitdiff
Override account derivation for Ledger wallets.
authorChris Duncan <chris@zoso.dev>
Mon, 4 Aug 2025 23:06:11 +0000 (16:06 -0700)
committerChris Duncan <chris@zoso.dev>
Mon, 4 Aug 2025 23:06:11 +0000 (16:06 -0700)
src/lib/ledger.ts

index af8b91181a6ba352f7ddfbd95234a413b610522b..40aa05c1811ca4cab93bf4379808de0ac65614a8 100644 (file)
@@ -5,6 +5,7 @@ import { ledgerUSBVendorId } from '@ledgerhq/devices'
 import { default as TransportBLE } from '@ledgerhq/hw-transport-web-ble'\r
 import { default as TransportUSB } from '@ledgerhq/hw-transport-webusb'\r
 import { default as TransportHID } from '@ledgerhq/hw-transport-webhid'\r
+import { Account, AccountList } from './account'\r
 import { ChangeBlock, ReceiveBlock, SendBlock } from './block'\r
 import { BIP44_COIN_NANO, BIP44_PURPOSE, HARDENED_OFFSET, LEDGER_ADPU_CODES, LEDGER_STATUS_CODES } from './constants'\r
 import { bytes, dec, hex } from './convert'\r
@@ -78,6 +79,7 @@ export class Ledger extends Wallet {
                return Ledger.create()\r
        }\r
 \r
+       #accounts: AccountList\r
        #status: DeviceStatus = 'DISCONNECTED'\r
        get status (): DeviceStatus { return this.#status }\r
 \r
@@ -87,6 +89,74 @@ export class Ledger extends Wallet {
                }\r
                Ledger.#isInternal = false\r
                super('Ledger')\r
+               this.#accounts = new AccountList()\r
+       }\r
+\r
+       /**\r
+       * Gets the public key for an account from the Ledger device.\r
+       *\r
+       * @param {number[]} indexes - Indexes of the accounts\r
+       * @returns {Promise<Account>}\r
+       */\r
+       async account (index: number): Promise<Account> {\r
+               const { status, publicKey } = await Ledger.#account(index)\r
+               if (status !== 'OK' || publicKey == null) {\r
+                       throw new Error(`Error getting Ledger account: ${status}`)\r
+               }\r
+               return Account.import({ index, publicKey })\r
+       }\r
+\r
+       /**\r
+       * Retrieves accounts from a wallet using its child key derivation function.\r
+       * Defaults to the first account at index 0.\r
+       *\r
+       * The returned object will have keys corresponding with the requested range\r
+       * of account indexes. The value of each key will be the Account derived for\r
+       * that index in the wallet.\r
+       *\r
+       * ```\r
+       * console.log(await wallet.accounts(5))\r
+       * // outputs sixth account of the wallet\r
+       * // {\r
+       * //   5: {\r
+       * //     privateKey: <...>,\r
+       * //     index: 5\r
+       * //   }\r
+       * // }\r
+       * ```\r
+       *\r
+       * @param {number} from - Start index of secret keys. Default: 0\r
+       * @param {number} to - End index of secret keys. Default: `from`\r
+       * @returns {AccountList} Object with keys of account indexes and values of the corresponding Accounts\r
+       */\r
+       async accounts (from: number = 0, to: number = from): Promise<AccountList> {\r
+               if (from > to) {\r
+                       const swap = from\r
+                       from = to\r
+                       to = swap\r
+               }\r
+               const output = new AccountList()\r
+               const indexes: number[] = []\r
+               for (let i = from; i <= to; i++) {\r
+                       if (this.#accounts[i] == null) {\r
+                               indexes.push(i)\r
+                       } else {\r
+                               output[i] = this.#accounts[i]\r
+                       }\r
+               }\r
+               if (indexes.length > 0) {\r
+                       const publicAccounts = []\r
+                       for (const index of indexes) {\r
+                               publicAccounts.push(await this.account(index))\r
+                       }\r
+                       for (const a of publicAccounts) {\r
+                               if (a.index == null) {\r
+                                       throw new RangeError('Index missing for Account')\r
+                               }\r
+                               output[a.index] = this.#accounts[a.index] = a\r
+                       }\r
+               }\r
+               return output\r
        }\r
 \r
        /**\r
@@ -539,23 +609,4 @@ export class Ledger extends Wallet {
 \r
                return { status, name, version }\r
        }\r
-\r
-       /**\r
-       * Gets the public key for an account from the Ledger device.\r
-       *\r
-       * @param {number[]} indexes - Indexes of the accounts\r
-       * @returns {Promise<Account>}\r
-       */\r
-       async ckd (indexes: number[]): Promise<KeyPair[]> {\r
-               const results: KeyPair[] = []\r
-               for (const index of indexes) {\r
-                       const { status, publicKey } = await Ledger.#account(index)\r
-                       if (status === 'OK' && publicKey != null) {\r
-                               results.push({ publicKey, index })\r
-                       } else {\r
-                               throw new Error(`Error getting Ledger account: ${status}`)\r
-                       }\r
-               }\r
-               return results\r
-       }\r
 }\r