@metamask/eth-ledger-bridge-keyring 12.0.3 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [12.1.0]
11
+
12
+ ### Added
13
+
14
+ - Expose device-management pass-throughs on the V2 `LedgerKeyring` wrapper: `hdPath` (getter), `bridge` (getter), `getDeviceId`, `setDeviceId`, `setHdPath`, `getFirstPage`, `getNextPage`, `getPreviousPage`, `forgetDevice`, `isUnlocked`, `attemptMakeApp`, `getAppNameAndVersion`. `forgetDevice` additionally clears the V2 account registry to keep it in sync with the inner keyring. ([#551](https://github.com/MetaMask/accounts/pull/551))
15
+
10
16
  ## [12.0.3]
11
17
 
12
18
  ### Changed
@@ -434,7 +440,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
434
440
 
435
441
  - Support new versions of ethereumjs/tx ([#68](https://github.com/MetaMask/eth-ledger-bridge-keyring/pull/68))
436
442
 
437
- [Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-ledger-bridge-keyring@12.0.3...HEAD
443
+ [Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-ledger-bridge-keyring@12.1.0...HEAD
444
+ [12.1.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-ledger-bridge-keyring@12.0.3...@metamask/eth-ledger-bridge-keyring@12.1.0
438
445
  [12.0.3]: https://github.com/MetaMask/accounts/compare/@metamask/eth-ledger-bridge-keyring@12.0.2...@metamask/eth-ledger-bridge-keyring@12.0.3
439
446
  [12.0.2]: https://github.com/MetaMask/accounts/compare/@metamask/eth-ledger-bridge-keyring@12.0.1...@metamask/eth-ledger-bridge-keyring@12.0.2
440
447
  [12.0.1]: https://github.com/MetaMask/accounts/compare/@metamask/eth-ledger-bridge-keyring@12.0.0...@metamask/eth-ledger-bridge-keyring@12.0.1
@@ -137,6 +137,96 @@ class LedgerKeyring extends v2_2.EthKeyringWrapper {
137
137
  this.registry.delete(accountId);
138
138
  });
139
139
  }
140
+ /**
141
+ * @returns The current derivation path used by the inner keyring.
142
+ */
143
+ get hdPath() {
144
+ return this.inner.hdPath;
145
+ }
146
+ /**
147
+ * @returns The bridge instance used by the inner keyring to communicate
148
+ * with the device.
149
+ */
150
+ get bridge() {
151
+ return this.inner.bridge;
152
+ }
153
+ /**
154
+ * @returns The device ID for the paired Ledger device.
155
+ */
156
+ getDeviceId() {
157
+ return this.inner.getDeviceId();
158
+ }
159
+ /**
160
+ * Set the device ID for the paired Ledger device.
161
+ *
162
+ * @param deviceId - The device ID to set.
163
+ */
164
+ setDeviceId(deviceId) {
165
+ this.inner.setDeviceId(deviceId);
166
+ }
167
+ /**
168
+ * Set the derivation path on the inner keyring.
169
+ *
170
+ * @param hdPath - The derivation path to set.
171
+ */
172
+ setHdPath(hdPath) {
173
+ this.inner.setHdPath(hdPath);
174
+ }
175
+ /**
176
+ * Fetch the first page of candidate addresses from the device.
177
+ *
178
+ * @returns The first page of accounts.
179
+ */
180
+ async getFirstPage() {
181
+ return this.inner.getFirstPage();
182
+ }
183
+ /**
184
+ * Fetch the next page of candidate addresses from the device.
185
+ *
186
+ * @returns The next page of accounts.
187
+ */
188
+ async getNextPage() {
189
+ return this.inner.getNextPage();
190
+ }
191
+ /**
192
+ * Fetch the previous page of candidate addresses from the device.
193
+ *
194
+ * @returns The previous page of accounts.
195
+ */
196
+ async getPreviousPage() {
197
+ return this.inner.getPreviousPage();
198
+ }
199
+ /**
200
+ * Clear the inner keyring's device-pairing state and accounts, and reset
201
+ * the V2 account registry to keep them in sync.
202
+ */
203
+ async forgetDevice() {
204
+ await this.withLock(async () => {
205
+ this.inner.forgetDevice();
206
+ this.registry.clear();
207
+ });
208
+ }
209
+ /**
210
+ * @returns Whether the inner keyring has an unlocked HD key.
211
+ */
212
+ isUnlocked() {
213
+ return this.inner.isUnlocked();
214
+ }
215
+ /**
216
+ * Attempt to open the Ethereum app on the connected Ledger device.
217
+ *
218
+ * @returns Whether the app was opened.
219
+ */
220
+ async attemptMakeApp() {
221
+ return this.inner.attemptMakeApp();
222
+ }
223
+ /**
224
+ * @returns The app name and version currently running on the connected
225
+ * Ledger device.
226
+ */
227
+ async getAppNameAndVersion() {
228
+ return this.inner.getAppNameAndVersion();
229
+ }
140
230
  }
141
231
  exports.LedgerKeyring = LedgerKeyring;
142
232
  _LedgerKeyring_instances = new WeakSet(), _LedgerKeyring_getChecksumHexAddress = function _LedgerKeyring_getChecksumHexAddress(address) {
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-keyring.cjs","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Bip44Account } from '@metamask/account-api';\nimport {\n EthAccountType,\n EthMethod,\n EthScope,\n KeyringAccountEntropyTypeOption,\n} from '@metamask/keyring-api';\nimport type { KeyringAccount, EntropySourceId } from '@metamask/keyring-api';\nimport { KeyringType } from '@metamask/keyring-api/v2';\nimport type {\n CreateAccountOptions,\n KeyringCapabilities,\n Keyring,\n} from '@metamask/keyring-api/v2';\nimport { EthKeyringWrapper } from '@metamask/keyring-sdk/v2';\nimport type { AccountId, EthKeyring } from '@metamask/keyring-utils';\nimport { add0x, getChecksumAddress } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { LedgerKeyring as LegacyLedgerKeyring } from '../ledger-keyring';\n\n/**\n * Methods supported by Ledger keyring EOA accounts.\n * Ledger keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).\n */\nconst LEDGER_KEYRING_METHODS = [\n EthMethod.SignTransaction,\n EthMethod.PersonalSign,\n EthMethod.SignTypedDataV4,\n];\n\nconst ledgerKeyringCapabilities: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n derivePath: true,\n },\n};\n\n/**\n * Ledger Live HD path constant.\n */\nconst LEDGER_LIVE_HD_PATH = `m/44'/60'/0'/0/0`;\n\n/**\n * BIP-44 standard HD path prefix constant for Ethereum.\n */\nconst BIP44_HD_PATH_PREFIX = `m/44'/60'/0'/0`;\n\n/**\n * Regex pattern for validating and parsing Ledger Live derivation paths.\n * Format: m/44'/60'/{index}'/0/0\n */\nconst LEDGER_LIVE_PATH_PATTERN = /^m\\/44'\\/60'\\/(\\d+)'\\/0\\/0$/u;\n\n/**\n * Regex pattern for validating and parsing non-Ledger-Live derivation paths.\n * Supports Legacy (m/44'/60'/0'/{index}), BIP44 (m/44'/60'/0'/0/{index}),\n * and custom paths that follow the m/44'/60'/... pattern.\n */\nconst INDEX_AT_END_PATH_PATTERN = /^(m\\/44'\\/60'(?:\\/\\d+'?)*)\\/(\\d+)$/u;\n\n/**\n * Concrete {@link Keyring} adapter for {@link LedgerKeyring}.\n *\n * This wrapper exposes the accounts and signing capabilities of the legacy\n * Ledger keyring via the unified V2 interface.\n *\n * All Ledger keyring accounts are BIP-44 derived from the device.\n */\nexport type LedgerKeyringOptions = {\n legacyKeyring: LegacyLedgerKeyring;\n entropySource: EntropySourceId;\n};\n\n// LegacyLedgerKeyring.signTransaction returns `TypedTransaction | OldEthJsTransaction` for\n// backwards compatibility with old ethereumjs-tx, but EthKeyring expects `TypedTxData`.\n// The runtime behavior is correct - we cast the type to satisfy the constraint.\ntype LedgerKeyringAsEthKeyring = LegacyLedgerKeyring & EthKeyring;\n\nexport class LedgerKeyring\n extends EthKeyringWrapper<\n LedgerKeyringAsEthKeyring,\n Bip44Account<KeyringAccount>\n >\n implements Keyring\n{\n readonly entropySource: EntropySourceId;\n\n constructor(options: LedgerKeyringOptions) {\n super({\n type: KeyringType.Ledger,\n inner: options.legacyKeyring as LedgerKeyringAsEthKeyring,\n capabilities: ledgerKeyringCapabilities,\n });\n this.entropySource = options.entropySource;\n }\n\n /**\n * Normalizes an address to a checksummed hex address.\n *\n * @param address - The address to normalize.\n * @returns The checksummed hex address.\n */\n #getChecksumHexAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n }\n\n /**\n * Parses a derivation path to extract the base HD path and account index.\n *\n * Supports two path formats:\n * - Ledger Live: m/44'/60'/{index}'/0/0 → base: m/44'/60'/0'/0/0, index from position 3\n * - Index at end: m/44'/60'/.../{index} → base: m/44'/60'/..., index from last segment\n *\n * @param derivationPath - The full derivation path.\n * @returns The base HD path and account index.\n * @throws If the path format is invalid.\n */\n #parseDerivationPath(derivationPath: string): {\n basePath: string;\n index: number;\n } {\n // Try Ledger Live format first: m/44'/60'/{index}'/0/0\n const ledgerLiveMatch = derivationPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (ledgerLiveMatch?.[1]) {\n return {\n // This constant is used by `inner.setHdPath` to determine which derivation\n // mode we should use (Ledger Live derivation mode here).\n basePath: LEDGER_LIVE_HD_PATH,\n index: parseInt(ledgerLiveMatch[1], 10),\n };\n }\n\n // Try index-at-end format: m/44'/60'/.../{index}\n const indexAtEndMatch = derivationPath.match(INDEX_AT_END_PATH_PATTERN);\n if (indexAtEndMatch) {\n // If the condition is true, indexAtEndMatch[1] and indexAtEndMatch[2] are defined, so\n // we can safely cast them to string.\n // This is necessary to get 100% code coverage.\n return {\n // Here, we use a derivation path prefix for `inner.setHdPath`\n // (prefix + index derivation mode).\n basePath: indexAtEndMatch[1] as string,\n index: parseInt(indexAtEndMatch[2] as string, 10),\n };\n }\n\n throw new Error(\n `Invalid derivation path format: ${derivationPath}. ` +\n `Expected Ledger Live (m/44'/60'/{index}'/0/0) or index-at-end (m/44'/60'/.../{index}) format.`,\n );\n }\n\n /**\n * Gets the index for an address from the account details.\n *\n * @param address - The address to get the index for.\n * @returns The index for the address.\n * @throws If the address is not found in account details.\n */\n #getIndexForAddress(address: Hex): number {\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details) {\n throw new Error(\n `Address ${checksummedAddress} not found in account details`,\n );\n }\n\n // Extract index from hdPath\n const { hdPath } = details;\n if (!hdPath) {\n throw new Error(`No HD path found for address ${checksummedAddress}`);\n }\n\n // Ledger supports multiple derivation path formats:\n // - Ledger Live (bip44: true): m/44'/60'/{index}'/0/0 - index at position 3\n // - Other paths (bip44: false): {hdPath}/{index} - index at end\n // - BIP44: m/44'/60'/0'/0/{index}\n // - Legacy: m/44'/60'/0'/{index}\n // - Custom paths via setHdPath\n //\n // We use the `bip44` flag to determine which extraction pattern to use.\n if (details.bip44) {\n // Ledger Live format: m/44'/60'/{index}'/0/0\n const match = hdPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (match?.[1]) {\n return parseInt(match[1], 10);\n }\n } else {\n // Index-at-end format: m/44'/60'/.../{index}\n const match = hdPath.match(INDEX_AT_END_PATH_PATTERN);\n if (match?.[2]) {\n return parseInt(match[2], 10);\n }\n }\n\n throw new Error(`Could not extract index from HD path: ${hdPath}`);\n }\n\n /**\n * Creates a Bip44Account object for the given address.\n *\n * @param address - The account address.\n * @param addressIndex - The account index in the derivation path.\n * @returns The created Bip44Account.\n */\n #createKeyringAccount(\n address: Hex,\n addressIndex: number,\n ): Bip44Account<KeyringAccount> {\n const id = this.registry.register(address);\n\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details?.hdPath) {\n throw new Error(\n `No HD path found for address ${checksummedAddress}. Cannot create account.`,\n );\n }\n\n const account: Bip44Account<KeyringAccount> = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...LEDGER_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Mnemonic,\n id: this.entropySource,\n groupIndex: addressIndex,\n derivationPath: details.hdPath,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n async getAccounts(): Promise<Bip44Account<KeyringAccount>[]> {\n const addresses = await this.inner.getAccounts();\n\n if (addresses.length === 0) {\n return [];\n }\n\n return addresses.map((address) => {\n // Check if we already have this account in the registry\n const existingId = this.registry.getAccountId(address);\n if (existingId) {\n const cached = this.registry.get(existingId);\n if (cached) {\n return cached;\n }\n }\n\n const addressIndex = this.#getIndexForAddress(address);\n return this.#createKeyringAccount(address, addressIndex);\n });\n }\n\n async createAccounts(\n options: CreateAccountOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n return this.withLock(async () => {\n if (\n options.type === 'bip44:derive-path' ||\n options.type === 'bip44:derive-index'\n ) {\n // Validate that the entropy source matches this keyring's entropy source\n if (options.entropySource !== this.entropySource) {\n throw new Error(\n `Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`,\n );\n }\n } else {\n throw new Error(\n `Unsupported account creation type for LedgerKeyring: ${String(\n options.type,\n )}`,\n );\n }\n\n // Check if an account at this index already exists with the same derivation path\n const currentAccounts = await this.getAccounts();\n\n let targetIndex: number;\n let basePath: string;\n let derivationPath: string;\n\n if (options.type === 'bip44:derive-path') {\n // Parse the derivation path to extract base path and index\n const parsed = this.#parseDerivationPath(options.derivationPath);\n targetIndex = parsed.index;\n basePath = parsed.basePath;\n\n derivationPath = options.derivationPath;\n } else {\n // derive-index uses BIP-44 standard path by default\n targetIndex = options.groupIndex;\n basePath = BIP44_HD_PATH_PREFIX;\n derivationPath = `${basePath}/${targetIndex}`;\n }\n\n const existingAccount = currentAccounts.find((account) => {\n return (\n account.options.entropy.groupIndex === targetIndex &&\n account.options.entropy.derivationPath === derivationPath\n );\n });\n\n if (existingAccount) {\n return [existingAccount];\n }\n\n // Derive the account at the specified index\n this.inner.setHdPath(basePath);\n this.inner.setAccountToUnlock(targetIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createKeyringAccount(newAddress, targetIndex);\n\n return [newAccount];\n });\n }\n\n /**\n * Delete an account from the keyring.\n *\n * @param accountId - The account ID to delete.\n */\n async deleteAccount(accountId: AccountId): Promise<void> {\n await this.withLock(async () => {\n const { address } = await this.getAccount(accountId);\n const hexAddress = this.toHexAddress(address);\n\n // Remove from the legacy keyring\n this.inner.removeAccount(hexAddress);\n\n // Remove from the registry\n this.registry.delete(accountId);\n });\n }\n}\n"]}
1
+ {"version":3,"file":"ledger-keyring.cjs","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Bip44Account } from '@metamask/account-api';\nimport {\n EthAccountType,\n EthMethod,\n EthScope,\n KeyringAccountEntropyTypeOption,\n} from '@metamask/keyring-api';\nimport type { KeyringAccount, EntropySourceId } from '@metamask/keyring-api';\nimport { KeyringType } from '@metamask/keyring-api/v2';\nimport type {\n CreateAccountOptions,\n KeyringCapabilities,\n Keyring,\n} from '@metamask/keyring-api/v2';\nimport { EthKeyringWrapper } from '@metamask/keyring-sdk/v2';\nimport type { AccountId, EthKeyring } from '@metamask/keyring-utils';\nimport { add0x, getChecksumAddress } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type {\n GetAppNameAndVersionResponse,\n LedgerBridge,\n LedgerBridgeOptions,\n} from '../ledger-bridge';\nimport type {\n AccountPage,\n LedgerKeyring as LegacyLedgerKeyring,\n} from '../ledger-keyring';\n\n/**\n * Methods supported by Ledger keyring EOA accounts.\n * Ledger keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).\n */\nconst LEDGER_KEYRING_METHODS = [\n EthMethod.SignTransaction,\n EthMethod.PersonalSign,\n EthMethod.SignTypedDataV4,\n];\n\nconst ledgerKeyringCapabilities: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n derivePath: true,\n },\n};\n\n/**\n * Ledger Live HD path constant.\n */\nconst LEDGER_LIVE_HD_PATH = `m/44'/60'/0'/0/0`;\n\n/**\n * BIP-44 standard HD path prefix constant for Ethereum.\n */\nconst BIP44_HD_PATH_PREFIX = `m/44'/60'/0'/0`;\n\n/**\n * Regex pattern for validating and parsing Ledger Live derivation paths.\n * Format: m/44'/60'/{index}'/0/0\n */\nconst LEDGER_LIVE_PATH_PATTERN = /^m\\/44'\\/60'\\/(\\d+)'\\/0\\/0$/u;\n\n/**\n * Regex pattern for validating and parsing non-Ledger-Live derivation paths.\n * Supports Legacy (m/44'/60'/0'/{index}), BIP44 (m/44'/60'/0'/0/{index}),\n * and custom paths that follow the m/44'/60'/... pattern.\n */\nconst INDEX_AT_END_PATH_PATTERN = /^(m\\/44'\\/60'(?:\\/\\d+'?)*)\\/(\\d+)$/u;\n\n/**\n * Concrete {@link Keyring} adapter for {@link LedgerKeyring}.\n *\n * This wrapper exposes the accounts and signing capabilities of the legacy\n * Ledger keyring via the unified V2 interface.\n *\n * All Ledger keyring accounts are BIP-44 derived from the device.\n */\nexport type LedgerKeyringOptions = {\n legacyKeyring: LegacyLedgerKeyring;\n entropySource: EntropySourceId;\n};\n\n// LegacyLedgerKeyring.signTransaction returns `TypedTransaction | OldEthJsTransaction` for\n// backwards compatibility with old ethereumjs-tx, but EthKeyring expects `TypedTxData`.\n// The runtime behavior is correct - we cast the type to satisfy the constraint.\ntype LedgerKeyringAsEthKeyring = LegacyLedgerKeyring & EthKeyring;\n\nexport class LedgerKeyring\n extends EthKeyringWrapper<\n LedgerKeyringAsEthKeyring,\n Bip44Account<KeyringAccount>\n >\n implements Keyring\n{\n readonly entropySource: EntropySourceId;\n\n constructor(options: LedgerKeyringOptions) {\n super({\n type: KeyringType.Ledger,\n inner: options.legacyKeyring as LedgerKeyringAsEthKeyring,\n capabilities: ledgerKeyringCapabilities,\n });\n this.entropySource = options.entropySource;\n }\n\n /**\n * Normalizes an address to a checksummed hex address.\n *\n * @param address - The address to normalize.\n * @returns The checksummed hex address.\n */\n #getChecksumHexAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n }\n\n /**\n * Parses a derivation path to extract the base HD path and account index.\n *\n * Supports two path formats:\n * - Ledger Live: m/44'/60'/{index}'/0/0 → base: m/44'/60'/0'/0/0, index from position 3\n * - Index at end: m/44'/60'/.../{index} → base: m/44'/60'/..., index from last segment\n *\n * @param derivationPath - The full derivation path.\n * @returns The base HD path and account index.\n * @throws If the path format is invalid.\n */\n #parseDerivationPath(derivationPath: string): {\n basePath: string;\n index: number;\n } {\n // Try Ledger Live format first: m/44'/60'/{index}'/0/0\n const ledgerLiveMatch = derivationPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (ledgerLiveMatch?.[1]) {\n return {\n // This constant is used by `inner.setHdPath` to determine which derivation\n // mode we should use (Ledger Live derivation mode here).\n basePath: LEDGER_LIVE_HD_PATH,\n index: parseInt(ledgerLiveMatch[1], 10),\n };\n }\n\n // Try index-at-end format: m/44'/60'/.../{index}\n const indexAtEndMatch = derivationPath.match(INDEX_AT_END_PATH_PATTERN);\n if (indexAtEndMatch) {\n // If the condition is true, indexAtEndMatch[1] and indexAtEndMatch[2] are defined, so\n // we can safely cast them to string.\n // This is necessary to get 100% code coverage.\n return {\n // Here, we use a derivation path prefix for `inner.setHdPath`\n // (prefix + index derivation mode).\n basePath: indexAtEndMatch[1] as string,\n index: parseInt(indexAtEndMatch[2] as string, 10),\n };\n }\n\n throw new Error(\n `Invalid derivation path format: ${derivationPath}. ` +\n `Expected Ledger Live (m/44'/60'/{index}'/0/0) or index-at-end (m/44'/60'/.../{index}) format.`,\n );\n }\n\n /**\n * Gets the index for an address from the account details.\n *\n * @param address - The address to get the index for.\n * @returns The index for the address.\n * @throws If the address is not found in account details.\n */\n #getIndexForAddress(address: Hex): number {\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details) {\n throw new Error(\n `Address ${checksummedAddress} not found in account details`,\n );\n }\n\n // Extract index from hdPath\n const { hdPath } = details;\n if (!hdPath) {\n throw new Error(`No HD path found for address ${checksummedAddress}`);\n }\n\n // Ledger supports multiple derivation path formats:\n // - Ledger Live (bip44: true): m/44'/60'/{index}'/0/0 - index at position 3\n // - Other paths (bip44: false): {hdPath}/{index} - index at end\n // - BIP44: m/44'/60'/0'/0/{index}\n // - Legacy: m/44'/60'/0'/{index}\n // - Custom paths via setHdPath\n //\n // We use the `bip44` flag to determine which extraction pattern to use.\n if (details.bip44) {\n // Ledger Live format: m/44'/60'/{index}'/0/0\n const match = hdPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (match?.[1]) {\n return parseInt(match[1], 10);\n }\n } else {\n // Index-at-end format: m/44'/60'/.../{index}\n const match = hdPath.match(INDEX_AT_END_PATH_PATTERN);\n if (match?.[2]) {\n return parseInt(match[2], 10);\n }\n }\n\n throw new Error(`Could not extract index from HD path: ${hdPath}`);\n }\n\n /**\n * Creates a Bip44Account object for the given address.\n *\n * @param address - The account address.\n * @param addressIndex - The account index in the derivation path.\n * @returns The created Bip44Account.\n */\n #createKeyringAccount(\n address: Hex,\n addressIndex: number,\n ): Bip44Account<KeyringAccount> {\n const id = this.registry.register(address);\n\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details?.hdPath) {\n throw new Error(\n `No HD path found for address ${checksummedAddress}. Cannot create account.`,\n );\n }\n\n const account: Bip44Account<KeyringAccount> = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...LEDGER_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Mnemonic,\n id: this.entropySource,\n groupIndex: addressIndex,\n derivationPath: details.hdPath,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n async getAccounts(): Promise<Bip44Account<KeyringAccount>[]> {\n const addresses = await this.inner.getAccounts();\n\n if (addresses.length === 0) {\n return [];\n }\n\n return addresses.map((address) => {\n // Check if we already have this account in the registry\n const existingId = this.registry.getAccountId(address);\n if (existingId) {\n const cached = this.registry.get(existingId);\n if (cached) {\n return cached;\n }\n }\n\n const addressIndex = this.#getIndexForAddress(address);\n return this.#createKeyringAccount(address, addressIndex);\n });\n }\n\n async createAccounts(\n options: CreateAccountOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n return this.withLock(async () => {\n if (\n options.type === 'bip44:derive-path' ||\n options.type === 'bip44:derive-index'\n ) {\n // Validate that the entropy source matches this keyring's entropy source\n if (options.entropySource !== this.entropySource) {\n throw new Error(\n `Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`,\n );\n }\n } else {\n throw new Error(\n `Unsupported account creation type for LedgerKeyring: ${String(\n options.type,\n )}`,\n );\n }\n\n // Check if an account at this index already exists with the same derivation path\n const currentAccounts = await this.getAccounts();\n\n let targetIndex: number;\n let basePath: string;\n let derivationPath: string;\n\n if (options.type === 'bip44:derive-path') {\n // Parse the derivation path to extract base path and index\n const parsed = this.#parseDerivationPath(options.derivationPath);\n targetIndex = parsed.index;\n basePath = parsed.basePath;\n\n derivationPath = options.derivationPath;\n } else {\n // derive-index uses BIP-44 standard path by default\n targetIndex = options.groupIndex;\n basePath = BIP44_HD_PATH_PREFIX;\n derivationPath = `${basePath}/${targetIndex}`;\n }\n\n const existingAccount = currentAccounts.find((account) => {\n return (\n account.options.entropy.groupIndex === targetIndex &&\n account.options.entropy.derivationPath === derivationPath\n );\n });\n\n if (existingAccount) {\n return [existingAccount];\n }\n\n // Derive the account at the specified index\n this.inner.setHdPath(basePath);\n this.inner.setAccountToUnlock(targetIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createKeyringAccount(newAddress, targetIndex);\n\n return [newAccount];\n });\n }\n\n /**\n * Delete an account from the keyring.\n *\n * @param accountId - The account ID to delete.\n */\n async deleteAccount(accountId: AccountId): Promise<void> {\n await this.withLock(async () => {\n const { address } = await this.getAccount(accountId);\n const hexAddress = this.toHexAddress(address);\n\n // Remove from the legacy keyring\n this.inner.removeAccount(hexAddress);\n\n // Remove from the registry\n this.registry.delete(accountId);\n });\n }\n\n /**\n * @returns The current derivation path used by the inner keyring.\n */\n get hdPath(): string {\n return this.inner.hdPath;\n }\n\n /**\n * @returns The bridge instance used by the inner keyring to communicate\n * with the device.\n */\n get bridge(): LedgerBridge<LedgerBridgeOptions> {\n return this.inner.bridge;\n }\n\n /**\n * @returns The device ID for the paired Ledger device.\n */\n getDeviceId(): string {\n return this.inner.getDeviceId();\n }\n\n /**\n * Set the device ID for the paired Ledger device.\n *\n * @param deviceId - The device ID to set.\n */\n setDeviceId(deviceId: string): void {\n this.inner.setDeviceId(deviceId);\n }\n\n /**\n * Set the derivation path on the inner keyring.\n *\n * @param hdPath - The derivation path to set.\n */\n setHdPath(hdPath: string): void {\n this.inner.setHdPath(hdPath);\n }\n\n /**\n * Fetch the first page of candidate addresses from the device.\n *\n * @returns The first page of accounts.\n */\n async getFirstPage(): Promise<AccountPage> {\n return this.inner.getFirstPage();\n }\n\n /**\n * Fetch the next page of candidate addresses from the device.\n *\n * @returns The next page of accounts.\n */\n async getNextPage(): Promise<AccountPage> {\n return this.inner.getNextPage();\n }\n\n /**\n * Fetch the previous page of candidate addresses from the device.\n *\n * @returns The previous page of accounts.\n */\n async getPreviousPage(): Promise<AccountPage> {\n return this.inner.getPreviousPage();\n }\n\n /**\n * Clear the inner keyring's device-pairing state and accounts, and reset\n * the V2 account registry to keep them in sync.\n */\n async forgetDevice(): Promise<void> {\n await this.withLock(async () => {\n this.inner.forgetDevice();\n this.registry.clear();\n });\n }\n\n /**\n * @returns Whether the inner keyring has an unlocked HD key.\n */\n isUnlocked(): boolean {\n return this.inner.isUnlocked();\n }\n\n /**\n * Attempt to open the Ethereum app on the connected Ledger device.\n *\n * @returns Whether the app was opened.\n */\n async attemptMakeApp(): Promise<boolean> {\n return this.inner.attemptMakeApp();\n }\n\n /**\n * @returns The app name and version currently running on the connected\n * Ledger device.\n */\n async getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse> {\n return this.inner.getAppNameAndVersion();\n }\n}\n"]}
@@ -3,7 +3,8 @@ import type { KeyringAccount, EntropySourceId } from "@metamask/keyring-api";
3
3
  import type { CreateAccountOptions, Keyring } from "@metamask/keyring-api/v2";
4
4
  import { EthKeyringWrapper } from "@metamask/keyring-sdk/v2";
5
5
  import type { AccountId, EthKeyring } from "@metamask/keyring-utils";
6
- import type { LedgerKeyring as LegacyLedgerKeyring } from "../ledger-keyring.cjs";
6
+ import type { GetAppNameAndVersionResponse, LedgerBridge, LedgerBridgeOptions } from "../ledger-bridge.cjs";
7
+ import type { AccountPage, LedgerKeyring as LegacyLedgerKeyring } from "../ledger-keyring.cjs";
7
8
  /**
8
9
  * Concrete {@link Keyring} adapter for {@link LedgerKeyring}.
9
10
  *
@@ -29,6 +30,69 @@ export declare class LedgerKeyring extends EthKeyringWrapper<LedgerKeyringAsEthK
29
30
  * @param accountId - The account ID to delete.
30
31
  */
31
32
  deleteAccount(accountId: AccountId): Promise<void>;
33
+ /**
34
+ * @returns The current derivation path used by the inner keyring.
35
+ */
36
+ get hdPath(): string;
37
+ /**
38
+ * @returns The bridge instance used by the inner keyring to communicate
39
+ * with the device.
40
+ */
41
+ get bridge(): LedgerBridge<LedgerBridgeOptions>;
42
+ /**
43
+ * @returns The device ID for the paired Ledger device.
44
+ */
45
+ getDeviceId(): string;
46
+ /**
47
+ * Set the device ID for the paired Ledger device.
48
+ *
49
+ * @param deviceId - The device ID to set.
50
+ */
51
+ setDeviceId(deviceId: string): void;
52
+ /**
53
+ * Set the derivation path on the inner keyring.
54
+ *
55
+ * @param hdPath - The derivation path to set.
56
+ */
57
+ setHdPath(hdPath: string): void;
58
+ /**
59
+ * Fetch the first page of candidate addresses from the device.
60
+ *
61
+ * @returns The first page of accounts.
62
+ */
63
+ getFirstPage(): Promise<AccountPage>;
64
+ /**
65
+ * Fetch the next page of candidate addresses from the device.
66
+ *
67
+ * @returns The next page of accounts.
68
+ */
69
+ getNextPage(): Promise<AccountPage>;
70
+ /**
71
+ * Fetch the previous page of candidate addresses from the device.
72
+ *
73
+ * @returns The previous page of accounts.
74
+ */
75
+ getPreviousPage(): Promise<AccountPage>;
76
+ /**
77
+ * Clear the inner keyring's device-pairing state and accounts, and reset
78
+ * the V2 account registry to keep them in sync.
79
+ */
80
+ forgetDevice(): Promise<void>;
81
+ /**
82
+ * @returns Whether the inner keyring has an unlocked HD key.
83
+ */
84
+ isUnlocked(): boolean;
85
+ /**
86
+ * Attempt to open the Ethereum app on the connected Ledger device.
87
+ *
88
+ * @returns Whether the app was opened.
89
+ */
90
+ attemptMakeApp(): Promise<boolean>;
91
+ /**
92
+ * @returns The app name and version currently running on the connected
93
+ * Ledger device.
94
+ */
95
+ getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse>;
32
96
  }
