Skip to content

Repository files navigation

electrumnemonic

A Go library and CLI for generating, validating, and deriving seeds from Electrum wallet mnemonics — both the legacy v1 (old-style, 12-word) format and the modern v2 format (with version-prefixed seed types: standard, segwit, and 2FA).

Why this package?

  • Multi-language wordlists — English, Chinese (Simplified), Japanese, Portuguese, and Spanish for v2.
  • Compressed and uncompressed keys — control WIF / public-key compression on derivation for both v1 and v2.
  • Multi-chain support — Bitcoin, Litecoin, Dash, Ravencoin, Bitcoin Cash, and Bitcoin SV out of the box.
  • BIP-39 skip — generation discards phrases that are also valid BIP-39 mnemonics, matching official Electrum behaviour.
  • Wordlist validation — v2 validation checks that every word exists in the active wordlist (not only the version prefix).
  • Full v1 + v2 coverage — encode/decode, generate, validate, seed derivation, and BIP32-style address derivation for both formats.

Features

  • Electrum v1 (electrumv1)
    • Generate 12-word mnemonics from random entropy
    • Encode/decode between hex entropy and mnemonic words
    • Validate mnemonics
    • Derive seed bytes (entropy) from a mnemonic
    • Derive addresses / WIF across supported networks (compressed or uncompressed)
  • Electrum v2 (electrumv2)
    • Generate mnemonics for a chosen seed version (standard, segwit, 2fa, 2fa-segwit) and strength (132 or 264 bits)
    • Encode/decode entropy to/from mnemonic words
    • Validate a mnemonic against a specific seed version prefix and wordlist membership
    • Derive a 64-byte BIP-39-style PBKDF2 seed from a mnemonic (with optional passphrase)
    • Detect which seed version a mnemonic matches
    • Multiple wordlists: English, Chinese (Simplified), Japanese, Portuguese, Spanish
    • Address / key derivation with compressed or uncompressed keys
  • CLI (electrumnemonic) wrapping both packages for quick command-line use

Installation

go get github.com/postnd/electrumnemonic

Or clone and build the CLI:

git clone https://github.com/postnd/electrumnemonic
cd electrumnemonic
go build -o electrumnemonic ./cmd/electrumnemonic

Library usage

Generate

package main

import (
	"fmt"
	"log"

	"github.com/postnd/electrumnemonic/pkg/electrumv1"
	"github.com/postnd/electrumnemonic/pkg/electrumv2"
)

func main() {
	// v1 — always 12 words
	m1, err := electrumv1.GenerateMnemonic()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("v1:", m1)

	// v2 — default segwit, 132-bit (~12 words); skip BIP-39-valid phrases
	m2, err := electrumv2.GenerateMnemonic(electrumv2.VersionSegwit, electrumv2.Strength132, true)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("v2 segwit 12w:", m2)

	// v2 — 24-word (264-bit) standard mnemonic
	m24, err := electrumv2.GenerateMnemonic(electrumv2.VersionStandard, electrumv2.Strength264, true)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("v2 standard 24w:", m24)
}

Validate

package main

import (
	"fmt"

	"github.com/postnd/electrumnemonic/pkg/electrumv1"
	"github.com/postnd/electrumnemonic/pkg/electrumv2"
)

func main() {
	// v1
	ok := electrumv1.ValidateMnemonic("heartbeat some whole laugh self distance okay radio battle pop quite cloud")
	fmt.Println("v1 valid:", ok)

	// v2 — checks wordlist membership + version prefix
	ok = electrumv2.ValidateMnemonic(
		"whip arrow install joy spin labor vivid certain view fire radar abuse",
		electrumv2.VersionSegwit,
	)
	fmt.Println("v2 segwit valid:", ok)

	// Detect any known version
	version := electrumv2.DetectVersion("donkey dish social eternal tissue inform puzzle athlete upon foot mutual noble")
	fmt.Println("v2 version:", version)
}

Derivation

package main

import (
	"fmt"
	"log"

	"github.com/postnd/electrumnemonic/pkg/electrumv1"
	"github.com/postnd/electrumnemonic/pkg/electrumv2"
	"github.com/postnd/electrumnemonic/pkg/network"
)

