Back to blog
2026-06-027 min read

Self-Custody: Keys, Seeds, and Derivation Paths

What self-custody actually requires: how a mnemonic becomes an address, what you must back up, and how to verify a wallet works without a network.

Self-CustodyWalletsBIP39Ethers.js

What Self-Custody Means in Practice

Self-custody means you hold the material that can move the funds. For most wallets that material is a mnemonic phrase: 12 or 24 words that derive every key the wallet will ever use.

The chain from phrase to address has three standards: BIP39 (mnemonic to seed), BIP32 (seed to master key), BIP44 (master key to accounts). Understanding them is what lets you verify a wallet instead of trusting its UI.

Mnemonic to Address, Step by Step

BIP39 turns entropy into words. The 12 or 24 words are a checksummed encoding of a random number. BIP32 takes the seed and walks down a tree of keys. BIP44 fixes the path so every wallet derives the same account from the same phrase.

Ethers.js hides most of this. The full path, for reference:

import { Wallet, HDNodeWallet } from "ethers";

// 1. Generate a phrase. This is what you back up.
const wallet = HDNodeWallet.createRandom();
console.log(wallet.mnemonic.phrase);
// "candy maple cake ..."

// 2. The same phrase always derives the same account.
const restored = HDNodeWallet.fromPhrase(wallet.mnemonic.phrase);
console.log(restored.address === wallet.address); // true

// 3. Derive account #0 on Ethereum mainnet (path m/44'/60'/0'/0/0)
const account = HDNodeWallet.fromPhrase(wallet.mnemonic.phrase, undefined, "m/44'/60'/0'/0/0");
console.log(account.address);

// 4. A passphrase changes everything. Empty passphrase !== "hunter2".
const secured = HDNodeWallet.fromPhrase(wallet.mnemonic.phrase, "hunter2");
console.log(secured.address !== wallet.address); // true

The derivation path m/44'/60'/0'/0/0 breaks down as: BIP44 purpose, coin type 60 (Ethereum), account 0, external chain, address index 0. Change the coin type and the same phrase produces an address on another chain. This is why a phrase can manage Ethereum and Solana keys from one backup.

What to Back Up

Back up exactly three things:

  • The words, in order. Wrong order, wrong wallet.
  • The passphrase, if you used one. A phrase without its passphrase is a different wallet, one the phrase alone does not open.
  • The derivation path, if it is unusual. Standard paths are recoverable by any wallet. Custom paths are not.

Do not back up: screenshots of the phrase, the phrase in cloud notes, the phrase in a chat. The words on paper, split across two physical locations, remain the baseline.

Verifying a Wallet Without a Network

Before moving real funds, prove the wallet holds the key. Sign a message and verify it. This works offline and never touches a chain:

import { Wallet, verifyMessage } from "ethers";

const wallet = new Wallet(account.privateKey);

const message = "I control this wallet";
const signature = await wallet.signMessage(message);

const recovered = verifyMessage(message, signature);
console.log(recovered === wallet.address); // true

If the recovered address matches, the software holds the key. Do the same test after restoring the phrase on a fresh device, before sending anything.

Common Failures

  • 12 vs 24 words: 24 words do not mean more security for the same entropy. Either is fine; both must be backed up fully.
  • Typing the phrase into a site: no legitimate service asks for the phrase. This rule alone filters most drainers.
  • Backing up after importing: the phrase you restored from must be rewritten and checked, not assumed.

Self-custody is a chain of verified steps, not a checkbox. Generate, back up, restore, verify. Then test with a small transfer. The rest is repetition.