wative-core 2.2.0 → 2.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +493 -9
  2. package/LICENSE +114 -0
  3. package/README.md +228 -8
  4. package/dist/artifacts/evm/index.cjs +1 -1
  5. package/dist/artifacts/evm/index.d.cts +2 -2
  6. package/dist/artifacts/evm/index.d.ts +2 -2
  7. package/dist/artifacts/evm/index.js +1 -1
  8. package/dist/artifacts/svm/index.cjs +1 -1
  9. package/dist/artifacts/svm/index.d.cts +3 -3
  10. package/dist/artifacts/svm/index.d.ts +3 -3
  11. package/dist/artifacts/svm/index.js +1 -1
  12. package/dist/{container-provider-i614LDE1.d.cts → container-provider-CkKGyHis.d.cts} +10 -3
  13. package/dist/{container-provider-C59yqd5Z.d.ts → container-provider-D_557LVN.d.ts} +10 -3
  14. package/dist/{evm-BLml33LX.d.cts → evm-Xw4zBLUH.d.ts} +2 -2
  15. package/dist/{evm-DTzqGlL6.d.ts → evm-gsdoVj16.d.cts} +2 -2
  16. package/dist/{file-sink-LS4hyXXi.d.ts → file-sink-C_SlT9IC.d.cts} +8 -5
  17. package/dist/{file-sink-B73MuigK.d.cts → file-sink-D33jvque.d.ts} +8 -5
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.cts +20 -7
  20. package/dist/index.d.ts +20 -7
  21. package/dist/index.js +1 -1
  22. package/dist/index.node.cjs +1 -1
  23. package/dist/index.node.d.cts +6 -6
  24. package/dist/index.node.d.ts +6 -6
  25. package/dist/index.node.js +1 -1
  26. package/dist/node/index.cjs +1 -1
  27. package/dist/node/index.d.cts +3 -3
  28. package/dist/node/index.d.ts +3 -3
  29. package/dist/node/index.js +1 -1
  30. package/dist/{svm-DieFbh9k.d.ts → svm-CTCJsYri.d.cts} +2 -2
  31. package/dist/{svm-CCzqNAWM.d.cts → svm-CUuqpl1h.d.ts} +2 -2
  32. package/dist/{workspace-CamTHxso.d.cts → workspace-Du_jy51u.d.cts} +19 -4
  33. package/dist/{workspace-CamTHxso.d.ts → workspace-Du_jy51u.d.ts} +19 -4
  34. package/{tests → examples}/11-subpath-imports.test.cjs +5 -2
  35. package/examples/15-package-resolution.test.cjs +262 -0
  36. package/examples/18-envelope-v3.test.cjs +106 -0
  37. package/package.json +6 -3
  38. package/tests/15-package-resolution.test.cjs +0 -76
  39. /package/{tests → examples}/01-quick-start.test.cjs +0 -0
  40. /package/{tests → examples}/02-hd-account.test.cjs +0 -0
  41. /package/{tests → examples}/03-pk-account.test.cjs +0 -0
  42. /package/{tests → examples}/04-network-management.test.cjs +0 -0
  43. /package/{tests → examples}/05-asset-management.test.cjs +0 -0
  44. /package/{tests → examples}/06-address-signing.test.cjs +0 -0
  45. /package/{tests → examples}/07-custom-provider.test.cjs +0 -0
  46. /package/{tests → examples}/08-persistence.test.cjs +0 -0
  47. /package/{tests → examples}/09-workspace-filter.test.cjs +0 -0
  48. /package/{tests → examples}/10-default-network.test.cjs +0 -0
  49. /package/{tests → examples}/12-logger.test.cjs +0 -0
  50. /package/{tests → examples}/13-workspace-config.test.cjs +0 -0
  51. /package/{tests → examples}/14-esm-runtime.test.mjs +0 -0
  52. /package/{tests → examples}/16-known-answer-vectors.test.cjs +0 -0
  53. /package/{tests → examples}/17-locked-state.test.cjs +0 -0
  54. /package/{tests → examples}/README.md +0 -0