func main() {
	// v2 segwit → Bitcoin native SegWit, compressed WIF
	addrs, err := electrumv2.FromMnemonic(
		"whip arrow install joy spin labor vivid certain view fire radar abuse",
		"", // passphrase
		network.NetworkBitcoin,
		false, // receiving chain
		1,     // count
		true,  // compressed
	)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("v2 path=%s address=%s wif=%s\n",
		addrs[0].DerivationPath, addrs[0].Address, addrs[0].PrivateKeyWIF)

	// v2 standard → Litecoin, uncompressed
	addrs, err = electrumv2.FromMnemonic(
		"regret elephant online fruit neck pave debate mansion mobile future spin suffer",
		"",
		network.NetworkLitecoin,
		false,
		1,
		false, // uncompressed
	)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("v2 LTC path=%s address=%s wif=%s\n",
		addrs[0].DerivationPath, addrs[0].Address, addrs[0].PrivateKeyWIF)

	// v1 → Bitcoin (uncompressed in this example)
	v1addrs, err := electrumv1.FromMnemonic(
		"heartbeat some whole laugh self distance okay radio battle pop quite cloud",
		network.NetworkBitcoin,
		false,
		1,
		false,
	)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("v1 path=%s address=%s wif=%s\n",
		v1addrs[0].DerivationPath, v1addrs[0].Address, v1addrs[0].PrivateKeyWIF)
}

Electrum v1 (encode / decode / seed)

package main

import (
	"encoding/hex"
	"fmt"
	"log"

	"github.com/postnd/electrumnemonic/pkg/electrumv1"
)

func main() {
	mnemonic, err := electrumv1.GenerateMnemonic()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("mnemonic:", mnemonic)

	mnemonic, err = electrumv1.Encode("232ccc9d2352b116c4e2222c04d73c86")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("encoded:", mnemonic)

	entropy, err := electrumv1.Decode(mnemonic)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("entropy:", hex.EncodeToString(entropy))

	ok := electrumv1.ValidateMnemonic(mnemonic)
	fmt.Println("valid:", ok)
}

Electrum v2 (seed / detect / languages)

package main

import (
	"encoding/hex"
	"fmt"
	"log"

	"github.com/postnd/electrumnemonic/pkg/electrumv2"
)

func main() {
	mnemonic, err := electrumv2.GenerateMnemonic(electrumv2.VersionSegwit, electrumv2.Strength132, true)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("mnemonic:", mnemonic)

	ok := electrumv2.ValidateMnemonic(mnemonic, electrumv2.VersionSegwit)
	fmt.Println("valid:", ok)

	seed, err := electrumv2.MnemonicToSeed(mnemonic, "optional-passphrase")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("seed:", hex.EncodeToString(seed))

	version := electrumv2.DetectVersion(mnemonic)
	fmt.Println("detected version:", version)

	electrumv2.SetWordlistLanguage(electrumv2.WordlistJapanese)
}

Supported seed versions:

Version Prefix Description
VersionStandard 01 Legacy P2PKH / P2SH
VersionSegwit 100 Native SegWit (bech32)
Version2FA 101 TrustedCoin 2FA (legacy)
Version2FASegwit 102 TrustedCoin 2FA + SegWit

Address / key derivation

Derivation lives inside the electrumv1 and electrumv2 packages and uses btcsuite/btcd's hdkeychain (v2) or Electrum's classic stretch-and-add scheme (v1).

FromMnemonic / FromSeed take a change bool to select receiving vs change:

Version Receiving (change=false) Change (change=true)
VersionSegwit / Version2FASegwit m/0'/0/<index> m/0'/1/<index>
VersionStandard / Version2FA m/0/<index> m/1/<index>
Electrum v1 m/0/<index> m/1/<index>

Built-in networks:

Network Network constant Native SegWit?
Bitcoin NetworkBitcoin Yes
Litecoin NetworkLitecoin Yes
Dash NetworkDash No
Ravencoin NetworkRavencoin No
Bitcoin Cash NetworkBitcoinCash No
Bitcoin SV NetworkBitcoinSV No

For networks without native SegWit support, deriving with VersionSegwit / Version2FASegwit will produce invalid addresses — use VersionStandard / Version2FA for those instead.

go get github.com/btcsuite/btcd/btcutil
go get github.com/btcsuite/btcd/btcutil/hdkeychain
go get github.com/btcsuite/btcd/chaincfg

Security note: private keys and WIFs derived this way control real funds if used on mainnet with a real mnemonic. Never print, log, or transmit them outside of a trusted, offline environment.

CLI usage

electrumnemonic v1 <command> [args]
electrumnemonic v2 <command> [args]

v1 commands

electrumnemonic v1 generate
electrumnemonic v1 encode <entropy-hex>
electrumnemonic v1 decode <mnemonic...>
electrumnemonic v1 validate <mnemonic...>

electrumnemonic v1 derive mnemonic [-network ...] [-change] [-count N] [-compressed] <mnemonic...>
electrumnemonic v1 derive seed     [-network ...] [-change] [-count N] [-compressed] <seed-hex>
electrumnemonic v1 derive wif      [-network ...] [-compressed] <wif>

