Wallets
The Wallet class — a derivation slot (HD) or one imported key (PK) — holding wallet.addresses (one Address per (vm, network)), tags via addTag(), and drop() through the account's wallet collection.
A Wallet is one key slot on an Account: either a single HD derivation index (m/44'/…/0'/0/{id}) or one imported private key. It owns the on-chain identities derived from that key — one Address per (vm, network) — plus a small set of tags. The mnemonic never lives here; it belongs to the account.
You obtain wallets from an account's wallets collection, never by constructing one:
import { Workspace } from "wative-core";
// The canonical all-zero BIP-39 phrase — publicly known, never fund it.
const MNEMONIC =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const ws = await Workspace.open({ path: "./vault", password: "wsp-pwd" });
const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
const wallet = acc.wallets[0];wallet.id
wallet.id is a read-only, non-negative integer — the HD derivation index for an HD account, or the import slot for a PK account. It is the value used to derive the wallet's addresses. On an HD account, shortening with sliceWallets frees the tail indices rather than retiring them: because derivation is deterministic, a later deriveWallets that reaches a freed index again re-derives the identical addresses.
acc.wallets[0].id; // 0
acc.wallets[1].id; // 1 (after acc.deriveWallets(2))The id is also the lookup key on the account's collection:
acc.wallets.byId(0); // Wallet | nullwallet.disabled
wallet.disabled is a read-only boolean — true when the wallet's derivation index is marked disabled on its account. Disabling is a soft, reversible marker: it does not change the wallet's keys or addresses. A disabled wallet is hidden from account.getWalletList() by default (pass force: true to include it) and is gated from key use — dumpPrivateKey, message and typed-data signing, and signing or sending a transaction all throw WALLET_DISABLED until the wallet is re-enabled.
await acc.deriveWallets(1);
const w = acc.wallets[1];
w.disabled; // false
await acc.disableAddressNos([w.id]);
w.disabled; // true
w.dumpPrivateKey("evm"); // → WativeError { code: "WALLET_DISABLED" }
await acc.enableAddressNos([w.id]);
w.disabled; // falseAddresses
wallet.addresses is a read-only list of the wallet's Address objects — exactly one per (vm, network). A wallet may hold several addresses that share a vm on different networks, but never two of the same vm on the same network.
wallet.addresses.length; // number of (vm, network) identities
wallet.addresses.map((a) => a.vm); // e.g. ["evm", "svm"]Finding an address by VM
The example tests locate an address by filtering the list:
const evm = wallet.addresses.find((a) => a.vm === "evm");
const svm = wallet.addresses.find((a) => a.vm === "svm");For the two chains that ship with a signer, two convenience getters return the first matching address already narrowed to its signer subtype:
| Getter | Returns | Notes |
|---|---|---|
wallet.evm | EvmSigner | null | first vm === "evm" address |
wallet.svm | SvmSigner | null | first vm === "svm" address |
const signer = wallet.evm; // EvmSigner | null
if (signer) signer.signMessage("hello");wallet.evm / wallet.svm hand back the exact EvmSigner / SvmSigner instance the wallet holds — the same object unlock keys and that carries the per-chain signing and transaction methods.
VM-agnostic accessors
When you don't want to name the chain, two accessors follow the owning account's default network instead of a fixed VM:
| Accessor | Returns | Notes |
|---|---|---|
wallet.vm | VmToken | the VM of the account's default network — the chain wallet.address selects. Tracks account.setDefaultNetwork. |
wallet.address | Address | null | this wallet's address for wallet.vm (i.e. the wallet.evm / wallet.svm the VM selects), or null when the wallet has no address for that VM. |
acc.defaultNetwork; // "ethereum" (an EVM network)
wallet.vm; // "evm"
wallet.address; // same object as wallet.evm
await acc.setDefaultNetwork("solana");
wallet.vm; // "svm" — follows the default
wallet.address; // now the SVM address (wallet.svm)wallet.address is a pure VM selector — it picks which of the wallet's own addresses to hand back, nothing more. The returned Address's .network is still its creation tag, not the account default; core never re-points an address at the default network. Resolving the default into an actual op (send / sign / balance) is composed one layer up, not by this accessor. wallet.vm falls back to "evm" only when the default slug can't be resolved.
A JSON snapshot
wallet.toJson() returns a plain, JSON-friendly array pairing each address's publicKey with its network:
wallet.toJson();
// [{ vm: "evm", publicKey: "0x…", network: { slug, chainId, name } }, …]Tags
Tags are short, free-form labels on a wallet. They are canonicalized on write (Unicode NFC, whitespace collapsed and trimmed, disallowed characters rejected), so labels that differ only in normalization or case-adjacent whitespace collapse to one entry. A wallet holds at most 64 tags.
| Member | Signature | Description |
|---|---|---|
tags | readonly string[] | a frozen copy of the current tags |
hasTag(tag) | boolean | true if the canonical form is present; never throws |
addTag(tag) | Promise<void> | validate, dedupe, persist; rejects an invalid tag or the 65th |
removeTag(tag) | Promise<void> | remove by canonical form; a no-op if absent |
clearTags() | Promise<void> | remove every tag |
await wallet.addTag("primary");
wallet.hasTag("primary"); // true
await wallet.removeTag("primary");addTag, removeTag and clearTags are asynchronous because each persists through the account's mutation queue; on a persist failure the in-memory tags roll back to their prior state.
Dropping a wallet
wallet.drop() applies to private-key accounts only. It wipes the wallet's imported key and removes it from the account. Two entry points reach the same drop — call either:
await wallet.drop(); // on the wallet itself
await acc.wallets.drop(wallet); // through the account's collectionOn an HD account both forms throw UNSUPPORTED_OP: an HD account's wallets are consecutive derivation indices, so shorten it from the tail with account.sliceWallets(fromIndex) — which frees the indices it removes — or hide an index with account.disableSlots(...).
An account must always keep at least one wallet holding at least one address. On a PK account, dropping the last wallet (or the wallet carrying its only addresses) is refused with an UNSUPPORTED_OP error — to remove the whole account, call account.drop() instead. See Accounts.
Read-only enforcement
wallet.addresses — like account.wallets — is a live, read-only view of the underlying array. The ReadonlyArray type is a compile-time claim; at runtime, any structural write through the view is refused with an UNSUPPORTED_OP error rather than silently mutating a list the next persist would write to disk:
wallet.addresses.push(x); // throws — the collection is read-only
wallet.addresses.length = 0; // throws
delete wallet.addresses[0]; // throws
acc.wallets.length = 0; // throws — same guard on the wallet collectionEvery mutating door is closed — index assignment, push / splice / sort / reverse, Object.freeze, preventExtensions and prototype changes all reject. Use the collection's own methods (acc.wallets.add, acc.wallets.drop, acc.deriveWallets) and the wallet's tag methods to change state.
wallet.dumpPrivateKey(vm) returns the decrypted private key for a VM. It throws ACCOUNT_LOCKED while the account is locked and WALLET_DISABLED while the wallet is disabled. Treat its result as highly sensitive — see Security.