Getting StartedCore Concepts

Core Concepts

The containment model — one Workspace holds Accounts, each Account holds Wallets, each Wallet holds one Address per (vm, network) — and the HD-vs-PK distinction that decides what a Wallet means.

Everything in wative-core nests inside a Workspace. Learn the containment model once and the rest of the API reads predictably: the same four objects appear everywhere, and the only real subtlety is what a Wallet means, which depends on whether its account is HD or PK.

The containment model

A workspace is one encrypted container guarded by one password. Everything else nests inside it:

Workspace                     one encrypted container, one password

├── Account                   an identity — HD (from a mnemonic) or PK (imported keys)
│   └── Wallet                one slot in that account
│       └── Address           one key on one chain — this is what signs

├── Network                   the chains you can reach (RPC, chain id)
└── Asset                     the tokens tracked on each network
ObjectWhat it is
WorkspaceThe top-level container. Holds the password and three collections — accounts, networks, assets — plus a built-in logger.
AccountAn identity. Either HD (one BIP-39 mnemonic) or PK (imported private keys). Can share the workspace password or carry its own.
WalletA unit inside an account, holding one Address per (vm, network) pair.
AddressAn on-chain identity for one chain. This is what signs messages and transactions.
NetworkChain metadata (RPC URL, chain id, native currency). 14 networks ship pre-loaded.
AssetToken metadata. 29 tokens ship pre-loaded.

wallet.addresses is a list — it holds one address per (vm, network) pair and may carry several. Read it with find rather than assuming a position:

const evm = wallet.addresses.find((a) => a.vm === "evm");

HD vs PK — what a Wallet means

The nesting is identical for both account kinds, but a Wallet means something different in each. This is the part most people get wrong first.

HD — one mnemonic, many slots

An HD account derives from a single BIP-39 mnemonic. A Wallet is a derivation slot, and it holds the EVM and Solana keys derived from that same slot along two different BIP-44 paths (m/44'/60'/… and m/44'/501'/…). The mnemonic is shared; the two private keys are independent.

Account "Trading Desk"   (HD)          one mnemonic
├── Wallet 0                           m/44'/60'/0'/0/0
│   ├── Address  vm: "evm"             0x9858EfFD…
│   └── Address  vm: "svm"             HAgk14JpMQ…      same slot, both chains
├── Wallet 1                           m/44'/60'/0'/0/1
│   ├── Address  vm: "evm"             0x6Fac4D18…
│   └── Address  vm: "svm"             Hh8QwFUA6…
└── …                                  account.deriveWallets(n)

PK — imported keys

A PK account has no mnemonic. Each import allocates its own Wallet, and that wallet gets an address on both chains: the key signs on its own curve, and the same 32 secret bytes seed a keypair on the other one. The chain you imported for comes first in addresses.

Account "Cold Storage"   (PK)          no mnemonic
├── Wallet 0                           the key passed to accounts.create()
│   ├── Address  vm: "evm"             0x90F8bf6A…    the imported key
│   └── Address  vm: "svm"             HaWmh8svNQ…    same secret, other curve
├── Wallet 1                           account.importPrivateKey(evmKey)
│   ├── Address  vm: "evm"             0xFFcf8FDE…
│   └── Address  vm: "svm"             Fgdy5QRxtP…
└── Wallet 2                           account.importPrivateKey(svmKey)
    ├── Address  vm: "svm"             3QVq8D876h…    the imported key
    └── Address  vm: "evm"             0x1a740984…    same secret, other curve

Side by side

HDPK
Created froma BIP-39 mnemonicone private key
Grows withderiveWallets(n)importPrivateKey(pk, vm)
A Wallet isa derivation slotone imported key
Addresses per Wallet2 — one evm, one svm2 — imported chain first
Those two addressesindependent keys, two pathsone key, both curves
dumpMnemonic()supportedthrows UNSUPPORTED_OP
sliceWallets(n)supportedthrows UNSUPPORTED_OP
importPrivateKey()throws UNSUPPORTED_OPsupported

See HD accounts and PK accounts for the full API of each.

Encryption at rest

Everything a workspace persists — accounts, wallets, networks, assets, config — is stored encrypted under your workspace password. Passwords are stretched with Argon2id (RFC 9106) before they ever become a key, which is why unlocking takes a moment. Key derivation runs on a bundled WebAssembly build by default, with no native binary and no build step; where WebAssembly is unavailable it degrades to a slower pure-JS path (see Node & Browser).

Sealed secrets — an account's mnemonic and each address's private key — are bound to their position with additional authenticated data, so an encrypted blob cannot be moved from one account or slot to another. Your storage backend only ever sees sealed bytes; it never encrypts or decrypts anything itself.

Locked and unlocked

Access is gated at two levels.

Workspace. Workspace.open({ password }) returns an unlocked workspace; omitting the password returns a locked one. Check ws.locked, drive it with ws.unlock(password) and ws.lock(). lock() wipes in-memory secrets and closes every account it materialized, leaving the workspace ready to unlock again. ws.close() is the terminal counterpart — after it, ws.closed reads true and the workspace refuses every operation with UNSUPPORTED_OP rather than pretending a reopen is possible.

Account. Each account locks independently. Check account.locked, unlock with account.tryUnlock(password?), and lock with the synchronous account.lock(). A freshly created account is already unlocked; after a workspace is locked and reopened, its accounts hydrate locked. Reading an address's publicKey works whether the account is locked or not — you only need to unlock the account to sign or to dump the mnemonic.

Last updated on