Skip to Content
📢 Breaking change: Applications using LedgerJS for transport implementation should migrate to the Device Management Kit (DMK). Learn more.
DocumentationLedger Wallet ProviderBlockchainsEVM

EVM with EIP-6963 and EIP-1193

This guide shows how to connect to Ledger Wallet Provider from an EVM dApp and sign messages or transactions through EIP-6963  discovery and the EIP-1193  provider API.

Ledger Wallet Provider registers an EIP-1193 provider when you call initializeLedgerProvider with the EVM factory and Ethereum is enabled in your partner dApp configuration (dAppIdentifier).

Complete Get started first so initializeLedgerProvider runs in the browser (use a dynamic import in SSR frameworks). Install @ledgerhq/ledger-wallet-provider-evm, pass createEvmBlockchainProvider, and confirm with Ledger that Ethereum is enabled for your dApp. Then follow this page for EIP-6963 discovery and signing.

Prerequisites

npm install @ledgerhq/ledger-wallet-provider @ledgerhq/ledger-wallet-provider-evm

Initialize once at application startup (see Get started and Configuration):

import { initializeLedgerProvider } from '@ledgerhq/ledger-wallet-provider' import { createEvmBlockchainProvider } from '@ledgerhq/ledger-wallet-provider-evm' import '@ledgerhq/ledger-wallet-provider/styles.css' const cleanup = initializeLedgerProvider({ dAppIdentifier: 'my-dapp', apiKey: 'your-api-key', blockchainProviderFactories: [ { family: 'ethereum', create: createEvmBlockchainProvider }, ], }) // Call cleanup() when your app unmounts to unregister the EVM provider too.

Supported methods

After connection, use provider.request({ method, params }). Methods handled locally by Ledger include:

MethodPurpose
eth_requestAccountsOpen the Ledger UI and return the selected address
eth_accountsReturn the currently selected account
eth_chainIdReturn the current chain ID (hex)
personal_sign / eth_signSign a message
eth_signTypedData / eth_signTypedData_v4Sign EIP-712 typed data
eth_signTransactionSign a transaction without broadcasting
eth_sendTransactionSign and broadcast a transaction
wallet_switchEthereumChainSwitch to a supported chain ID

Read methods such as eth_call and eth_getBalance are forwarded to the node RPC. See API reference for the full list, events, and error codes.

Discover and connect

Listen for EIP-6963 announcements, then request accounts:

import type { EIP6963ProviderDetail } from '@ledgerhq/ledger-wallet-provider-evm' let provider: EIP6963ProviderDetail['provider'] | undefined window.addEventListener('eip6963:announceProvider', ((event: CustomEvent<EIP6963ProviderDetail>) => { const { provider: announced, info } = event.detail if (info.name.toLowerCase().includes('ledger')) { provider = announced } }) as EventListener) window.dispatchEvent(new Event('eip6963:requestProvider')) if (!provider) { throw new Error('Ledger EVM provider not found. Call initializeLedgerProvider first.') } const accounts = await provider.request({ method: 'eth_requestAccounts', params: [], }) as string[] const account = accounts[0]

Registration: A family is registered only when you pass its factory and it is enabled in your dApp config. EVM factory + Ethereum enabled → EIP-6963 / EIP-1193. Solana factory + Solana enabled → Wallet Standard. Both → both surfaces from the same initializeLedgerProvider call. Account pickers are filtered by the requesting family (eth_requestAccounts only lists EVM-compatible accounts).

Sign a message

const signature = await provider.request({ method: 'personal_sign', params: ['Hello from my EVM dApp', account], })

For EIP-712 typed data, use eth_signTypedData_v4 with your typed-data payload. Signing opens the Ledger Wallet Provider UI so the user can confirm on their device.

Sign a transaction

const signedTx = await provider.request({ method: 'eth_signTransaction', params: [ { from: account, to: '0x…', value: '0x0', // gas, data, chainId, … }, ], }) // `signedTx` is the signed raw transaction (you broadcast yourself if needed)

Sign and send a transaction

const txHash = await provider.request({ method: 'eth_sendTransaction', params: [ { from: account, to: '0x…', value: '0x0', }, ], })

Switch chain

await provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x1' }], // Ethereum mainnet })

Cleanup

Call the function returned by initializeLedgerProvider when your application unmounts. Cleanup removes the Ledger UI and unregisters any providers that were registered for your partner configuration.

const cleanup = initializeLedgerProvider({ /* options */ }) // Later: cleanup()

Troubleshooting

The eip6963:announceProvider event never fires. Confirm initializeLedgerProvider ran in the browser before dispatching eip6963:requestProvider, that you passed createEvmBlockchainProvider, and that Ethereum is enabled for your dAppIdentifier. On unsupported platforms (no Web HID / Web Bluetooth), initialization is a no-op and no provider is registered — see Requirements.

request() rejects with error code 4100 (“Unauthorized”). Call eth_requestAccounts first, or the user disconnected. Request accounts again to reopen the Ledger UI.

request() rejects with error code -32603 and message “Ledger Provider is busy”. Blocking requests (connect / sign) are handled one at a time. Wait for the current Ledger UI flow to finish before starting another.

See also

Last updated on
Ledger
Copyright © Ledger SAS. All rights reserved. Ledger, Ledger Stax, Ledger Flex, Ledger Nano, Ledger Nano S, Ledger OS, Ledger Wallet, [LEDGER] (logo), [L] (logo) are trademarks owned by Ledger SAS.