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| Object | What it is |
|---|---|
Workspace | The top-level container. Holds the password and three collections — accounts, networks, assets — plus a built-in logger. |
Account | An identity. Either HD (one BIP-39 mnemonic) or PK (imported private keys). Can share the workspace password or carry its own. |
Wallet | A unit inside an account, holding one Address per (vm, network) pair. |
Address | An on-chain identity for one chain. This is what signs messages and transactions. |
Network | Chain metadata (RPC URL, chain id, native currency). 14 networks ship pre-loaded. |
Asset | Token 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 curveOne secret controls both addresses in a PK wallet. There is only one key to import, so both addresses come from it — whoever learns that key holds both chains. This is not how an HD account works, where a slot's two addresses come from independent private keys. For independent keys per chain, use an HD account, or import a separate key for each chain.
Side by side
| HD | PK | |
|---|---|---|
| Created from | a BIP-39 mnemonic | one private key |
| Grows with | deriveWallets(n) | importPrivateKey(pk, vm) |
A Wallet is | a derivation slot | one imported key |
Addresses per Wallet | 2 — one evm, one svm | 2 — imported chain first |
| Those two addresses | independent keys, two paths | one key, both curves |
dumpMnemonic() | supported | throws UNSUPPORTED_OP |
sliceWallets(n) | supported | throws UNSUPPORTED_OP |
importPrivateKey() | throws UNSUPPORTED_OP | supported |
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.
An account created with hasOwnPassword: false shares the workspace password and unlocks with it. An account created with hasOwnPassword: true carries its own password, and tryUnlock() must be given it. See Accounts.