--- /dev/null
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { ledgerVendorId } from '.'
+
+export async function _disconnect (): Promise<void> {
+ try {
+ const hidDevices = await navigator?.hid?.getDevices?.() ?? []
+ const usbDevices = await navigator?.usb?.getDevices?.() ?? []
+ const devices = [...hidDevices, ...usbDevices]
+ .filter(device => device.vendorId === ledgerVendorId)
+ await Promise.allSettled(devices.map(device => device.forget?.() ?? Promise.resolve()))
+ } catch (err) {
+ console.warn('Ledger.disconnect()', err)
+ } finally {
+ await new Promise(r => setTimeout(r, 5000))
+ console.log('Transient user activation period timed out')
+ }
+}
import { signBlock, signNonce } from './sign'
import { _open } from './open'
import { _close } from './close'
+import { _disconnect } from './disconnect'
export type LedgerStatus = 'UNSUPPORTED' | 'DISCONNECTED' | 'BUSY' | 'LOCKED' | 'CONNECTED'
export type LedgerTransport = typeof TransportHID | typeof TransportBLE | typeof TransportUSB
...dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4)
])
+/**
+ * Vendor ID assigned to Ledger for HID and USB interfaces.
+ * https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/devices/src/index.ts#L164
+ */
+export const ledgerVendorId: 0x2c97 = 0x2c97
+
export const STATUS_CODES: Readonly<Record<number, string>> = Object.freeze({
...Object.fromEntries(Object.entries(StatusCodes).map(([k, v]) => [+v, k])),
0x6807: 'APPLICATION_NOT_INSTALLED',
return true
}
- /**
- * Vendor ID assigned to Ledger for HID and USB interfaces.
- * https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/devices/src/index.ts#L164
- */
- static get ledgerVendorId (): 0x2c97 {
- return 0x2c97
- }
-
/**
* Status of the Ledger device connection.
*
*/
static async disconnect (): Promise<void> {
queue(async () => {
- try {
- this.#isPolling = false
- const hidDevices = await navigator?.hid?.getDevices?.() ?? []
- const usbDevices = await navigator?.usb?.getDevices?.() ?? []
- const devices = [...hidDevices, ...usbDevices]
- .filter(device => device.vendorId === this.ledgerVendorId)
- await Promise.allSettled(devices.map(device => device.forget?.() ?? Promise.resolve()))
- this.#setStatus('DISCONNECTED')
- } catch (err) {
- console.warn('Ledger.disconnect()', err)
- } finally {
- await new Promise(r => setTimeout(r, 5000))
- console.log('Transient user activation period timed out')
- }
+ _disconnect()
+ this.#isPolling = false
+ this.#setStatus('DISCONNECTED')
})
}
static async #poll (): Promise<void> {
try {
const isHidPaired = (await navigator.hid?.getDevices?.() ?? [])
- .some(device => device.vendorId === this.ledgerVendorId)
+ .some(device => device.vendorId === ledgerVendorId)
const isUsbPaired = (await navigator.usb?.getDevices?.() ?? [])
- .some(device => device.vendorId === this.ledgerVendorId)
+ .some(device => device.vendorId === ledgerVendorId)
if ((this.#transport === TransportHID && isHidPaired)
|| (this.#transport === TransportUSB && isUsbPaired)) {
await this.connect()