v2 commands

electrumnemonic v2 generate [-version segwit|standard|2fa|2fa-segwit] [-strength 132|264] [-lang en|zh|ja|pt|es]
electrumnemonic v2 encode <entropy-hex>
electrumnemonic v2 decode <mnemonic...>
electrumnemonic v2 validate [-version ...] [-lang ...] <mnemonic...>
electrumnemonic v2 seed [-passphrase ...] [-lang ...] <mnemonic...>
electrumnemonic v2 detect <mnemonic...>

electrumnemonic v2 derive mnemonic [-passphrase ...] [-network ...] [-change] [-count N] [-compressed] [-lang ...] <mnemonic...>
electrumnemonic v2 derive seed     [-version ...] [-network ...] [-change] [-count N] [-compressed] <seed-hex>
electrumnemonic v2 derive wif      [-version ...] [-network ...] <wif>

-network accepts bitcoin, litecoin, dash, ravencoin, bitcoincash, or bitcoinsv (default bitcoin). -count sets how many addresses to derive (default 1; ignored by derive wif). -change derives the change (internal) chain instead of receiving. -compressed (default true) controls WIF / public-key compression. Dash, Ravencoin, Bitcoin Cash, and Bitcoin SV don't support native SegWit — use -version standard or -version 2fa when targeting those networks.

Examples

# Generate a new v2 segwit mnemonic (~12 words)
electrumnemonic v2 generate

# Generate a 24-word (264-bit) standard mnemonic in Spanish
electrumnemonic v2 generate -version standard -strength 264 -lang es

# Validate a mnemonic against the segwit version (also checks wordlist)
electrumnemonic v2 validate -version segwit "abandon abandon abandon ... about"

# Derive a seed with a passphrase
electrumnemonic v2 seed -passphrase "my secret" "abandon abandon abandon ... about"

# Detect a mnemonic's seed version
electrumnemonic v2 detect "abandon abandon abandon ... about"

# Derive 5 compressed receiving addresses from a v2 mnemonic
electrumnemonic v2 derive mnemonic -count 5 "abandon abandon abandon ... about"

# Derive 3 change addresses (uncompressed)
electrumnemonic v2 derive mnemonic -change -count 3 -compressed=false "abandon abandon abandon ... about"

# Derive a Litecoin address
electrumnemonic v2 derive mnemonic -network litecoin "abandon abandon abandon ... about"

# Recover the address for a WIF private key
electrumnemonic v2 derive wif -version segwit <wif-private-key>

# Generate a v1 mnemonic and derive addresses
electrumnemonic v1 generate
electrumnemonic v1 derive mnemonic -network bitcoin -count 3 "heartbeat some whole ..."

How it works

  • v1 mnemonics encode 16 bytes of entropy into 12 words using Electrum's original algorithm (each group of 3 words encodes 4 bytes via modular arithmetic against the wordlist size).
  • v2 mnemonics encode arbitrary entropy as a base-N number (N = wordlist size) and validate seed type by computing HMAC-SHA512("Seed version", normalized_mnemonic) and checking that the resulting hex digest starts with the version's prefix. Seeds are then derived via PBKDF2-HMAC-SHA512 (2048 iterations, salt "electrum" + passphrase), matching Electrum's own seed derivation.
  • Generation skips any phrase that is also a valid BIP-39 mnemonic (same as official Electrum).
  • Validation for v2 requires every word to exist in the active wordlist in addition to a matching version prefix.
  • Mnemonic normalization follows Electrum's approach: NFKD Unicode normalization, lowercasing, stripping combining marks, and collapsing whitespace.

Project layout

.
├── cmd/electrumnemonic/     # CLI entrypoint
├── pkg/electrumv1/          # Legacy (pre-2.0) mnemonic + derivation
├── pkg/electrumv2/          # Modern versioned mnemonic + derivation
├── pkg/network/             # Shared network params and Address type
└── wordlist/                # v1 and v2 wordlists (multiple languages for v2)

Credits

  • tyler-smith/go-bip39 — BIP-39 wordlists (English, Chinese Simplified, Japanese, Portuguese, Spanish) used by the v2 package, and BIP-39 validity checks during mnemonic generation.

References

Security note

This code handles cryptographic wallet seed material. Review it carefully, run it in a trusted/offline environment when generating real wallet seeds, and never share generated mnemonics or entropy with anyone.

License

This project is licensed under the MIT License — see LICENSE for the full text.

About

Go library and CLI for Electrum v1/v2 mnemonic generation, validation, seed derivation, and multi-chain address/key recovery (Bitcoin, Litecoin, Dash, and more)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages