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.
- 🔐 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
AnySignervectors 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.
go get github.com/ranjbar-dev/hd-walletimport hdwallet "github.com/ranjbar-dev/hd-wallet"Requires Go 1.23+.
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])
}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
})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 elementThe 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
}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.
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.
Verified byte-for-byte against Trust Wallet Core's 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) |
import ethpb "github.com/ranjbar-dev/hd-wallet/txproto/ethereum"
out, _ := w.SignTransaction(hdwallet.ETH, 0, ðpb.SigningInput{ /* … */ })Bitcoin transaction building is on the roadmap (
tx_roadmap.go).
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-712Plus standalone EVM tooling: EncodeRLP/DecodeRLP, ABIEncode/ABIDecode,
ABIFunctionSelector, EthereumPersonalMessageHash, EIP712Hash.
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).
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 → addressA 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()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. |
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 bufraw, _ := 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.Getenvfor the mnemonic: environment variables are already immutable strings and cannot be wiped.
Residual exposure: the underlying
tyler-smith/go-bip39API only acceptsstring, so the library makes a single short-livedstringcopy for validation and seed derivation. It is GC-bounded, and every durable copy of the mnemonic and seed is sealed in a memguard enclave.
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.
| 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 |
| 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 |
| 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.
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 (same reason — no authoritative vector yet): Bitcoin
transaction building; 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/tx_roadmap.go; and Cosmos ADR-36 message
signing — see message_cosmos_test.go.
Contributions with test vectors welcome.
"Trust Wallet–compatible" is proven, not asserted. The test suite layers three independent sources of truth:
- Encoders (
encoders_test.go) — every address encoder is run against the exact addresses Trust Wallet Core'sCoinAddressDerivationTestsproduces for a fixed key, isolating address-format correctness. - Derivation (
slip10_test.go) — ed25519 and nist256p1 derivation are checked against the official SLIP-0010 specification test vectors (including non-hardened P-256 derivation). - 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'sHDWalletTestsmnemonic 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.
| 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.
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>").
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- Secrets are stored in
memguardenclaves (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 anddefer memguard.Purge()at program exit. - Caveat:
FromMnemonic(string)andGenerateMnemonic() stringinvolve a Gostringthat cannot be wiped (a limitation of the BIP-39 API). PreferFromMnemonicBytesandWithMnemonicfor the strongest guarantees.
Found a vulnerability? Please open a private security advisory rather than a public issue.
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):
- CI runs build, tests (
-race),govulncheck, andgosec. - A new semver tag is created and pushed.
- The Go module proxy is warmed (
proxy.golang.org), which publishes the version and triggers pkg.go.dev indexing. - 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).
MIT © Amir Ranjbar
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.