From bdad11a7c703bc812fb13f4ea755c9da069f83b7 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Thu, 27 Nov 2025 12:32:10 +0800 Subject: [PATCH 1/2] accounts/usbwallet: add support for Ledger Nano Gen5 --- accounts/usbwallet/hub.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go index 81457b7da27a..92d246de5e30 100644 --- a/accounts/usbwallet/hub.go +++ b/accounts/usbwallet/hub.go @@ -82,6 +82,7 @@ func NewLedgerHub() (*Hub, error) { 0x0005, /* Ledger Nano S Plus */ 0x0006, /* Ledger Nano FTS */ 0x0007, /* Ledger Flex */ + 0x0008, /* Ledger Nano Gen5 */ 0x0000, /* WebUSB Ledger Blue */ 0x1000, /* WebUSB Ledger Nano S */ @@ -89,6 +90,7 @@ func NewLedgerHub() (*Hub, error) { 0x5000, /* WebUSB Ledger Nano S Plus */ 0x6000, /* WebUSB Ledger Nano FTS */ 0x7000, /* WebUSB Ledger Flex */ + 0x8000, /* WebUSB Ledger Nano Gen5 */ }, 0xffa0, 0, newLedgerDriver) } From a52bb5aa00b923f3742787a63b0a2cf29670ae9a Mon Sep 17 00:00:00 2001 From: mmsqe Date: Thu, 27 Nov 2025 12:34:10 +0800 Subject: [PATCH 2/2] add test --- accounts/usbwallet/hub.go | 58 ++++++++++++++++++++-------------- accounts/usbwallet/hub_test.go | 29 +++++++++++++++++ 2 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 accounts/usbwallet/hub_test.go diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go index 92d246de5e30..4c5b37c41a64 100644 --- a/accounts/usbwallet/hub.go +++ b/accounts/usbwallet/hub.go @@ -43,6 +43,36 @@ const refreshCycle = time.Second // trashing. const refreshThrottling = 500 * time.Millisecond +const ( + // deviceUsagePage identifies Ledger devices by HID usage page (0xffa0) on Windows and macOS. + // See: https://github.com/LedgerHQ/ledger-live/blob/05a2980e838955a11a1418da638ef8ac3df4fb74/libs/ledgerjs/packages/hw-transport-node-hid-noevents/src/TransportNodeHid.ts + deviceUsagePage = 0xffa0 + // deviceInterface identifies Ledger devices by USB interface number (0) on Linux. + deviceInterface = 0 +) + +// ledgerProductIDs contains all supported Ledger USB product IDs (legacy and WebUSB). +// Device definitions taken from +// https://github.com/LedgerHQ/ledger-live/blob/05a2980e838955a11a1418da638ef8ac3df4fb74/libs/ledgerjs/packages/devices/src/index.ts +var ledgerProductIDs = []uint16{ + // Original product IDs + 0x0000, /* Ledger Blue */ + 0x0001, /* Ledger Nano S */ + 0x0004, /* Ledger Nano X */ + 0x0005, /* Ledger Nano S Plus */ + 0x0006, /* Ledger Nano FTS */ + 0x0007, /* Ledger Flex */ + 0x0008, /* Ledger Nano Gen5 */ + + 0x0000, /* WebUSB Ledger Blue */ + 0x1000, /* WebUSB Ledger Nano S */ + 0x4000, /* WebUSB Ledger Nano X */ + 0x5000, /* WebUSB Ledger Nano S Plus */ + 0x6000, /* WebUSB Ledger Nano FTS */ + 0x7000, /* WebUSB Ledger Flex */ + 0x8000, /* WebUSB Ledger Nano Gen5 */ +} + // Hub is a accounts.Backend that can find and handle generic USB hardware wallets. type Hub struct { scheme string // Protocol scheme prefixing account and wallet URLs. @@ -70,28 +100,7 @@ type Hub struct { // NewLedgerHub creates a new hardware wallet manager for Ledger devices. func NewLedgerHub() (*Hub, error) { - return newHub(LedgerScheme, 0x2c97, []uint16{ - - // Device definitions taken from - // https://github.com/LedgerHQ/ledger-live/blob/595cb73b7e6622dbbcfc11867082ddc886f1bf01/libs/ledgerjs/packages/devices/src/index.ts - - // Original product IDs - 0x0000, /* Ledger Blue */ - 0x0001, /* Ledger Nano S */ - 0x0004, /* Ledger Nano X */ - 0x0005, /* Ledger Nano S Plus */ - 0x0006, /* Ledger Nano FTS */ - 0x0007, /* Ledger Flex */ - 0x0008, /* Ledger Nano Gen5 */ - - 0x0000, /* WebUSB Ledger Blue */ - 0x1000, /* WebUSB Ledger Nano S */ - 0x4000, /* WebUSB Ledger Nano X */ - 0x5000, /* WebUSB Ledger Nano S Plus */ - 0x6000, /* WebUSB Ledger Nano FTS */ - 0x7000, /* WebUSB Ledger Flex */ - 0x8000, /* WebUSB Ledger Nano Gen5 */ - }, 0xffa0, 0, newLedgerDriver) + return newHub(LedgerScheme, 0x2c97, ledgerProductIDs, deviceUsagePage, deviceInterface, newLedgerDriver) } // NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices. @@ -168,7 +177,7 @@ func (hub *Hub) refreshWallets() { return } } - infos, err := hid.Enumerate(hub.vendorID, 0) + infos, err := usbEnumerate(hub.vendorID, 0) if err != nil { failcount := hub.enumFails.Add(1) if runtime.GOOS == "linux" { @@ -288,3 +297,6 @@ func (hub *Hub) updater() { hub.stateLock.Unlock() } } + +// usbEnumerate is an alias for hid.Enumerate, allowing for easier mocking/testing. +var usbEnumerate = hid.Enumerate diff --git a/accounts/usbwallet/hub_test.go b/accounts/usbwallet/hub_test.go new file mode 100644 index 000000000000..3b360296cb29 --- /dev/null +++ b/accounts/usbwallet/hub_test.go @@ -0,0 +1,29 @@ +package usbwallet + +import ( + "fmt" + "testing" + + "github.com/karalabe/hid" +) + +func TestWallets(t *testing.T) { + devices := make([]hid.DeviceInfo, 0) + for i, productID := range ledgerProductIDs { + devices = append(devices, hid.DeviceInfo{ + ProductID: productID, UsagePage: deviceUsagePage, Interface: deviceInterface, + Path: fmt.Sprintf("/dev/hidraw%d", i), + }) + } + usbEnumerate = func(vendorID, productID uint16) ([]hid.DeviceInfo, error) { return devices, nil } + defer func() { usbEnumerate = hid.Enumerate }() + + hub, err := NewLedgerHub() + if err != nil { + t.Fatalf("Failed to create hub: %v", err) + } + wallets := hub.Wallets() + if len(wallets) != len(devices) { + t.Errorf("Expected %d wallets, got %d", len(devices), len(wallets)) + } +}