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 ¶
- Variables
- func GenerateMnemonic() (string, error)
- func Verify(curve Curve, pub, data []byte, sig *Signature) bool
- type Coin
- type Curve
- type HDWallet
- func (w *HDWallet) Address(symbol Symbol) (string, error)
- func (w *HDWallet) AddressIndex(symbol Symbol, index uint32) (string, error)
- func (w *HDWallet) AllAddresses() (map[Symbol]string, error)
- func (w *HDWallet) Destroy()
- func (w *HDWallet) Mnemonic() (*memguard.LockedBuffer, error)
- func (w *HDWallet) PublicKey(symbol Symbol) ([]byte, error)
- func (w *HDWallet) PublicKeyIndex(symbol Symbol, index uint32) ([]byte, error)
- func (w *HDWallet) Sign(symbol Symbol, data []byte) (*Signature, error)
- func (w *HDWallet) SignIndex(symbol Symbol, index uint32, data []byte) (*Signature, error)
- func (w *HDWallet) WithMnemonic(fn func(mnemonic []byte) error) error
- type Signature
- type Symbol
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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 ¶
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.
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.
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).
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
PublicKey returns the public key for symbol at address index 0. See PublicKeyIndex.
func (*HDWallet) PublicKeyIndex ¶ added in v0.2.0
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
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
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 ¶
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
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
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
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.
Source Files
¶
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. |