hdwallet

package module
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: MIT Imports: 55 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 129 networks using the same derivation paths and address formats Trust Wallet uses by default — so seeds are interchangeable between the two. Beyond derivation it adds EVM tooling (RLP, ABI, EIP-191, EIP-712), protobuf transaction signing for core families (EVM, Tron, XRP, Cosmos, Solana — no broadcast), secure private-key import/export, and address validation/parsing.

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. Private-key import/export goes through the same memguard pattern — there is still no raw key getter.
  • 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; transaction signers reproduce Trust Wallet Core's AnySigner vectors byte-for-byte. See Verification.
  • 🌐 129 networks across 5 curves in use. secp256k1 (Bitcoin-style, 50+ EVM chains, ~30 Cosmos chains, XRP, Tron), ed25519 (Solana, Stellar, …), NIST P-256 (NEO), ed25519-blake2b (Nano), and curve25519 (Waves). 8 curve schemes are implemented in total.
  • ✍️ Signing at every level. Raw ECDSA/EdDSA signing for every network, EVM message signing (EIP-191/EIP-712), and full protobuf transaction signing (EVM, Tron, XRP, Cosmos, Solana) that returns broadcast-ready raw transactions. Derived keys are wiped after each use.
  • 🧩 Extensible. Add a network with a single registry row.
  • 📦 Focused dependency surface. btcd (secp256k1/bech32/base58), go-bip39, x/crypto, memguard, protobuf, and curve libraries (edwards25519, schnorrkel, gnark-crypto) for the additional schemes.

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
Bitcoin address types

Address/AddressIndex return the chain default (native SegWit, BIP-84 for BTC/LTC). BitcoinAddress derives any of the four standard formats at its standard BIP path (arguments are account, change, index):

legacy,  _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2PKH, 0, 0, 0)      // 1…   (BIP-44)
nested,  _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2SHP2WPKH, 0, 0, 0) // 3…   (BIP-49)
native,  _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2WPKH, 0, 0, 0)     // bc1q… (BIP-84)
taproot, _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2TR, 0, 0, 0)       // bc1p… (BIP-86)

Available for BTC and LTC; verified against the official BIP-44/49/84/86 test vectors. ValidateAddress/ParseAddress accept all four formats.

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 the low-level primitive. For Ethereum message signing and full transaction building, use the higher-level APIs below.

Transaction signing (protobuf, no broadcast)

SignTransaction builds, serializes, and signs a broadcast-ready raw transaction from a protobuf SigningInput, mirroring Trust Wallet Core's AnySigner. It returns the signed bytes/hex — it does not broadcast.

