hdwallet

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2026 License: MIT Imports: 30 Imported by: 0

README

hd-wallet

Go Reference Go Report Card CI

A Trust Wallet–compatible, security-focused hierarchical-deterministic (HD) wallet library for Go.

Generate a BIP-39 mnemonic (or import one) and derive receive addresses for 33 networks across 3 elliptic-curve families using the same derivation paths and address formats Trust Wallet uses by default — so seeds are interchangeable between the two.

Sensitive material (the mnemonic and derived seed) is never held as a plain Go string or a long-lived byte slice. It lives in encrypted, page-locked memguard enclaves and is decrypted only for the microseconds of a single derivation.


Why this library

  • 🔐 Secrets isolated in RAM. Encrypted enclaves, memory locked against swap (mlock/VirtualLock), guard pages, and automatic wiping. No mnemonic-as-string, no exported secret fields.
  • Provably Trust Wallet–compatible. Every address encoder is tested against Trust Wallet Core's own vectors; key derivation is tested against the SLIP-0010 specification. See Verification.
  • 🌐 33 networks, 3 curves. secp256k1 (Bitcoin-style, EVM, Cosmos, XRP, Tron), ed25519 (Solana, Stellar, Polkadot, …), and NIST P-256 (NEO).
  • ✍️ Raw signing for every network (ECDSA + EdDSA). Derived keys are wiped after each signature and are never exported.
  • 🧩 Extensible. Add a network with a single registry row.
  • 📦 Small dependency surface. btcd (secp256k1/bech32/base58), go-bip39, x/crypto, and memguard.

Install

go get github.com/ranjbar-dev/hd-wallet
import hdwallet "github.com/ranjbar-dev/hd-wallet"

Requires Go 1.23+.


Quick start

package main

