HD Wallet Account
Create accounts from Hierarchical Deterministic (HD) keys and custom derivation paths
A Hierarchical Deterministic (HD) Account is derived from an HD Key and an optional HD path. It can sign transactions and messages with the private key derived at that path.
viem-go uses go-bip32 for BIP-32 HD derivation. The HDKey interface exposes Derive(path), PrivateKey(), and PublicKey().
Import
import "github.com/ChefBingbong/viem-go/accounts"In viem (TypeScript), HDKey is re-exported from @scure/bip32. In viem-go, you obtain an HDKey from accounts.SeedToHDKey(seed) or from a key that implements the accounts.HDKey interface.
Usage
From mnemonic (recommended)
For most use cases, derive an account directly from a mnemonic with MnemonicToAccount and optional path. That path is applied internally via HD derivation.
import "github.com/ChefBingbong/viem-go/accounts"
mnemonic := "test test test test test test test test test test test junk"
// Default path m/44'/60'/0'/0/0account, err := accounts.MnemonicToAccount(mnemonic)
// Custom pathaccount, err := accounts.MnemonicToAccount(mnemonic, &accounts.MnemonicToAccountOptions{ HDOptions: accounts.HDOptions{Path: "m/44'/60'/0'/0/5"},})From HD key (seed or custom source)
When you already have a seed (or another HD key source), create a master HDKey with SeedToHDKey, derive at the desired path, then call HDKeyToAccount:
import "github.com/ChefBingbong/viem-go/accounts"
seed := accounts.MnemonicToSeed(mnemonic, "")
masterKey, err := accounts.SeedToHDKey(seed)if err != nil { log.Fatal(err)}
derivedKey, err := masterKey.Derive("m/44'/60'/0'/0/0")if err != nil { log.Fatal(err)}
account, err := accounts.HDKeyToAccount(derivedKey)if err != nil { log.Fatal(err)}
fmt.Printf("Address: %s", account.GetAddress())In viem (TypeScript), HDKey has static methods like fromMasterSeed, fromExtendedKey, fromJSON. In viem-go, the main entry point from raw seed is SeedToHDKey(seed); the resulting key’s Derive(path) parses BIP-32 path strings (e.g. m/44'/60'/0'/0/0).
Parameters
hdKey
- Type:
accounts.HDKey(Go) /HDKey(viem)
Interface in Go:
Derive(path string) (HDKey, error)PrivateKey() []bytePublicKey() []byte
The key must be derived at the desired path (or pass path via options; see below). viem-go does not expose a separate “mnemonic to HD key” helper; use MnemonicToSeed + SeedToHDKey or MnemonicToAccount with path.
opts (optional)
- Type:
HDKeyToAccountOptions(Go), which embedsHDOptions.
options.accountIndex
- Type:
int(Go) /number(viem) - Default:
0
Account index in path m/44'/60'/{accountIndex}'/0/0.
options.addressIndex
- Type:
int(Go) /number(viem) - Default:
0
Address index in path m/44'/60'/0'/0/{addressIndex}.
options.changeIndex
- Type:
int(Go) /number(viem) - Default:
0
Change index in path m/44'/60'/0'/{changeIndex}/0.
options.path
- Type:
string - Format: e.g.
m/44'/60'/5'/0/2
When set, this path is used for derivation instead of building from indices. If the hdKey is already derived to the correct node, you can pass empty options or a path that matches.
// Default path m/44'/60'/0'/0/0account, err := accounts.HDKeyToAccount(hdKey)
// Custom pathaccount, err := accounts.HDKeyToAccount(hdKey, accounts.HDKeyToAccountOptions{ HDOptions: accounts.HDOptions{Path: "m/44'/60'/5'/0/2"},})
// Or use indicesaccount, err := accounts.HDKeyToAccount(hdKey, accounts.HDKeyToAccountOptions{ HDOptions: accounts.HDOptions{ AccountIndex: 1, AddressIndex: 6, ChangeIndex: 0, },})HD path reference
| Path | Description |
|---|---|
m/44'/60'/0'/0/0 | Standard first Ethereum address |
m/44'/60'/0'/0/n | nth address (same account) |
m/44'/60'/0'/1/n | Internal/change addresses |
m/44'/60'/n'/0/0 | Different “accounts” (hardened) |
Path parsing in viem-go supports m/ prefix, ' or h for hardened, and numeric segments. Use accounts.DefaultHDPath(accountIndex, changeIndex, addressIndex) to build the default path string.
Accessing the HD key (Go)
HDKeyToAccount returns an *HDAccount, which embeds a local account and exposes the underlying key:
hdAccount, _ := accounts.HDKeyToAccount(derivedKey)
address := hdAccount.GetAddress()
hdKey := hdAccount.GetHdKey()
// Derive another path from the same parent if you kept a reference
child, _ := parentKey.Derive("m/44'/60'/0'/0/1")
Signing
HD accounts support the same signing methods as Private Key Account: Sign, SignMessage, SignTransaction, SignTypedData, SignAuthorization.
sig, err := account.SignMessage(signature.NewSignableMessage("Hello"))
signedTx, err := account.SignTransaction(tx)
sig, err := account.SignTypedData(typedData)
signedAuth, err := account.SignAuthorization(authRequest)
Batch account generation
Generate multiple accounts from one mnemonic by iterating the path (e.g. address index):
func deriveAccounts(mnemonic string, count int) ([]*accounts.HDAccount, error) { out := make([]*accounts.HDAccount, count) for i := 0; i < count; i++ { acc, err := accounts.MnemonicToAccount(mnemonic, &accounts.MnemonicToAccountOptions{ HDOptions: accounts.HDOptions{AddressIndex: i}, }) if err != nil { return nil, err } out[i] = acc } return out, nil}
accounts, _ := deriveAccounts(mnemonic, 10)for i, acc := range accounts { fmt.Printf("Account %d: %s", i, acc.GetAddress())}Return type
- Go:
HDKeyToAccount(hdKey HDKey, opts ...HDKeyToAccountOptions) (*HDAccount, error) - Must variant:
MustHDKeyToAccount(hdKey HDKey, opts ...HDKeyToAccountOptions) *HDAccount
Error handling
- ErrInvalidHDPath — path string is invalid or derivation failed (e.g. invalid segment).
Account source
The account’s source is set to AccountSourceHD when created via HDKeyToAccount, and AccountSourceMnemonic when created via MnemonicToAccount:
fmt.Println(account.GetSource()) // "hd" or "mnemonic"
fmt.Println(account.GetType()) // "local"