Coverage note: address derivation/validation spans all 129 networks, but transaction building covers only the families in the table below. For any other chain you can derive and validate addresses but must assemble and sign the transaction yourself (use the raw Sign/SignIndex primitive on the chain's sighash). You also supply chain state — fees/gas, nonce/sequence, recent blockhash, UTXOs — in the SigningInput; this library does no network I/O.

Verified against authoritative signing vectors for:

Family Coverage
EVM legacy (EIP-155) + EIP-2930 (access list) + EIP-1559, native + ERC-20 + arbitrary contract call + contract creation (deploy) + EIP-2930/1559 access lists. Select the format with tx_mode (exported hdwallet.EthTxModeLegacy/EthTxModeEIP2930/EthTxModeEIP1559). All registered EVM chains.
Tron TRX transfer + TRC-20 token transfer (TriggerSmartContract)
XRP Payment
Cosmos bank MsgSend, staking MsgDelegate/MsgUndelegate, MsgWithdrawDelegatorReward, multi-message (protobuf direct mode). All standard secp256k1 Cosmos chains, plus EVMOS (ethermint eth_secp256k1: keccak256 SignDoc + ethermint pubkey type URL). Other ethermint chains (INJ/CANTO/ZETA) stay roadmap — Injective uses a different pubkey type URL, so each needs its own vector.
Solana system transfer + SPL token transfer (TransferChecked)
Bitcoin BTC/LTC SegWit: spends P2WPKH (BIP-143) and Taproot key-path (BIP-341 / BIP-340 Schnorr) inputs; outputs to any address type; deterministic coin-selection + change. Verified against btcd (P2WPKH byte-identical; Taproot sighash + BIP-340 verify) and the BIP-143 spec vector.
import ethpb "github.com/ranjbar-dev/hd-wallet/txproto/ethereum"

out, _ := w.SignTransaction(hdwallet.ETH, 0, &ethpb.SigningInput{ /* … */ })

Bitcoin spending currently covers P2WPKH and Taproot key-path inputs; legacy P2PKH and nested P2SH-P2WPKH input spending remain on the roadmap.

Ethereum message signing (EIP-191 / EIP-712)
sig, _ := w.SignMessage(hdwallet.ETH, 0, []byte("Hello, world!"))   // EIP-191 personal_sign
addr, _ := hdwallet.RecoverEthereumAddress([]byte("Hello, world!"), sig)

sig2, _ := w.SignTypedData(hdwallet.ETH, 0, typedDataJSON)           // EIP-712

Plus standalone EVM tooling: EncodeRLP/DecodeRLP, ABIEncode/ABIDecode, ABIFunctionSelector, EthereumPersonalMessageHash, EIP712Hash.

Bitcoin & Solana message signing

Non-EVM message signing, each pinned byte-for-byte to its Trust Wallet Core MessageSigner vector:

// Bitcoin "signmessage" standard → base64; verifies against a legacy P2PKH address.
sig, _  := w.SignBitcoinMessage(hdwallet.BTC, 0, []byte("test signature"))
ok      := hdwallet.VerifyBitcoinMessage("19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", []byte("test signature"), sig)

// Solana off-chain message (raw ed25519) → base58.
ssig, _ := w.SignSolanaMessage(hdwallet.SOL, 0, []byte("Hello world"))
sok     := hdwallet.VerifySolanaMessage(addr, []byte("Hello world"), ssig)

Cosmos ADR-36 arbitrary-message signing is on the roadmap (no authoritative Trust Wallet Core vector to verify against yet).

Address validation & parsing
ok  := hdwallet.IsValidAddress(hdwallet.ETH, "0x…")     // bool
err := hdwallet.ValidateAddress(hdwallet.BTC, "bc1q…")  // descriptive error
payload, _ := hdwallet.ParseAddress(hdwallet.ETH, "0x…")
addr, _ := hdwallet.AddressFromPublicKey(hdwallet.ETH, pubKey) // external key → address
Importing / exporting a raw private key (securely)

A wallet can be built from a single private key, and the leaf key can be exported — always through the same memguard pattern as the mnemonic (no raw []byte getter; the key is wiped when your callback returns):

w, _ := hdwallet.FromPrivateKeyBytes(keyBytes, hdwallet.Secp256k1) // wipes keyBytes
// or FromPrivateKeyBuffer(*memguard.LockedBuffer, curve) — zero-copy, strongest

_ = w.WithPrivateKey(hdwallet.ETH, 0, func(priv []byte) error {     // wiped on return
    // use priv; do not let it escape
    return nil
})
buf, _ := w.PrivateKey(hdwallet.ETH, 0)                             // caller Destroys
defer buf.Destroy()

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

129 networks across 5 curves in use. SupportedCoins() returns the live, authoritative list; CoinInfo(symbol) gives each coin's curve and path. Every chain below is verified against a Trust Wallet Core address vector.

secp256k1 (110)
Group Symbols Path
Bitcoin-family / UTXO BTC, LTC, DOGE, BCH, DASH, ZEC, BTG, DGB, GRS, SYS, VIA, QTUM, RVN, KMD, FIRO, MONA, XVG, PIVX, NEBL, STRAX, ZEN, BCD, XEC, FLUX per-chain (e.g. m/84'/0'/0'/0/0)
Ethereum / EVM (same key & address) ETH, BNB, MATIC, AVAX, ARB, OP, FTM, BASE, CRO, GNO, CELO, ETC, RBTC, KAIA, AURORA, GLMR, MOVR, BOBA, METIS, OPBNB, POLZKEVM, MANTA, ZKSYNC, LINEA, SCROLL, MANTLE, BLAST, RONIN, HECO, OKT, KCS, WAN, POA, CLO, GO, TT, VET, IOTX, THETA, NEON, MERLIN, LIGHT, SONIC, ZENEON, ONE, EVMOS, INJ, CANTO, ZETAEVM m/44'/60'/0'/0/0
Tron TRX m/44'/195'/0'/0/0
XRP Ledger XRP m/44'/144'/0'/0/0
Cosmos SDK (bech32, per-chain HRP) ATOM, OSMO, JUNO, TIA, LUNA, KAVA, SCRT, BAND, RUNE, STARS, AXL, STRD, BLD, CRE, KUJI, CMDX, NTRN, SOMM, FET, MARS, UMEE, COREUM, QSR, XPRT, AKT, NOBLE, SEI, DYDX, BLZ, CRYPTOORG, ZETA m/44'/118'/0'/0/0 (some differ)
EOS-family / Filecoin EOS, WAX, FIO, FIL per-chain
ed25519 (15)
Symbol Network Path
SOL Solana m/44'/501'/0'
XLM Stellar m/44'/148'/0'
DOT · KSM Polkadot · Kusama (SS58) m/44'/354'/0'/0'/0' · m/44'/434'/0'/0'/0'
NEAR · XTZ · SUI · APTOS · ALGO NEAR · Tezos · Sui · Aptos · Algorand per-chain
EGLD · HBAR · IOST · ROSE · KIN · AE MultiversX · Hedera · IOST · Oasis · Kin · Aeternity per-chain
nist256p1 (2) · ed25519-blake2b (1) · curve25519 (1)
Symbol Network Curve Path
NEO NEO (legacy) nist256p1 m/44'/888'/0'/0/0
ONT Ontology nist256p1 m/44'/1024'/0'/0/0
XNO Nano ed25519-blake2b m/44'/165'/0'
WAVES Waves curve25519 m/44'/5741564'/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 (deferred — implemented but not yet vector-matched, or scheme not wired)

Some curve schemes are implemented but not yet wired to a registered chain, because a fund-critical address must match an authoritative vector first:

  • Cardano (ed25519-extended / CIP-1852) — derivation needs BIP-39 entropy (not the seed); entropy is not yet plumbed into the wallet.
  • StarkNet (starkex) — the EIP-2645 seed→key grind lacks an authoritative Trust Wallet Core vector (sign/verify are vector-verified).
  • sr25519 (native Polkadot/Kusama) — implemented; sign/verify round-trip only.
  • Zilliqa (Schnorr), TON, ICON, and a handful of long-tail chains whose address scheme isn't reproduced against a vector yet (see registry.go).

Deferred signing features: Bitcoin transaction building now spends P2WPKH and Taproot key-path inputs (BTC/LTC); still deferred are legacy P2PKH and nested P2SH-P2WPKH input spending (pre-BIP-143 / wrapped-witness sighash). The ethermint-keyed Cosmos chains beyond EVMOS (INJ/CANTO/ZETA — each needs its own vector since the pubkey type URL enters the signed bytes) — see tx_families.go; and Cosmos ADR-36 message signing — see message_cosmos_test.go.

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.
NewHDWalletWithWordCount(words int) (*HDWallet, error) New wallet with a 12/15/18/21/24-word mnemonic. Also NewHDWalletWithEntropy(bits).
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).
FromMnemonicWithPassphrase([]byte, []byte) (*HDWallet, error) Import with a BIP-39 passphrase (the "25th word").
FromMnemonicBufferWithPassphrase(buf, pass *memguard.LockedBuffer) (*HDWallet, error) Passphrase import, both secrets in memguard buffers.
GenerateMnemonic() (string, error) Generate a mnemonic without building a wallet. Also GenerateMnemonicWithWordCount(words).
(*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) AddressPath(symbol Symbol, path string) (string, error) Address at an arbitrary absolute BIP-32 path. Also SignPath/PublicKeyPath/WithPrivateKeyPath/PrivateKeyPath.
(*HDWallet) AddressAt(symbol Symbol, account, change, index uint32) (string, error) Address by BIP-44 account/change/index. Also SignAt/PublicKeyAt.
(*HDWallet) AllAddresses() (map[Symbol]string, error) Addresses for all networks. Also AllAddressesAt(index) for any index.
(*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.

Private-key import / export (same memguard discipline as the mnemonic):

Function / method Purpose
FromPrivateKeyBytes([]byte, Curve) (*HDWallet, error) Key-only wallet from a byte slice (wiped on use). Any 32-byte-scalar curve.
FromPrivateKeyBuffer(*memguard.LockedBuffer, Curve) (*HDWallet, error) Key-only wallet from a memguard buffer (zero-copy).
(*HDWallet) WithPrivateKey(symbol, index, func([]byte) error) error Use the leaf private key, auto-wiped.
(*HDWallet) PrivateKey(symbol, index) (*memguard.LockedBuffer, error) Leaf key buffer (caller Destroys).
FromWIF([]byte) (*HDWallet, error) · (*HDWallet) WithWIF / WIF Import/export a Bitcoin WIF (secp256k1).
(*HDWallet) AccountXPub(symbol, account) (string, error) · WithAccountXPrv Export account-level BIP-32 extended keys (secp256k1).
WatchOnlyFromXPub(xpub string, symbol) (*WatchWallet, error) Watch-only address derivation from an xpub — no seed.

Transaction & Ethereum message signing:

Function / method Purpose
(*HDWallet) SignTransaction(symbol, index, proto.Message) (proto.Message, error) Build+sign a raw tx (EVM/Tron/XRP/Cosmos/Solana; no broadcast).
(*HDWallet) SignMessage(symbol, index, []byte) ([]byte, error) EIP-191 personal_sign → 65-byte r‖s‖v.
(*HDWallet) SignTypedData(symbol, index, []byte) ([]byte, error) EIP-712 typed-data signature.
(*HDWallet) SignBitcoinMessage(symbol, index, []byte) (string, error) Bitcoin signmessage → base64. With VerifyBitcoinMessage.
(*HDWallet) SignSolanaMessage(symbol, index, []byte) (string, error) Solana off-chain message → base58. With VerifySolanaMessage.
RecoverEthereumAddress([]byte, []byte) (string, error) · VerifyEthereumMessage / …TypedData Recover/verify EIP-191/712 signers.
EncodeRLP/DecodeRLP · ABIEncode/ABIDecode · ABIFunctionSelector · EIP712Hash Standalone EVM encoding utilities.

Address validation / parsing (AnyAddress-style):

Function Purpose
IsValidAddress(symbol, addr) bool · ValidateAddress(symbol, addr) error Validate an address for a network.
ParseAddress(symbol, addr) ([]byte, error) Decode an address to its payload.
AddressFromPublicKey(symbol, pub) (string, error) Derive an address from an external public key.

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

View Source
const (
	EthTxModeLegacy  uint32 = 0 // EIP-155 legacy
	EthTxModeEIP2930 uint32 = 1 // type-1 access-list
	EthTxModeEIP1559 uint32 = 2 // type-2 fee market
)

EVM transaction modes, selecting the serialization format produced by SignTransaction for an EVM chain. They are the values of ethereum.SigningInput.tx_mode; use them instead of bare integers.

Variables

View Source
var (
	// ErrABIType is returned for an unrecognized or unsupported ABI type string.
	ErrABIType = errors.New("hdwallet: unsupported ABI type")
	// ErrABIValue is returned when a Go value does not match its ABI type.
	ErrABIValue = errors.New("hdwallet: ABI value does not match type")
	// ErrABIDecode is returned when ABI-encoded input is malformed.
	ErrABIDecode = errors.New("hdwallet: malformed ABI encoding")
)

ABI-related errors.

View Source
var (
	// ErrRLPCanonical is returned when a decoded item is not in canonical form
	// (e.g. a leading zero in a length prefix, or a single byte < 0x80 wrapped in
	// a string header). Ethereum requires canonical RLP.
	ErrRLPCanonical = errors.New("hdwallet: non-canonical RLP encoding")
	// ErrRLPTruncated is returned when the input ends before a declared length.
	ErrRLPTruncated = errors.New("hdwallet: truncated RLP input")
	// ErrRLPTrailing is returned by DecodeRLP when bytes remain after one item.
	ErrRLPTrailing = errors.New("hdwallet: trailing bytes after RLP item")
)

RLP-related errors.

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")
	// ErrInvalidPrivateKey is returned when an imported private key has the wrong
	// length or is otherwise invalid for its curve.
	ErrInvalidPrivateKey = errors.New("hdwallet: invalid private key")
	// ErrUnsupportedCurve is returned when a curve is not one of the supported
	// elliptic curves.
	ErrUnsupportedCurve = errors.New("hdwallet: unsupported curve")
	// ErrCurveMismatch is returned when an operation targets a coin whose curve
	// differs from the curve of a key-only wallet's imported private key.
	ErrCurveMismatch = errors.New("hdwallet: coin curve does not match imported key curve")
	// ErrKeyOnlyWallet is returned by mnemonic/seed-only operations (Mnemonic,
	// WithMnemonic, AllAddresses) on a wallet imported from a raw private key.
	ErrKeyOnlyWallet = errors.New("hdwallet: operation not available on a private-key-only wallet")
	// ErrKeyOnlyIndex is returned when a non-zero address/sign index is requested
	// on a key-only wallet, which has a single leaf key and no HD path.
	ErrKeyOnlyIndex = errors.New("hdwallet: private-key-only wallet supports only index 0")
	// ErrPathArity is returned by the structured account/change/index helpers
	// (AddressAt, SignAt, PublicKeyAt) when a coin's path template is not a
	// 5-element BIP-44/BIP-84 path; use the AddressPath/SignPath primitives instead.
	ErrPathArity = errors.New("hdwallet: coin path is not a 5-element BIP-44/84 path; use the *Path methods")
	// ErrExtKeyUnsupportedCurve is returned by the extended-key (xprv/xpub) and
	// watch-only APIs for a non-secp256k1 coin: BIP-32 extended keys and public
	// (non-hardened) child derivation apply only to secp256k1; the SLIP-0010
	// ed25519/nist256p1 schemes support hardened derivation only.
	ErrExtKeyUnsupportedCurve = errors.New("hdwallet: extended keys / watch-only require a secp256k1 coin")
	// ErrInvalidWordCount is returned when a requested mnemonic length is not a
	// valid BIP-39 word count (12, 15, 18, 21, or 24) or entropy size in bits
	// (128, 160, 192, 224, or 256).
	ErrInvalidWordCount = errors.New("hdwallet: invalid mnemonic word count")
	// ErrNoEntropy is returned by Cardano (ADA) operations on a wallet that has no
	// BIP-39 entropy to derive from. Cardano's Icarus master key is built from the
	// mnemonic entropy, not the seed, so a wallet imported from a raw private key,
	// WIF, or extended public key (which carry no mnemonic) cannot derive Cardano
	// addresses or signatures.
	ErrNoEntropy = errors.New("hdwallet: Cardano requires a mnemonic-derived wallet (no BIP-39 entropy available)")
)

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

View Source
var (
	// ErrTxUnsupported is returned when SignTransaction is asked to sign for a
	// symbol whose family has no transaction builder.
	ErrTxUnsupported = errors.New("hdwallet: transaction signing not supported for coin")
	// ErrTxInput is returned when the SigningInput proto type does not match the
	// family selected by symbol, or a required field is missing/invalid.
	ErrTxInput = errors.New("hdwallet: invalid transaction signing input")
)

Transaction-signing errors.

View Source
var (
	// ErrEIP712 is returned for malformed typed data or unsupported types.
	ErrEIP712 = errors.New("hdwallet: invalid EIP-712 typed data")
)

EIP-712-related errors.

View Source
var (
	// ErrEthSignature is returned when an Ethereum signature is malformed.
	ErrEthSignature = errors.New("hdwallet: invalid ethereum signature")
)

Ethereum message errors.

View Source
var ErrInvalidAddress = errors.New("hdwallet: invalid address")

ErrInvalidAddress is the base sentinel for all address-validation failures. Specific reasons (bad checksum, wrong prefix, wrong length, …) wrap it with %w, so errors.Is(err, ErrInvalidAddress) matches any validation failure while the message still describes the precise cause.

View Source
var ErrInvalidWIF = errors.New("hdwallet: invalid WIF private key")

ErrInvalidWIF is returned for a malformed WIF string or a WIF export requested for a non-secp256k1 coin.

View Source
var ErrNoTxID = errors.New("hdwallet: signing output has no transaction id")

ErrNoTxID is returned by TransactionID when a SigningOutput carries no transaction id (an empty id field) or when the argument is not one of the six recognised per-family *…SigningOutput types (including a nil proto.Message).

View Source
var ErrNotRecoverable = errors.New("hdwallet: coin curve does not support recoverable signatures")

ErrNotRecoverable is returned by SignBitcoinMessage when the coin's curve is not secp256k1 (only secp256k1 produces a recoverable signature).

View Source
var ErrTxDecode = errors.New("hdwallet: malformed transaction bytes")

ErrTxDecode is returned when raw transaction bytes are malformed, truncated or otherwise not a transaction this decoder understands. The decoder never panics and never reads past the input.

Functions

func ABIEncode added in v0.2.2

func ABIEncode(name string, args []ABIValue) ([]byte, error)

ABIEncode encodes a function call: selector(name,types...) || head/tail(args). The signature is formed from name and the argument types, so name must be the bare function name (e.g. "transfer"), not the full signature.

func ABIEncodeParams added in v0.2.2

func ABIEncodeParams(values []ABIValue) ([]byte, error)

ABIEncodeParams encodes a tuple of values (no selector) using the head/tail layout. This is the encoding used for the arguments of a call and for nested tuples/arrays.

func ABIFunctionSelector added in v0.2.2

func ABIFunctionSelector(name string, types []string) []byte

ABIFunctionSelector returns the 4-byte selector for a function signature built from name and the given argument types (the canonical forms are used).

func AddressFromPublicKey added in v0.2.2

func AddressFromPublicKey(symbol Symbol, pub []byte) (string, error)

AddressFromPublicKey derives the address for symbol directly from an external public key, reusing the registry's encoder (read-only). pub must be the curve's expected public-key form: a 33-byte compressed key for secp256k1/nist256p1, or a 32-byte key for ed25519. An unknown symbol returns ErrUnsupportedCoin; a malformed key is reported by the underlying encoder.

func BuildPSBT added in v0.6.0

func BuildPSBT(symbol Symbol, in *txbtc.SigningInput) ([]byte, error)

BuildPSBT builds an unsigned PSBT (BIP-174) for symbol from in, returning the serialized packet. Coin selection (planBitcoinTx) chooses which UTXOs become inputs and computes the recipient/change outputs exactly as the direct signer would, so a subsequent SignPSBT/FinalizePSBT/ExtractPSBTTx produces the same transaction. Every chosen input must be a segwit type (P2WPKH, P2SH-P2WPKH or P2TR); a legacy P2PKH input returns ErrTxInput.

func EIP712Hash added in v0.2.2

func EIP712Hash(typedDataJSON []byte) ([]byte, error)

EIP712Hash parses MetaMask-shape typed-data JSON and returns the 32-byte keccak256 digest to sign (the 0x1901-prefixed hash).

func EncodeRLP added in v0.2.2

func EncodeRLP(item RLPItem) []byte

EncodeRLP encodes an RLP tree to its canonical byte serialization.

func EstimateBitcoinFee added in v0.6.0

func EstimateBitcoinFee(inputs []BitcoinInputKind, outputs []BitcoinOutputKind, satPerVbyte int64) int64

EstimateBitcoinFee returns the estimated fee (satoshis) for a transaction of the given input/output kinds at satPerVbyte sat/vbyte. It is fee-planning guidance, not a consensus value: fee = EstimateTxVsize * satPerVbyte.

func EstimateTxVsize added in v0.6.0

func EstimateTxVsize(inputs []BitcoinInputKind, outputs []BitcoinOutputKind) int64

EstimateTxVsize returns an approximate virtual size (vbytes) for a transaction spending the given input kinds to the given output kinds. It is a coarse estimate for fee planning only (not consensus-exact): per-type constants are measured against btcd's blockchain/txsizes, with a fixed overhead for the version, locktime, the input/output counts and the amortised SegWit marker/flag when any input carries a witness.

ponytail: coarse per-type constants, good enough for fee math; refine if fee-rate accuracy ever matters.

func EthereumPersonalMessageHash added in v0.2.2

func EthereumPersonalMessageHash(message []byte) []byte

EthereumPersonalMessageHash returns the 32-byte keccak256 digest that EIP-191 personal_sign signs for the given message. len(message) is rendered as its decimal ASCII representation, per the standard.

func ExtractPSBTTx added in v0.6.0

func ExtractPSBTTx(psbtBytes []byte) ([]byte, error)

ExtractPSBTTx finalizes (if needed) and extracts the network-serialized signed transaction from a signed PSBT.

func FinalizePSBT added in v0.6.0

func FinalizePSBT(psbtBytes []byte) ([]byte, error)

FinalizePSBT runs the BIP-174 Finalizer over a fully signed packet and returns the finalized serialized PSBT.

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 GenerateMnemonicWithWordCount added in v0.3.1

func GenerateMnemonicWithWordCount(words int) (string, error)

GenerateMnemonicWithWordCount returns a fresh BIP-39 mnemonic of the given length in words (12, 15, 18, 21, or 24) as a string. Like GenerateMnemonic, the returned string cannot be securely wiped; prefer NewHDWalletWithWordCount for sensitive use. An unsupported word count returns an error wrapping ErrInvalidWordCount.

func IsValidAddress added in v0.2.2

func IsValidAddress(symbol Symbol, addr string) bool

IsValidAddress reports whether addr is a syntactically and checksum-valid address for the given network. It is a convenience wrapper over ValidateAddress.

func ParseAddress added in v0.2.2

func ParseAddress(symbol Symbol, addr string) ([]byte, error)

ParseAddress decodes addr for symbol, verifies its checksum, and returns the decoded payload — e.g. the 20-byte hash160 for Bitcoin/Cosmos/Tron, the 32-byte public key for Solana/Stellar/SS58, or the 20/32-byte account identifier for EVM/Sui/Aptos. An unknown symbol returns ErrUnsupportedCoin; an invalid address returns an error wrapping ErrInvalidAddress.

func RecoverEthereumAddress added in v0.2.2

func RecoverEthereumAddress(message, sig []byte) (string, error)

RecoverEthereumAddress recovers the EIP-55 checksummed Ethereum address that produced sig over message under EIP-191 personal_sign. It is a convenience for callers that want the signer's address rather than a boolean match.

func TransactionID added in v0.9.0

func TransactionID(out proto.Message) (string, error)

TransactionID returns one canonical transaction id for any SigningOutput produced by (*HDWallet).SignTransaction, hiding the per-family differences in field name, byte order and text encoding behind a single accessor so callers need not special-case each chain.

out must be one of the six per-family SigningOutput messages. The id source, by family, is:

  • Bitcoin (and the UTXO altcoins BTC/LTC/DOGE/DASH/BCH/ZEC) — the conventional "txid" reverse(sha256d(tx without witnesses)); from the bytes field TransactionId.
  • Tron — sha256(raw_data); from the bytes field Id.
  • Ethereum / EVM — keccak256(signed RLP); from the string field TxId, which the signer stores as "0x"+hex.
  • Cosmos — sha256(TxRaw broadcast bytes); from the string field TxId, which the signer stores as upper-case hex.
  • Ripple / XRP — sha512Half(signed tx) (SHA-512(tx)[:32]); from the string field TxId, which the signer stores as upper-case hex.

For these five hash-based families the result is normalised to LOWER-CASE hex with NO "0x" prefix, irrespective of how the underlying field stores it. The two byte-typed ids (Bitcoin TransactionId, Tron Id) are already in display (big-endian) order and are hex-encoded as-is — no reversal is applied here.

Solana is the deliberate exception: its transaction id is NOT a hash but the base58-encoded fee-payer signature (Solana identifies a transaction by its first signature). It is returned exactly as the signer produced it — base58, unchanged — and must not be interpreted as hex.

An empty id, or any message that is not one of the six recognised SigningOutput types (including a nil proto.Message), returns ErrNoTxID. The helper reads only the public output and touches no secret material.

func ValidateAddress added in v0.2.2

func ValidateAddress(symbol Symbol, addr string) error

ValidateAddress returns nil if addr is a valid address for symbol, or a descriptive error wrapping ErrInvalidAddress (bad checksum, wrong prefix, wrong length, …) or ErrUnsupportedCoin for an unknown symbol.

func ValidateMnemonic added in v0.9.0

func ValidateMnemonic(mnemonic string) error

ValidateMnemonic reports whether mnemonic is a valid BIP-39 seed phrase. It returns nil if the phrase is valid and ErrInvalidMnemonic otherwise, applying the same wordlist, word-count, and checksum checks FromMnemonic does — but without constructing a wallet or retaining the phrase, so an import UI can validate user input before building anything. Surrounding whitespace is trimmed first, exactly as the constructors do.

func ValidateMnemonicBytes added in v0.9.0

func ValidateMnemonicBytes(mnemonic []byte) error

ValidateMnemonicBytes is ValidateMnemonic for a mnemonic held in a byte slice, mirroring the FromMnemonic/FromMnemonicBytes string/bytes pairing. Unlike the constructors it neither wipes nor modifies the slice: this is a read-only validity check, not a wallet entry point.

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.

func VerifyBitcoinMessage added in v0.3.1

func VerifyBitcoinMessage(address string, message []byte, sigBase64 string) bool

VerifyBitcoinMessage reports whether sigBase64 is a valid Bitcoin signed-message signature of message by the key behind a legacy P2PKH (base58check, "1"-prefix) address. It recovers the public key from the compact signature and checks its legacy address equals address.

func VerifyEthereumMessage added in v0.2.2

func VerifyEthereumMessage(addressOrPubKey string, message, sig []byte) bool

VerifyEthereumMessage reports whether sig (65-byte r||s||v, v in {27,28} or {0,1}) is a valid EIP-191 personal_sign signature of message by the signer identified by addressOrPubKey. addressOrPubKey may be a 0x-hex Ethereum address (20 bytes), or a hex/raw secp256k1 public key (33-byte compressed or 65-byte uncompressed). It ecrecovers the signer and compares.

func VerifyEthereumTypedData added in v0.2.2

func VerifyEthereumTypedData(addressOrPubKey string, typedDataJSON, sig []byte) bool

VerifyEthereumTypedData is the EIP-712 counterpart of VerifyEthereumMessage.

func VerifySignature added in v0.8.0

func VerifySignature(symbol Symbol, pub, data []byte, sig *Signature) (bool, error)

VerifySignature reports whether sig is a valid signature of data by the public key pub for the coin symbol. It is the Symbol-keyed counterpart to Sign: the curve is resolved from the registry rather than supplied directly.

As with Sign/SignIndex, data is the 32-byte digest for ECDSA chains (secp256k1, nist256p1, starkex) and the raw message for ed25519 chains. A non-32-byte input for an ECDSA chain returns a wrapped ErrInvalidDigest, mirroring SignIndex; an unknown symbol returns a wrapped ErrUnsupportedCoin.

It needs no secret and so is a free function, not a wallet method.

func VerifySolanaMessage added in v0.3.1

func VerifySolanaMessage(address string, message []byte, sigBase58 string) bool

VerifySolanaMessage reports whether sigBase58 is a valid Solana off-chain message signature for message under the given base58 account address (which is itself the 32-byte ed25519 public key).

Types

type ABIValue added in v0.2.2

type ABIValue struct {
	Type  string
	Value any
}

ABIValue is one ABI argument: Type is the canonical type string (e.g. "uint256", "address", "bytes", "uint256[]", "(address,uint256)") and Value is the Go value:

address, bytesN, bytes, string -> []byte
uint*/int*                     -> *big.Int
bool                           -> bool
arrays, tuples                 -> []ABIValue (elements/fields)

func ABIDecode added in v0.2.2

func ABIDecode(types []string, input []byte) (selector []byte, values []ABIValue, err error)

ABIDecode decodes input that begins with a 4-byte selector followed by the arguments encoded for the given types. The returned selector is the leading 4 bytes (not verified against any signature).

func ABIDecodeParams added in v0.2.2

func ABIDecodeParams(types []string, data []byte) ([]ABIValue, error)

ABIDecodeParams decodes the head/tail-encoded data for the given tuple of types into ABIValues. data must be the argument region (no selector).

type BitcoinAddressType added in v0.5.0

type BitcoinAddressType int

BitcoinAddressType selects which standard Bitcoin script/address format BitcoinAddress produces. Each maps to a BIP "purpose" derivation path.

const (
	// P2PKH is a legacy pay-to-public-key-hash address (base58check, "1..."),
	// derived under BIP-44 (m/44').
	P2PKH BitcoinAddressType = iota
	// P2SHP2WPKH is a nested SegWit pay-to-witness-public-key-hash wrapped in
	// pay-to-script-hash (base58check, "3..."), derived under BIP-49 (m/49').
	P2SHP2WPKH
	// P2WPKH is a native SegWit v0 address (bech32, "bc1q..."), derived under
	// BIP-84 (m/84'). This is the registry default for BTC/LTC.
	P2WPKH
	// P2TR is a Taproot v1 key-path address (bech32m, "bc1p..."), derived under
	// BIP-86 (m/86').
	P2TR
)

func (BitcoinAddressType) String added in v0.5.0

func (t BitcoinAddressType) String() string

String returns a short human-readable name for the address type.

type BitcoinInputKind added in v0.6.0

type BitcoinInputKind int

BitcoinInputKind identifies the script type of an input being spent, for the fee/size estimator (EstimateTxVsize / EstimateBitcoinFee).

const (
	// InputP2PKH is a legacy pay-to-pubkey-hash input (no witness).
	InputP2PKH BitcoinInputKind = iota
	// InputP2SHP2WPKH is a nested SegWit (BIP-49) P2SH-P2WPKH input.
	InputP2SHP2WPKH
	// InputP2WPKH is a native SegWit v0 P2WPKH input.
	InputP2WPKH
	// InputP2TR is a Taproot key-path P2TR input.
	InputP2TR
)

type BitcoinOutputKind added in v0.6.0

type BitcoinOutputKind int

BitcoinOutputKind identifies the script type of an output, for the fee/size estimator.

const (
	// OutputP2PKH is a legacy pay-to-pubkey-hash output (25-byte script).
	OutputP2PKH BitcoinOutputKind = iota
	// OutputP2SH is a pay-to-script-hash output (23-byte script).
	OutputP2SH
	// OutputP2WPKH is a native SegWit v0 P2WPKH output (22-byte script).
	OutputP2WPKH
	// OutputP2TR is a Taproot P2TR output (34-byte script).
	OutputP2TR
)

type BtcTxFields added in v0.6.0

type BtcTxFields struct {
	Version    int32
	Vin        []BtcVin
	Vout       []BtcVout
	LockTime   uint32
	HasWitness bool
}

BtcTxFields holds the decoded, display-ready fields of a Bitcoin-family transaction.

func DecodeBitcoinTx added in v0.6.0

func DecodeBitcoinTx(symbol Symbol, raw []byte) (*BtcTxFields, error)

DecodeBitcoinTx decodes a raw Bitcoin-family transaction (signed or unsigned) for symbol, whose btcAddrParams select the HRP / version bytes used to render output addresses. Malformed or truncated input returns ErrTxDecode; the function never panics and never reads past `raw`.

type BtcVin added in v0.6.0

type BtcVin struct {
	TxID     string // 32-byte prev txid, big-endian hex (explorer order)
	Vout     uint32
	Sequence uint32
}

BtcVin is one decoded transaction input. TxID is rendered big-endian (the display/explorer order, reversed from the internal little-endian wire order).

type BtcVout added in v0.6.0

type BtcVout struct {
	Value     int64  // satoshis
	ScriptHex string // scriptPubKey, hex
	Address   string // rendered address, empty if nonstandard
	Type      string // "p2pkh" / "p2sh" / "p2wpkh" / "p2tr" / "nonstandard"
}

BtcVout is one decoded transaction output. Address is the rendered destination for a recognised standard script (empty with Type "nonstandard" otherwise).

type Coin

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

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.

Decimals is the number of fractional digits in the chain's native unit (e.g. 8 for Bitcoin satoshis, 18 for Ethereum wei, 6 for Cosmos uatom) and is used to format on-chain balances. ChainID is the EIP-155 numeric chain id used to build EVM transactions; it is non-zero only for EVM chains (the evmTxChains set in tx_families.go) and 0 for every non-EVM coin. Both mirror the Trust Wallet Core coins registry. The SLIP-44 coin type is NOT stored — it is derived from Path via the SLIP44 method below.

func CoinInfo

func CoinInfo(symbol Symbol) (Coin, bool)

CoinInfo returns the static registry entry for a symbol.

func (Coin) SLIP44 added in v0.6.0

func (c Coin) SLIP44() uint32

SLIP44 returns the SLIP-44 coin type for the coin, derived from the second element of its BIP-32 Path (e.g. "m/44'/60'/0'/0/0" → 60). The hardened flag is stripped. It is computed from Path rather than stored separately so the two can never drift; a malformed path returns 0.

type CosmosCoin added in v0.8.0

type CosmosCoin struct {
	Denom  string
	Amount string
}

CosmosCoin is one decoded { denom, amount } coin (amount is a decimal string, the on-wire Cosmos representation).

type CosmosMessage added in v0.8.0

type CosmosMessage struct {
	TypeURL string

	// MsgSend.
	FromAddress string
	ToAddress   string

	// MsgDelegate / MsgUndelegate / MsgWithdrawDelegatorReward.
	DelegatorAddress string
	ValidatorAddress string

	// Amount: MsgSend carries a repeated coin set; MsgDelegate / MsgUndelegate
	// carry a single coin (one element). MsgWithdrawReward carries none.
	Amount []CosmosCoin
}

CosmosMessage is one decoded transaction message. TypeURL is always set; the remaining fields are populated according to the message type (only the relevant subset is non-empty).

type CosmosTxFields added in v0.8.0

type CosmosTxFields struct {
	Messages   []CosmosMessage
	Memo       string
	FeeAmount  []CosmosCoin
	GasLimit   uint64
	Sequence   uint64
	Signatures [][]byte
}

CosmosTxFields holds the decoded, display-ready fields of a Cosmos transaction.

func DecodeCosmosTx added in v0.8.0

func DecodeCosmosTx(raw []byte) (*CosmosTxFields, error)

DecodeCosmosTx decodes a raw (broadcast) Cosmos TxRaw blob into its display fields. Malformed or truncated input returns ErrTxDecode; the function never panics.

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
	// Ed25519Blake2bNano is the ed25519 EdDSA variant Nano uses: identical to
	// ed25519 except the internal 512-bit hash is BLAKE2b-512 instead of SHA-512
	// (both for key expansion and the R/challenge hashes). SLIP-0010 ed25519
	// derivation is used for the leaf private key. Matches Trust Wallet Core's
	// TWCurveED25519Blake2bNano.
	Ed25519Blake2bNano
	// Curve25519 is the public-key/signing scheme Waves uses: the leaf private
	// key is derived via SLIP-0010 ed25519, the public key is the X25519
	// (Montgomery) point, and signing is ed25519 with the public-key sign bit
	// folded into S[63] (the "curve25519_sign" construction). Matches Trust
	// Wallet Core's TWCurveCurve25519.
	Curve25519
	// Ed25519ExtendedCardano is BIP32-Ed25519 (CIP-1852) with 64-byte extended
	// private keys, as used by Cardano. The master secret comes from the Icarus
	// (PBKDF2-HMAC-SHA512 over the BIP-39 entropy) scheme. Matches Trust Wallet
	// Core's TWCurveED25519ExtendedCardano.
	Ed25519ExtendedCardano
	// Starkex is the STARK curve (StarkNet/StarkEx), EIP-2645 key derivation with
	// grinding, RFC-6979 deterministic ECDSA. Matches Trust Wallet Core's
	// TWCurveStarkex.
	Starkex
	// Sr25519 is schnorrkel/ristretto255, the native key scheme for
	// Polkadot/Kusama. NOTE: this is NOT part of Trust Wallet Core's curve set
	// (TWC uses plain ed25519 for Polkadot); it is provided here as an additional
	// curve for native substrate signing.
	Sr25519
)

func (Curve) String

func (c Curve) String() string

String returns the SLIP-0010/BIP-32 name of the curve for diagnostics. The strings for the Trust Wallet Core curves match TWCurve.h exactly.

type ERC20Call added in v0.6.0

type ERC20Call struct {
	Method    string   // "transfer" or "approve"
	Recipient string   // "0x"-prefixed 20-byte address (the `to`/`spender`)
	Amount    *big.Int // the uint256 amount
}

ERC20Call is a decoded ERC-20 method call recognised in the transaction's calldata (transfer or approve). It is nil on EthTxFields unless the calldata's 4-byte selector and shape match one of those methods.

type EthAccessTuple added in v0.6.0

type EthAccessTuple struct {
	Address     string   // "0x"-prefixed 20-byte address
	StorageKeys [][]byte // each 32 bytes
}

EthAccessTuple is one decoded EIP-2930 access-list entry: the accessed address (0x-hex) and the 32-byte storage keys accessed under it.

type EthTxFields added in v0.6.0

type EthTxFields struct {
	Type     EthTxType
	ChainID  *big.Int
	Nonce    *big.Int
	GasPrice *big.Int // legacy + 2930

	MaxPriorityFeePerGas *big.Int // 1559
	MaxFeePerGas         *big.Int // 1559

	GasLimit *big.Int
	To       string // "0x"-hex; empty = contract creation
	Value    *big.Int
	Data     []byte

	AccessList []EthAccessTuple // 2930 + 1559

	// Signature scalars (present in a signed tx). YParity is the bare recovery id
	// (0/1) for typed txs; for legacy it is the raw v as a *big.Int.
	V *big.Int
	R *big.Int
	S *big.Int

	// ERC20 is set when Data decodes to a recognised ERC-20 transfer/approve call.
	ERC20 *ERC20Call
}

EthTxFields holds the decoded, display-ready fields of an EVM transaction. Numeric quantities are *big.Int (nil means absent/zero); To is a "0x"-prefixed hex address, empty for contract creation. The fields populated depend on Type: MaxPriorityFeePerGas/MaxFeePerGas/AccessList are EIP-1559/2930 only, GasPrice is legacy/2930 only, ChainID is decoded for all (derived from v via EIP-155 for a legacy tx whose v >= 35; nil for a legacy tx with the pre-155 v of 27/28).

func DecodeEthereumTx added in v0.6.0

func DecodeEthereumTx(raw []byte) (*EthTxFields, error)

DecodeEthereumTx decodes a raw EVM transaction (signed or unsigned) into its display fields. It branches on the first byte: 0x02 => EIP-1559, 0x01 => EIP-2930, any byte >= 0xc0 (an RLP list prefix) => legacy. Malformed or truncated input returns ErrTxDecode; the function never panics.

type EthTxType added in v0.6.0

type EthTxType int

EthTxType identifies the EVM transaction envelope a raw blob decoded as.

const (
	// EthTxLegacy is a pre-EIP-2718 legacy (EIP-155) transaction: a bare RLP
	// list with no type-byte envelope.
	EthTxLegacy EthTxType = iota
	// EthTxEIP2930 is a type-1 (0x01) access-list transaction.
	EthTxEIP2930
	// EthTxEIP1559 is a type-2 (0x02) fee-market transaction.
	EthTxEIP1559
)

func (EthTxType) String added in v0.6.0

func (t EthTxType) String() string

String returns a short human-readable name for the transaction type.

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.

It uses the empty BIP-39 passphrase (Trust Wallet's default). For a passphrase-protected wallet, use FromMnemonicBufferWithPassphrase.

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 FromMnemonicBufferWithPassphrase added in v0.3.0

func FromMnemonicBufferWithPassphrase(buf, passphrase *memguard.LockedBuffer) (*HDWallet, error)

FromMnemonicBufferWithPassphrase is the most secure passphrase entry point: both the mnemonic and the BIP-39 passphrase are supplied in page-locked, encrypted-at-rest memguard buffers and used without an intermediate plaintext heap copy (aside from the unavoidable transient BIP-39 string conversion).

The wallet takes ownership of both buffers and destroys them; do not use either afterwards. passphrase may be nil for the empty passphrase. Surrounding whitespace in the mnemonic buffer is trimmed; the passphrase is used verbatim.

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.

It uses the empty BIP-39 passphrase (Trust Wallet's default). For a passphrase-protected ("hidden") wallet, use FromMnemonicWithPassphrase.

func FromMnemonicWithPassphrase added in v0.3.0

func FromMnemonicWithPassphrase(mnemonic, passphrase []byte) (*HDWallet, error)

FromMnemonicWithPassphrase builds a wallet from a mnemonic and an optional BIP-39 passphrase (the "25th word"), both held in byte slices. A different passphrase derives a completely different set of addresses from the same mnemonic; the empty passphrase (nil or empty slice) matches FromMnemonicBytes and Trust Wallet's default.

Both slices are wiped before the function returns; callers must not reuse them. Like the mnemonic, the passphrase is briefly converted to a Go string internally for BIP-39 seed derivation (see the package secret-handling notes).

func FromPrivateKeyBuffer added in v0.2.2

func FromPrivateKeyBuffer(buf *memguard.LockedBuffer, curve Curve) (*HDWallet, error)

FromPrivateKeyBuffer builds a key-only wallet from a raw private key held in a memguard LockedBuffer. This is the most secure key-import entry point: the key 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 (mirrors FromMnemonicBuffer).

The wallet takes ownership of buf and destroys it; do not use buf afterwards. See FromPrivateKeyBytes for the semantics of a key-only wallet.

func FromPrivateKeyBytes added in v0.2.2

func FromPrivateKeyBytes(priv []byte, curve Curve) (*HDWallet, error)

FromPrivateKeyBytes builds a key-only wallet from a raw private key held in a byte slice. The slice is wiped before this function returns; callers must not reuse it (mirrors FromMnemonicBytes).

curve identifies the elliptic curve the key belongs to (Secp256k1, Ed25519, or Nist256p1). The resulting wallet can derive addresses, public keys, sign, and export the key for any registered coin whose curve matches; mismatched-curve coins return ErrCurveMismatch. A key-only wallet has no HD path — the imported key is the leaf — so only index 0 is valid and there is no mnemonic to read (Mnemonic/WithMnemonic/AllAddresses return ErrKeyOnlyWallet).

The key must be exactly 32 bytes and non-zero, or ErrInvalidPrivateKey is returned.

func FromWIF added in v0.3.0

func FromWIF(wif []byte) (*HDWallet, error)

FromWIF builds a key-only secp256k1 wallet from a Bitcoin WIF private key held in a byte slice. The slice is wiped before returning; callers must not reuse it. Both compressed and uncompressed mainnet (0x80) WIF are accepted, and the imported key may then be used with any secp256k1 coin.

Like the mnemonic path, the WIF is briefly converted to a Go string internally for base58 decoding (it cannot be wiped and is GC-bounded).

func NewHDWallet

func NewHDWallet() (*HDWallet, error)

NewHDWallet creates a wallet with a fresh 12-word (128-bit) mnemonic. It is a convenience wrapper over NewHDWalletWithWordCount(12).

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 NewHDWalletWithEntropy added in v0.3.1

func NewHDWalletWithEntropy(bits int) (*HDWallet, error)

NewHDWalletWithEntropy creates a wallet with a fresh mnemonic generated from bits of BIP-39 entropy. bits must be one of 128, 160, 192, 224, or 256 (yielding 12, 15, 18, 21, or 24 words); any other value returns an error wrapping ErrInvalidWordCount.

func NewHDWalletWithWordCount added in v0.3.1

func NewHDWalletWithWordCount(words int) (*HDWallet, error)

NewHDWalletWithWordCount creates a wallet with a fresh mnemonic of the given length in words. words must be one of 12, 15, 18, 21, or 24; any other value returns an error wrapping ErrInvalidWordCount.

func (*HDWallet) AccountXPub added in v0.3.0

func (w *HDWallet) AccountXPub(symbol Symbol, account uint32) (string, error)

AccountXPub returns the BIP-32 extended PUBLIC key (xpub) for symbol's account at the given index — i.e. the neutered key at m/purpose'/coin'/account'. It is public and safe to share; feed it to WatchOnlyFromXPub to derive addresses without the seed. symbol must be a secp256k1 coin.

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) AddressAt added in v0.3.0

func (w *HDWallet) AddressAt(symbol Symbol, account, change, index uint32) (string, error)

AddressAt returns the address for symbol at the given BIP-44 account, change branch, and address index, building the path "m/purpose'/coin'/account'/change/ index" from the coin's template. It requires a standard 5-element path; coins whose template has a different shape (e.g. SOL's "m/44'/501'/0'") return ErrPathArity — use AddressPath for those.

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) AddressPath added in v0.3.0

func (w *HDWallet) AddressPath(symbol Symbol, path string) (string, error)

AddressPath returns the address for symbol derived at the given absolute BIP-32 path (e.g. "m/44'/60'/1'/0/5"). The path's curve scheme is the coin's; only the child indices are taken from path. An invalid path or unknown symbol returns an error; a key-only wallet returns ErrKeyOnlyWallet.

func (*HDWallet) AddressRange added in v0.8.0

func (w *HDWallet) AddressRange(symbol Symbol, start, count uint32) ([]string, error)

AddressRange derives count consecutive addresses for a single coin symbol starting at index start, varying the final element of the coin's BIP-32 path (preserving its hardened flag) exactly as AddressIndex does. The returned slice is in ascending index order: element i is the address at start+i. A count of 0 returns an empty, non-nil slice.

The seed enclave is opened exactly once and every address is derived inside that single decryption window. start+count must not exceed 2^31 (0x80000000); a larger range returns an out-of-range error, as does an unknown symbol (wrapping ErrUnsupportedCoin). It is only available on seed-based wallets; a key-only wallet (imported from a single private key) returns ErrKeyOnlyWallet.

func (*HDWallet) AllAddresses

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

AllAddresses derives the first address (index 0) for every supported coin. It is exactly equivalent to AllAddressesAt(0).

func (*HDWallet) AllAddressesAt added in v0.3.1

func (w *HDWallet) AllAddressesAt(index uint32) (map[Symbol]string, error)

AllAddressesAt derives the address at the given index for every supported coin, varying the final element of each coin's BIP-32 path (preserving its hardened flag) exactly as AddressIndex does. The seed enclave is opened exactly once and every coin is derived inside that single decryption window.

index must be below 2^31 (0x80000000); a larger value returns an error. It is only available on seed-based wallets; a key-only wallet (imported from a single private key) has no seed to enumerate over and returns ErrKeyOnlyWallet.

func (*HDWallet) BitcoinAddress added in v0.5.0

func (w *HDWallet) BitcoinAddress(symbol Symbol, t BitcoinAddressType, account, change, index uint32) (string, error)

BitcoinAddress returns the address for symbol in the given Bitcoin address type, derived at the type's standard BIP path "m/purpose'/coinType'/account'/change/index" (purpose = 44/49/84/86 for P2PKH/P2SHP2WPKH/P2WPKH/P2TR; coinType taken from the coin's registry path).

It is available for chains in btcAddrParams (BTC, LTC); any other symbol returns ErrUnsupportedCoin. account/change/index must each be below 2^31. Like the other derivation methods it is seed-only and the leaf key is wiped after use; a key-only wallet returns ErrKeyOnlyWallet.

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) PrivateKey added in v0.2.2

func (w *HDWallet) PrivateKey(symbol Symbol, index uint32) (*memguard.LockedBuffer, error)

PrivateKey returns the raw leaf private key for symbol at the given address index in a page-locked, encrypted-at-rest memguard buffer. This is a lower-level accessor: the caller MUST call Destroy on the returned buffer when finished, or the decrypted key lingers in memory. Prefer WithPrivateKey, which wipes the key automatically when its callback returns (mirrors Mnemonic).

The returned buffer holds a copy taken inside the wallet's protected derivation window; the wallet's own working copy is wiped before this returns. See WithPrivateKey for the seed/key-only semantics and the key encoding.

func (*HDWallet) PrivateKeyPath added in v0.3.0

func (w *HDWallet) PrivateKeyPath(symbol Symbol, path string) (*memguard.LockedBuffer, error)

PrivateKeyPath returns the raw leaf private key for symbol derived at the given absolute BIP-32 path in a page-locked memguard buffer; the caller MUST Destroy it (mirrors PrivateKey). Prefer WithPrivateKeyPath, which wipes automatically.

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) PublicKeyAt added in v0.3.0

func (w *HDWallet) PublicKeyAt(symbol Symbol, account, change, index uint32) ([]byte, error)

PublicKeyAt is the account/change/index counterpart of PublicKeyPath.

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) PublicKeyPath added in v0.3.0

func (w *HDWallet) PublicKeyPath(symbol Symbol, path string) ([]byte, error)

PublicKeyPath returns the public key for symbol derived at the given absolute BIP-32 path: the 33-byte compressed key for secp256k1/nist256p1, or the 32-byte key for ed25519-family curves.

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) SignAt added in v0.3.0

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

SignAt is the account/change/index counterpart of SignPath.

func (*HDWallet) SignBitcoinMessage added in v0.3.1

func (w *HDWallet) SignBitcoinMessage(symbol Symbol, index uint32, message []byte) (string, error)

SignBitcoinMessage signs message under the Bitcoin signed-message standard with the key derived for symbol at the given address index, returning the base64 compact signature. symbol must be a secp256k1 coin (e.g. BTC, LTC); the derived private key is wiped immediately after signing and never leaves the package.

The signature commits only to the message and key, so it is independent of the address format; this library derives compressed keys, so the header byte marks the key compressed (verifiers recover the compressed key / its legacy address).

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) SignMessage added in v0.2.2

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

SignMessage signs message with EIP-191 personal_sign using the key for symbol at the given address index, returning a 65-byte r||s||v signature (v = 27/28). symbol must be a secp256k1/EVM coin (e.g. ETH); other curves return an error.

func (*HDWallet) SignPSBT added in v0.6.0

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

SignPSBT parses psbtBytes, signs every input controlled by the (symbol,index) key, attaches the signatures, and returns the updated serialized PSBT. The leaf key is derived under the package's seed discipline and wiped on return.

func (*HDWallet) SignPath added in v0.3.0

func (w *HDWallet) SignPath(symbol Symbol, path string, data []byte) (*Signature, error)

SignPath signs data with the key for symbol derived at the given absolute BIP-32 path. The ECDSA-vs-ed25519 input rule is identical to SignIndex (ECDSA curves want the 32-byte digest; ed25519 wants the message).

func (*HDWallet) SignSolanaMessage added in v0.3.1

func (w *HDWallet) SignSolanaMessage(symbol Symbol, index uint32, message []byte) (string, error)

SignSolanaMessage signs message with the ed25519 key derived for symbol at the given address index and returns the base58-encoded 64-byte signature.

symbol must be a Solana / ed25519 coin (e.g. SOL). The derived private key is wiped immediately after signing and never leaves the package.

func (*HDWallet) SignTransaction added in v0.2.2

func (w *HDWallet) SignTransaction(symbol Symbol, index uint32, input proto.Message) (proto.Message, error)

SignTransaction signs a transaction for symbol using the key derived at the given address index and returns a per-chain protobuf SigningOutput.

input must be the SigningInput proto for symbol's family (e.g. *ethereum.SigningInput for ETH/EVM, *tron.SigningInput for TRX, …). The returned proto.Message is the matching SigningOutput, holding the signed serialized raw-transaction bytes plus a hex/base58/base64 convenience form. No network calls are made.

The derived private key is wiped immediately after signing and never leaves the package. An unknown symbol returns ErrTxUnsupported; a wrong input type returns ErrTxInput.

func (*HDWallet) SignTypedData added in v0.2.2

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

SignTypedData signs the EIP-712 digest of MetaMask-shape typed-data JSON using the key for symbol at the given address index, returning a 65-byte r||s||v signature (v = 27/28).

func (*HDWallet) WIF added in v0.3.0

func (w *HDWallet) WIF(symbol Symbol, index uint32) (*memguard.LockedBuffer, error)

WIF returns the leaf key for symbol at the given address index as a Bitcoin mainnet compressed WIF in a page-locked memguard buffer; the caller MUST Destroy it. Prefer WithWIF, which wipes automatically.

func (*HDWallet) WithAccountXPrv added in v0.3.0

func (w *HDWallet) WithAccountXPrv(symbol Symbol, account uint32, fn func(xprv []byte) error) error

WithAccountXPrv runs fn with the BIP-32 extended PRIVATE key (xprv) string for symbol's account, then wipes the byte slice (mirrors WithPrivateKey/WithWIF). The xprv is a full secret — anyone holding it can derive every key under the account. The slice passed to fn must not escape fn. symbol must be secp256k1.

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

func (*HDWallet) WithPrivateKey added in v0.2.2

func (w *HDWallet) WithPrivateKey(symbol Symbol, index uint32, fn func(priv []byte) error) error

WithPrivateKey runs fn with the raw leaf private key for symbol at the given address index and wipes the key as soon as fn returns. This is the safe export primitive — the key never escapes into a value the caller must remember to clear (mirrors WithMnemonic). The slice passed to fn must not escape fn.

It works for both wallet modes: seed wallets derive the key for symbol/index; key-only wallets return their imported key (curve must match symbol's curve, and index must be 0). The key is the 32-byte scalar/seed for the coin's curve.

For ECDSA chains this is the signing scalar; for ed25519 it is the 32-byte seed (not the 64-byte expanded key).

func (*HDWallet) WithPrivateKeyPath added in v0.3.0

func (w *HDWallet) WithPrivateKeyPath(symbol Symbol, path string, fn func(priv []byte) error) error

WithPrivateKeyPath runs fn with the raw leaf private key for symbol derived at the given absolute BIP-32 path and wipes the key as soon as fn returns (mirrors WithPrivateKey). The slice passed to fn must not escape fn.

func (*HDWallet) WithWIF added in v0.3.0

func (w *HDWallet) WithWIF(symbol Symbol, index uint32, fn func(wif []byte) error) error

WithWIF runs fn with the leaf private key for symbol at the given address index encoded as a Bitcoin mainnet compressed WIF, then wipes the WIF bytes (mirrors WithPrivateKey). symbol must be a secp256k1 coin. The slice passed to fn must not escape fn.

type RLPItem added in v0.2.2

type RLPItem struct {
	IsList bool
	Str    []byte
	List   []RLPItem
}

RLPItem is a node in an RLP tree: exactly one of Str (a byte string, a leaf) or List (an ordered list of items) is meaningful. IsList selects which. The zero value is the empty string item (encodes to 0x80).

func DecodeRLP added in v0.2.2

func DecodeRLP(data []byte) (RLPItem, error)

DecodeRLP decodes a single, complete RLP item. It is strict: the entire input must be exactly one canonical item with no trailing bytes.

func RLPList added in v0.2.2

func RLPList(items ...RLPItem) RLPItem

RLPList builds a list RLPItem from the given child items.

func RLPString added in v0.2.2

func RLPString(b []byte) RLPItem

RLPString builds a leaf RLPItem holding the given bytes.

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 SolanaInstruction added in v0.8.0

type SolanaInstruction struct {
	ProgramIDIndex byte
	ProgramID      string // base58 of the referenced account key ("" if out of range)
	Accounts       []byte // account-key indices the instruction operates on
	Data           []byte // raw instruction data

	// Type names a recognised instruction: "systemTransfer" or
	// "tokenTransferChecked" (empty otherwise).
	Type     string
	Lamports uint64 // systemTransfer
	Amount   uint64 // tokenTransferChecked
	Decimals byte   // tokenTransferChecked
}

SolanaInstruction is one decoded instruction. ProgramID and the decoded transfer fields are best-effort: ProgramID is empty if the program index is out of range, and Type is "" for an instruction the decoder does not recognise.

type SolanaTxFields added in v0.8.0

type SolanaTxFields struct {
	Signatures            [][]byte
	NumRequiredSignatures byte
	NumReadonlySigned     byte
	NumReadonlyUnsigned   byte
	AccountKeys           []string // base58
	RecentBlockhash       string   // base58
	Instructions          []SolanaInstruction
}

SolanaTxFields holds the decoded, display-ready fields of a Solana transaction.

func DecodeSolanaTx added in v0.8.0

func DecodeSolanaTx(raw []byte) (*SolanaTxFields, error)

DecodeSolanaTx decodes a raw signed Solana (legacy) transaction into its display fields. Malformed or truncated input returns ErrTxDecode; the function never panics and never reads past `raw`.

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 — additional UTXO chains.
	GRS   Symbol = "GRS"   // Groestlcoin (segwit)
	DGB   Symbol = "DGB"   // DigiByte (segwit)
	BTG   Symbol = "BTG"   // Bitcoin Gold (segwit)
	SYS   Symbol = "SYS"   // Syscoin (segwit)
	VIA   Symbol = "VIA"   // Viacoin (segwit)
	QTUM  Symbol = "QTUM"  // Qtum (base58check P2PKH)
	RVN   Symbol = "RVN"   // Ravencoin (base58check P2PKH)
	KMD   Symbol = "KMD"   // Komodo (base58check P2PKH)
	FIRO  Symbol = "FIRO"  // Firo (base58check P2PKH)
	MONA  Symbol = "MONA"  // MonaCoin (base58check P2PKH)
	XVG   Symbol = "XVG"   // Verge (base58check P2PKH)
	PIVX  Symbol = "PIVX"  // PIVX (base58check P2PKH)
	NEBL  Symbol = "NEBL"  // Neblio (base58check P2PKH)
	STRAX Symbol = "STRAX" // Stratis (segwit)
	ZEN   Symbol = "ZEN"   // Horizen (base58check 2-byte)
	BCD   Symbol = "BCD"   // Bitcoin Diamond (base58check P2PKH)
	XEC   Symbol = "XEC"   // eCash (CashAddr)
	FLUX  Symbol = "FLUX"  // Flux/Zelcash (base58check 2-byte)

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

	// secp256k1 — EOS-family public-key strings.
	EOS Symbol = "EOS"
	WAX Symbol = "WAX"
	FIO Symbol = "FIO"

	// secp256k1 — Filecoin (f1 base32 address).
	FIL Symbol = "FIL"

	// 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 — additional EVM chains (Ethereum address format, EIP-55).
	ETC      Symbol = "ETC"
	RONIN    Symbol = "RONIN"
	ZKSYNC   Symbol = "ZKSYNC"
	LINEA    Symbol = "LINEA"
	SCROLL   Symbol = "SCROLL"
	MANTLE   Symbol = "MANTLE"
	BLAST    Symbol = "BLAST"
	KAIA     Symbol = "KAIA"
	AURORA   Symbol = "AURORA"
	GLMR     Symbol = "GLMR"
	MOVR     Symbol = "MOVR"
	BOBA     Symbol = "BOBA"
	METIS    Symbol = "METIS"
	OPBNB    Symbol = "OPBNB"
	POLZKEVM Symbol = "POLZKEVM"
	MANTA    Symbol = "MANTA"
	RBTC     Symbol = "RBTC"
	HECO     Symbol = "HECO"
	OKT      Symbol = "OKT"
	KCS      Symbol = "KCS"
	WAN      Symbol = "WAN"
	POA      Symbol = "POA"
	CLO      Symbol = "CLO"
	GO       Symbol = "GO"
	TT       Symbol = "TT"
	VET      Symbol = "VET"
	IOTX     Symbol = "IOTX"
	THETA    Symbol = "THETA"
	NEON     Symbol = "NEON"
	MERLIN   Symbol = "MERLIN"
	LIGHT    Symbol = "LIGHT"
	SONIC    Symbol = "SONIC"
	ZENEON   Symbol = "ZENEON"
	ZETAEVM  Symbol = "ZETAEVM"

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

	// secp256k1 — additional Cosmos SDK chains (hash160 bech32, per-chain HRP).
	LUNA      Symbol = "LUNA"
	KAVA      Symbol = "KAVA"
	SCRT      Symbol = "SCRT"
	BAND      Symbol = "BAND"
	RUNE      Symbol = "RUNE"
	STARS     Symbol = "STARS"
	AXL       Symbol = "AXL"
	STRD      Symbol = "STRD"
	BLD       Symbol = "BLD"
	CRE       Symbol = "CRE"
	KUJI      Symbol = "KUJI"
	CMDX      Symbol = "CMDX"
	NTRN      Symbol = "NTRN"
	SOMM      Symbol = "SOMM"
	FET       Symbol = "FET"
	MARS      Symbol = "MARS"
	UMEE      Symbol = "UMEE"
	COREUM    Symbol = "COREUM"
	QSR       Symbol = "QSR"
	XPRT      Symbol = "XPRT"
	AKT       Symbol = "AKT"
	NOBLE     Symbol = "NOBLE"
	SEI       Symbol = "SEI"
	DYDX      Symbol = "DYDX"
	BLZ       Symbol = "BLZ"
	CRYPTOORG Symbol = "CRYPTOORG"

	// secp256k1 — Cosmos chains with EVM-style keys (keccak address, bech32).
	EVMOS Symbol = "EVMOS"
	INJ   Symbol = "INJ"
	CANTO Symbol = "CANTO"
	ZETA  Symbol = "ZETA"
	ONE   Symbol = "ONE"

	// 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"

	// ed25519 (SLIP-0010) — additional chains.
	EGLD Symbol = "EGLD" // MultiversX (bech32 of pubkey)
	IOST Symbol = "IOST" // IOST (base58 of pubkey)
	HBAR Symbol = "HBAR" // Hedera (0.0.<DER-encoded pubkey hex>)
	ROSE Symbol = "ROSE" // Oasis (bech32 of context-hashed pubkey)
	KIN  Symbol = "KIN"  // Kin (Stellar strkey)
	AE   Symbol = "AE"   // Aeternity (ak_ base58check)

	// nist256p1 (SLIP-0010).
	NEO Symbol = "NEO"
	ONT Symbol = "ONT" // Ontology (same NEO-style address)

	// new-curve chains (SLIP-0010 ed25519 leaf key, chain-specific signing).
	XNO   Symbol = "XNO"   // Nano (ed25519-blake2b)
	WAVES Symbol = "WAVES" // Waves (curve25519)

	// ed25519-extended (BIP32-Ed25519 / CIP-1852).
	ADA Symbol = "ADA" // Cardano (Icarus master from BIP-39 entropy; addr1 base address)

)

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.

type TronContract added in v0.8.0

type TronContract struct {
	Type     int32  // 1 = TransferContract, 31 = TriggerSmartContract
	TypeName string // "TransferContract" / "TriggerSmartContract" / "" (unknown)
	TypeURL  string // the google.protobuf.Any type_url

	OwnerAddress string // base58check "T..." address

	// TransferContract.
	ToAddress string // base58check "T..." address
	Amount    int64

	// TriggerSmartContract.
	ContractAddress string // base58check "T..." address
	Data            []byte // raw call data (e.g. TRC-20 transfer calldata)
}

TronContract is one decoded contract entry. Type is the Tron ContractType enum value; the populated fields depend on it.

type TronTxFields added in v0.8.0

type TronTxFields struct {
	RefBlockBytes []byte
	RefBlockHash  []byte
	Expiration    int64
	Timestamp     int64
	FeeLimit      int64 // 0 when absent
	Contracts     []TronContract
}

TronTxFields holds the decoded, display-ready fields of a Tron transaction's raw_data.

func DecodeTronTx added in v0.8.0

func DecodeTronTx(raw []byte) (*TronTxFields, error)

DecodeTronTx decodes a raw_data protobuf blob into its display fields. Malformed or truncated input returns ErrTxDecode; the function never panics.

type WatchWallet added in v0.3.0

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

WatchWallet derives receive/change addresses for a single account from its extended PUBLIC key (xpub) — no seed, no private keys, no signing. It is the watch-only counterpart of HDWallet for secp256k1 coins.

func WatchOnlyFromXPub added in v0.3.0

func WatchOnlyFromXPub(xpub string, symbol Symbol) (*WatchWallet, error)

WatchOnlyFromXPub builds a watch-only wallet for symbol from an account-level extended key string. Both xpub and xprv strings are accepted; an xprv is immediately neutered so the WatchWallet never holds private material. symbol must be a secp256k1 coin whose address format matches the xpub's chain.

func (*WatchWallet) Address added in v0.3.0

func (ww *WatchWallet) Address(change, index uint32) (string, error)

Address returns the address at the account's change/index branch (e.g. change=0, index=5 -> .../0/5). Both must be non-hardened (< 2^31); public derivation cannot produce hardened children.

func (*WatchWallet) PublicKey added in v0.3.0

func (ww *WatchWallet) PublicKey(change, index uint32) ([]byte, error)

PublicKey returns the 33-byte compressed public key at the change/index branch.

type XrpTxFields added in v0.8.0

type XrpTxFields struct {
	TransactionType uint16
	TransactionName string // "Payment" for type 0
	Account         string // "r..." address
	Destination     string // "r..." address
	Amount          uint64 // drops
	Fee             uint64 // drops
	Sequence        uint32
	Flags           uint32

	DestinationTag     *uint32
	LastLedgerSequence *uint32

	SigningPubKey []byte
	TxnSignature  []byte
}

XrpTxFields holds the decoded, display-ready fields of an XRP Payment. DestinationTag and LastLedgerSequence are pointers so absence (the field was not on the wire) is distinguishable from a zero value.

func DecodeRippleTx added in v0.8.0

func DecodeRippleTx(raw []byte) (*XrpTxFields, error)

DecodeRippleTx decodes a serialized XRP transaction into its display fields. Malformed or truncated input returns ErrTxDecode; the function never panics and never reads past `raw`.

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.
txproto

Jump to

Keyboard shortcuts

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