# How Ledger Hardware Wallet Support is Integrated into Nautilus Wallet

> Discover how Nautilus Wallet integrates Ledger hardware wallet support using ledger-ergo-js and Ledger Web transports. Learn about its layered architecture for secure device communication and signing.

- Repository: [Nautilus Team/nautilus-wallet](https://github.com/nautls/nautilus-wallet)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Nautilus Wallet integrates Ledger hardware wallet support through the `ledger-ergo-js` library and official Ledger Web transports, implementing a layered architecture that abstracts device communication via the `createTransport` helper while isolating signing logic in the `Prover` class and UI interactions in dedicated Vue components.**

Nautilus Wallet is an open-source browser extension wallet designed for the Ergo blockchain. To provide users with secure key storage and transaction signing capabilities, the codebase implements comprehensive Ledger hardware wallet support that spans transport configuration, device detection, wallet onboarding, and cryptographic signing workflows.

## Transport Layer Abstraction and Configuration

The integration begins with a minimal abstraction over Ledger's official Web transport libraries. In [`src/common/ledger.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/common/ledger.ts), the `createTransport(type)` function returns either a WebHID or WebUSB transport instance based on the user's preference.

```typescript
// src/common/ledger.ts
export function createTransport(type: TransportType): Promise<Transport> {
  return type === "webhid"
    ? WebHIDTransport.create()          // <‑‑ WebHID path
    : WebUSBTransport.create();         // <‑‑ WebUSB path
}

```

The default transport preference is stored in [`src/constants/settings.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/constants/settings.ts), where the configuration object specifies `"webusb"` as the initial value. The `appStore` ([`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts)) persists this choice and propagates it to both the UI components and the transaction signing logic, allowing runtime switching between transport protocols without code changes.

## Device Detection and Application State Management

Device interaction is handled by the [`LedgerDevice.vue`](https://github.com/nautls/nautilus-wallet/blob/main/LedgerDevice.vue) component located in [`src/components/LedgerDevice.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/components/LedgerDevice.vue). On component mount, it registers `navigator.usb` event listeners for `connect` and `disconnect` events to detect Ledger devices (identified by `vendorId === 0x2c97`).

When a device is detected, the component invokes `createTransport(app.settings.ledger.transport)` to instantiate a communication channel. It then constructs an `ErgoLedgerApp` instance and verifies that the Ergo application is open on the device. If the current application differs from "Ergo", the code calls `device.openApp("Ergo")` and waits for the application switch to complete.

```typescript
// src/components/LedgerDevice.vue – openErgoApp()
ledger = new ErgoLedgerApp(await createTransport(app.settings.ledger.transport));
const currentApp = await ledger.device.getCurrentAppInfo();
if (currentApp.name !== "Ergo") {
  await ledger.device.openApp("Ergo");
}

```

The component maintains UI state—ready, loading, error, or locked—through a `setState()` callback, ensuring users receive real-time feedback during the connection process.

## Wallet Onboarding and Extended Public Key Extraction

New Ledger wallets are added through the [`LedgerConnectView.vue`](https://github.com/nautls/nautilus-wallet/blob/main/LedgerConnectView.vue) component, accessible via the dedicated route `/add/hw/ledger` defined in [`src/extension/popup/router.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/popup/router.ts). When the user initiates the connection, the view invokes `ledgerDevice.openErgoApp()` to ensure the Ergo application is active, then proceeds to extract the wallet's extended public key.

```typescript
// src/views/add/LedgerConnectView.vue – add()
const ledgerApp = new ErgoLedgerApp(
  await createTransport(app.settings.ledger.transport)
).useAuthToken();

const ledgerXpk = await ledgerApp.getExtendedPublicKey("m/44'/429'/0'");
const extendedPublicKey = hex.encode(
  HdKey.fromPublicKey(ledgerXpk, "m/0").extendedPublicKey
);

const walletId = await app.putWallet({
  type: WalletType.Ledger,
  name: walletName.value,
  extendedPublicKey,
});

```

The derivation path `m/44'/429'/0'` follows Ergo's BIP44 standard. The returned public key is transformed into a read-only wallet entry with type `WalletType.Ledger` and persisted via `app.putWallet()` in the `appStore`. This design keeps private keys confined to the hardware device while allowing the wallet software to generate addresses and monitor balances.

## Transaction Signing with the Prover Class

When signing transactions, the `Prover` class in [`src/chains/ergo/transaction/prover.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/chains/ergo/transaction/prover.ts) checks the `this.#useLedger` flag. For Ledger-backed wallets, it instantiates the transport using the stored preference (`this.#ledgerTransportType`), creates an authenticated `ErgoLedgerApp` session via `useAuthToken()`, and calls `signTx()` with mapped inputs and outputs.

```typescript
// src/chains/ergo/transaction/prover.ts – #signTx()
if (this.#useLedger) {
  const ledgerApp = new ErgoLedgerApp(
    await createTransport(this.#ledgerTransportType)
  ).useAuthToken();

  const proofs = await ledgerApp.signTx({
    inputs: mapLedgerInputs(unsigned, unspentBoxes, this.#from),
    dataInputs: mapLedgerDataInputs(dataInputs),
    outputs: mapLedgerOutputs(unsigned),
    distinctTokenIds: unsigned.distinct_token_ids(),
    changeMap: { … }
  }, MAINNET ? Network.Mainnet : Network.Testnet);

  return Transaction.from_unsigned_tx(unsigned, proofs);
}

```

The `signTx` method receives Ergo-specific transaction parameters including distinct token IDs and network identification. Progress updates flow back to the UI through the `StateCallback` mechanism established in [`LedgerDevice.vue`](https://github.com/nautls/nautilus-wallet/blob/main/LedgerDevice.vue), providing visual feedback during the physical device confirmation process.

## Routing and State Persistence

The hardware wallet flow is integrated into the application's routing system through [`src/extension/popup/router.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/popup/router.ts), which registers the `/add/hw/ledger` path to render the connection wizard. State management is centralized in [`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts), where the `settings.ledger.transport` property maintains the user's transport preference across sessions.

This architecture ensures that all Ledger-related code remains isolated behind the `createTransport` abstraction, simplifying testing and future transport protocol additions while maintaining a consistent interface for device communication throughout the wallet lifecycle.

## Summary

- **Transport abstraction**: The `createTransport()` helper in [`src/common/ledger.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/common/ledger.ts) unifies WebHID and WebUSB protocols, allowing runtime configuration via [`src/constants/settings.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/constants/settings.ts).
- **Device detection**: [`src/components/LedgerDevice.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/components/LedgerDevice.vue) handles USB attach/detach events and ensures the Ergo app is open before proceeding.
- **Key extraction**: [`src/views/add/LedgerConnectView.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/views/add/LedgerConnectView.vue) exports the extended public key at path `m/44'/429'/0'` and creates a read-only `WalletType.Ledger` entry via the `appStore`.
- **Transaction signing**: The `Prover` class in [`src/chains/ergo/transaction/prover.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/chains/ergo/transaction/prover.ts) manages the `signTx()` workflow, mapping Ergo transaction data to Ledger-compatible formats and handling network-specific parameters.
- **Integration points**: Dedicated routing in [`src/extension/popup/router.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/popup/router.ts) and centralized state management in [`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts) provide a cohesive user experience for hardware wallet operations.

## Frequently Asked Questions

### Which transport protocols does Nautilus support for Ledger connectivity?

Nautilus supports both **WebHID** and **WebUSB** transports through the official `@ledgerhq/hw-transport-webhid` and `@ledgerhq/hw-transport-webusb` libraries. The active transport is configurable via user settings and defaults to WebUSB as defined in [`src/constants/settings.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/constants/settings.ts).

### How does Nautilus verify that the correct application is open on the Ledger device?

During the connection flow, [`src/components/LedgerDevice.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/components/LedgerDevice.vue) queries the device’s current application information using `ledger.device.getCurrentAppInfo()`. If the returned name does not match "Ergo", the component invokes `device.openApp("Ergo")` to switch applications automatically before proceeding with key extraction or signing.

### What derivation path does Nautilus use when importing Ledger wallets?

Nautilus uses the standard Ergo BIP44 derivation path **`m/44'/429'/0'`** when calling `ledgerApp.getExtendedPublicKey()`. This path corresponds to Ergo's coin type (429) and extracts the account-level extended public key for generating addresses while keeping private keys secured on the hardware device.

### Where is the Ledger signing logic implemented for Ergo transactions?

The signing logic resides in **[`src/chains/ergo/transaction/prover.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/chains/ergo/transaction/prover.ts)** within the `Prover` class. When `this.#useLedger` is enabled, this class creates an authenticated `ErgoLedgerApp` instance, maps transaction inputs and outputs to Ledger-compatible formats using helper functions like `mapLedgerInputs()`, and executes `signTx()` to generate cryptographic proofs without exposing private keys to the host environment.