package/README.md CHANGED
@@ -1,7 +1,88 @@
1
1
  # Wative Core 2
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/wative-core?color=cb3837&logo=npm)](https://www.npmjs.com/package/wative-core)
4
+ [![CI](https://github.com/braady/wative-core/actions/workflows/ci.yml/badge.svg)](https://github.com/braady/wative-core/actions/workflows/ci.yml)
5
+ [![node](https://img.shields.io/node/v/wative-core)](https://nodejs.org)
6
+ [![license](https://img.shields.io/badge/license-Modified%20MIT-blue)](./LICENSE)
7
+
3
8
  Hyperscale crypto wallet management library — multi-workspace, multi-chain, originally designed for on-chain market makers.
4
9
 
10
+ Runs in Node and in the browser. Wallets live in one encrypted container: on disk
11
+ under Node, in IndexedDB in a browser, or in any storage you plug in yourself.
12
+
13
+ ## How it fits together
14
+
15
+ A workspace is the container. Everything else nests inside it:
16
+
17
+ ```
18
+ Workspace one encrypted container, one password
19
+ │
20
+ ├── Account an identity — HD (from a mnemonic) or PK (imported keys)
21
+ │ └── Wallet one slot in that account
22
+ │ └── Address one key on one chain — this is what signs
23
+ │
24
+ ├── Network the chains you can reach (RPC, chain id)
25
+ └── Asset the tokens tracked on each network
26
+ ```
27
+
28
+ ### Two kinds of Account
29
+
30
+ The nesting is the same for both, but a `Wallet` means something different in
31
+ each — and that is the part most people get wrong first.
32
+
33
+ **HD** — one mnemonic, many slots. A `Wallet` is a derivation slot, and it holds
34
+ the EVM *and* Solana keys derived from that same slot:
35
+
36
+ ```
37
+ Account "Trading Desk" (HD) one mnemonic
38
+ ├── Wallet 0 m/44'/60'/0'/0/0
39
+ │ ├── Address vm: "evm" 0x9858EfFD…
40
+ │ └── Address vm: "svm" HAgk14JpMQ… same slot, both chains
41
+ ├── Wallet 1 m/44'/60'/0'/0/1
42
+ │ ├── Address vm: "evm" 0x6Fac4D18…
43
+ │ └── Address vm: "svm" Hh8QwFUA6…
44
+ └── … account.deriveWallets(n)
45
+ ```
46
+
47
+ **PK** — imported keys. A `Wallet` is one key you handed in, on the one chain
48
+ you imported it for. Wallets have no relationship to each other:
49
+
50
+ ```
51
+ Account "Cold Storage" (PK) no mnemonic
52
+ ├── Wallet 0 the key passed to accounts.create()
53
+ │ └── Address vm: "evm" 0x90F8bf6A…
54
+ ├── Wallet 1 account.importPrivateKey(pk, "evm")
55
+ │ └── Address vm: "evm" 0xFFcf8FDE…
56
+ └── Wallet 2 account.importPrivateKey(pk, "svm")
57
+ └── Address vm: "svm" 7WktogJEd… one chain per wallet
58
+ ```
59
+
60
+ A PK account can hold as many keys as you like, and they can be on different
61
+ chains — but it refuses the same identity twice.
62
+
63
+ | | HD | PK |
64
+ | ---------------------- | ----------------------------- | ------------------------------- |
65
+ | Created from | a BIP-39 mnemonic | one private key |
66
+ | Grows with | `deriveWallets(n)` | `importPrivateKey(pk, vm)` |
67
+ | A `Wallet` is | a derivation slot | one imported key |
68
+ | Addresses per `Wallet` | **2** — one evm, one svm | **1** — the chain you imported |
69
+ | `dumpMnemonic()` | ✔ | throws `UNSUPPORTED_OP` |
70
+ | `sliceWallets(n)` | ✔ | throws `UNSUPPORTED_OP` |
71
+ | `importPrivateKey()` | throws `UNSUPPORTED_OP` | ✔ |
72
+
73
+ ```ts
74
+ // HD — one slot gives you the matching keys on both chains
75
+ const hd = await ws.accounts.create("Trading Desk", password, mnemonic);
76
+ await hd.deriveWallets(5); // slots 1..5, alongside slot 0
77
+ hd.wallets[0].addresses.find((a) => a.vm === "evm");
78
+ hd.wallets[0].addresses.find((a) => a.vm === "svm");
79
+
80
+ // PK — each import is its own wallet, on one chain
81
+ const pk = await ws.accounts.create("Cold Storage", password, privateKey);
82
+ await pk.importPrivateKey(anotherEvmKey, "evm"); // -> a new Wallet
83
+ await pk.importPrivateKey(aSolanaKey, "svm"); // -> another new Wallet
84
+ ```
85
+
5
86
  ## Install
6
87
 
7
88
  ```bash
@@ -71,9 +152,25 @@ await ws.lock();
71
152
  | Extra setup | none | `Buffer` polyfill |
72
153
  | Keys can vanish| no | yes — storage is evictable |
73
154
 
74
- `HybridProvider` and `FileSink` are also re-exported from the package root in
75
- Node, so existing imports keep working. New code should take them from
76
- `wative-core/node`.
155
+ `HybridProvider`, `HybridProviderV3` and `FileSink` are re-exported from the
156
+ package root in Node.
157
+
158
+ Take the provider from the **same entry point** as `Workspace`:
159
+
160
+ ```ts
161
+ // Right — one entry point.
162
+ import { Workspace, HybridProvider } from "wative-core";
163
+ const ws = await Workspace.open(new HybridProvider("~/wallets"), password);
164
+
165
+ // Wrong — rejected with a PARAMETER_ERROR naming the fix.
166
+ import { Workspace } from "wative-core";
167
+ import { HybridProvider } from "wative-core/node";
168
+ ```
169
+
170
+ Each entry point is a separate bundle with its own class identities and module
171
+ state, so a provider built by one is not recognised by a `Workspace` from the
172
+ other. `Workspace.open` rejects it with an error naming the fix. A bare
173
+ `import "wative-core/node"` for its side-effects is unaffected.
77
174
 
78
175
  ### Browser: the Buffer polyfill
79
176
 
@@ -126,9 +223,131 @@ const backup = await provider.exportContainer(); // still encrypted
126
223
  await otherProvider.importContainer(backup); // same password opens it
127
224
  ```
128
225
 
226
+ ## Key derivation
227
+
228
+ Passwords are stretched with Argon2id (RFC 9106) at t=3, m=64 MiB, p=1 before
229
+ they ever touch a key. That is deliberately expensive, and an account unlock
230
+ runs one derivation per sealed secret — so the implementation matters.
231
+
232
+ By default it runs on a vendored WebAssembly build: no native binary, no build
233
+ step, nothing to install. Measured over a full workspace lifecycle it is about
234
+ **25x** faster than the pure-JS implementation it replaced.
235
+
236
+ ### Browser: grant `'wasm-unsafe-eval'`
237
+
238
+ ```
239
+ Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'
240
+ ```
241
+
242
+ Without it the browser blocks WebAssembly compilation entirely — synchronously
243
+ and asynchronously, main thread and worker alike. **Nothing throws.** Key
244
+ derivation quietly falls back to a pure-JS implementation that produces
245
+ identical bytes roughly 17x slower, so unlocking a wallet with a dozen addresses
246
+ goes from under a second to tens of seconds. The fallback is announced once on
247
+ the console and reported permanently by `argon2BackendInfo()`.
248
+
249
+ ## Upgrading to 2.3.0
250
+
251
+ **v1 containers can no longer be opened.** Support for the v1 envelope
252
+ (PBKDF2-HMAC-SHA256, written by wative-core 1.x) was removed and there is no
253
+ migration path in this release. Containers written by any 2.x version are v2 and
254
+ open unchanged.
255
+
256
+ If you hold a v1 container, recover it with **2.2.x or earlier** before
257
+ upgrading. `exportContainer()` will not do it — it copies sealed bytes without
258
+ decrypting, so the export is as unreadable as the original. Use the accessors
259
+ that return plaintext:
260
+
261
+ ```ts
262
+ // under wative-core 2.2.x — NOT 2.3.0, which can no longer open a v1 container
263
+ import { Workspace, HybridProvider } from "wative-core";
264
+
265
+ const ws = await Workspace.open(new HybridProvider("~/wallets"), password);
266
+
267
+ for (const account of ws.accounts) {
268
+ await account.tryUnlock(password);
269
+
270
+ // HD accounts: the mnemonic regenerates every derived key, so it is the only
271
+ // thing you need.
272
+ if (account.organizationType === "HD") {
273
+ console.log(account.slug, "mnemonic:", account.dumpMnemonic());
274
+ continue;
275
+ }
276
+
277
+ // PK accounts: there is no mnemonic, so every imported key must be kept.
278
+ // `dumpPrivateKey` lives on the WALLET and takes the VM of the address you
279
+ // want — a wallet holds at most one address per VM.
280
+ for (const wallet of account.wallets) {
281
+ for (const address of wallet.addresses) {
282
+ console.log(account.slug, wallet.id, address.vm, wallet.dumpPrivateKey(address.vm));
283
+ }
284
+ }
285
+ }
286
+ ```
287
+
288
+ Keep that material somewhere safe, upgrade, then re-import it with
289
+ `accounts.create(...)` and `importPrivateKey(...)`.
290
+
291
+ Three public members were removed with v1: `Provider.passwordTanren`,
292
+ `ContainerProvider.passwordTanren` and `HybridProvider.kdfIterations`. All three
293
+ existed only to expose the PBKDF2 derivation.
294
+
295
+ ### One derivation per container: `HybridProviderV3` (Node only)
296
+
297
+ ```ts
298
+ import { Workspace, HybridProviderV3 } from "wative-core";
299
+
300
+ const ws = await Workspace.open(new HybridProviderV3("~/wallets"), password);
301
+ ```
302
+
303
+ The default provider derives an Argon2 key per sealed secret, so unlocking an
304
+ account costs one derivation per address — at production parameters that is
305
+ essentially the whole cost, and it grows with the account. `HybridProviderV3`
306
+ derives once per container instead. Measured: unlocking a 50-address account
307
+ goes from 7.3 s to 144 ms, and the v3 figure does not grow with the address
308
+ count.
309
+
310
+ It is opt-in, and Node-only: it derives through native `@node-rs/argon2`, which
311
+ has no browser build. In a browser use `HybridProvider`, which needs no binary
312
+ and no install and is already about 25x faster than what preceded it.
313
+
314
+ ⚠️ **The speedup applies to records written as v3, not to ones you already
315
+ have.** Pointing `HybridProviderV3` at a container built by 2.x keeps every
316
+ existing secret on its own salt, so it keeps costing one derivation each — the
317
+ container still opens, it just does not get faster. Rewriting an account's
318
+ secrets with `account.resetPassword(old, new)` converts them. A container
319
+ reaches one derivation once all of its records have been written as v3, which
320
+ happens on its own only for containers created under v3.
321
+
322
+ The first v3 unlock of an existing container costs one extra derivation, once,
323
+ and converts its CONFIG record to v3 — after which wative-core 2.2.x and earlier
324
+ can no longer open it.
325
+
326
+ Reads do not depend on the choice — every record carries its own version byte,
327
+ so either 2.3.0 provider opens either format, in both directions. ⚠️ But **older
328
+ library versions cannot read v3**: wative-core 2.2.x and earlier know only v1 and
329
+ v2 and will fail with `Unknown envelope version: 3`. Adopting v3 is a one-way
330
+ door with respect to the library version.
331
+
332
+ ### Checking which implementation ran
333
+
334
+ ```ts
335
+ import { argon2BackendInfo } from "wative-core";
336
+
337
+ argon2BackendInfo();
338
+ // { backend: "wasm", wasm: true, overrides: [] }
339
+ // { backend: "noble", wasm: false, reason: "...", overrides: [] } ← degraded
340
+ // { backend: "unresolved", wasm: false, ..., overrides: ["node-rs"] }
341
+ ```
342
+
343
+ `backend` is the process-wide default. A provider may carry its own — a
344
+ `HybridProviderV3` does — so check **`overrides`** to confirm the native path
345
+ took. It is a pure read and resolves nothing, so before the first unlock it
346
+ honestly answers `"unresolved"`.
347
+
129
348
  ## Runnable examples
130
349
 
131
- A `tests/` folder ships with the package — runnable JavaScript files demonstrating every domain's use cases. Read them top-to-bottom as walkthroughs, or run them as integration tests via Node's built-in test runner.
350
+ An `examples/` folder ships with the package — runnable JavaScript files demonstrating every domain's use cases. Read them top-to-bottom as walkthroughs, or run them as integration tests via Node's built-in test runner.
132
351
 
133
352
  Copy them out of `node_modules` first, then run the whole set:
134
353
 
@@ -140,10 +359,10 @@ node --test wative-examples/
140
359
  The copy is required: Node's test runner skips anything under `node_modules`, so pointing `--test` there finds no files. To run a single example without copying, execute it directly — a `node:test` file runs itself:
141
360
 
142
361
  ```bash
143
- node node_modules/wative-core/tests/01-quick-start.test.cjs
362
+ node node_modules/wative-core/examples/01-quick-start.test.cjs
144
363
  ```
145
364
 
146
- Files cover: quick start, HD vs PK accounts, network management, asset management, address signing, custom storage backends, persistence, workspace search, default-network selection, subpath imports, the workspace logger, workspace config, the ESM entry, and package resolution. See [tests/README.md](./tests/README.md) for the index.
365
+ Files cover: quick start, HD vs PK accounts, network management, asset management, address signing, custom storage backends, persistence, workspace search, default-network selection, subpath imports, the workspace logger, workspace config, the ESM entry, and package resolution. See [examples/README.md](./examples/README.md) for the index.
147
366
 
148
367
  ## What you can do
149
368
 
@@ -155,7 +374,7 @@ The library is organized around seven things you'll work with:
155
374
  - **`Address`** — an on-chain identity. Sign messages, build transactions, send them.
156
375
  - **`Network`** — chain metadata. 10 networks ship pre-loaded.
157
376
  - **`Asset`** — token metadata. 25 tokens ship pre-loaded.
158
- - **`Transaction`** — `EvmTransaction` or `SvmTransaction`. Subscribe to lifecycle events (`change` / `confirmed` / `failed`) or await terminal states (`whenSubmitted` / `whenMined` / `whenConfirmed` / `whenFinalized`).
377
+ - **`Transaction`** — `EvmTransaction` or `SvmTransaction`. Subscribe to lifecycle events (`change` / `confirmed` / `failed`) or await terminal states (`whenSubmitted` / `whenMined` / `whenConfirmed` / `whenFinalized`). Tracking stops at first inclusion, so `whenFinalized()` rejects `UNSUPPORTED_OP` unless the node reported finality directly.
159
378
 
160
379
  Everything is encrypted on disk under your workspace password.
161
380
 
@@ -487,10 +706,11 @@ import { TokenProgram, Token2022Program } from "wative-core/artifacts/svm";
487
706
 
488
707
  ## Compatibility notes
489
708
 
709
+ - **Content-Security-Policy (browser)**: grant `'wasm-unsafe-eval'` in `script-src`, or key derivation silently degrades to a ~17x slower pure-JS path. See [Key derivation](#key-derivation).
490
710
  - **Runtime**: Node.js 22.12+. Deno / Bun / browser support is partial. Some chain libraries that ship as compiled dependencies do not have universal builds yet.
491
711
  - **Platform builds**: a few compiled dependencies lack pre-built binaries for Windows-ARM64 and Alpine-musl. Most installs on macOS / Linux x64 / Linux ARM64 won't notice.
492
712
  - **One process at a time**: opening the same workspace from two Node processes simultaneously isn't supported.
493
713
 
494
714
  ## License
495
715
 
496
- [BUSL-1.1](./LICENSE) — Business Source License 1.1.
716
+ [Modified MIT](./LICENSE) — MIT terms with one added condition: products or services earning over US$50,000 monthly revenue must display "Wative" in their user interface. Everything else is standard MIT. The `LICENSE` file also carries third-party notices for the compiled components embedded in `dist/`.