@synfutures/viem-ledger
v0.0.4
Published
Ledger helpers for viem accounts
Downloads
142
Readme
@synfutures/viem-ledger
Minimal Ledger helpers for viem, following ethers-ledger conventions:
ledgerToAccount({ signerId | path | index })→Account<'ledger'>- Built-in path parsing using standard BIP44 format:
m/44'/60'/{account_index}'/0/0 - Env overrides:
LEDGER_PATH(full path) orLEDGER_INDEX(account index, default0) - Address validation modes:
never | initializationOnly | firstTransactionPerAddress (default) | everyTransaction
Features:
- Transport/Eth singleton with retry on
TransportLocked - Transaction resolution (
externalPlugins+erc20) for proper on-device prompts - EIP-712 signing support
- Basic app checks (warn if not on Ethereum app, or arbitrary data disabled)
Usage:
import { ledgerToAccount } from '@synfutures/viem-ledger';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
const account = await ledgerToAccount({ signerId: 'ledger:0' });
const walletClient = createWalletClient({ account, chain: base, transport: http() });Notes:
- Node 18+ and
@ledgerhq/hw-transport-node-hidrequire USB/HID access. - Keep your Ledger on and open the Ethereum app. Contract data should be enabled for DeFi/ERC20.
Path Format (BIP44)
The derivation path follows the BIP44 standard for Ethereum, using account-based indexing:
m / 44' / 60' / {account_index}' / 0 / 0
│ │ │ │ │ └─ Address index (always 0)
│ │ │ │ └───── Change (always 0 for Ethereum)
│ │ │ └────────────────────── Account index (incremented for multiple accounts)
│ │ └──────────────────────────── Coin type (60 = Ethereum)
│ └────────────────────────────────── Purpose (44 = BIP44)
└────────────────────────────────────── Master keyThis matches the behavior of MetaMask, Ledger Live, and web3-context's LedgerSignerModule, where creating multiple accounts increments the account_index, not the address_index.
Why account-based indexing?
Most wallets (MetaMask, Ledger Live) use the account level for creating multiple wallets:
- Account 0:
m/44'/60'/0'/0/0(default) - Account 1:
m/44'/60'/1'/0/0(second account) - Account 2:
m/44'/60'/2'/0/0(third account)
This ensures backward compatibility with existing configurations.
Usage Examples
Using account index (recommended)
// m/44'/60'/0'/0/0 (Account 0)
await ledgerToAccount({ signerId: 'ledger:0' });
await ledgerToAccount({ index: 0 });
// m/44'/60'/5'/0/0 (Account 5)
await ledgerToAccount({ signerId: 'ledger:5' });
await ledgerToAccount({ index: 5 });Using explicit full path
// Custom full path (account 3)
await ledgerToAccount({ signerId: 'ledger:m/44\'/60\'/3\'/0/0' });
await ledgerToAccount({ path: 'm/44\'/60\'/3\'/0/0' });
// Custom full path with different address_index
await ledgerToAccount({ path: 'm/44\'/60\'/0\'/0/5' });Custom derivations
- Base path is fixed to
m/44'/60'(ethers-ledger style). If you need a non-standard path (different coin/account/change), pass the full path viapathorsignerId: 'ledger:m/.../0/0'.
Environment Variables
LEDGER_INDEX (account index)
Sets the default account index:
# Use account 3 by default
LEDGER_INDEX=3 # → m/44'/60'/3'/0/0LEDGER_PATH (full path)
Sets the complete derivation path:
# Use specific full path
LEDGER_PATH="m/44'/60'/5'/0/0" # → m/44'/60'/5'/0/0Precedence
LEDGER_INDEXtakes precedence overLEDGER_PATH- Only
LEDGER_INDEX=5set → usesm/44'/60'/5'/0/0 - Only
LEDGER_PATH=m/44'/60'/10'/0/0set → uses that path as-is - Both set →
LEDGER_INDEXwins
Batch Usage
ledgerToAccount accepts a single signerId/path/index per call. If you need range expansion (e.g. ledger:0-2), expand it in your app (or use expandSignerIdPattern from @synfutures/viem-kit) and call ledgerToAccount for each entry.
Migration Notes
If migrating from ethers-ledger or web3-context:
- ✅
ledger:0still maps to the same address (m/44'/60'/0'/0/0) - ✅
ledger:1still maps to the same address (m/44'/60'/1'/0/0) - ✅
LEDGER_INDEX=Nbehavior is preserved - ✅ No breaking changes for existing configurations