33
97
  export {};
34
98
  //# sourceMappingURL=ledger-keyring.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-keyring.d.cts","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B;AAO1D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EACV,oBAAoB,EAEpB,OAAO,EACR,iCAAiC;AAClC,OAAO,EAAE,iBAAiB,EAAE,iCAAiC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,gCAAgC;AAIrE,OAAO,KAAK,EAAE,aAAa,IAAI,mBAAmB,EAAE,8BAA0B;AA2C9E;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,eAAe,CAAC;CAChC,CAAC;AAKF,KAAK,yBAAyB,GAAG,mBAAmB,GAAG,UAAU,CAAC;AAElE,qBAAa,aACX,SAAQ,iBAAiB,CACvB,yBAAyB,EACzB,YAAY,CAAC,cAAc,CAAC,CAE9B,YAAW,OAAO;;IAElB,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;gBAE5B,OAAO,EAAE,oBAAoB;IA2JnC,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAsBtD,cAAc,CAClB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAmE1C;;;;OAIG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYzD"}
1
+ {"version":3,"file":"ledger-keyring.d.cts","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B;AAO1D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EACV,oBAAoB,EAEpB,OAAO,EACR,iCAAiC;AAClC,OAAO,EAAE,iBAAiB,EAAE,iCAAiC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,gCAAgC;AAIrE,OAAO,KAAK,EACV,4BAA4B,EAC5B,YAAY,EACZ,mBAAmB,EACpB,6BAAyB;AAC1B,OAAO,KAAK,EACV,WAAW,EACX,aAAa,IAAI,mBAAmB,EACrC,8BAA0B;AA2C3B;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,eAAe,CAAC;CAChC,CAAC;AAKF,KAAK,yBAAyB,GAAG,mBAAmB,GAAG,UAAU,CAAC;AAElE,qBAAa,aACX,SAAQ,iBAAiB,CACvB,yBAAyB,EACzB,YAAY,CAAC,cAAc,CAAC,CAE9B,YAAW,OAAO;;IAElB,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;gBAE5B,OAAO,EAAE,oBAAoB;IA2JnC,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAsBtD,cAAc,CAClB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAmE1C;;;;OAIG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxD;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACH,IAAI,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,CAE9C;IAED;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;OAIG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAInC;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAI1C;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAIzC;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC;IAI7C;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxC;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,4BAA4B,CAAC;CAGpE"}
@@ -3,7 +3,8 @@ import type { KeyringAccount, EntropySourceId } from "@metamask/keyring-api";
3
3
  import type { CreateAccountOptions, Keyring } from "@metamask/keyring-api/v2";
4
4
  import { EthKeyringWrapper } from "@metamask/keyring-sdk/v2";
5
5
  import type { AccountId, EthKeyring } from "@metamask/keyring-utils";
6
- import type { LedgerKeyring as LegacyLedgerKeyring } from "../ledger-keyring.mjs";
6
+ import type { GetAppNameAndVersionResponse, LedgerBridge, LedgerBridgeOptions } from "../ledger-bridge.mjs";
7
+ import type { AccountPage, LedgerKeyring as LegacyLedgerKeyring } from "../ledger-keyring.mjs";
7
8
  /**
8
9
  * Concrete {@link Keyring} adapter for {@link LedgerKeyring}.
9
10
  *
@@ -29,6 +30,69 @@ export declare class LedgerKeyring extends EthKeyringWrapper<LedgerKeyringAsEthK
29
30
  * @param accountId - The account ID to delete.
30
31
  */
31
32
  deleteAccount(accountId: AccountId): Promise<void>;
33
+ /**
34
+ * @returns The current derivation path used by the inner keyring.
35
+ */
36
+ get hdPath(): string;
37
+ /**
38
+ * @returns The bridge instance used by the inner keyring to communicate
39
+ * with the device.
40
+ */
41
+ get bridge(): LedgerBridge<LedgerBridgeOptions>;
42
+ /**
43
+ * @returns The device ID for the paired Ledger device.
44
+ */
45
+ getDeviceId(): string;
46
+ /**
47
+ * Set the device ID for the paired Ledger device.
48
+ *
49
+ * @param deviceId - The device ID to set.
50
+ */
51
+ setDeviceId(deviceId: string): void;
52
+ /**
53
+ * Set the derivation path on the inner keyring.
54
+ *
55
+ * @param hdPath - The derivation path to set.
56
+ */
57
+ setHdPath(hdPath: string): void;
58
+ /**
59
+ * Fetch the first page of candidate addresses from the device.
60
+ *
61
+ * @returns The first page of accounts.
62
+ */
63
+ getFirstPage(): Promise<AccountPage>;
64
+ /**
65
+ * Fetch the next page of candidate addresses from the device.
66
+ *
67
+ * @returns The next page of accounts.
68
+ */
69
+ getNextPage(): Promise<AccountPage>;
70
+ /**
71
+ * Fetch the previous page of candidate addresses from the device.
72
+ *
73
+ * @returns The previous page of accounts.
74
+ */
75
+ getPreviousPage(): Promise<AccountPage>;
76
+ /**
77
+ * Clear the inner keyring's device-pairing state and accounts, and reset
78
+ * the V2 account registry to keep them in sync.
79
+ */
80
+ forgetDevice(): Promise<void>;
81
+ /**
82
+ * @returns Whether the inner keyring has an unlocked HD key.
83
+ */
84
+ isUnlocked(): boolean;
85
+ /**
86
+ * Attempt to open the Ethereum app on the connected Ledger device.
87
+ *
88
+ * @returns Whether the app was opened.
89
+ */
90
+ attemptMakeApp(): Promise<boolean>;
91
+ /**
92
+ * @returns The app name and version currently running on the connected
93
+ * Ledger device.
94
+ */
95
+ getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse>;
32
96
  }
33
97
  export {};
34
98
  //# sourceMappingURL=ledger-keyring.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-keyring.d.mts","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B;AAO1D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EACV,oBAAoB,EAEpB,OAAO,EACR,iCAAiC;AAClC,OAAO,EAAE,iBAAiB,EAAE,iCAAiC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,gCAAgC;AAIrE,OAAO,KAAK,EAAE,aAAa,IAAI,mBAAmB,EAAE,8BAA0B;AA2C9E;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,eAAe,CAAC;CAChC,CAAC;AAKF,KAAK,yBAAyB,GAAG,mBAAmB,GAAG,UAAU,CAAC;AAElE,qBAAa,aACX,SAAQ,iBAAiB,CACvB,yBAAyB,EACzB,YAAY,CAAC,cAAc,CAAC,CAE9B,YAAW,OAAO;;IAElB,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;gBAE5B,OAAO,EAAE,oBAAoB;IA2JnC,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAsBtD,cAAc,CAClB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAmE1C;;;;OAIG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYzD"}
1
+ {"version":3,"file":"ledger-keyring.d.mts","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B;AAO1D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EACV,oBAAoB,EAEpB,OAAO,EACR,iCAAiC;AAClC,OAAO,EAAE,iBAAiB,EAAE,iCAAiC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,gCAAgC;AAIrE,OAAO,KAAK,EACV,4BAA4B,EAC5B,YAAY,EACZ,mBAAmB,EACpB,6BAAyB;AAC1B,OAAO,KAAK,EACV,WAAW,EACX,aAAa,IAAI,mBAAmB,EACrC,8BAA0B;AA2C3B;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,eAAe,CAAC;CAChC,CAAC;AAKF,KAAK,yBAAyB,GAAG,mBAAmB,GAAG,UAAU,CAAC;AAElE,qBAAa,aACX,SAAQ,iBAAiB,CACvB,yBAAyB,EACzB,YAAY,CAAC,cAAc,CAAC,CAE9B,YAAW,OAAO;;IAElB,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;gBAE5B,OAAO,EAAE,oBAAoB;IA2JnC,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAsBtD,cAAc,CAClB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;IAmE1C;;;;OAIG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxD;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACH,IAAI,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,CAE9C;IAED;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;OAIG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAInC;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAI1C;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAIzC;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC;IAI7C;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxC;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,4BAA4B,CAAC;CAGpE"}
@@ -134,6 +134,96 @@ export class LedgerKeyring extends EthKeyringWrapper {
134
134
  this.registry.delete(accountId);
135
135
  });
136
136
  }
137
+ /**
138
+ * @returns The current derivation path used by the inner keyring.
139
+ */
140
+ get hdPath() {
141
+ return this.inner.hdPath;
142
+ }
143
+ /**
144
+ * @returns The bridge instance used by the inner keyring to communicate
145
+ * with the device.
146
+ */
147
+ get bridge() {
148
+ return this.inner.bridge;
149
+ }
150
+ /**
151
+ * @returns The device ID for the paired Ledger device.
152
+ */
153
+ getDeviceId() {
154
+ return this.inner.getDeviceId();
155
+ }
156
+ /**
157
+ * Set the device ID for the paired Ledger device.
158
+ *
159
+ * @param deviceId - The device ID to set.
160
+ */
161
+ setDeviceId(deviceId) {
162
+ this.inner.setDeviceId(deviceId);
163
+ }
164
+ /**
165
+ * Set the derivation path on the inner keyring.
166
+ *
167
+ * @param hdPath - The derivation path to set.
168
+ */
169
+ setHdPath(hdPath) {
170
+ this.inner.setHdPath(hdPath);
171
+ }
172
+ /**
173
+ * Fetch the first page of candidate addresses from the device.
174
+ *
175
+ * @returns The first page of accounts.
176
+ */
177
+ async getFirstPage() {
178
+ return this.inner.getFirstPage();
179
+ }
180
+ /**
181
+ * Fetch the next page of candidate addresses from the device.
182
+ *
183
+ * @returns The next page of accounts.
184
+ */
185
+ async getNextPage() {
186
+ return this.inner.getNextPage();
187
+ }
188
+ /**
189
+ * Fetch the previous page of candidate addresses from the device.
190
+ *
191
+ * @returns The previous page of accounts.
192
+ */
193
+ async getPreviousPage() {
194
+ return this.inner.getPreviousPage();
195
+ }
196
+ /**
197
+ * Clear the inner keyring's device-pairing state and accounts, and reset
198
+ * the V2 account registry to keep them in sync.
199
+ */
200
+ async forgetDevice() {
201
+ await this.withLock(async () => {
202
+ this.inner.forgetDevice();
203
+ this.registry.clear();
204
+ });
205
+ }
206
+ /**
207
+ * @returns Whether the inner keyring has an unlocked HD key.
208
+ */
209
+ isUnlocked() {
210
+ return this.inner.isUnlocked();
211
+ }
212
+ /**
213
+ * Attempt to open the Ethereum app on the connected Ledger device.
214
+ *
215
+ * @returns Whether the app was opened.
216
+ */
217
+ async attemptMakeApp() {
218
+ return this.inner.attemptMakeApp();
219
+ }
220
+ /**
221
+ * @returns The app name and version currently running on the connected
222
+ * Ledger device.
223
+ */
224
+ async getAppNameAndVersion() {
225
+ return this.inner.getAppNameAndVersion();
226
+ }
137
227
  }
