Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ledger Setup

Sign transactions with a Ledger hardware wallet. The CLI integrates via @ledgerhq/hw-app-eth over USB-HID — every write prompts on the device before broadcast.

{
  "profiles": {
    "cold": {
      "chain": "ethereum",
      "rpcUrl": "https://eth.llamarpc.com",
      "signer": { "type": "ledger", "derivationPath": "m/44'/60'/0'/0/0" }
    }
  }
}

Install the optional deps

Ledger support is opt-in — the native HID stack adds a few hundred KB of bindings most consumers don’t want. The SDK lists them under optionalDependencies so a stock pnpm add @koed_jang/apyx-sdk does not pull them in:

pnpm add @ledgerhq/hw-transport-node-hid @ledgerhq/hw-app-eth

Both packages must be present at runtime, dynamically loaded by the CLI when it sees a signer.type = "ledger" profile. If they’re missing the CLI fails with a clear hint:

apyx: Ledger support requires optional deps. Install them with:
  pnpm add @ledgerhq/hw-transport-node-hid @ledgerhq/hw-app-eth

Add a Ledger profile

Edit the config file directly (the wizard supports it via signer type: ledger):

{
  "defaultProfile": "default",
  "profiles": {
    "default": {
      "chain": "ethereum",
      "rpcUrl": "https://eth.llamarpc.com",
      "signer": { "type": "key", "keyPath": "~/.apyx/keys/default" }
    },
    "cold": {
      "chain": "ethereum",
      "rpcUrl": "https://eth.llamarpc.com",
      "signer": {
        "type": "ledger",
        "derivationPath": "m/44'/60'/0'/0/0"
      }
    }
  }
}

The derivationPath is whatever path Ledger Live shows for the account you want — m/44'/60'/0'/0/N for the Nth account on a Legacy wallet, m/44'/60'/N'/0/0 for the Nth on a default 24-word setup.

Use it

  1. Plug the device in.

  2. Unlock it.

  3. Open the Ethereum app on the device.

  4. Run the CLI:

    $ apyx repl --profile cold
    @apyx-labs/sdk
    profile: cold (from /path/to/cwd/apyx.config.json)
    chain:   ethereum (1)
    rpc:     https://eth.llamarpc.com
    account: 0x1234…abcd
    

The address is fetched from the device — no key material ever leaves the device.

What you’ll see during a write

Every approve, deposit, redeem, etc. prompts on the device’s screen. You confirm:

  • the destination contract,
  • the function and decoded arguments,
  • the chainId,
  • the gas budget.

Only after you press Approve on the hardware does the CLI hand the tx to the RPC. There is no way to “auto-sign” — that’s the point.

apyx> await apyx.apyUSD.deposit({ assets: 1_000_000n, receiver: account.address })
[ledger]: review tx on device…
[ledger]: signed
{ hash: '0xbc9e…1d44', wait: [Function: wait] }

Reject on the device and the CLI surfaces a User refused on Ledger error.

Linux: udev rules

Linux needs a one-time udev rule so non-root processes can talk to the Ledger HID interface. The Ledger team publishes the canonical script:

wget -q -O - https://raw.githubusercontent.com/LedgerHQ/udev-rules/master/add_udev_rules.sh \
  | sudo bash

Re-plug the device after the rules land. Without this you’ll see Cannot open device when the CLI tries to enumerate USB.

macOS and Windows do not need extra rules — the bundled drivers work out of the box.

Cleanup

The HID transport is closed when:

  • The REPL exits via .exit, Ctrl-D, or Ctrl-C.
  • A non-interactive subcommand finishes (success or error).

If the device starts behaving oddly (DisconnectedDeviceDuringOperation, HID handle stuck), unplug + re-plug to reset the kernel-side state.

Testing without a device

The Ledger code path has an e2e spec that exercises the production dispatch using a mock factory backed by a real private key — no physical hardware required. See test/e2e/cli/ledger.e2e.spec.ts. That same factory hook lets you swap a mock in your own test harness; pass ledgerFactory to resolveSigner.