Skip to main content
This example demonstrates how to make x402 payments using a Ledger hardware wallet on Solana. Hardware wallets provide the highest level of security by keeping private keys on a physical device that never leaves your possession. Source: GitHub › scripts/solana-example/ledger-sol-payment.ts

When to Use Ledger

Use Ledger when:
  • You need maximum security for your keys
  • You’re handling valuable assets or high-value transactions
  • You want physical confirmation of transactions
  • You’re building applications that prioritize security
Use software wallets when:
  • You need programmatic or automated transactions
  • You’re building automated systems
  • You’re testing or developing quickly

Full Example

import {
  createLedgerSolanaWallet,
  selectLedgerAccount,
  createReadlineInterface,
} from "@faremeter/wallet-ledger";
import { createPaymentHandler } from "@faremeter/x-solana-settlement";
import { wrap as wrapFetch } from "@faremeter/fetch";

// Parse command line arguments
const args = process.argv.slice(2);
const port = args[0] ?? "3000";
const endpoint = args[1] ?? "protected";
const url = `http://localhost:${port}/${endpoint}`;

const ui = await createReadlineInterface(process);

ui.message("Connecting to Ledger for Solana payments...");
ui.message("\nRequired Ledger Settings:");
ui.message("1. Open the Solana app on your Ledger");
ui.message("2. When prompted, approve the transaction on your Ledger");

const selected = await selectLedgerAccount(ui, "solana", 5);

if (!selected) {
  process.exit(0);
}

ui.message(`\nUsing account: ${selected.address}`);

const ledgerWallet = await createLedgerSolanaWallet("devnet", selected.path);

const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(ledgerWallet)],
});

ui.message(`\nMaking payment request to ${url}...`);
ui.message("When prompted, confirm the transaction on your Ledger...");

let req;
try {
  req = await fetchWithPayer(url);
} catch (fetchError) {
  await ledgerWallet.disconnect();
  const errorMessage = (fetchError as Error).message;
  ui.message(`\nError: ${errorMessage}`);
  if (
    errorMessage.includes("fetch failed") ??
    errorMessage.includes("ECONNREFUSED")
  ) {
    ui.message(`Are you sure the Faremeter server is running on port ${port}?`);
  }
  process.exit(1);
}

ui.message(`Status: ${req.status}`);
ui.message(`Headers: ${JSON.stringify(Object.fromEntries(req.headers))}`);
const response = await req.json();
ui.message(`Response: ${JSON.stringify(response)}`);

await ledgerWallet.disconnect();
ui.message(`\nSuccess! Ledger payment completed.`);
await ui.close();

Step-by-Step Breakdown

1. Create User Interface

const ui = await createReadlineInterface(process);
Creates a CLI interface for interacting with the user. This is needed for:
  • Displaying prompts and instructions
  • Showing account selection
  • Providing feedback during the payment process

2. Select Ledger Account

ui.message("1. Open the Solana app on your Ledger");
ui.message("2. When prompted, approve the transaction on your Ledger");

const selected = await selectLedgerAccount(ui, "solana", 5);
This function:
  • Scans the first 5 Solana accounts on your Ledger
  • Displays them in the terminal
  • Prompts you to select which account to use
  • Returns the selected account’s address and derivation path

3. Create Ledger Wallet

const ledgerWallet = await createLedgerSolanaWallet("devnet", selected.path);
Connects to your Ledger device and creates a wallet interface. The wallet:
  • Uses the selected derivation path
  • Signs transactions on the device
  • Never exposes private keys to your computer

4. Make Payment

const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(ledgerWallet)],
});

const req = await fetchWithPayer(url);
When a payment is required:
  1. The payment handler creates a transaction
  2. The transaction is sent to your Ledger
  3. You physically confirm on the device
  4. The signed transaction is submitted

5. Cleanup

await ledgerWallet.disconnect();
Always disconnect from the Ledger when done to release the USB connection.

Ledger Setup Requirements

Ledger Solana App Documentation

For detailed setup instructions for the Solana app on Ledger devices, see the official Ledger support documentation.

Hardware Setup

  1. Connect your Ledger device via USB
  2. Unlock your Ledger with your PIN
  3. Open the Solana app on your Ledger

Software Requirements

  • Ledger Live installed (or Ledger CLI tools)
  • Node.js with USB support (works on macOS, Linux, Windows)

Command Line Arguments

  • First argument: Server port (default: 3000)
  • Second argument: Endpoint path (default: protected)
Example:
tsx ledger-sol-payment.ts 3000 protected

Security Considerations

  • Physical confirmation: Every transaction requires you to approve on the device
  • Private keys never leave device: Keys stay secure even if your computer is compromised
  • Transaction verification: Always verify transaction details on the Ledger screen before approving

Error Handling

The example includes error handling for:
  • Missing Ledger connection
  • Server connection failures
  • Transaction rejections on the device

Comparison: Ledger vs Software Wallets

import { createLedgerSolanaWallet } from "@faremeter/wallet-ledger";

const ledgerWallet = await createLedgerSolanaWallet(
  "devnet",
  "44'/501'/0'/0'"  // Derivation path
);

// Requires physical confirmation on device
const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(ledgerWallet)],
});
Tradeoffs:
  • Ledger: Maximum security, requires physical device, slower for automation
  • Software: Fast, automated, but keys stored on computer