wative-core 2.4.1 → 2.4.3
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.
- package/CHANGELOG.md +20 -0
- package/README.md +59 -55
- package/dist/artifacts/evm/index.cjs +1 -1
- package/dist/artifacts/evm/index.d.cts +2 -2
- package/dist/artifacts/evm/index.d.ts +2 -2
- package/dist/artifacts/evm/index.js +1 -1
- package/dist/artifacts/svm/index.cjs +1 -1
- package/dist/artifacts/svm/index.d.cts +3 -3
- package/dist/artifacts/svm/index.d.ts +3 -3
- package/dist/artifacts/svm/index.js +1 -1
- package/dist/{container-provider--npoeR2N.d.ts → container-provider-B8M0ylrK.d.ts} +1 -1
- package/dist/{container-provider-Ca_5z1uQ.d.cts → container-provider-YxyMpkhG.d.cts} +1 -1
- package/dist/{evm-EHpaW_sK.d.ts → evm-Bo3Dy492.d.ts} +1 -1
- package/dist/{evm-hSJJOVG3.d.cts → evm-DA6Sh6jv.d.cts} +1 -1
- package/dist/{file-sink-Bk5zYHw-.d.ts → file-sink-Be5iLbuT.d.ts} +2 -2
- package/dist/{file-sink-7ZAutH45.d.cts → file-sink-KCCpKHRj.d.cts} +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/dist/index.node.cjs +1 -1
- package/dist/index.node.d.cts +5 -5
- package/dist/index.node.d.ts +5 -5
- package/dist/index.node.js +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +3 -3
- package/dist/node/index.d.ts +3 -3
- package/dist/node/index.js +1 -1
- package/dist/{svm-B5q8YaV9.d.ts → svm-B51NevS8.d.ts} +1 -1
- package/dist/{svm-CXFwmeMo.d.cts → svm-DswX6XRv.d.cts} +1 -1
- package/dist/{workspace-kq3QyAgm.d.cts → workspace-CBxKR80D.d.cts} +7 -43
- package/dist/{workspace-kq3QyAgm.d.ts → workspace-CBxKR80D.d.ts} +7 -43
- package/examples/01-quick-start.test.cjs +1 -2
- package/examples/02-hd-account.test.cjs +1 -2
- package/examples/03-pk-account.test.cjs +39 -1
- package/examples/04-network-management.test.cjs +1 -2
- package/examples/05-asset-management.test.cjs +1 -2
- package/examples/06-address-signing.test.cjs +1 -2
- package/examples/07-custom-provider.test.cjs +2 -8
- package/examples/08-persistence.test.cjs +1 -2
- package/examples/09-workspace-filter.test.cjs +1 -2
- package/examples/10-default-network.test.cjs +1 -3
- package/examples/11-subpath-imports.test.cjs +2 -6
- package/examples/12-logger.test.cjs +1 -3
- package/examples/13-workspace-config.test.cjs +1 -5
- package/examples/14-esm-runtime.test.mjs +0 -9
- package/examples/15-package-resolution.test.cjs +5 -83
- package/examples/16-known-answer-vectors.test.cjs +4 -22
- package/examples/17-locked-state.test.cjs +2 -23
- package/examples/18-envelope-v3.test.cjs +2 -21
- package/examples/19-simple-provider.test.cjs +1 -19
- package/examples/README.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `wative-core` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
|
+
## [2.4.3] — 2026-08-11
|
|
6
|
+
|
|
7
|
+
Documentation only. No API or behaviour changes from 2.4.2.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- The README now leads with what a `Record` is and where records can live, and states plainly that one imported key controls both addresses in a PK wallet.
|
|
11
|
+
|
|
12
|
+
## [2.4.2] — 2026-08-11
|
|
13
|
+
|
|
14
|
+
Importing a private key is easier, and one import now reaches both chains.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **BREAKING.** A PK wallet now holds two addresses — the chain you imported for, plus one on the other chain derived from the same secret, so one key controls both. `addresses[0]` is unchanged; `addresses.length` is now 2. Existing stored wallets keep their single address.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `importPrivateKey(pk)` no longer needs a `vm` — the chain is read from the key — and accepts the JSON byte array `solana-keygen` writes.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- An EVM private key outside the valid range is refused as `INVALID_PRIVATE_KEY` instead of surfacing as an untyped error.
|
|
24
|
+
|
|
5
25
|
## [2.4.1] — 2026-08-11
|
|
6
26
|
|
|
7
27
|
One breaking item, and it is the kind worth reading before upgrading: a
|
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
Hyperscale crypto wallet management library — multi-workspace, multi-chain, originally designed for on-chain market makers.
|
|
9
9
|
|
|
10
|
-
Runs in Node and in the browser.
|
|
11
|
-
|
|
10
|
+
Runs in Node and in the browser environments. Everything a workspace persists — wallets, accounts, networks, assets — is encrypted `Record` layer, sealed by the library exclusively seen by creators.
|
|
11
|
+
|
|
12
|
+
Where those records live is yours to choose: files, IndexedDB, Docker, NSA, databases, CDN, RAM, etc. Any encryption algorithm, any format, any storage media.
|
|
12
13
|
|
|
13
14
|
## How it fits together
|
|
14
15
|
|
|
@@ -44,28 +45,55 @@ Account "Trading Desk" (HD) one mnemonic
|
|
|
44
45
|
└── … account.deriveWallets(n)
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
**PK** — imported keys.
|
|
48
|
-
|
|
48
|
+
**PK** — imported keys. Each import allocates its own `Wallet`, and that wallet
|
|
49
|
+
gets an address on **both** chains: the key signs on its own curve, and the same
|
|
50
|
+
32 secret bytes seed a keypair on the other one. The chain you imported for
|
|
51
|
+
comes first.
|
|
49
52
|
|
|
50
53
|
```
|
|
51
54
|
Account "Cold Storage" (PK) no mnemonic
|
|
52
55
|
├── Wallet 0 the key passed to accounts.create()
|
|
53
|
-
│
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
│ ├── Address vm: "evm" 0x90F8bf6A… the imported key
|
|
57
|
+
│ └── Address vm: "svm" HaWmh8svNQ… same secret, other curve
|
|
58
|
+
├── Wallet 1 account.importPrivateKey(evmKey)
|
|
59
|
+
│ ├── Address vm: "evm" 0xFFcf8FDE…
|
|
60
|
+
│ └── Address vm: "svm" Fgdy5QRxtP…
|
|
61
|
+
└── Wallet 2 account.importPrivateKey(svmKey)
|
|
62
|
+
├── Address vm: "svm" 3QVq8D876h… the imported key
|
|
63
|
+
└── Address vm: "evm" 0x1a740984… same secret, other curve
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
A PK account can hold as many keys as you like, on any mix of chains — but it
|
|
67
|
+
refuses the same identity twice, on either chain.
|
|
68
|
+
|
|
69
|
+
> **`wallet.addresses` is a list.** It holds one address per `(vm, network)`
|
|
70
|
+
> pair and is free to carry several. Read it rather than assuming a position:
|
|
71
|
+
>
|
|
72
|
+
> ```ts
|
|
73
|
+
> const evm = wallet.addresses.find((a) => a.vm === "evm");
|
|
74
|
+
> ```
|
|
75
|
+
|
|
76
|
+
**Which chain a key is for is read off the key** — `vm` is optional:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
await acc.importPrivateKey(evmKey); // 64 hex chars -> evm
|
|
80
|
+
await acc.importPrivateKey(solanaKey); // base58 -> svm
|
|
81
|
+
await acc.importPrivateKey(readFileSync("id.json")); // solana-keygen -> svm
|
|
82
|
+
await acc.importPrivateKey(key, "evm"); // or say so explicitly
|
|
58
83
|
```
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
The formats do not overlap, so this is a decision rather than a guess. A value
|
|
86
|
+
that is neither is refused with `INVALID_PRIVATE_KEY` — including 32 bytes of
|
|
87
|
+
hex that is not a valid secp256k1 scalar, and a bare 32-byte base58 value,
|
|
88
|
+
which is indistinguishable from a Solana *public* key.
|
|
62
89
|
|
|
63
90
|
| | HD | PK |
|
|
64
91
|
| ---------------------- | ----------------------------- | ------------------------------- |
|
|
65
92
|
| Created from | a BIP-39 mnemonic | one private key |
|
|
66
93
|
| Grows with | `deriveWallets(n)` | `importPrivateKey(pk, vm)` |
|
|
67
94
|
| A `Wallet` is | a derivation slot | one imported key |
|
|
68
|
-
| Addresses per `Wallet`
|
|
95
|
+
| Addresses per `Wallet` | **2** — one evm, one svm | **2** — imported chain first |
|
|
96
|
+
| Those two addresses | independent keys, two paths | **one key, both curves** |
|
|
69
97
|
| `dumpMnemonic()` | ✔ | throws `UNSUPPORTED_OP` |
|
|
70
98
|
| `sliceWallets(n)` | ✔ | throws `UNSUPPORTED_OP` |
|
|
71
99
|
| `importPrivateKey()` | throws `UNSUPPORTED_OP` | ✔ |
|
|
@@ -77,10 +105,10 @@ await hd.deriveWallets(5); // slots 1..5, alongside slot 0
|
|
|
77
105
|
hd.wallets[0].addresses.find((a) => a.vm === "evm");
|
|
78
106
|
hd.wallets[0].addresses.find((a) => a.vm === "svm");
|
|
79
107
|
|
|
80
|
-
// PK — each import is its own wallet, on
|
|
108
|
+
// PK — each import is its own wallet, with an address on both chains
|
|
81
109
|
const pk = await ws.accounts.create("Cold Storage", password, privateKey);
|
|
82
|
-
await pk.importPrivateKey(anotherEvmKey
|
|
83
|
-
await pk.importPrivateKey(aSolanaKey
|
|
110
|
+
await pk.importPrivateKey(anotherEvmKey); // -> a new Wallet, vm inferred
|
|
111
|
+
await pk.importPrivateKey(aSolanaKey); // -> another new Wallet
|
|
84
112
|
```
|
|
85
113
|
|
|
86
114
|
## Install
|
|
@@ -150,7 +178,6 @@ await ws.lock();
|
|
|
150
178
|
| Storage | encrypted files on disk | IndexedDB |
|
|
151
179
|
| Where it lives | `Workspace.open({ path })`, or default | `IdbProvider.create(name)` |
|
|
152
180
|
| Extra setup | none | `Buffer` polyfill |
|
|
153
|
-
| Keys can vanish| no | yes — storage is evictable |
|
|
154
181
|
|
|
155
182
|
`HybridProvider`, `HybridProviderV3` and `FileSink` are re-exported from the
|
|
156
183
|
package root in Node.
|
|
@@ -233,17 +260,6 @@ produces the same keys.
|
|
|
233
260
|
Nothing to install: it runs on a bundled WebAssembly build by default, with no
|
|
234
261
|
native binary and no build step.
|
|
235
262
|
|
|
236
|
-
### Browser: grant `'wasm-unsafe-eval'`
|
|
237
|
-
|
|
238
|
-
```
|
|
239
|
-
Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
Optional. Without it nothing breaks and nothing throws — key derivation falls
|
|
243
|
-
back to a slower implementation that produces identical results, so unlocking a
|
|
244
|
-
wallet with a dozen addresses takes tens of seconds instead of about one. Grant
|
|
245
|
-
it if that matters to you. `argon2BackendInfo()` reports which one ran.
|
|
246
|
-
|
|
247
263
|
### One derivation per container: `HybridProviderV3` (Node only)
|
|
248
264
|
|
|
249
265
|
```ts
|
|
@@ -262,24 +278,13 @@ It is the Node default where `@node-rs/argon2` is installed, and Node-only: that
|
|
|
262
278
|
package has no browser build. Construct it yourself only to pick a location, or
|
|
263
279
|
to be sure you get it rather than the fallback.
|
|
264
280
|
|
|
265
|
-
Two things to plan for:
|
|
266
|
-
|
|
267
|
-
- **An existing workspace does not get faster on its own.** Secrets written
|
|
268
|
-
before the switch keep their own keys, so they keep costing one derivation
|
|
269
|
-
each. `account.resetPassword(old, new)` rewrites them.
|
|
270
|
-
- ⚠️ **Older versions of this library cannot open a V3 workspace**, and there is
|
|
271
|
-
no way back. Once a workspace has been opened with V3, keep using a version
|
|
272
|
-
that supports it.
|
|
273
|
-
|
|
274
281
|
### Checking which implementation ran
|
|
275
282
|
|
|
276
283
|
```ts
|
|
277
284
|
import { argon2BackendInfo } from "wative-core";
|
|
278
285
|
|
|
279
286
|
argon2BackendInfo();
|
|
280
|
-
// { backend: "wasm", wasm
|
|
281
|
-
// { backend: "noble", wasm: false, reason: "...", overrides: [] } ← degraded
|
|
282
|
-
// { backend: "unresolved", wasm: false, ..., overrides: ["node-rs"] }
|
|
287
|
+
// -> { backend: "wasm" | "noble" | "unresolved", wasm, reason?, overrides }
|
|
283
288
|
```
|
|
284
289
|
|
|
285
290
|
`backend` is the process-wide default. A provider may carry its own — a
|
|
@@ -312,7 +317,7 @@ The library is organized around seven things you'll work with:
|
|
|
312
317
|
|
|
313
318
|
- **`Workspace`** — your top-level container. Holds the password, your accounts, your network and asset list, and a built-in logger.
|
|
314
319
|
- **`Account`** — either an HD account (one BIP-39 mnemonic, can derive many wallets) or a PK account (raw private keys you import one at a time). Each account can have its own password or share the workspace password.
|
|
315
|
-
- **`Wallet`** — a unit inside an account
|
|
320
|
+
- **`Wallet`** — a unit inside an account, holding one address per `(vm, network)`. Both kinds give you an EVM and a Solana address: an HD slot derives one per chain from the mnemonic, an import derives its sibling from the imported key. Treat `wallet.addresses` as the list it is.
|
|
316
321
|
- **`Address`** — an on-chain identity. Sign messages, build transactions, send them.
|
|
317
322
|
- **`Network`** — chain metadata. 10 networks ship pre-loaded.
|
|
318
323
|
- **`Asset`** — token metadata. 25 tokens ship pre-loaded.
|
|
@@ -388,13 +393,10 @@ So a typical lifecycle looks like:
|
|
|
388
393
|
// First run on a fresh machine — env unset, no <cwd>/.wative2 → creates ~/.wative2
|
|
389
394
|
await Workspace.open({ password: "wsp-pwd" });
|
|
390
395
|
|
|
391
|
-
//
|
|
392
|
-
// $ mkdir .wative2
|
|
393
|
-
// Now <cwd>/.wative2 exists → tier 2 wins
|
|
396
|
+
// After `mkdir .wative2`, <cwd>/.wative2 exists and tier 2 wins
|
|
394
397
|
await Workspace.open({ password: "wsp-pwd" });
|
|
395
398
|
|
|
396
|
-
// Or
|
|
397
|
-
// $ WATIVE_WORKSPACE_PATH=/srv/wative-staging node app.js
|
|
399
|
+
// Or set WATIVE_WORKSPACE_PATH=/srv/wative-staging to override
|
|
398
400
|
await Workspace.open({ password: "wsp-pwd" });
|
|
399
401
|
```
|
|
400
402
|
|
|
@@ -454,6 +456,15 @@ await hd.wallets.drop(hd.wallets[10]);
|
|
|
454
456
|
|
|
455
457
|
### Address — sign messages, build transactions
|
|
456
458
|
|
|
459
|
+
> ⚠️ **One secret controls both addresses in a PK wallet.** There is only one
|
|
460
|
+
> key to import, so both addresses come from it. Whoever learns that key holds
|
|
461
|
+
> both chains.
|
|
462
|
+
>
|
|
463
|
+
> This is not how an HD account works. There, a slot's two addresses come from
|
|
464
|
+
> two different derivation paths — the mnemonic is shared, the private keys are
|
|
465
|
+
> independent. If you want independent keys per chain, use an HD account, or
|
|
466
|
+
> import a separate key for each and read `addresses[0]` of each wallet.
|
|
467
|
+
|
|
457
468
|
```ts
|
|
458
469
|
const evmAddr = hd.wallets[0].addresses.find((a) => a.vm === "evm")!;
|
|
459
470
|
const svmAddr = hd.wallets[0].addresses.find((a) => a.vm === "svm")!;
|
|
@@ -547,11 +558,7 @@ const evmTx = new EvmTransaction({
|
|
|
547
558
|
from: evmAddr.publicKey as EvmAddress,
|
|
548
559
|
to: "0x1234567890123456789012345678901234567890" as EvmAddress,
|
|
549
560
|
value: 10_000_000n,
|
|
550
|
-
|
|
551
|
-
// and there is no `network` field on a transaction — both are refused
|
|
552
|
-
// rather than guessed, because a signed transaction for the wrong chain is
|
|
553
|
-
// still a valid transaction on that chain.
|
|
554
|
-
chainId: 8453 as ChainId, // Base
|
|
561
|
+
chainId: 8453 as ChainId, // required, no default; there is no `network` field
|
|
555
562
|
gasLimit: 21000n,
|
|
556
563
|
maxFeePerGas: 50_000_000_000n,
|
|
557
564
|
maxPriorityFeePerGas: 1_000_000_000n,
|
|
@@ -576,8 +583,7 @@ const evmTx = evmAddr.buildTransaction({
|
|
|
576
583
|
gasLimit: 21000n,
|
|
577
584
|
});
|
|
578
585
|
const rawEvm = evmTx.toRawTx();
|
|
579
|
-
// { from, to, value, data, type, chainId, gasLimit, ... }
|
|
580
|
-
// pass `rawEvm` to web3.eth.sendTransaction, ethers Signer, viem walletClient, etc.
|
|
586
|
+
// { from, to, value, data, type, chainId, gasLimit, ... } — hand to web3/ethers/viem
|
|
581
587
|
```
|
|
582
588
|
|
|
583
589
|
```ts
|
|
@@ -587,9 +593,7 @@ const svmTx = svmAddr.buildTransaction({
|
|
|
587
593
|
amount: 1_000_000n,
|
|
588
594
|
});
|
|
589
595
|
const rawSvm = await svmTx.toRawTx();
|
|
590
|
-
// rawSvm.add(
|
|
591
|
-
// rawSvm.sign(theirKeypair);
|
|
592
|
-
// connection.sendRawTransaction(rawSvm.serialize());
|
|
596
|
+
// then rawSvm.add(...) / .sign(...) / connection.sendRawTransaction(rawSvm.serialize())
|
|
593
597
|
```
|
|
594
598
|
|
|
595
599
|
For SVM, if you supply `recentBlockhash` at construction time, `toRawTx()` builds entirely offline — no Address binding required.
|