All articles
solanawalletsbip44ed25519

Solana derivation paths: BIP44, m/44'/501', and the Ledger gotcha

Solana wallets derive keys via BIP44 with coin type 501. The canonical path, the Ledger variant, and why two wallets show different addresses from one seed.

Solana wallets are deterministically derived from a BIP39 seed phrase using BIP44 paths. The canonical path uses coin type 501 (Solana's assigned SLIP-44 slot). Knowing the exact path matters for two reasons: importing a seed from a different wallet correctly, and understanding why Ledger sometimes shows a different first address than Phantom.

The canonical paths

text
Phantom, Solflare, Backpack, Glow (and most software wallets):
  Default first account:    m/44'/501'/0'/0'
  Second account:           m/44'/501'/1'/0'
  Third account:            m/44'/501'/2'/0'
  ...

Ledger (Solana app):
  Default first account:    m/44'/501'/0'
  Second account:           m/44'/501'/1'
  Third account:            m/44'/501'/2'

solana-keygen (CLI default, no derivation path):
  Uses the seed bytes directly as the ed25519 private key (no BIP44).
  This is NOT a BIP44 path — it's the raw 64-byte expanded seed.

Three different addresses from the same seed phrase, depending on which wallet imports it:

  • Phantom imports it as m/44'/501'/0'/0'
  • Ledger's Solana app imports it as m/44'/501'/0' (one fewer level)
  • solana-keygen uses the raw seed bytes (no path at all)

This is the source of the most common "I imported my Phantom seed into Solflare and got a different wallet" bug. The derivation path was different. The fix: explicitly select the matching path when importing.

The structure

BIP44 paths read as:

text
m / purpose' / coin_type' / account' / change' / address_index'
m / 44'      / 501'       / 0'        / 0'       (Phantom default)
m / 44'      / 501'       / 0'                   (Ledger default)

Each level is a child key derivation. The ' suffix marks hardened derivation — the child key can only be derived from the parent private key, not the public key. All Solana paths use fully hardened derivation (every level has the apostrophe).

Hardened-only matters because non-hardened derivation lets you derive child public keys from a parent public key. With hardened-only, leaking a public key never compromises sibling addresses — a security property Solana wallets rely on.

Why ed25519 changes BIP32

BIP32 was designed for Bitcoin's secp256k1 curve. Solana uses ed25519. The two curves have different math, so the derivation function had to be adapted — SLIP-10 is the ed25519 variant of BIP32 that every modern Solana wallet implements.

SLIP-10 enforces hardened-only derivation for ed25519 (the math for non-hardened derivation on ed25519 doesn't work cleanly). That's why every level of a Solana derivation path ends in an apostrophe.

Deriving in code

typescript
import { Keypair } from "@solana/web3.js"
import { derivePath } from "ed25519-hd-key"
import * as bip39 from "bip39"

const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
const seed = await bip39.mnemonicToSeed(mnemonic)   // 64 bytes

// Phantom-style derivation (m/44'/501'/0'/0')
const phantomPath = "m/44'/501'/0'/0'"
const phantomKey = derivePath(phantomPath, seed.toString("hex")).key
const phantomKp = Keypair.fromSeed(phantomKey)
console.log("Phantom default:", phantomKp.publicKey.toBase58())

// Ledger-style derivation (m/44'/501'/0')
const ledgerPath = "m/44'/501'/0'"
const ledgerKey = derivePath(ledgerPath, seed.toString("hex")).key
const ledgerKp = Keypair.fromSeed(ledgerKey)
console.log("Ledger default:", ledgerKp.publicKey.toBase58())

// solana-keygen-style (no BIP44, raw seed)
const cliKp = Keypair.fromSeed(seed.slice(0, 32))
console.log("CLI default:", cliKp.publicKey.toBase58())

Three different public keys from the exact same mnemonic. Verify which one matches the address you expect before treating any of them as "your wallet".

Why Phantom uses the extra level

Phantom's m/44'/501'/N'/0' adds a fourth hardened level (/0' for change) to match Ethereum-style address management — different change paths for different purposes. Ledger's Solana app stuck with the three-level form that matches Solana's original CLI conventions.

Neither is "wrong". They're just incompatible. The software-wallet ecosystem standardised on the four-level form; Ledger's firmware locked in the three-level form earlier and can't change without invalidating existing addresses.

Practical recipes

  • Importing a Phantom seed into Solflare: select "Derivation path" and pick the Phantom variant (sometimes labelled m/44'/501'/0'/0').
  • Importing a Ledger seed into Phantom: connect the Ledger directly (Phantom respects the Ledger path). Don't type the seed in — that defeats the point of cold storage.
  • Importing a solana-keygen file into a software wallet: most wallets accept the JSON byte-array directly. The raw key is the wallet — no derivation involved.
  • Generating a fresh wallet from seed in code: stick to m/44'/501'/0'/0' for maximum compatibility with the rest of the Solana wallet ecosystem.

References

Same seed, three possible addresses depending on the path. When importing across wallets, always confirm which derivation path the source wallet used.

Solana derivation paths: BIP44, m/44'/501', and the Ledger gotcha | devrels.xyz