import (
	"fmt"
	"log"

	"github.com/awnumar/memguard"
	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

func main() {
	defer memguard.Purge() // wipe all protected memory on exit

	// Create a wallet with a fresh 12-word mnemonic...
	w, err := hdwallet.NewHDWallet()
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy() // wipe this wallet's secrets when done

	// ...or import one:
	// w, _ := hdwallet.FromMnemonic("abandon abandon ... about")

	// Symbols are a typed enum (hdwallet.Symbol) — use the exported constants
	// for compile-time checking and autocomplete.
	btc, _ := w.Address(hdwallet.BTC)
	eth, _ := w.Address(hdwallet.ETH)
	sol, _ := w.Address(hdwallet.SOL)
	fmt.Println(btc, eth, sol)

	all, _ := w.AllAddresses() // map[hdwallet.Symbol]string for every network
	fmt.Println(all[hdwallet.ATOM])
}
Reading the mnemonic safely

The mnemonic is never exposed as a field. Read it only when needed, through a buffer that is wiped immediately afterwards:

err := w.WithMnemonic(func(mnemonic []byte) error {
	fmt.Printf("%s\n", mnemonic) // do not let the slice escape this function
	return nil
})
Multiple addresses per chain

Address returns the first receive address; AddressIndex derives any index by replacing the final element of the chain's path (preserving its hardened flag):

a0, _ := w.AddressIndex(hdwallet.BTC, 0) // bc1q...306fyu (same as w.Address(hdwallet.BTC))
a1, _ := w.AddressIndex(hdwallet.BTC, 1) // bc1q...rkf9g — second receive address
sol1, _ := w.AddressIndex(hdwallet.SOL, 1) // account-based chains vary the hardened element
Error handling

The package exports sentinel errors for use with errors.Is: ErrInvalidMnemonic, ErrUnsupportedCoin, and ErrDestroyed.

if _, err := w.Address("NOPE"); errors.Is(err, hdwallet.ErrUnsupportedCoin) {
	// unknown symbol
}
Signing (raw)

Sign/SignIndex produce a signature with the derived private key for any supported chain. The key is wiped immediately after signing and never leaves the package — there is no way to extract a private key.

There is one inherent rule, driven by the cryptography:

  • ECDSA chains (secp256k1, nist256p1 — BTC, ETH, ATOM, NEO, …): pass the 32-byte digest your chain signs. Pre-hash the message yourself with the chain's hash (keccak256 for Ethereum/Tron, double-SHA256 for Bitcoin, SHA-256 for Cosmos, …).
  • ed25519 chains (SOL, XLM, DOT, …): pass the message; the EdDSA scheme hashes internally.
digest := sha256.Sum256(txBytes)         // chain-specific pre-hash for ECDSA
sig, _ := w.Sign(hdwallet.BTC, digest[:])

sig.Bytes()        // 64-byte R||S (ECDSA) or 64-byte ed25519 signature  → Cosmos, Solana
sig.Recoverable()  // 65-byte R||S||V (secp256k1 only)                   → Ethereum/EVM, Tron
sig.DER()          // ASN.1 DER (ECDSA)                                  → Bitcoin family

pub, _ := w.PublicKey(hdwallet.BTC)
ok := hdwallet.Verify(hdwallet.Secp256k1, pub, digest[:], sig)

SignIndex(symbol, index, data) and PublicKeyIndex(symbol, index) work with non-zero address indices. ECDSA inputs that are not 32 bytes return ErrInvalidDigest.

This is raw signing only — you build and serialize the transaction (with a chain SDK or your own encoder) and hand the digest/message here. The signing primitives and Signature encodings are designed to be reused as the core for full per-chain transaction builders later.


Passing a mnemonic in securely

The golden rule: never let the mnemonic become a Go string in your code — strings are immutable and can never be wiped from memory. Choose the entry point that matches how securely you can hold the secret:

Entry point Security When
FromMnemonicBuffer(*memguard.LockedBuffer) 🟢 Strongest Mnemonic stays in page-locked, encrypted memory end-to-end; sealed zero-copy into the wallet.
FromMnemonicBytes([]byte) 🟡 Good You have a mutable []byte; it is wiped inside the call.
FromMnemonic(string) 🔴 Weakest Convenience only; the string cannot be wiped. Avoid for real funds.
Most secure: hand off a memguard buffer (zero-copy)

The wallet takes ownership of the buffer and destroys it — there is no extra unprotected copy anywhere in your process.

import (
	"os"

	"github.com/awnumar/memguard"
	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

func loadWallet() (*hdwallet.HDWallet, error) {
	defer memguard.Purge()

	// Read one line straight into locked, encrypted memory — never a string.
	buf, err := memguard.NewBufferFromReaderUntil(os.Stdin, '\n')
	if err != nil {
		return nil, err
	}
	// Ownership transfers to the wallet; buf is destroyed for you.
	return hdwallet.FromMnemonicBuffer(buf)
}

From a secrets manager / KMS that returns raw bytes:

raw := fetchFromVault()                    // []byte from your secret store
buf := memguard.NewBufferFromBytes(raw)    // copies into protected memory, wipes raw
w, err := hdwallet.FromMnemonicBuffer(buf) // takes ownership of buf
Good: a mutable byte slice
raw, _ := os.ReadFile("mnemonic.txt") // []byte, never a string
mn := bytes.TrimSpace(raw)
w, err := hdwallet.FromMnemonicBytes(mn) // mn is zeroed inside the call
for i := range raw {                     // wipe any trimmed remainder
	raw[i] = 0
}

Avoid os.Getenv for the mnemonic: environment variables are already immutable strings and cannot be wiped.

Residual exposure: the underlying tyler-smith/go-bip39 API only accepts string, so the library makes a single short-lived string copy for validation and seed derivation. It is GC-bounded, and every durable copy of the mnemonic and seed is sealed in a memguard enclave.


Supported networks

Symbol Network Curve Path
BTC Bitcoin (native SegWit) secp256k1 m/84'/0'/0'/0/0
LTC Litecoin (native SegWit) secp256k1 m/84'/2'/0'/0/0
DOGE Dogecoin secp256k1 m/44'/3'/0'/0/0
BCH Bitcoin Cash (CashAddr) secp256k1 m/44'/145'/0'/0/0
DASH Dash secp256k1 m/44'/5'/0'/0/0
ZEC Zcash (transparent) secp256k1 m/44'/133'/0'/0/0
ETH Ethereum (EIP-55) secp256k1 m/44'/60'/0'/0/0
BNB · MATIC · AVAX · ARB · OP · FTM · BASE · CRO · GNO · CELO EVM chains secp256k1 m/44'/60'/0'/0/0
TRX Tron secp256k1 m/44'/195'/0'/0/0
XRP XRP Ledger secp256k1 m/44'/144'/0'/0/0
ATOM · OSMO · JUNO · TIA Cosmos SDK chains secp256k1 m/44'/118'/0'/0/0
SOL Solana ed25519 m/44'/501'/0'
XLM Stellar ed25519 m/44'/148'/0'
DOT Polkadot (SS58) ed25519 m/44'/354'/0'/0'/0'
KSM Kusama (SS58) ed25519 m/44'/434'/0'/0'/0'
NEAR NEAR ed25519 m/44'/397'/0'
ALGO Algorand ed25519 m/44'/283'/0'/0'/0'
SUI Sui ed25519 m/44'/784'/0'/0'/0'
APTOS Aptos ed25519 m/44'/637'/0'/0'/0'
XTZ Tezos (tz1) ed25519 m/44'/1729'/0'/0'
NEO NEO (legacy) nist256p1 m/44'/888'/0'/0/0

All paths derive receive address index 0 and an empty BIP-39 passphrase (Trust Wallet's default).

Note on Polkadot/Kusama: Trust Wallet derives these on ed25519 via SLIP-0010. The native Polkadot ecosystem (e.g. Polkadot.js) defaults to sr25519 with a different scheme, so addresses there will differ. This library matches Trust Wallet, which is the stated compatibility target.

Roadmap

Cardano (ed25519ExtendedCardano / CIP-1852) and Waves (curve25519) use fundamentally different derivation schemes and are intentionally deferred rather than shipped half-verified — a wrong address means lost funds. Contributions with test vectors welcome.


Verification

"Trust Wallet–compatible" is proven, not asserted. The test suite layers three independent sources of truth:

  1. Encoders (encoders_test.go) — every address encoder is run against the exact addresses Trust Wallet Core's CoinAddressDerivationTests produces for a fixed key, isolating address-format correctness.
  2. Derivation (slip10_test.go) — ed25519 and nist256p1 derivation are checked against the official SLIP-0010 specification test vectors (including non-hardened P-256 derivation).
  3. End-to-end (hdwallet_test.go) — full mnemonic→seed→derive→encode against the BIP-84 spec (BTC), the canonical ETH vector, and Trust Wallet Core's HDWalletTests mnemonic vectors (NEAR ed25519, Cosmos secp256k1).
go test -race -cover ./...

Always verify before sending funds. Import your mnemonic into Trust Wallet and confirm the address for any chain you intend to use with real value.


API

Function / method Purpose
NewHDWallet() (*HDWallet, error) New wallet with a fresh 12-word mnemonic.
FromMnemonic(string) (*HDWallet, error) Import from a mnemonic string (least secure).
FromMnemonicBytes([]byte) (*HDWallet, error) Import from a byte slice (wiped on use).
FromMnemonicBuffer(*memguard.LockedBuffer) (*HDWallet, error) Import from a memguard buffer (most secure; zero-copy).
GenerateMnemonic() (string, error) Generate a mnemonic without building a wallet.
(*HDWallet) Address(symbol Symbol) (string, error) First receive address for one network.
(*HDWallet) AddressIndex(symbol Symbol, index uint32) (string, error) Nth address/account for one network.
(*HDWallet) AllAddresses() (map[Symbol]string, error) Addresses for all networks.
(*HDWallet) Sign(symbol Symbol, data []byte) (*Signature, error) Sign a digest (ECDSA) / message (ed25519) at index 0.
(*HDWallet) SignIndex(symbol Symbol, index uint32, data []byte) (*Signature, error) Sign with the key at a given index.
(*HDWallet) PublicKey(symbol Symbol) ([]byte, error) Public key at index 0.
(*HDWallet) PublicKeyIndex(symbol Symbol, index uint32) ([]byte, error) Public key at a given index.
Verify(curve Curve, pub, data []byte, sig *Signature) bool Verify a signature.
(*HDWallet) WithMnemonic(func([]byte) error) error Use the mnemonic, auto-wiped.
(*HDWallet) Mnemonic() (*memguard.LockedBuffer, error) Mnemonic buffer (caller Destroys).
(*HDWallet) Destroy() Wipe the wallet's secrets.
SupportedCoins() []Symbol Sorted list of symbols.
CoinInfo(symbol Symbol) (Coin, bool) Registry entry for a symbol.

Symbol is a typed string enum; the package exports a constant for every supported network (hdwallet.BTC, hdwallet.ETH, hdwallet.SOL, …). Pass these constants instead of raw strings for compile-time safety. Symbol also has String() string and IsValid() bool helpers.


Adding a network

Append one row to the registry in registry.go:

"FOO": {"Foochain", "FOO", Secp256k1, "m/44'/9999'/0'/0/0", encodeFoo},

Provide an Encode func(pub []byte) (string, error) for the address format (the compressed key for secp256k1/nist256p1, the raw 32-byte key for ed25519), and add a test vector. EVM chains can reuse encodeETH; Cosmos chains can reuse cosmosEncoder("<hrp>").


Demo CLI

go run ./cmd/hdwallet                       # fresh wallet, prints addresses
go run ./cmd/hdwallet -mnemonic "abandon ... about"
go run ./cmd/hdwallet -show-mnemonic        # demo only; printing defeats isolation

Security

  • Secrets are stored in memguard enclaves (encrypted at rest in RAM, pages locked against swap, guarded with canaries, auto-wiped).
  • Private keys derived during an operation are zeroed immediately after the address is computed.
  • Call w.Destroy() per wallet and defer memguard.Purge() at program exit.
  • Caveat: FromMnemonic(string) and GenerateMnemonic() string involve a Go string that cannot be wiped (a limitation of the BIP-39 API). Prefer FromMnemonicBytes and WithMnemonic for the strongest guarantees.

Found a vulnerability? Please open a private security advisory rather than a public issue.


Publishing & releasing

Releases are fully automated. Every push to main that passes the test and security gates is tagged and published by CI (.github/workflows/ci.yml):

  1. CI runs build, tests (-race), govulncheck, and gosec.
  2. A new semver tag is created and pushed.
  3. The Go module proxy is warmed (proxy.golang.org), which publishes the version and triggers pkg.go.dev indexing.
  4. A GitHub Release with auto-generated notes is created.

Control the version bump from the commit message:

Marker in commit message Result
[major] x+1.0.0
[minor] x.y+1.0
(none) x.y.z+1 (patch)
[skip release] no release for that push

The first release is v0.1.0. Requires the repo to be public and the default GITHUB_TOKEN to have write access (Settings → Actions → General → Workflow permissions → "Read and write permissions"). If tag protection rules block the bot, supply a Personal Access Token instead.

To release manually instead, just push a tag (git tag v1.2.3 && git push origin v1.2.3).


License

MIT © Amir Ranjbar

Disclaimer

This software is provided "as is", without warranty of any kind. You are responsible for safeguarding your own keys and funds. Always test with small amounts first and verify addresses against a reference wallet.

Documentation

Overview

Package hdwallet is a Trust Wallet-compatible hierarchical-deterministic wallet for Go.

It generates a BIP-39 mnemonic (or imports one) and derives receive addresses for many networks using the same derivation paths and address formats Trust Wallet uses by default, so seeds are interchangeable between the two.

Secrets (the mnemonic and the derived seed) are never held as plain Go strings or long-lived byte slices. They are stored in encrypted, page-locked memguard enclaves and decrypted only for the duration of a single derivation. Always call (*HDWallet).Destroy when finished, and consider memguard.Purge on program exit.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidMnemonic is returned when a mnemonic fails BIP-39 validation.
	ErrInvalidMnemonic = errors.New("hdwallet: invalid mnemonic")
	// ErrUnsupportedCoin is returned for a symbol not in the registry.
	ErrUnsupportedCoin = errors.New("hdwallet: unsupported coin")
	// ErrDestroyed is returned by operations on a wallet whose secrets were wiped.
	ErrDestroyed = errors.New("hdwallet: wallet has been destroyed")
	// ErrInvalidDigest is returned when an ECDSA signing input is not 32 bytes.
	ErrInvalidDigest = errors.New("hdwallet: digest must be 32 bytes")
)

Exported sentinel errors. Consumers can match them with errors.Is; errors that add context (e.g. the offending symbol) wrap these with %w.

Functions

func GenerateMnemonic

func GenerateMnemonic() (string, error)

GenerateMnemonic returns a fresh 12-word BIP-39 mnemonic as a string.

The returned string cannot be securely wiped; for sensitive use derive a wallet with NewHDWallet (which keeps the mnemonic in protected memory) and read it back via Mnemonic or WithMnemonic only when required.

func Verify added in v0.2.0

func Verify(curve Curve, pub, data []byte, sig *Signature) bool

Verify reports whether sig is a valid signature of data by the public key pub on the given curve. For ECDSA curves data is the 32-byte digest that was signed; for ed25519 it is the message.

Types

type Coin

type Coin struct {
	Name   string
	Symbol Symbol
	Curve  Curve
	Path   string
	Encode func(pub []byte) (string, error)
}

Coin describes a supported network: its curve, BIP-32 derivation path, and the function that turns a derived public key into an address string. Adding a network is a single entry in the registry below.

func CoinInfo

func CoinInfo(symbol Symbol) (Coin, bool)

CoinInfo returns the static registry entry for a symbol.

type Curve

type Curve int

Curve identifies the elliptic curve a coin derives keys on. Each curve has a distinct derivation scheme (BIP-32 for secp256k1, SLIP-0010 for the others).

const (
	// Secp256k1 covers Bitcoin-style, Ethereum/EVM, Cosmos, XRP and Tron.
	Secp256k1 Curve = iota
	// Ed25519 covers Solana, Stellar, Polkadot, NEAR, Algorand, Sui, Aptos, Tezos.
	Ed25519
	// Nist256p1 (NIST P-256) covers NEO.
	Nist256p1
)

func (Curve) String

func (c Curve) String() string

String returns the SLIP-0010/BIP-32 name of the curve for diagnostics.

type HDWallet

type HDWallet struct {
	// contains filtered or unexported fields
}

HDWallet is an HD wallet derived from a BIP-39 mnemonic. Its sensitive material is protected in memory; see the package documentation. All methods are safe for concurrent use.

func FromMnemonic

func FromMnemonic(mnemonic string) (*HDWallet, error)

FromMnemonic builds a wallet from an existing 12/24-word mnemonic string.

Prefer FromMnemonicBytes where possible: a Go string cannot be wiped from memory, so any mnemonic held as a string lingers until garbage-collected.

func FromMnemonicBuffer

func FromMnemonicBuffer(buf *memguard.LockedBuffer) (*HDWallet, error)

FromMnemonicBuffer builds a wallet from a mnemonic held in a memguard LockedBuffer. This is the most secure entry point: the mnemonic stays in page-locked, encrypted-at-rest memory from your code all the way into the wallet's sealed enclave, with no intermediate plaintext copy on the Go heap.

The wallet takes ownership of buf and destroys it; do not use buf afterwards. Surrounding whitespace in the buffer is trimmed before use.

Example

ExampleFromMnemonicBuffer shows the most secure way to import a mnemonic: the phrase lives in a page-locked, encrypted memguard buffer and is handed to the wallet without an intermediate plaintext copy. The wallet takes ownership of the buffer and destroys it.

package main

import (
	"fmt"
	"log"

	"github.com/awnumar/memguard"

	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

// exampleMnemonic is the standard BIP-39 test vector mnemonic. Never hard-code a
// real mnemonic in source; this one holds no funds and is used only for docs.
const exampleMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

func main() {
	// In real code, read the mnemonic straight into protected memory (for
	// example with memguard.NewBufferFromReaderUntil over stdin).
	buf := memguard.NewBufferFromBytes([]byte(exampleMnemonic))

	w, err := hdwallet.FromMnemonicBuffer(buf)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy()

	addr, _ := w.Address(hdwallet.BTC)
	fmt.Println(addr)
}
Output:
bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu

func FromMnemonicBytes

func FromMnemonicBytes(mnemonic []byte) (*HDWallet, error)

FromMnemonicBytes builds a wallet from a mnemonic held in a byte slice. The slice is wiped before the function returns; callers must not reuse it.

func NewHDWallet

func NewHDWallet() (*HDWallet, error)

NewHDWallet creates a wallet with a fresh 12-word (128-bit) mnemonic.

Example

ExampleNewHDWallet creates a wallet with a fresh, random mnemonic and derives an address. Output is omitted because the mnemonic (and therefore the address) is random on every run.

package main

import (
	"fmt"
	"log"
	"strings"

	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

func main() {
	w, err := hdwallet.NewHDWallet()
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy() // wipe the wallet's secrets when finished

	addr, err := w.Address(hdwallet.ETH)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(strings.HasPrefix(addr, "0x"))
}
Output:
true

func (*HDWallet) Address

func (w *HDWallet) Address(symbol Symbol) (string, error)

Address returns the first receive address (index 0) for a coin symbol, e.g. "BTC", "ETH", "SOL", "ATOM". Use SupportedCoins to list every symbol.

It is exactly equivalent to AddressIndex(symbol, 0).

Example

ExampleHDWallet_Address derives the first receive address for several chains from a fixed mnemonic, producing deterministic, Trust Wallet-compatible addresses.

package main

import (
	"fmt"
	"log"

	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

// exampleMnemonic is the standard BIP-39 test vector mnemonic. Never hard-code a
// real mnemonic in source; this one holds no funds and is used only for docs.
const exampleMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

func main() {
	w, err := hdwallet.FromMnemonic(exampleMnemonic)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy()

	btc, _ := w.Address(hdwallet.BTC)
	eth, _ := w.Address(hdwallet.ETH)
	fmt.Println(btc)
	fmt.Println(eth)
}
Output:
bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
0x9858EfFD232B4033E47d90003D41EC34EcaEda94

func (*HDWallet) AddressIndex

func (w *HDWallet) AddressIndex(symbol Symbol, index uint32) (string, error)

AddressIndex returns the address for a coin symbol derived with the final element of the coin's BIP-32 path replaced by index, preserving that element's hardened flag (a trailing "'").

For BIP-44/BIP-84 chains whose path ends in "/0/0" (change/address_index), this varies the non-hardened receive address index — e.g. for BTC (m/84'/0'/0'/0/0), index 1 derives m/84'/0'/0'/0/1. For account-based chains whose path ends in a hardened element such as "/0'" (e.g. SOL, m/44'/501'/0'), this varies that final hardened element — index 1 derives m/44'/501'/1'.

index must be below 2^31 (0x80000000); a larger value returns an error, as does an unknown symbol (wrapping ErrUnsupportedCoin) or a destroyed wallet.

Example

ExampleHDWallet_AddressIndex derives multiple receive addresses for the same chain by varying the final path index.

package main

import (
	"fmt"
	"log"

	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

// exampleMnemonic is the standard BIP-39 test vector mnemonic. Never hard-code a
// real mnemonic in source; this one holds no funds and is used only for docs.
const exampleMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

func main() {
	w, err := hdwallet.FromMnemonic(exampleMnemonic)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy()

	first, _ := w.AddressIndex(hdwallet.BTC, 0)
	second, _ := w.AddressIndex(hdwallet.BTC, 1)
	fmt.Println(first)
	fmt.Println(second)
}
Output:
bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
bc1qnjg0jd8228aq7egyzacy8cys3knf9xvrerkf9g

func (*HDWallet) AllAddresses

func (w *HDWallet) AllAddresses() (map[Symbol]string, error)

AllAddresses derives the first address for every supported coin. The seed enclave is opened exactly once and every coin is derived inside that single decryption window.

func (*HDWallet) Destroy

func (w *HDWallet) Destroy()

Destroy wipes the wallet's secret material from memory. The wallet is unusable afterwards. It is safe to call multiple times.

func (*HDWallet) Mnemonic

func (w *HDWallet) Mnemonic() (*memguard.LockedBuffer, error)

Mnemonic returns the wallet's mnemonic in a page-locked buffer. This is a lower-level accessor: the caller MUST call Destroy on the returned buffer when finished with it, or the decrypted phrase lingers in memory. Prefer WithMnemonic, which wipes the decrypted copy automatically when its callback returns.

func (*HDWallet) PublicKey added in v0.2.0

func (w *HDWallet) PublicKey(symbol Symbol) ([]byte, error)

PublicKey returns the public key for symbol at address index 0. See PublicKeyIndex.

func (*HDWallet) PublicKeyIndex added in v0.2.0

func (w *HDWallet) PublicKeyIndex(symbol Symbol, index uint32) ([]byte, error)

PublicKeyIndex returns the public key derived for symbol at the given address index: the 33-byte compressed key for secp256k1/nist256p1, or the 32-byte key for ed25519. Signing callers need this to build or verify transactions.

func (*HDWallet) Sign added in v0.2.0

func (w *HDWallet) Sign(symbol Symbol, data []byte) (*Signature, error)

Sign signs data with the key for symbol at address index 0. See SignIndex.

Example

ExampleHDWallet_Sign signs a digest and verifies it with the derived public key. For ECDSA chains (BTC/ETH/…) pass the 32-byte digest your chain hashes; for ed25519 chains pass the message itself.

package main

import (
	"crypto/sha256"
	"fmt"
	"log"

	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

// exampleMnemonic is the standard BIP-39 test vector mnemonic. Never hard-code a
// real mnemonic in source; this one holds no funds and is used only for docs.
const exampleMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

func main() {
	w, err := hdwallet.FromMnemonic(exampleMnemonic)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy()

	digest := sha256.Sum256([]byte("transaction bytes to sign"))
	sig, err := w.Sign(hdwallet.BTC, digest[:])
	if err != nil {
		log.Fatal(err)
	}

	pub, _ := w.PublicKey(hdwallet.BTC)
	fmt.Println(hdwallet.Verify(hdwallet.Secp256k1, pub, digest[:], sig))
}
Output:
true

func (*HDWallet) SignIndex added in v0.2.0

func (w *HDWallet) SignIndex(symbol Symbol, index uint32, data []byte) (*Signature, error)

SignIndex signs data with the private key derived for symbol at the given address index and returns the signature.

For ECDSA chains (secp256k1, nist256p1 — e.g. BTC, ETH, ATOM, NEO) data must be the 32-byte digest the chain signs; pre-hash the message with the chain's hash function (keccak256 for Ethereum/Tron, double-SHA256 for Bitcoin, SHA-256 for Cosmos, …). For ed25519 chains (e.g. SOL, XLM, DOT) data is the message itself; the EdDSA scheme hashes internally.

The derived private key is wiped immediately after signing and never leaves the package.

func (*HDWallet) WithMnemonic

func (w *HDWallet) WithMnemonic(fn func(mnemonic []byte) error) error

WithMnemonic runs fn with the plaintext mnemonic bytes and wipes the decrypted copy as soon as fn returns. The slice passed to fn must not escape fn.

Example

ExampleHDWallet_WithMnemonic shows the safe pattern for reading the mnemonic back: the decrypted copy is wiped automatically when the callback returns, and the slice must not escape it.

package main

import (
	"fmt"
	"log"
	"strings"

	hdwallet "github.com/ranjbar-dev/hd-wallet"
)

// exampleMnemonic is the standard BIP-39 test vector mnemonic. Never hard-code a
// real mnemonic in source; this one holds no funds and is used only for docs.
const exampleMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

func main() {
	w, err := hdwallet.FromMnemonic(exampleMnemonic)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Destroy()

	err = w.WithMnemonic(func(mnemonic []byte) error {
		fmt.Println(len(strings.Fields(string(mnemonic))), "words")
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
}
Output:
12 words

type Signature added in v0.2.0

type Signature struct {
	Curve      Curve
	R, S       []byte // ECDSA signature scalars (nil for ed25519)
	RecoveryID byte   // secp256k1 public-key recovery id (0 or 1)
	// contains filtered or unexported fields
}

Signature is the result of signing with a derived key.

For ECDSA curves (secp256k1, nist256p1), R and S are the signature scalars and the value is available as a 64-byte R||S string, an ASN.1 DER encoding, and — for secp256k1 — a 65-byte recoverable form. For ed25519, R and S are nil and the 64-byte signature is available via Bytes.

func (*Signature) Bytes added in v0.2.0

func (s *Signature) Bytes() []byte

Bytes returns the 64-byte signature: R||S for ECDSA curves, or the raw 64-byte signature for ed25519. Cosmos and Solana use this form.

func (*Signature) DER added in v0.2.0

func (s *Signature) DER() []byte

DER returns the ASN.1 DER encoding (SEQUENCE of two INTEGERs) used by Bitcoin-family chains. It is valid for ECDSA curves; for ed25519 it returns nil.

func (*Signature) Recoverable added in v0.2.0

func (s *Signature) Recoverable() []byte

Recoverable returns the 65-byte [R||S||V] signature, where V is the recovery id (0 or 1), used by Ethereum/EVM chains and Tron. Callers add the chain's V offset (e.g. 27, or 35+2*chainID for EIP-155) as needed. It is only valid for secp256k1; for other curves it returns nil.

type Symbol

type Symbol string

Symbol is a typed network identifier used to look up a registry entry. Use the exported constants below (hdwallet.BTC, hdwallet.ETH, …) when calling methods such as (*HDWallet).Address and AddressIndex; the typed enum gives compile-time checking and editor autocomplete instead of bare strings.

const (
	// secp256k1 — Bitcoin-style UTXO chains.
	BTC  Symbol = "BTC"
	LTC  Symbol = "LTC"
	DOGE Symbol = "DOGE"
	BCH  Symbol = "BCH"
	DASH Symbol = "DASH"
	ZEC  Symbol = "ZEC"

	// secp256k1 — account-based / keccak.
	ETH Symbol = "ETH"
	TRX Symbol = "TRX"
	XRP Symbol = "XRP"

	// secp256k1 — EVM chains (same key & address format as Ethereum).
	BNB   Symbol = "BNB"
	MATIC Symbol = "MATIC"
	AVAX  Symbol = "AVAX"
	ARB   Symbol = "ARB"
	OP    Symbol = "OP"
	FTM   Symbol = "FTM"
	BASE  Symbol = "BASE"
	CRO   Symbol = "CRO"
	GNO   Symbol = "GNO"
	CELO  Symbol = "CELO"

	// secp256k1 — Cosmos SDK chains.
	ATOM Symbol = "ATOM"
	OSMO Symbol = "OSMO"
	JUNO Symbol = "JUNO"
	TIA  Symbol = "TIA"

	// ed25519 (SLIP-0010).
	SOL   Symbol = "SOL"
	XLM   Symbol = "XLM"
	DOT   Symbol = "DOT"
	KSM   Symbol = "KSM"
	NEAR  Symbol = "NEAR"
	ALGO  Symbol = "ALGO"
	SUI   Symbol = "SUI"
	APTOS Symbol = "APTOS"
	XTZ   Symbol = "XTZ"

	// nist256p1 (SLIP-0010).
	NEO Symbol = "NEO"
)

Supported network symbols. These mirror the registry below and match Trust Wallet's tickers.

func SupportedCoins

func SupportedCoins() []Symbol

SupportedCoins lists the registered coin symbols in sorted order.

func (Symbol) IsValid

func (s Symbol) IsValid() bool

IsValid reports whether the symbol is a registered network.

func (Symbol) String

func (s Symbol) String() string

String implements fmt.Stringer.

Directories

Path Synopsis
cmd
hdwallet command
Command hdwallet is a small demo CLI for the hd-wallet library: it generates a fresh BIP-39 mnemonic (or imports one) and prints the receive address for every supported network.
Command hdwallet is a small demo CLI for the hd-wallet library: it generates a fresh BIP-39 mnemonic (or imports one) and prints the receive address for every supported network.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL