]> git.codecow.com Git - libnemo.git/commitdiff
Avoid self references by using static this. Explictly type transport responses and...
authorChris Duncan <chris@zoso.dev>
Wed, 17 Sep 2025 15:00:22 +0000 (08:00 -0700)
committerChris Duncan <chris@zoso.dev>
Wed, 17 Sep 2025 15:00:22 +0000 (08:00 -0700)
src/lib/ledger.ts

index 5649717216efdee4353a16719c21e41d183d3726..fa0de3d48d6701fbbc729645d2c5f63a5975ade2 100644 (file)
@@ -1,8 +1,11 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
+//@ts-expect-error
 import { default as TransportBLE } from '@ledgerhq/hw-transport-web-ble'
+//@ts-expect-error
 import { default as TransportHID } from '@ledgerhq/hw-transport-webhid'
+//@ts-expect-error
 import { default as TransportUSB } from '@ledgerhq/hw-transport-webusb'
 import { LedgerStatus, LedgerAccountResponse, LedgerResponse, LedgerSignResponse, LedgerVersionResponse } from '#types'
 import { Account } from './account'
@@ -37,7 +40,7 @@ export class Ledger {
                paramUnused: 0x00
        })
        static #DERIVATION_PATH: Uint8Array = new Uint8Array([
-               Ledger.#ADPU_CODES.bip32DerivationLevel,
+               this.#ADPU_CODES.bip32DerivationLevel,
                ...dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4),
                ...dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4)
        ])
@@ -106,16 +109,16 @@ export class Ledger {
                        throw new TypeError('Invalid account index')
                }
                const account = dec.toBytes(index + HARDENED_OFFSET, 4)
-               const data = new Uint8Array([...Ledger.#DERIVATION_PATH, ...account])
+               const data = new Uint8Array([...this.#DERIVATION_PATH, ...account])
 
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.account, show ? 1 : 0, Ledger.#ADPU_CODES.paramUnused, data as Buffer)
-                       .catch(err => dec.toBytes(err.statusCode)) as Uint8Array
+                       .send(this.#ADPU_CODES.class, this.#ADPU_CODES.account, show ? 1 : 0, this.#ADPU_CODES.paramUnused, data as Buffer)
+                       .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array
                await transport.close()
 
                const statusCode = bytes.toDec(response.slice(-2)) as number
-               const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
+               const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
                if (status !== 'OK') {
                        return { status, publicKey: null, address: null }
                }
@@ -144,14 +147,14 @@ export class Ledger {
        */
        static async connect (api?: 'hid' | 'ble' | 'usb'): Promise<LedgerStatus> {
                if (api !== undefined) {
-                       if (api === 'hid' && Ledger.#transport !== TransportHID) {
-                               Ledger.#transport = TransportHID
+                       if (api === 'hid' && this.#transport !== TransportHID) {
+                               this.#transport = TransportHID
                        }
-                       if (api === 'ble' && Ledger.#transport !== TransportBLE) {
-                               Ledger.#transport = TransportBLE
+                       if (api === 'ble' && this.#transport !== TransportBLE) {
+                               this.#transport = TransportBLE
                        }
-                       if (api === 'usb' && Ledger.#transport !== TransportUSB) {
-                               Ledger.#transport = TransportUSB
+                       if (api === 'usb' && this.#transport !== TransportUSB) {
+                               this.#transport = TransportUSB
                        }
                }
                try {
@@ -219,13 +222,13 @@ export class Ledger {
                                throw new RangeError(`Index outside allowed range 0-${HARDENED_OFFSET}`, { cause: index })
                        }
                        if (frontier != null) {
-                               const { status } = await Ledger.#cacheBlock(index, frontier)
+                               const { status } = await this.#cacheBlock(index, frontier)
                                if (status !== 'OK') {
                                        throw new Error('Failed to cache frontier block in ledger', { cause: status })
                                }
                        }
                        console.log('Waiting for signature confirmation on Ledger device...')
-                       const { status, signature, hash } = await Ledger.#signBlock(index, block)
+                       const { status, signature, hash } = await this.#signBlock(index, block)
                        if (status !== 'OK') {
                                throw new Error('Signing with ledger failed', { cause: status })
                        }
@@ -306,7 +309,7 @@ export class Ledger {
                        .sign(testWallet, 0)
                const testSignature = testSendBlock.signature
                try {
-                       const ledgerSignature = await Ledger.sign(0, testSendBlock, testOpenBlock)
+                       const ledgerSignature = await this.sign(0, testSendBlock, testOpenBlock)
                        return ledgerSignature === testSignature
                } catch (err) {
                        throw new Error('Failed to verify wallet', { cause: err })
@@ -345,16 +348,16 @@ export class Ledger {
                const representative = hex.toBytes(block.representative.publicKey, 32)
                const balance = hex.toBytes(block.balance.toString(16), 16)
                const signature = hex.toBytes(block.signature, 64)
-               const data = new Uint8Array([Ledger.#ADPU_CODES.bip32DerivationLevel, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature])
+               const data = new Uint8Array([this.#ADPU_CODES.bip32DerivationLevel, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature])
 
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.cacheBlock, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, data as Buffer)
-                       .then(res => bytes.toDec(res))
-                       .catch(err => err.statusCode) as number
+                       .send(this.#ADPU_CODES.class, this.#ADPU_CODES.cacheBlock, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, data as Buffer)
+                       .then((res: Buffer) => bytes.toDec(res))
+                       .catch((err: any) => err.statusCode) as number
                await transport.close()
 
-               return { status: Ledger.#STATUS_CODES[response] }
+               return { status: this.#STATUS_CODES[response] }
        }
 
        /**
@@ -371,17 +374,17 @@ export class Ledger {
        * @returns Status of command
        */
        static async #close (): Promise<LedgerResponse> {
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(0xb0, 0xa7, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused)
-                       .then(res => bytes.toDec(res))
-                       .catch(err => err.statusCode) as number
-               return new Promise(r => setTimeout(r, 1000, { status: Ledger.#STATUS_CODES[response] }))
+                       .send(0xb0, 0xa7, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused)
+                       .then((res: Buffer) => bytes.toDec(res))
+                       .catch((err: any) => err.statusCode) as number
+               return new Promise(r => setTimeout(r, 1000, { status: this.#STATUS_CODES[response] }))
        }
 
        static #onConnectHid = async (e: HIDConnectionEvent): Promise<void> => {
                console.log(e)
-               if (e.device?.vendorId === ledgerUSBVendorId) {
+               if (e.device?.vendorId === this.UsbVendorId) {
                        console.log('Ledger connected via HID')
                        await this.connect()
                        this.#polling = setInterval(this.connect, 1000)
@@ -393,7 +396,7 @@ export class Ledger {
 
        static #onDisconnectHid = async (e: HIDConnectionEvent): Promise<void> => {
                console.log(e)
-               if (e.device?.vendorId === ledgerUSBVendorId) {
+               if (e.device?.vendorId === this.UsbVendorId) {
                        console.log('Ledger disconnected via HID')
                        clearInterval(this.#polling)
                        await this.connect()
@@ -406,7 +409,7 @@ export class Ledger {
 
        static #onConnectUsb = async (e: USBConnectionEvent): Promise<void> => {
                console.log(e)
-               if (e.device?.vendorId === ledgerUSBVendorId) {
+               if (e.device?.vendorId === this.UsbVendorId) {
                        console.log('Ledger connected via USB')
                        await this.connect()
                        this.#polling = setInterval(this.connect, 1000)
@@ -418,7 +421,7 @@ export class Ledger {
 
        static #onDisconnectUsb = async (e: USBConnectionEvent): Promise<void> => {
                console.log(e)
-               if (e.device?.vendorId === ledgerUSBVendorId) {
+               if (e.device?.vendorId === this.UsbVendorId) {
                        console.log('Ledger disconnected via USB')
                        clearInterval(this.#polling)
                        await this.connect()
@@ -444,12 +447,12 @@ export class Ledger {
        */
        static async #open (): Promise<LedgerResponse> {
                const name = new TextEncoder().encode('Nano')
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(0xe0, 0xd8, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, name as Buffer)
-                       .then(res => bytes.toDec(res))
-                       .catch(err => err.statusCode) as number
-               return new Promise(r => setTimeout(r, 1000, { status: Ledger.#STATUS_CODES[response] }))
+                       .send(0xe0, 0xd8, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, name as Buffer)
+                       .then((res: Buffer) => bytes.toDec(res))
+                       .catch((err: any) => err.statusCode) as number
+               return new Promise(r => setTimeout(r, 1000, { status: this.#STATUS_CODES[response] }))
        }
 
        /**
@@ -475,16 +478,16 @@ export class Ledger {
                const link = block.link
                const representative = hex.toBytes(block.representative.publicKey, 32)
                const balance = hex.toBytes(BigInt(block.balance).toString(16), 16)
-               const data = new Uint8Array([...Ledger.#DERIVATION_PATH, ...account, ...previous, ...link, ...representative, ...balance])
+               const data = new Uint8Array([...this.#DERIVATION_PATH, ...account, ...previous, ...link, ...representative, ...balance])
 
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.signBlock, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, data as Buffer)
-                       .catch(err => dec.toBytes(err.statusCode)) as Uint8Array
+                       .send(this.#ADPU_CODES.class, this.#ADPU_CODES.signBlock, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, data as Buffer)
+                       .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array
                await transport.close()
 
                const statusCode = bytes.toDec(response.slice(-2)) as number
-               const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
+               const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
 
                if (response.byteLength === 2) {
                        return { status, signature: null }
@@ -516,16 +519,16 @@ export class Ledger {
                }
 
                const derivationAccount = dec.toBytes(index + HARDENED_OFFSET, 4)
-               const data = new Uint8Array([...Ledger.#DERIVATION_PATH, ...derivationAccount, ...nonce])
+               const data = new Uint8Array([...this.#DERIVATION_PATH, ...derivationAccount, ...nonce])
 
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.signNonce, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, data as Buffer)
-                       .catch(err => dec.toBytes(err.statusCode)) as Uint8Array
+                       .send(this.#ADPU_CODES.class, this.#ADPU_CODES.signNonce, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, data as Buffer)
+                       .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array
                await transport.close()
 
                const statusCode = bytes.toDec(response.slice(-2)) as number
-               const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
+               const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
 
                if (response.byteLength === 2) {
                        return { status, signature: null }
@@ -547,14 +550,14 @@ export class Ledger {
        * @returns Status, process name, and version
        */
        static async #version (): Promise<LedgerVersionResponse> {
-               const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout)
+               const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout)
                const response = await transport
-                       .send(0xb0, Ledger.#ADPU_CODES.version, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused)
-                       .catch(err => dec.toBytes(err.statusCode)) as Uint8Array
+                       .send(0xb0, this.#ADPU_CODES.version, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused)
+                       .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array
                await transport.close()
 
                const statusCode = bytes.toDec(response.slice(-2)) as number
-               const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
+               const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR'
                if (status !== 'OK') {
                        return { status, name: null, version: null }
                }