Quick Start
Open a Workspace, create an HD account, derive wallets, and read the EVM and Solana addresses — the same code in Node and the browser.
This page takes you from an empty project to a set of live EVM and Solana addresses. The API is identical in Node and the browser — only where the encrypted records live differs, which the two tabs below show.
Install
pnpm add wative-corenpm i wative-coreNode.js 22.12+ is required for the filesystem backend. The browser build has no Node requirement.
Agent quick-start
Building with a coding agent (Claude Code, Codex, Cursor)? One command drops a repo-local kit that keeps the agent on wative-core's API instead of drifting back to viem / ethers / @solana/web3.js:
pnpm dlx wative-core init-agentnpx wative-core init-agentIt writes a small, managed kit into your repo:
| Path | What it is |
|---|---|
AGENTS.md | A project-root brief that coding-agent harnesses auto-load — the drift rule and a paste-in ESLint block. |
.wative/llms.txt | The one-screen capability index with exact call signatures — point your agent here. |
.wative/interop-matrix.md | The full library × function landscape (viem / ethers / web3.js / @solana/web3.js vs wative-core). |
.wative/eslint-no-reinvent.mjs | An importable ESLint flat-config — the hard lint gate. |
.claude/skills/wative-core/ + hooks | A Claude Code skill and real-time pre-write hooks (Claude Code + Codex) that block a banned crypto import the moment it's written. |
It also stamps .wative/.version with the wative-core version the kit was generated from. After upgrading wative-core, re-run init-agent to refresh the kit; in CI, run npx wative-core init-agent --check to fail the build when an upgrade has outdated it.
Prefer to steer the agent by hand? The same tables live on Coming from viem / ethers?, and the capability index is served at /llms.txt.
Open, create, derive
import { Workspace } from "wative-core";
import "wative-core/node"; // installs the filesystem backend (side-effect import)
const password = "your-workspace-password";
// No path → the default location is used. Auto-selects create vs. open.
const ws = await Workspace.open({ password });
console.log(ws.locked); // false — a password was supplied
const account = await ws.accounts.create(
"Trading Desk",
password,
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
);
console.log(account.wallets.length); // 1 — slot 0 is created for you
await account.deriveWallets(4);
console.log(account.wallets.length); // 5 — slot 0 plus four more
const wallet = account.wallets[0];
const evm = wallet.addresses.find((a) => a.vm === "evm");
const svm = wallet.addresses.find((a) => a.vm === "svm");
console.log(evm?.publicKey); // 0x… (EVM)
console.log(svm?.publicKey); // base58 (Solana)
await ws.lock();
console.log(ws.locked); // trueimport { Workspace, IdbProvider } from "wative-core";
// One-time global setup — see Node & Browser for the Buffer polyfill.
const password = "your-workspace-password";
// Records live in IndexedDB in the current origin.
const provider = await IdbProvider.create("my-dapp");
const ws = await Workspace.open({ provider, password });
// Everything from here is identical to the Node example.
const account = await ws.accounts.create(
"Trading Desk",
password,
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
);
await account.deriveWallets(4);
const wallet = account.wallets[0];
const evm = wallet.addresses.find((a) => a.vm === "evm");
const svm = wallet.addresses.find((a) => a.vm === "svm");
await ws.lock();The only difference between the two tabs is the storage line. In Node, import "wative-core/node" registers the filesystem backend so Workspace.open({ password }) writes encrypted files to disk. In the browser you build an IdbProvider and pass it in. See Node & Browser for the full list of what differs, including the required Buffer polyfill.
What each step does
Open a Workspace
Workspace.open() decides create-vs-open from what is already at the location — there is no create flag. Passing a password returns an unlocked workspace; omitting it returns a locked one you unlock() later. See Opening a workspace.
Create an HD account
ws.accounts.create(displayName, password, secret) seeds an HD account from a BIP-39 mnemonic. A newly created account already holds one wallet — slot 0 — so account.wallets.length is 1 before you derive anything.
Derive wallets
account.deriveWallets(n) adds n derivation slots and resolves to { before, after } (the wallet counts on each side of the call). It is transactional — if any derivation throws, no wallets are added. Calling deriveWallets(4) on a fresh account leaves you with 5 wallets: slot 0 plus four more.
Read the addresses
Each HD wallet carries one address per chain. Read wallet.addresses as the list it is, and select by vm rather than by position:
const evm = wallet.addresses.find((a) => a.vm === "evm");
const svm = wallet.addresses.find((a) => a.vm === "svm");evm.publicKey is a 0x-prefixed EVM address; svm.publicKey is a base58 Solana address. Public keys are readable whether or not the account is unlocked — you only need to unlock to sign. See Addresses.
Lock when done
await ws.lock() ends the session: it wipes in-memory secrets, closes the account handles, and drops the password. ws.locked reads true afterward. Reopen with Workspace.open() or ws.unlock(password).