138
228
  _LedgerKeyring_instances = new WeakSet(), _LedgerKeyring_getChecksumHexAddress = function _LedgerKeyring_getChecksumHexAddress(address) {
139
229
  return getChecksumAddress(add0x(address));
@@ -1 +1 @@
1
- {"version":3,"file":"ledger-keyring.mjs","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Bip44Account } from '@metamask/account-api';\nimport {\n EthAccountType,\n EthMethod,\n EthScope,\n KeyringAccountEntropyTypeOption,\n} from '@metamask/keyring-api';\nimport type { KeyringAccount, EntropySourceId } from '@metamask/keyring-api';\nimport { KeyringType } from '@metamask/keyring-api/v2';\nimport type {\n CreateAccountOptions,\n KeyringCapabilities,\n Keyring,\n} from '@metamask/keyring-api/v2';\nimport { EthKeyringWrapper } from '@metamask/keyring-sdk/v2';\nimport type { AccountId, EthKeyring } from '@metamask/keyring-utils';\nimport { add0x, getChecksumAddress } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { LedgerKeyring as LegacyLedgerKeyring } from '../ledger-keyring';\n\n/**\n * Methods supported by Ledger keyring EOA accounts.\n * Ledger keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).\n */\nconst LEDGER_KEYRING_METHODS = [\n EthMethod.SignTransaction,\n EthMethod.PersonalSign,\n EthMethod.SignTypedDataV4,\n];\n\nconst ledgerKeyringCapabilities: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n derivePath: true,\n },\n};\n\n/**\n * Ledger Live HD path constant.\n */\nconst LEDGER_LIVE_HD_PATH = `m/44'/60'/0'/0/0`;\n\n/**\n * BIP-44 standard HD path prefix constant for Ethereum.\n */\nconst BIP44_HD_PATH_PREFIX = `m/44'/60'/0'/0`;\n\n/**\n * Regex pattern for validating and parsing Ledger Live derivation paths.\n * Format: m/44'/60'/{index}'/0/0\n */\nconst LEDGER_LIVE_PATH_PATTERN = /^m\\/44'\\/60'\\/(\\d+)'\\/0\\/0$/u;\n\n/**\n * Regex pattern for validating and parsing non-Ledger-Live derivation paths.\n * Supports Legacy (m/44'/60'/0'/{index}), BIP44 (m/44'/60'/0'/0/{index}),\n * and custom paths that follow the m/44'/60'/... pattern.\n */\nconst INDEX_AT_END_PATH_PATTERN = /^(m\\/44'\\/60'(?:\\/\\d+'?)*)\\/(\\d+)$/u;\n\n/**\n * Concrete {@link Keyring} adapter for {@link LedgerKeyring}.\n *\n * This wrapper exposes the accounts and signing capabilities of the legacy\n * Ledger keyring via the unified V2 interface.\n *\n * All Ledger keyring accounts are BIP-44 derived from the device.\n */\nexport type LedgerKeyringOptions = {\n legacyKeyring: LegacyLedgerKeyring;\n entropySource: EntropySourceId;\n};\n\n// LegacyLedgerKeyring.signTransaction returns `TypedTransaction | OldEthJsTransaction` for\n// backwards compatibility with old ethereumjs-tx, but EthKeyring expects `TypedTxData`.\n// The runtime behavior is correct - we cast the type to satisfy the constraint.\ntype LedgerKeyringAsEthKeyring = LegacyLedgerKeyring & EthKeyring;\n\nexport class LedgerKeyring\n extends EthKeyringWrapper<\n LedgerKeyringAsEthKeyring,\n Bip44Account<KeyringAccount>\n >\n implements Keyring\n{\n readonly entropySource: EntropySourceId;\n\n constructor(options: LedgerKeyringOptions) {\n super({\n type: KeyringType.Ledger,\n inner: options.legacyKeyring as LedgerKeyringAsEthKeyring,\n capabilities: ledgerKeyringCapabilities,\n });\n this.entropySource = options.entropySource;\n }\n\n /**\n * Normalizes an address to a checksummed hex address.\n *\n * @param address - The address to normalize.\n * @returns The checksummed hex address.\n */\n #getChecksumHexAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n }\n\n /**\n * Parses a derivation path to extract the base HD path and account index.\n *\n * Supports two path formats:\n * - Ledger Live: m/44'/60'/{index}'/0/0 → base: m/44'/60'/0'/0/0, index from position 3\n * - Index at end: m/44'/60'/.../{index} → base: m/44'/60'/..., index from last segment\n *\n * @param derivationPath - The full derivation path.\n * @returns The base HD path and account index.\n * @throws If the path format is invalid.\n */\n #parseDerivationPath(derivationPath: string): {\n basePath: string;\n index: number;\n } {\n // Try Ledger Live format first: m/44'/60'/{index}'/0/0\n const ledgerLiveMatch = derivationPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (ledgerLiveMatch?.[1]) {\n return {\n // This constant is used by `inner.setHdPath` to determine which derivation\n // mode we should use (Ledger Live derivation mode here).\n basePath: LEDGER_LIVE_HD_PATH,\n index: parseInt(ledgerLiveMatch[1], 10),\n };\n }\n\n // Try index-at-end format: m/44'/60'/.../{index}\n const indexAtEndMatch = derivationPath.match(INDEX_AT_END_PATH_PATTERN);\n if (indexAtEndMatch) {\n // If the condition is true, indexAtEndMatch[1] and indexAtEndMatch[2] are defined, so\n // we can safely cast them to string.\n // This is necessary to get 100% code coverage.\n return {\n // Here, we use a derivation path prefix for `inner.setHdPath`\n // (prefix + index derivation mode).\n basePath: indexAtEndMatch[1] as string,\n index: parseInt(indexAtEndMatch[2] as string, 10),\n };\n }\n\n throw new Error(\n `Invalid derivation path format: ${derivationPath}. ` +\n `Expected Ledger Live (m/44'/60'/{index}'/0/0) or index-at-end (m/44'/60'/.../{index}) format.`,\n );\n }\n\n /**\n * Gets the index for an address from the account details.\n *\n * @param address - The address to get the index for.\n * @returns The index for the address.\n * @throws If the address is not found in account details.\n */\n #getIndexForAddress(address: Hex): number {\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details) {\n throw new Error(\n `Address ${checksummedAddress} not found in account details`,\n );\n }\n\n // Extract index from hdPath\n const { hdPath } = details;\n if (!hdPath) {\n throw new Error(`No HD path found for address ${checksummedAddress}`);\n }\n\n // Ledger supports multiple derivation path formats:\n // - Ledger Live (bip44: true): m/44'/60'/{index}'/0/0 - index at position 3\n // - Other paths (bip44: false): {hdPath}/{index} - index at end\n // - BIP44: m/44'/60'/0'/0/{index}\n // - Legacy: m/44'/60'/0'/{index}\n // - Custom paths via setHdPath\n //\n // We use the `bip44` flag to determine which extraction pattern to use.\n if (details.bip44) {\n // Ledger Live format: m/44'/60'/{index}'/0/0\n const match = hdPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (match?.[1]) {\n return parseInt(match[1], 10);\n }\n } else {\n // Index-at-end format: m/44'/60'/.../{index}\n const match = hdPath.match(INDEX_AT_END_PATH_PATTERN);\n if (match?.[2]) {\n return parseInt(match[2], 10);\n }\n }\n\n throw new Error(`Could not extract index from HD path: ${hdPath}`);\n }\n\n /**\n * Creates a Bip44Account object for the given address.\n *\n * @param address - The account address.\n * @param addressIndex - The account index in the derivation path.\n * @returns The created Bip44Account.\n */\n #createKeyringAccount(\n address: Hex,\n addressIndex: number,\n ): Bip44Account<KeyringAccount> {\n const id = this.registry.register(address);\n\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details?.hdPath) {\n throw new Error(\n `No HD path found for address ${checksummedAddress}. Cannot create account.`,\n );\n }\n\n const account: Bip44Account<KeyringAccount> = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...LEDGER_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Mnemonic,\n id: this.entropySource,\n groupIndex: addressIndex,\n derivationPath: details.hdPath,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n async getAccounts(): Promise<Bip44Account<KeyringAccount>[]> {\n const addresses = await this.inner.getAccounts();\n\n if (addresses.length === 0) {\n return [];\n }\n\n return addresses.map((address) => {\n // Check if we already have this account in the registry\n const existingId = this.registry.getAccountId(address);\n if (existingId) {\n const cached = this.registry.get(existingId);\n if (cached) {\n return cached;\n }\n }\n\n const addressIndex = this.#getIndexForAddress(address);\n return this.#createKeyringAccount(address, addressIndex);\n });\n }\n\n async createAccounts(\n options: CreateAccountOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n return this.withLock(async () => {\n if (\n options.type === 'bip44:derive-path' ||\n options.type === 'bip44:derive-index'\n ) {\n // Validate that the entropy source matches this keyring's entropy source\n if (options.entropySource !== this.entropySource) {\n throw new Error(\n `Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`,\n );\n }\n } else {\n throw new Error(\n `Unsupported account creation type for LedgerKeyring: ${String(\n options.type,\n )}`,\n );\n }\n\n // Check if an account at this index already exists with the same derivation path\n const currentAccounts = await this.getAccounts();\n\n let targetIndex: number;\n let basePath: string;\n let derivationPath: string;\n\n if (options.type === 'bip44:derive-path') {\n // Parse the derivation path to extract base path and index\n const parsed = this.#parseDerivationPath(options.derivationPath);\n targetIndex = parsed.index;\n basePath = parsed.basePath;\n\n derivationPath = options.derivationPath;\n } else {\n // derive-index uses BIP-44 standard path by default\n targetIndex = options.groupIndex;\n basePath = BIP44_HD_PATH_PREFIX;\n derivationPath = `${basePath}/${targetIndex}`;\n }\n\n const existingAccount = currentAccounts.find((account) => {\n return (\n account.options.entropy.groupIndex === targetIndex &&\n account.options.entropy.derivationPath === derivationPath\n );\n });\n\n if (existingAccount) {\n return [existingAccount];\n }\n\n // Derive the account at the specified index\n this.inner.setHdPath(basePath);\n this.inner.setAccountToUnlock(targetIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createKeyringAccount(newAddress, targetIndex);\n\n return [newAccount];\n });\n }\n\n /**\n * Delete an account from the keyring.\n *\n * @param accountId - The account ID to delete.\n */\n async deleteAccount(accountId: AccountId): Promise<void> {\n await this.withLock(async () => {\n const { address } = await this.getAccount(accountId);\n const hexAddress = this.toHexAddress(address);\n\n // Remove from the legacy keyring\n this.inner.removeAccount(hexAddress);\n\n // Remove from the registry\n this.registry.delete(accountId);\n });\n }\n}\n"]}
1
+ {"version":3,"file":"ledger-keyring.mjs","sourceRoot":"","sources":["../../src/v2/ledger-keyring.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,+BAA+B,EAChC,8BAA8B;AAE/B,OAAO,EAAE,WAAW,EAAE,iCAAiC;AAMvD,OAAO,EAAE,iBAAiB,EAAE,iCAAiC;AAE7D,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,wBAAwB;AAa5D;;;GAGG;AACH,MAAM,sBAAsB,GAAG;IAC7B,SAAS,CAAC,eAAe;IACzB,SAAS,CAAC,YAAY;IACtB,SAAS,CAAC,eAAe;CAC1B,CAAC;AAEF,MAAM,yBAAyB,GAAwB;IACrD,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;IACtB,KAAK,EAAE;QACL,WAAW,EAAE,IAAI;QACjB,UAAU,EAAE,IAAI;KACjB;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAE/C;;GAEG;AACH,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAE9C;;;GAGG;AACH,MAAM,wBAAwB,GAAG,8BAA8B,CAAC;AAEhE;;;;GAIG;AACH,MAAM,yBAAyB,GAAG,qCAAqC,CAAC;AAoBxE,MAAM,OAAO,aACX,SAAQ,iBAGP;IAKD,YAAY,OAA6B;QACvC,KAAK,CAAC;YACJ,IAAI,EAAE,WAAW,CAAC,MAAM;YACxB,KAAK,EAAE,OAAO,CAAC,aAA0C;YACzD,YAAY,EAAE,yBAAyB;SACxC,CAAC,CAAC;;QACH,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC7C,CAAC;IAoJD,KAAK,CAAC,WAAW;QACf,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAEjD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YAC/B,wDAAwD;YACxD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC7C,IAAI,MAAM,EAAE,CAAC;oBACX,OAAO,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;YAED,MAAM,YAAY,GAAG,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,EAAqB,OAAO,CAAC,CAAC;YACvD,OAAO,uBAAA,IAAI,qEAAsB,MAA1B,IAAI,EAAuB,OAAO,EAAE,YAAY,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,OAA6B;QAE7B,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC9B,IACE,OAAO,CAAC,IAAI,KAAK,mBAAmB;gBACpC,OAAO,CAAC,IAAI,KAAK,oBAAoB,EACrC,CAAC;gBACD,yEAAyE;gBACzE,IAAI,OAAO,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;oBACjD,MAAM,IAAI,KAAK,CACb,sCAAsC,IAAI,CAAC,aAAa,WAAW,OAAO,CAAC,aAAa,GAAG,CAC5F,CAAC;gBACJ,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CACb,wDAAwD,MAAM,CAC5D,OAAO,CAAC,IAAI,CACb,EAAE,CACJ,CAAC;YACJ,CAAC;YAED,iFAAiF;YACjF,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YAEjD,IAAI,WAAmB,CAAC;YACxB,IAAI,QAAgB,CAAC;YACrB,IAAI,cAAsB,CAAC;YAE3B,IAAI,OAAO,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACzC,2DAA2D;gBAC3D,MAAM,MAAM,GAAG,uBAAA,IAAI,oEAAqB,MAAzB,IAAI,EAAsB,OAAO,CAAC,cAAc,CAAC,CAAC;gBACjE,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;gBAC3B,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;gBAE3B,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,oDAAoD;gBACpD,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;gBACjC,QAAQ,GAAG,oBAAoB,CAAC;gBAChC,cAAc,GAAG,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC;YAChD,CAAC;YAED,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;gBACvD,OAAO,CACL,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,KAAK,WAAW;oBAClD,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,KAAK,cAAc,CAC1D,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,eAAe,EAAE,CAAC;gBACpB,OAAO,CAAC,eAAe,CAAC,CAAC;YAC3B,CAAC;YAED,4CAA4C;YAC5C,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,CAAC,UAAU,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAErD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAClD,CAAC;YAED,MAAM,UAAU,GAAG,uBAAA,IAAI,qEAAsB,MAA1B,IAAI,EAAuB,UAAU,EAAE,WAAW,CAAC,CAAC;YAEvE,OAAO,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,SAAoB;QACtC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YAE9C,iCAAiC;YACjC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAErC,2BAA2B;YAC3B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,QAAgB;QAC1B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,MAAc;QACtB,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC7B,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,oBAAoB;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,CAAC;IAC3C,CAAC;CACF;+HA9VwB,OAAe;IACpC,OAAO,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC,mFAaoB,cAAsB;IAIzC,uDAAuD;IACvD,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACvE,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,2EAA2E;YAC3E,yDAAyD;YACzD,QAAQ,EAAE,mBAAmB;YAC7B,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SACxC,CAAC;IACJ,CAAC;IAED,iDAAiD;IACjD,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACxE,IAAI,eAAe,EAAE,CAAC;QACpB,sFAAsF;QACtF,qCAAqC;QACrC,+CAA+C;QAC/C,OAAO;YACL,8DAA8D;YAC9D,oCAAoC;YACpC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAW;YACtC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAW,EAAE,EAAE,CAAC;SAClD,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,KAAK,CACb,mCAAmC,cAAc,IAAI;QACnD,+FAA+F,CAClG,CAAC;AACJ,CAAC,iFASmB,OAAY;IAC9B,MAAM,kBAAkB,GAAG,uBAAA,IAAI,sEAAuB,MAA3B,IAAI,EAAwB,OAAO,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAE9D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,WAAW,kBAAkB,+BAA+B,CAC7D,CAAC;IACJ,CAAC;IAED,4BAA4B;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,gCAAgC,kBAAkB,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,oDAAoD;IACpD,4EAA4E;IAC5E,gEAAgE;IAChE,oCAAoC;IACpC,mCAAmC;IACnC,iCAAiC;IACjC,EAAE;IACF,wEAAwE;IACxE,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,6CAA6C;QAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACrD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACf,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,6CAA6C;QAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACtD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACf,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,yCAAyC,MAAM,EAAE,CAAC,CAAC;AACrE,CAAC,qFAUC,OAAY,EACZ,YAAoB;IAEpB,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE3C,MAAM,kBAAkB,GAAG,uBAAA,IAAI,sEAAuB,MAA3B,IAAI,EAAwB,OAAO,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAE9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,gCAAgC,kBAAkB,0BAA0B,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAiC;QAC5C,EAAE;QACF,IAAI,EAAE,cAAc,CAAC,GAAG;QACxB,OAAO;QACP,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QACrC,OAAO,EAAE,CAAC,GAAG,sBAAsB,CAAC;QACpC,OAAO,EAAE;YACP,OAAO,EAAE;gBACP,IAAI,EAAE,+BAA+B,CAAC,QAAQ;gBAC9C,EAAE,EAAE,IAAI,CAAC,aAAa;gBACtB,UAAU,EAAE,YAAY;gBACxB,cAAc,EAAE,OAAO,CAAC,MAAM;aAC/B;SACF;KACF,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import type { Bip44Account } from '@metamask/account-api';\nimport {\n EthAccountType,\n EthMethod,\n EthScope,\n KeyringAccountEntropyTypeOption,\n} from '@metamask/keyring-api';\nimport type { KeyringAccount, EntropySourceId } from '@metamask/keyring-api';\nimport { KeyringType } from '@metamask/keyring-api/v2';\nimport type {\n CreateAccountOptions,\n KeyringCapabilities,\n Keyring,\n} from '@metamask/keyring-api/v2';\nimport { EthKeyringWrapper } from '@metamask/keyring-sdk/v2';\nimport type { AccountId, EthKeyring } from '@metamask/keyring-utils';\nimport { add0x, getChecksumAddress } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type {\n GetAppNameAndVersionResponse,\n LedgerBridge,\n LedgerBridgeOptions,\n} from '../ledger-bridge';\nimport type {\n AccountPage,\n LedgerKeyring as LegacyLedgerKeyring,\n} from '../ledger-keyring';\n\n/**\n * Methods supported by Ledger keyring EOA accounts.\n * Ledger keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).\n */\nconst LEDGER_KEYRING_METHODS = [\n EthMethod.SignTransaction,\n EthMethod.PersonalSign,\n EthMethod.SignTypedDataV4,\n];\n\nconst ledgerKeyringCapabilities: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n derivePath: true,\n },\n};\n\n/**\n * Ledger Live HD path constant.\n */\nconst LEDGER_LIVE_HD_PATH = `m/44'/60'/0'/0/0`;\n\n/**\n * BIP-44 standard HD path prefix constant for Ethereum.\n */\nconst BIP44_HD_PATH_PREFIX = `m/44'/60'/0'/0`;\n\n/**\n * Regex pattern for validating and parsing Ledger Live derivation paths.\n * Format: m/44'/60'/{index}'/0/0\n */\nconst LEDGER_LIVE_PATH_PATTERN = /^m\\/44'\\/60'\\/(\\d+)'\\/0\\/0$/u;\n\n/**\n * Regex pattern for validating and parsing non-Ledger-Live derivation paths.\n * Supports Legacy (m/44'/60'/0'/{index}), BIP44 (m/44'/60'/0'/0/{index}),\n * and custom paths that follow the m/44'/60'/... pattern.\n */\nconst INDEX_AT_END_PATH_PATTERN = /^(m\\/44'\\/60'(?:\\/\\d+'?)*)\\/(\\d+)$/u;\n\n/**\n * Concrete {@link Keyring} adapter for {@link LedgerKeyring}.\n *\n * This wrapper exposes the accounts and signing capabilities of the legacy\n * Ledger keyring via the unified V2 interface.\n *\n * All Ledger keyring accounts are BIP-44 derived from the device.\n */\nexport type LedgerKeyringOptions = {\n legacyKeyring: LegacyLedgerKeyring;\n entropySource: EntropySourceId;\n};\n\n// LegacyLedgerKeyring.signTransaction returns `TypedTransaction | OldEthJsTransaction` for\n// backwards compatibility with old ethereumjs-tx, but EthKeyring expects `TypedTxData`.\n// The runtime behavior is correct - we cast the type to satisfy the constraint.\ntype LedgerKeyringAsEthKeyring = LegacyLedgerKeyring & EthKeyring;\n\nexport class LedgerKeyring\n extends EthKeyringWrapper<\n LedgerKeyringAsEthKeyring,\n Bip44Account<KeyringAccount>\n >\n implements Keyring\n{\n readonly entropySource: EntropySourceId;\n\n constructor(options: LedgerKeyringOptions) {\n super({\n type: KeyringType.Ledger,\n inner: options.legacyKeyring as LedgerKeyringAsEthKeyring,\n capabilities: ledgerKeyringCapabilities,\n });\n this.entropySource = options.entropySource;\n }\n\n /**\n * Normalizes an address to a checksummed hex address.\n *\n * @param address - The address to normalize.\n * @returns The checksummed hex address.\n */\n #getChecksumHexAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n }\n\n /**\n * Parses a derivation path to extract the base HD path and account index.\n *\n * Supports two path formats:\n * - Ledger Live: m/44'/60'/{index}'/0/0 → base: m/44'/60'/0'/0/0, index from position 3\n * - Index at end: m/44'/60'/.../{index} → base: m/44'/60'/..., index from last segment\n *\n * @param derivationPath - The full derivation path.\n * @returns The base HD path and account index.\n * @throws If the path format is invalid.\n */\n #parseDerivationPath(derivationPath: string): {\n basePath: string;\n index: number;\n } {\n // Try Ledger Live format first: m/44'/60'/{index}'/0/0\n const ledgerLiveMatch = derivationPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (ledgerLiveMatch?.[1]) {\n return {\n // This constant is used by `inner.setHdPath` to determine which derivation\n // mode we should use (Ledger Live derivation mode here).\n basePath: LEDGER_LIVE_HD_PATH,\n index: parseInt(ledgerLiveMatch[1], 10),\n };\n }\n\n // Try index-at-end format: m/44'/60'/.../{index}\n const indexAtEndMatch = derivationPath.match(INDEX_AT_END_PATH_PATTERN);\n if (indexAtEndMatch) {\n // If the condition is true, indexAtEndMatch[1] and indexAtEndMatch[2] are defined, so\n // we can safely cast them to string.\n // This is necessary to get 100% code coverage.\n return {\n // Here, we use a derivation path prefix for `inner.setHdPath`\n // (prefix + index derivation mode).\n basePath: indexAtEndMatch[1] as string,\n index: parseInt(indexAtEndMatch[2] as string, 10),\n };\n }\n\n throw new Error(\n `Invalid derivation path format: ${derivationPath}. ` +\n `Expected Ledger Live (m/44'/60'/{index}'/0/0) or index-at-end (m/44'/60'/.../{index}) format.`,\n );\n }\n\n /**\n * Gets the index for an address from the account details.\n *\n * @param address - The address to get the index for.\n * @returns The index for the address.\n * @throws If the address is not found in account details.\n */\n #getIndexForAddress(address: Hex): number {\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details) {\n throw new Error(\n `Address ${checksummedAddress} not found in account details`,\n );\n }\n\n // Extract index from hdPath\n const { hdPath } = details;\n if (!hdPath) {\n throw new Error(`No HD path found for address ${checksummedAddress}`);\n }\n\n // Ledger supports multiple derivation path formats:\n // - Ledger Live (bip44: true): m/44'/60'/{index}'/0/0 - index at position 3\n // - Other paths (bip44: false): {hdPath}/{index} - index at end\n // - BIP44: m/44'/60'/0'/0/{index}\n // - Legacy: m/44'/60'/0'/{index}\n // - Custom paths via setHdPath\n //\n // We use the `bip44` flag to determine which extraction pattern to use.\n if (details.bip44) {\n // Ledger Live format: m/44'/60'/{index}'/0/0\n const match = hdPath.match(LEDGER_LIVE_PATH_PATTERN);\n if (match?.[1]) {\n return parseInt(match[1], 10);\n }\n } else {\n // Index-at-end format: m/44'/60'/.../{index}\n const match = hdPath.match(INDEX_AT_END_PATH_PATTERN);\n if (match?.[2]) {\n return parseInt(match[2], 10);\n }\n }\n\n throw new Error(`Could not extract index from HD path: ${hdPath}`);\n }\n\n /**\n * Creates a Bip44Account object for the given address.\n *\n * @param address - The account address.\n * @param addressIndex - The account index in the derivation path.\n * @returns The created Bip44Account.\n */\n #createKeyringAccount(\n address: Hex,\n addressIndex: number,\n ): Bip44Account<KeyringAccount> {\n const id = this.registry.register(address);\n\n const checksummedAddress = this.#getChecksumHexAddress(address);\n const details = this.inner.accountDetails[checksummedAddress];\n\n if (!details?.hdPath) {\n throw new Error(\n `No HD path found for address ${checksummedAddress}. Cannot create account.`,\n );\n }\n\n const account: Bip44Account<KeyringAccount> = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...LEDGER_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Mnemonic,\n id: this.entropySource,\n groupIndex: addressIndex,\n derivationPath: details.hdPath,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n async getAccounts(): Promise<Bip44Account<KeyringAccount>[]> {\n const addresses = await this.inner.getAccounts();\n\n if (addresses.length === 0) {\n return [];\n }\n\n return addresses.map((address) => {\n // Check if we already have this account in the registry\n const existingId = this.registry.getAccountId(address);\n if (existingId) {\n const cached = this.registry.get(existingId);\n if (cached) {\n return cached;\n }\n }\n\n const addressIndex = this.#getIndexForAddress(address);\n return this.#createKeyringAccount(address, addressIndex);\n });\n }\n\n async createAccounts(\n options: CreateAccountOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n return this.withLock(async () => {\n if (\n options.type === 'bip44:derive-path' ||\n options.type === 'bip44:derive-index'\n ) {\n // Validate that the entropy source matches this keyring's entropy source\n if (options.entropySource !== this.entropySource) {\n throw new Error(\n `Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`,\n );\n }\n } else {\n throw new Error(\n `Unsupported account creation type for LedgerKeyring: ${String(\n options.type,\n )}`,\n );\n }\n\n // Check if an account at this index already exists with the same derivation path\n const currentAccounts = await this.getAccounts();\n\n let targetIndex: number;\n let basePath: string;\n let derivationPath: string;\n\n if (options.type === 'bip44:derive-path') {\n // Parse the derivation path to extract base path and index\n const parsed = this.#parseDerivationPath(options.derivationPath);\n targetIndex = parsed.index;\n basePath = parsed.basePath;\n\n derivationPath = options.derivationPath;\n } else {\n // derive-index uses BIP-44 standard path by default\n targetIndex = options.groupIndex;\n basePath = BIP44_HD_PATH_PREFIX;\n derivationPath = `${basePath}/${targetIndex}`;\n }\n\n const existingAccount = currentAccounts.find((account) => {\n return (\n account.options.entropy.groupIndex === targetIndex &&\n account.options.entropy.derivationPath === derivationPath\n );\n });\n\n if (existingAccount) {\n return [existingAccount];\n }\n\n // Derive the account at the specified index\n this.inner.setHdPath(basePath);\n this.inner.setAccountToUnlock(targetIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createKeyringAccount(newAddress, targetIndex);\n\n return [newAccount];\n });\n }\n\n /**\n * Delete an account from the keyring.\n *\n * @param accountId - The account ID to delete.\n */\n async deleteAccount(accountId: AccountId): Promise<void> {\n await this.withLock(async () => {\n const { address } = await this.getAccount(accountId);\n const hexAddress = this.toHexAddress(address);\n\n // Remove from the legacy keyring\n this.inner.removeAccount(hexAddress);\n\n // Remove from the registry\n this.registry.delete(accountId);\n });\n }\n\n /**\n * @returns The current derivation path used by the inner keyring.\n */\n get hdPath(): string {\n return this.inner.hdPath;\n }\n\n /**\n * @returns The bridge instance used by the inner keyring to communicate\n * with the device.\n */\n get bridge(): LedgerBridge<LedgerBridgeOptions> {\n return this.inner.bridge;\n }\n\n /**\n * @returns The device ID for the paired Ledger device.\n */\n getDeviceId(): string {\n return this.inner.getDeviceId();\n }\n\n /**\n * Set the device ID for the paired Ledger device.\n *\n * @param deviceId - The device ID to set.\n */\n setDeviceId(deviceId: string): void {\n this.inner.setDeviceId(deviceId);\n }\n\n /**\n * Set the derivation path on the inner keyring.\n *\n * @param hdPath - The derivation path to set.\n */\n setHdPath(hdPath: string): void {\n this.inner.setHdPath(hdPath);\n }\n\n /**\n * Fetch the first page of candidate addresses from the device.\n *\n * @returns The first page of accounts.\n */\n async getFirstPage(): Promise<AccountPage> {\n return this.inner.getFirstPage();\n }\n\n /**\n * Fetch the next page of candidate addresses from the device.\n *\n * @returns The next page of accounts.\n */\n async getNextPage(): Promise<AccountPage> {\n return this.inner.getNextPage();\n }\n\n /**\n * Fetch the previous page of candidate addresses from the device.\n *\n * @returns The previous page of accounts.\n */\n async getPreviousPage(): Promise<AccountPage> {\n return this.inner.getPreviousPage();\n }\n\n /**\n * Clear the inner keyring's device-pairing state and accounts, and reset\n * the V2 account registry to keep them in sync.\n */\n async forgetDevice(): Promise<void> {\n await this.withLock(async () => {\n this.inner.forgetDevice();\n this.registry.clear();\n });\n }\n\n /**\n * @returns Whether the inner keyring has an unlocked HD key.\n */\n isUnlocked(): boolean {\n return this.inner.isUnlocked();\n }\n\n /**\n * Attempt to open the Ethereum app on the connected Ledger device.\n *\n * @returns Whether the app was opened.\n */\n async attemptMakeApp(): Promise<boolean> {\n return this.inner.attemptMakeApp();\n }\n\n /**\n * @returns The app name and version currently running on the connected\n * Ledger device.\n */\n async getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse> {\n return this.inner.getAppNameAndVersion();\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/eth-ledger-bridge-keyring",
3
- "version": "12.0.3",
3
+ "version": "12.1.0",
4
4
  "description": "A MetaMask compatible keyring, for ledger hardware wallets",
5
5
  "keywords": [
6
6
  "ethereum",