{"id":51175438,"url":"https://github.com/ranjbar-dev/hd-wallet","last_synced_at":"2026-06-27T03:30:30.895Z","repository":{"id":366355614,"uuid":"1275957910","full_name":"ranjbar-dev/hd-wallet","owner":"ranjbar-dev","description":"hd wallet for generating addresses on multiple networks and get mnemonic that is importable in trust wallet","archived":false,"fork":false,"pushed_at":"2026-06-21T14:12:22.000Z","size":30,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-21T14:28:18.279Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ranjbar-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-21T11:09:04.000Z","updated_at":"2026-06-21T14:11:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ranjbar-dev/hd-wallet","commit_stats":null,"previous_names":["ranjbar-dev/hd-wallet"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/ranjbar-dev/hd-wallet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranjbar-dev%2Fhd-wallet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranjbar-dev%2Fhd-wallet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranjbar-dev%2Fhd-wallet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranjbar-dev%2Fhd-wallet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ranjbar-dev","download_url":"https://codeload.github.com/ranjbar-dev/hd-wallet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ranjbar-dev%2Fhd-wallet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34840899,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-27T03:30:27.664Z","updated_at":"2026-06-27T03:30:30.882Z","avatar_url":"https://github.com/ranjbar-dev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hd-wallet\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/ranjbar-dev/hd-wallet.svg)](https://pkg.go.dev/github.com/ranjbar-dev/hd-wallet)\n[![Go Report Card](https://goreportcard.com/badge/github.com/ranjbar-dev/hd-wallet)](https://goreportcard.com/report/github.com/ranjbar-dev/hd-wallet)\n[![CI](https://github.com/ranjbar-dev/hd-wallet/actions/workflows/ci.yml/badge.svg)](https://github.com/ranjbar-dev/hd-wallet/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nA **Trust Wallet–compatible**, security-focused **hierarchical-deterministic (HD) wallet** library for Go.\n\nGenerate 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**.\n\nSensitive 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](https://github.com/awnumar/memguard) enclaves and is decrypted only for the microseconds of a single derivation.\n\n---\n\n## Why this library\n\n- 🔐 **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.\n- ✅ **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](#verification).\n- 🌐 **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.\n- ✍️ **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.\n- 🧩 **Extensible.** Add a network with a single registry row.\n- 📦 **Focused dependency surface.** btcd (secp256k1/bech32/base58), go-bip39, x/crypto, memguard, protobuf, and curve libraries (edwards25519, schnorrkel, gnark-crypto) for the additional schemes.\n\n---\n\n## Install\n\n```bash\ngo get github.com/ranjbar-dev/hd-wallet\n```\n\n```go\nimport hdwallet \"github.com/ranjbar-dev/hd-wallet\"\n```\n\nRequires Go 1.23+.\n\n---\n\n## Quick start\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/awnumar/memguard\"\n\thdwallet \"github.com/ranjbar-dev/hd-wallet\"\n)\n\nfunc main() {\n\tdefer memguard.Purge() // wipe all protected memory on exit\n\n\t// Create a wallet with a fresh 12-word mnemonic...\n\tw, err := hdwallet.NewHDWallet()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer w.Destroy() // wipe this wallet's secrets when done\n\n\t// ...or import one:\n\t// w, _ := hdwallet.FromMnemonic(\"abandon abandon ... about\")\n\n\t// Symbols are a typed enum (hdwallet.Symbol) — use the exported constants\n\t// for compile-time checking and autocomplete.\n\tbtc, _ := w.Address(hdwallet.BTC)\n\teth, _ := w.Address(hdwallet.ETH)\n\tsol, _ := w.Address(hdwallet.SOL)\n\tfmt.Println(btc, eth, sol)\n\n\tall, _ := w.AllAddresses() // map[hdwallet.Symbol]string for every network\n\tfmt.Println(all[hdwallet.ATOM])\n}\n```\n\n### Reading the mnemonic safely\n\nThe mnemonic is never exposed as a field. Read it only when needed, through a\nbuffer that is wiped immediately afterwards:\n\n```go\nerr := w.WithMnemonic(func(mnemonic []byte) error {\n\tfmt.Printf(\"%s\\n\", mnemonic) // do not let the slice escape this function\n\treturn nil\n})\n```\n\n### Multiple addresses per chain\n\n`Address` returns the first receive address; `AddressIndex` derives any index by\nreplacing the final element of the chain's path (preserving its hardened flag):\n\n```go\na0, _ := w.AddressIndex(hdwallet.BTC, 0) // bc1q...306fyu (same as w.Address(hdwallet.BTC))\na1, _ := w.AddressIndex(hdwallet.BTC, 1) // bc1q...rkf9g — second receive address\nsol1, _ := w.AddressIndex(hdwallet.SOL, 1) // account-based chains vary the hardened element\n```\n\n### Bitcoin address types\n\n`Address`/`AddressIndex` return the chain default (native SegWit, BIP-84 for\nBTC/LTC). `BitcoinAddress` derives any of the four standard formats at its\nstandard BIP path (arguments are `account, change, index`):\n\n```go\nlegacy,  _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2PKH, 0, 0, 0)      // 1…   (BIP-44)\nnested,  _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2SHP2WPKH, 0, 0, 0) // 3…   (BIP-49)\nnative,  _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2WPKH, 0, 0, 0)     // bc1q… (BIP-84)\ntaproot, _ := w.BitcoinAddress(hdwallet.BTC, hdwallet.P2TR, 0, 0, 0)       // bc1p… (BIP-86)\n```\n\nAvailable for BTC and LTC; verified against the official BIP-44/49/84/86 test\nvectors. `ValidateAddress`/`ParseAddress` accept all four formats.\n\n### Error handling\n\nThe package exports sentinel errors for use with `errors.Is`:\n`ErrInvalidMnemonic`, `ErrUnsupportedCoin`, and `ErrDestroyed`.\n\n```go\nif _, err := w.Address(\"NOPE\"); errors.Is(err, hdwallet.ErrUnsupportedCoin) {\n\t// unknown symbol\n}\n```\n\n### Signing (raw)\n\n`Sign`/`SignIndex` produce a signature with the derived private key for any\nsupported chain. The key is wiped immediately after signing and **never leaves\nthe package** — there is no way to extract a private key.\n\nThere is one inherent rule, driven by the cryptography:\n\n- **ECDSA chains** (secp256k1, nist256p1 — BTC, ETH, ATOM, NEO, …): pass the\n  **32-byte digest** your chain signs. Pre-hash the message yourself with the\n  chain's hash (keccak256 for Ethereum/Tron, double-SHA256 for Bitcoin, SHA-256\n  for Cosmos, …).\n- **ed25519 chains** (SOL, XLM, DOT, …): pass the **message**; the EdDSA scheme\n  hashes internally.\n\n```go\ndigest := sha256.Sum256(txBytes)         // chain-specific pre-hash for ECDSA\nsig, _ := w.Sign(hdwallet.BTC, digest[:])\n\nsig.Bytes()        // 64-byte R||S (ECDSA) or 64-byte ed25519 signature  → Cosmos, Solana\nsig.Recoverable()  // 65-byte R||S||V (secp256k1 only)                   → Ethereum/EVM, Tron\nsig.DER()          // ASN.1 DER (ECDSA)                                  → Bitcoin family\n\npub, _ := w.PublicKey(hdwallet.BTC)\nok := hdwallet.Verify(hdwallet.Secp256k1, pub, digest[:], sig)\n```\n\n`SignIndex(symbol, index, data)` and `PublicKeyIndex(symbol, index)` work with\nnon-zero address indices. ECDSA inputs that are not 32 bytes return\n`ErrInvalidDigest`.\n\n\u003e This is the low-level primitive. For Ethereum message signing and full\n\u003e transaction building, use the higher-level APIs below.\n\n### Transaction signing (protobuf, no broadcast)\n\n`SignTransaction` builds, serializes, and signs a **broadcast-ready raw\ntransaction** from a protobuf `SigningInput`, mirroring Trust Wallet Core's\n`AnySigner`. It returns the signed bytes/hex — it does **not** broadcast.\n\n\u003e **Coverage note:** address derivation/validation spans **all 129 networks**,\n\u003e but transaction building covers only the families in the table below. For any\n\u003e other chain you can derive and validate addresses but must assemble and sign\n\u003e the transaction yourself (use the raw `Sign`/`SignIndex` primitive on the\n\u003e chain's sighash). You also supply chain state — fees/gas, nonce/sequence,\n\u003e recent blockhash, UTXOs — in the `SigningInput`; this library does no network\n\u003e I/O.\n\nVerified against authoritative signing vectors for:\n\n| Family | Coverage |\n|---|---|\n| **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. |\n| **Tron** | TRX transfer + TRC-20 token transfer (TriggerSmartContract) |\n| **XRP** | Payment |\n| **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. |\n| **Solana** | system transfer + SPL token transfer (TransferChecked) |\n| **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. |\n\n```go\nimport ethpb \"github.com/ranjbar-dev/hd-wallet/txproto/ethereum\"\n\nout, _ := w.SignTransaction(hdwallet.ETH, 0, \u0026ethpb.SigningInput{ /* … */ })\n```\n\n\u003e Bitcoin spending currently covers P2WPKH and Taproot key-path inputs; legacy\n\u003e P2PKH and nested P2SH-P2WPKH input spending remain on the roadmap.\n\n### Ethereum message signing (EIP-191 / EIP-712)\n\n```go\nsig, _ := w.SignMessage(hdwallet.ETH, 0, []byte(\"Hello, world!\"))   // EIP-191 personal_sign\naddr, _ := hdwallet.RecoverEthereumAddress([]byte(\"Hello, world!\"), sig)\n\nsig2, _ := w.SignTypedData(hdwallet.ETH, 0, typedDataJSON)           // EIP-712\n```\n\nPlus standalone EVM tooling: `EncodeRLP`/`DecodeRLP`, `ABIEncode`/`ABIDecode`,\n`ABIFunctionSelector`, `EthereumPersonalMessageHash`, `EIP712Hash`.\n\n### Bitcoin \u0026 Solana message signing\n\nNon-EVM message signing, each pinned byte-for-byte to its Trust Wallet Core\n`MessageSigner` vector:\n\n```go\n// Bitcoin \"signmessage\" standard → base64; verifies against a legacy P2PKH address.\nsig, _  := w.SignBitcoinMessage(hdwallet.BTC, 0, []byte(\"test signature\"))\nok      := hdwallet.VerifyBitcoinMessage(\"19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X\", []byte(\"test signature\"), sig)\n\n// Solana off-chain message (raw ed25519) → base58.\nssig, _ := w.SignSolanaMessage(hdwallet.SOL, 0, []byte(\"Hello world\"))\nsok     := hdwallet.VerifySolanaMessage(addr, []byte(\"Hello world\"), ssig)\n```\n\n\u003e Cosmos ADR-36 arbitrary-message signing is on the roadmap (no authoritative\n\u003e Trust Wallet Core vector to verify against yet).\n\n### Address validation \u0026 parsing\n\n```go\nok  := hdwallet.IsValidAddress(hdwallet.ETH, \"0x…\")     // bool\nerr := hdwallet.ValidateAddress(hdwallet.BTC, \"bc1q…\")  // descriptive error\npayload, _ := hdwallet.ParseAddress(hdwallet.ETH, \"0x…\")\naddr, _ := hdwallet.AddressFromPublicKey(hdwallet.ETH, pubKey) // external key → address\n```\n\n### Importing / exporting a raw private key (securely)\n\nA wallet can be built from a single private key, and the leaf key can be exported\n— always through the same memguard pattern as the mnemonic (no raw `[]byte`\ngetter; the key is wiped when your callback returns):\n\n```go\nw, _ := hdwallet.FromPrivateKeyBytes(keyBytes, hdwallet.Secp256k1) // wipes keyBytes\n// or FromPrivateKeyBuffer(*memguard.LockedBuffer, curve) — zero-copy, strongest\n\n_ = w.WithPrivateKey(hdwallet.ETH, 0, func(priv []byte) error {     // wiped on return\n    // use priv; do not let it escape\n    return nil\n})\nbuf, _ := w.PrivateKey(hdwallet.ETH, 0)                             // caller Destroys\ndefer buf.Destroy()\n```\n\n---\n\n## Passing a mnemonic in securely\n\nThe 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:\n\n| Entry point | Security | When |\n|---|---|---|\n| `FromMnemonicBuffer(*memguard.LockedBuffer)` | 🟢 Strongest | Mnemonic stays in page-locked, encrypted memory end-to-end; sealed zero-copy into the wallet. |\n| `FromMnemonicBytes([]byte)` | 🟡 Good | You have a mutable `[]byte`; it is wiped inside the call. |\n| `FromMnemonic(string)` | 🔴 Weakest | Convenience only; the string cannot be wiped. Avoid for real funds. |\n\n### Most secure: hand off a memguard buffer (zero-copy)\n\nThe wallet takes ownership of the buffer and destroys it — there is no extra unprotected copy anywhere in your process.\n\n```go\nimport (\n\t\"os\"\n\n\t\"github.com/awnumar/memguard\"\n\thdwallet \"github.com/ranjbar-dev/hd-wallet\"\n)\n\nfunc loadWallet() (*hdwallet.HDWallet, error) {\n\tdefer memguard.Purge()\n\n\t// Read one line straight into locked, encrypted memory — never a string.\n\tbuf, err := memguard.NewBufferFromReaderUntil(os.Stdin, '\\n')\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Ownership transfers to the wallet; buf is destroyed for you.\n\treturn hdwallet.FromMnemonicBuffer(buf)\n}\n```\n\nFrom a secrets manager / KMS that returns raw bytes:\n\n```go\nraw := fetchFromVault()                    // []byte from your secret store\nbuf := memguard.NewBufferFromBytes(raw)    // copies into protected memory, wipes raw\nw, err := hdwallet.FromMnemonicBuffer(buf) // takes ownership of buf\n```\n\n### Good: a mutable byte slice\n\n```go\nraw, _ := os.ReadFile(\"mnemonic.txt\") // []byte, never a string\nmn := bytes.TrimSpace(raw)\nw, err := hdwallet.FromMnemonicBytes(mn) // mn is zeroed inside the call\nfor i := range raw {                     // wipe any trimmed remainder\n\traw[i] = 0\n}\n```\n\n\u003e **Avoid `os.Getenv`** for the mnemonic: environment variables are already\n\u003e immutable strings and cannot be wiped.\n\n\u003e **Residual exposure:** the underlying `tyler-smith/go-bip39` API only accepts\n\u003e `string`, so the library makes a single short-lived `string` copy for\n\u003e validation and seed derivation. It is GC-bounded, and every durable copy of\n\u003e the mnemonic and seed is sealed in a memguard enclave.\n\n---\n\n## Supported networks\n\n**129 networks** across 5 curves in use. `SupportedCoins()` returns the live,\nauthoritative list; `CoinInfo(symbol)` gives each coin's curve and path. Every\nchain below is verified against a Trust Wallet Core address vector.\n\n#### secp256k1 (110)\n\n| Group | Symbols | Path |\n|---|---|---|\n| 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`) |\n| Ethereum / EVM (same key \u0026 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` |\n| Tron | TRX | `m/44'/195'/0'/0/0` |\n| XRP Ledger | XRP | `m/44'/144'/0'/0/0` |\n| 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) |\n| EOS-family / Filecoin | EOS, WAX, FIO, FIL | per-chain |\n\n#### ed25519 (15)\n\n| Symbol | Network | Path |\n|---|---|---|\n| SOL | Solana | `m/44'/501'/0'` |\n| XLM | Stellar | `m/44'/148'/0'` |\n| DOT · KSM | Polkadot · Kusama (SS58) | `m/44'/354'/0'/0'/0'` · `m/44'/434'/0'/0'/0'` |\n| NEAR · XTZ · SUI · APTOS · ALGO | NEAR · Tezos · Sui · Aptos · Algorand | per-chain |\n| EGLD · HBAR · IOST · ROSE · KIN · AE | MultiversX · Hedera · IOST · Oasis · Kin · Aeternity | per-chain |\n\n#### nist256p1 (2) · ed25519-blake2b (1) · curve25519 (1)\n\n| Symbol | Network | Curve | Path |\n|---|---|---|---|\n| NEO | NEO (legacy) | nist256p1 | `m/44'/888'/0'/0/0` |\n| ONT | Ontology | nist256p1 | `m/44'/1024'/0'/0/0` |\n| XNO | Nano | ed25519-blake2b | `m/44'/165'/0'` |\n| WAVES | Waves | curve25519 | `m/44'/5741564'/0'/0'/0'` |\n\nAll paths derive receive address index 0 and an empty BIP-39 passphrase\n(Trust Wallet's default).\n\n\u003e **Note on Polkadot/Kusama:** Trust Wallet derives these on **ed25519** via\n\u003e SLIP-0010. The native Polkadot ecosystem (e.g. Polkadot.js) defaults to\n\u003e **sr25519** with a different scheme, so addresses there will differ. This\n\u003e library matches **Trust Wallet**, which is the stated compatibility target.\n\n### Roadmap (deferred — implemented but not yet vector-matched, or scheme not wired)\n\nSome curve schemes are implemented but **not yet wired to a registered chain**,\nbecause a fund-critical address must match an authoritative vector first:\n\n- **Cardano** (`ed25519-extended` / CIP-1852) — derivation needs BIP-39 *entropy*\n  (not the seed); entropy is not yet plumbed into the wallet.\n- **StarkNet** (`starkex`) — the EIP-2645 seed→key grind lacks an authoritative\n  Trust Wallet Core vector (sign/verify are vector-verified).\n- **sr25519** (native Polkadot/Kusama) — implemented; sign/verify round-trip only.\n- **Zilliqa** (Schnorr), **TON**, **ICON**, and a handful of long-tail chains whose\n  address scheme isn't reproduced against a vector yet (see `registry.go`).\n\nDeferred signing features: Bitcoin transaction building now spends **P2WPKH** and\n**Taproot key-path** inputs (BTC/LTC); still deferred are **legacy P2PKH** and\n**nested P2SH-P2WPKH** input spending (pre-BIP-143 / wrapped-witness sighash). The\n**ethermint-keyed Cosmos** chains beyond EVMOS (INJ/CANTO/ZETA — each needs its\nown vector since the pubkey type URL enters the signed bytes) — see\n`tx_families.go`; and **Cosmos ADR-36** message signing — see\n`message_cosmos_test.go`.\n\nContributions with test vectors welcome.\n\n---\n\n## Verification\n\n\"Trust Wallet–compatible\" is proven, not asserted. The test suite layers three\nindependent sources of truth:\n\n1. **Encoders** (`encoders_test.go`) — every address encoder is run against the\n   exact addresses Trust Wallet Core's `CoinAddressDerivationTests` produces for\n   a fixed key, isolating address-format correctness.\n2. **Derivation** (`slip10_test.go`) — ed25519 and nist256p1 derivation are\n   checked against the official **SLIP-0010** specification test vectors\n   (including non-hardened P-256 derivation).\n3. **End-to-end** (`hdwallet_test.go`) — full mnemonic→seed→derive→encode\n   against the BIP-84 spec (BTC), the canonical ETH vector, and Trust Wallet\n   Core's `HDWalletTests` mnemonic vectors (NEAR ed25519, Cosmos secp256k1).\n\n```bash\ngo test -race -cover ./...\n```\n\n\u003e **Always verify before sending funds.** Import your mnemonic into Trust Wallet\n\u003e and confirm the address for any chain you intend to use with real value.\n\n---\n\n## API\n\n| Function / method | Purpose |\n|---|---|\n| `NewHDWallet() (*HDWallet, error)` | New wallet with a fresh 12-word mnemonic. |\n| `NewHDWalletWithWordCount(words int) (*HDWallet, error)` | New wallet with a 12/15/18/21/24-word mnemonic. Also `NewHDWalletWithEntropy(bits)`. |\n| `FromMnemonic(string) (*HDWallet, error)` | Import from a mnemonic string (least secure). |\n| `FromMnemonicBytes([]byte) (*HDWallet, error)` | Import from a byte slice (wiped on use). |\n| `FromMnemonicBuffer(*memguard.LockedBuffer) (*HDWallet, error)` | Import from a memguard buffer (most secure; zero-copy). |\n| `FromMnemonicWithPassphrase([]byte, []byte) (*HDWallet, error)` | Import with a BIP-39 passphrase (the \"25th word\"). |\n| `FromMnemonicBufferWithPassphrase(buf, pass *memguard.LockedBuffer) (*HDWallet, error)` | Passphrase import, both secrets in memguard buffers. |\n| `GenerateMnemonic() (string, error)` | Generate a mnemonic without building a wallet. Also `GenerateMnemonicWithWordCount(words)`. |\n| `(*HDWallet) Address(symbol Symbol) (string, error)` | First receive address for one network. |\n| `(*HDWallet) AddressIndex(symbol Symbol, index uint32) (string, error)` | Nth address/account for one network. |\n| `(*HDWallet) AddressPath(symbol Symbol, path string) (string, error)` | Address at an arbitrary absolute BIP-32 path. Also `SignPath`/`PublicKeyPath`/`WithPrivateKeyPath`/`PrivateKeyPath`. |\n| `(*HDWallet) AddressAt(symbol Symbol, account, change, index uint32) (string, error)` | Address by BIP-44 account/change/index. Also `SignAt`/`PublicKeyAt`. |\n| `(*HDWallet) AllAddresses() (map[Symbol]string, error)` | Addresses for all networks. Also `AllAddressesAt(index)` for any index. |\n| `(*HDWallet) Sign(symbol Symbol, data []byte) (*Signature, error)` | Sign a digest (ECDSA) / message (ed25519) at index 0. |\n| `(*HDWallet) SignIndex(symbol Symbol, index uint32, data []byte) (*Signature, error)` | Sign with the key at a given index. |\n| `(*HDWallet) PublicKey(symbol Symbol) ([]byte, error)` | Public key at index 0. |\n| `(*HDWallet) PublicKeyIndex(symbol Symbol, index uint32) ([]byte, error)` | Public key at a given index. |\n| `Verify(curve Curve, pub, data []byte, sig *Signature) bool` | Verify a signature. |\n| `(*HDWallet) WithMnemonic(func([]byte) error) error` | Use the mnemonic, auto-wiped. |\n| `(*HDWallet) Mnemonic() (*memguard.LockedBuffer, error)` | Mnemonic buffer (caller `Destroy`s). |\n| `(*HDWallet) Destroy()` | Wipe the wallet's secrets. |\n| `SupportedCoins() []Symbol` | Sorted list of symbols. |\n| `CoinInfo(symbol Symbol) (Coin, bool)` | Registry entry for a symbol. |\n\n**Private-key import / export** (same memguard discipline as the mnemonic):\n\n| Function / method | Purpose |\n|---|---|\n| `FromPrivateKeyBytes([]byte, Curve) (*HDWallet, error)` | Key-only wallet from a byte slice (wiped on use). Any 32-byte-scalar curve. |\n| `FromPrivateKeyBuffer(*memguard.LockedBuffer, Curve) (*HDWallet, error)` | Key-only wallet from a memguard buffer (zero-copy). |\n| `(*HDWallet) WithPrivateKey(symbol, index, func([]byte) error) error` | Use the leaf private key, auto-wiped. |\n| `(*HDWallet) PrivateKey(symbol, index) (*memguard.LockedBuffer, error)` | Leaf key buffer (caller `Destroy`s). |\n| `FromWIF([]byte) (*HDWallet, error)` · `(*HDWallet) WithWIF` / `WIF` | Import/export a Bitcoin WIF (secp256k1). |\n| `(*HDWallet) AccountXPub(symbol, account) (string, error)` · `WithAccountXPrv` | Export account-level BIP-32 extended keys (secp256k1). |\n| `WatchOnlyFromXPub(xpub string, symbol) (*WatchWallet, error)` | Watch-only address derivation from an xpub — no seed. |\n\n**Transaction \u0026 Ethereum message signing:**\n\n| Function / method | Purpose |\n|---|---|\n| `(*HDWallet) SignTransaction(symbol, index, proto.Message) (proto.Message, error)` | Build+sign a raw tx (EVM/Tron/XRP/Cosmos/Solana; no broadcast). |\n| `(*HDWallet) SignMessage(symbol, index, []byte) ([]byte, error)` | EIP-191 `personal_sign` → 65-byte r‖s‖v. |\n| `(*HDWallet) SignTypedData(symbol, index, []byte) ([]byte, error)` | EIP-712 typed-data signature. |\n| `(*HDWallet) SignBitcoinMessage(symbol, index, []byte) (string, error)` | Bitcoin `signmessage` → base64. With `VerifyBitcoinMessage`. |\n| `(*HDWallet) SignSolanaMessage(symbol, index, []byte) (string, error)` | Solana off-chain message → base58. With `VerifySolanaMessage`. |\n| `RecoverEthereumAddress([]byte, []byte) (string, error)` · `VerifyEthereumMessage` / `…TypedData` | Recover/verify EIP-191/712 signers. |\n| `EncodeRLP`/`DecodeRLP` · `ABIEncode`/`ABIDecode` · `ABIFunctionSelector` · `EIP712Hash` | Standalone EVM encoding utilities. |\n\n**Address validation / parsing (`AnyAddress`-style):**\n\n| Function | Purpose |\n|---|---|\n| `IsValidAddress(symbol, addr) bool` · `ValidateAddress(symbol, addr) error` | Validate an address for a network. |\n| `ParseAddress(symbol, addr) ([]byte, error)` | Decode an address to its payload. |\n| `AddressFromPublicKey(symbol, pub) (string, error)` | Derive an address from an external public key. |\n\n`Symbol` is a typed string enum; the package exports a constant for every\nsupported network (`hdwallet.BTC`, `hdwallet.ETH`, `hdwallet.SOL`, …). Pass these\nconstants instead of raw strings for compile-time safety. `Symbol` also has\n`String() string` and `IsValid() bool` helpers.\n\n---\n\n## Adding a network\n\nAppend one row to the registry in `registry.go`:\n\n```go\n\"FOO\": {\"Foochain\", \"FOO\", Secp256k1, \"m/44'/9999'/0'/0/0\", encodeFoo},\n```\n\nProvide an `Encode func(pub []byte) (string, error)` for the address format\n(the compressed key for secp256k1/nist256p1, the raw 32-byte key for ed25519),\nand add a test vector. EVM chains can reuse `encodeETH`; Cosmos chains can reuse\n`cosmosEncoder(\"\u003chrp\u003e\")`.\n\n---\n\n## Demo CLI\n\n```bash\ngo run ./cmd/hdwallet                       # fresh wallet, prints addresses\ngo run ./cmd/hdwallet -mnemonic \"abandon ... about\"\ngo run ./cmd/hdwallet -show-mnemonic        # demo only; printing defeats isolation\n```\n\n---\n\n## Security\n\n- Secrets are stored in `memguard` enclaves (encrypted at rest in RAM, pages\n  locked against swap, guarded with canaries, auto-wiped).\n- Private keys derived during an operation are zeroed immediately after the\n  address is computed.\n- Call `w.Destroy()` per wallet and `defer memguard.Purge()` at program exit.\n- **Caveat:** `FromMnemonic(string)` and `GenerateMnemonic() string` involve a Go\n  `string` that cannot be wiped (a limitation of the BIP-39 API). Prefer\n  `FromMnemonicBytes` and `WithMnemonic` for the strongest guarantees.\n\nFound a vulnerability? Please open a private security advisory rather than a\npublic issue.\n\n---\n\n## Publishing \u0026 releasing\n\nReleases are **fully automated**. Every push to `main` that passes the test and\nsecurity gates is tagged and published by CI (`.github/workflows/ci.yml`):\n\n1. CI runs build, tests (`-race`), `govulncheck`, and `gosec`.\n2. A new semver tag is created and pushed.\n3. The Go module proxy is warmed (`proxy.golang.org`), which publishes the\n   version and triggers [pkg.go.dev](https://pkg.go.dev/github.com/ranjbar-dev/hd-wallet) indexing.\n4. A GitHub Release with auto-generated notes is created.\n\nControl the version bump from the **commit message**:\n\n| Marker in commit message | Result |\n|---|---|\n| `[major]` | `x+1.0.0` |\n| `[minor]` | `x.y+1.0` |\n| _(none)_ | `x.y.z+1` (patch) |\n| `[skip release]` | no release for that push |\n\nThe first release is `v0.1.0`. Requires the repo to be **public** and the\ndefault `GITHUB_TOKEN` to have write access (Settings → Actions → General →\nWorkflow permissions → \"Read and write permissions\"). If tag protection rules\nblock the bot, supply a Personal Access Token instead.\n\nTo release manually instead, just push a tag (`git tag v1.2.3 \u0026\u0026 git push origin v1.2.3`).\n\n---\n\n## License\n\n[MIT](LICENSE) © Amir Ranjbar\n\n## Disclaimer\n\nThis software is provided \"as is\", without warranty of any kind. You are\nresponsible for safeguarding your own keys and funds. Always test with small\namounts first and verify addresses against a reference wallet.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Franjbar-dev%2Fhd-wallet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Franjbar-dev%2Fhd-wallet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Franjbar-dev%2Fhd-wallet/lists"}