wative-core 1.2.2 → 2.2.1

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 (62) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/LICENSE +55 -0
  3. package/README.md +577 -0
  4. package/dist/artifacts/evm/index.cjs +1 -0
  5. package/dist/artifacts/evm/index.d.cts +6 -0
  6. package/dist/artifacts/evm/index.d.ts +6 -0
  7. package/dist/artifacts/evm/index.js +1 -0
  8. package/dist/artifacts/svm/index.cjs +1 -0
  9. package/dist/artifacts/svm/index.d.cts +9 -0
  10. package/dist/artifacts/svm/index.d.ts +9 -0
  11. package/dist/artifacts/svm/index.js +1 -0
  12. package/dist/container-provider-C59yqd5Z.d.ts +48 -0
  13. package/dist/container-provider-i614LDE1.d.cts +48 -0
  14. package/dist/evm-BLml33LX.d.cts +34 -0
  15. package/dist/evm-DTzqGlL6.d.ts +34 -0
  16. package/dist/file-sink-B73MuigK.d.cts +44 -0
  17. package/dist/file-sink-LS4hyXXi.d.ts +44 -0
  18. package/dist/index.cjs +1 -0
  19. package/dist/index.d.cts +194 -0
  20. package/dist/index.d.ts +194 -0
  21. package/dist/index.js +1 -0
  22. package/dist/index.node.cjs +1 -0
  23. package/dist/index.node.d.cts +6 -0
  24. package/dist/index.node.d.ts +6 -0
  25. package/dist/index.node.js +1 -0
  26. package/dist/node/index.cjs +1 -0
  27. package/dist/node/index.d.cts +7 -0
  28. package/dist/node/index.d.ts +7 -0
  29. package/dist/node/index.js +1 -0
  30. package/dist/svm-CCzqNAWM.d.cts +59 -0
  31. package/dist/svm-DieFbh9k.d.ts +59 -0
  32. package/dist/workspace-CamTHxso.d.cts +737 -0
  33. package/dist/workspace-CamTHxso.d.ts +737 -0
  34. package/examples/01-quick-start.test.cjs +37 -0
  35. package/examples/02-hd-account.test.cjs +71 -0
  36. package/examples/03-pk-account.test.cjs +59 -0
  37. package/examples/04-network-management.test.cjs +94 -0
  38. package/examples/05-asset-management.test.cjs +117 -0
  39. package/examples/06-address-signing.test.cjs +55 -0
  40. package/examples/07-custom-provider.test.cjs +164 -0
  41. package/examples/08-persistence.test.cjs +105 -0
  42. package/examples/09-workspace-filter.test.cjs +84 -0
  43. package/examples/10-default-network.test.cjs +70 -0
  44. package/examples/11-subpath-imports.test.cjs +24 -0
  45. package/examples/12-logger.test.cjs +44 -0
  46. package/examples/13-workspace-config.test.cjs +43 -0
  47. package/examples/14-esm-runtime.test.mjs +81 -0
  48. package/examples/15-package-resolution.test.cjs +76 -0
  49. package/examples/16-known-answer-vectors.test.cjs +141 -0
  50. package/examples/17-locked-state.test.cjs +162 -0
  51. package/examples/README.md +72 -0
  52. package/package.json +85 -39
  53. package/lib/account.d.ts +0 -69
  54. package/lib/index.d.ts +0 -6
  55. package/lib/index.esm.js +0 -1
  56. package/lib/index.umd.js +0 -1
  57. package/lib/mnemonic.d.ts +0 -3
  58. package/lib/utils.d.ts +0 -21
  59. package/src/account.ts +0 -749
  60. package/src/index.ts +0 -11
  61. package/src/mnemonic.ts +0 -2084
  62. package/src/utils.ts +0 -132
@@ -0,0 +1,105 @@
1
+ // 08 — Persistence. Verify that accounts, networks, custom assets, and the
2
+ // logger configuration all round-trip correctly across `lock()` + reopen.
3
+
4
+ const test = require("node:test");
5
+ const assert = require("node:assert");
6
+ const { mkdtempSync } = require("node:fs");
7
+ const { tmpdir } = require("node:os");
8
+ const { join } = require("node:path");
9
+
10
+ const { Workspace, Network, Asset } = require("wative-core");
11
+
12
+ const MNEMONIC =
13
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
14
+
15
+ test("persistence — multiple HD + PK accounts round-trip", async () => {
16
+ const root = mkdtempSync(join(tmpdir(), "wative-persist-acc-"));
17
+ let ws = await Workspace.open({ path: root, password: "wsp-pwd" });
18
+
19
+ const hd = await ws.accounts.create("Desk One", "wsp-pwd", MNEMONIC);
20
+ await hd.deriveWallets(3);
21
+
22
+ const pk = await ws.accounts.create(
23
+ "Hot Wallet",
24
+ "wsp-pwd",
25
+ "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
26
+ undefined,
27
+ { kind: "PK" },
28
+ );
29
+
30
+ await ws.lock();
31
+ ws = await Workspace.open({ path: root, password: "wsp-pwd" });
32
+ assert.strictEqual(ws.accounts.length, 2);
33
+ const reopenHd = ws.accounts.bySlug(hd.slug);
34
+ const reopenPk = ws.accounts.bySlug(pk.slug);
35
+ assert.ok(reopenHd && reopenPk);
36
+ assert.strictEqual(reopenHd.organizationType, "HD");
37
+ assert.strictEqual(reopenPk.organizationType, "PK");
38
+ assert.strictEqual(reopenHd.wallets.length, 4);
39
+ assert.strictEqual(reopenPk.wallets.length, 1);
40
+
41
+ await ws.lock();
42
+ });
43
+
44
+ test("persistence — added user network round-trips with all fields", async () => {
45
+ const root = mkdtempSync(join(tmpdir(), "wative-persist-net-"));
46
+ let ws = await Workspace.open({ path: root, password: "wsp-pwd" });
47
+
48
+ await ws.networks.add(new Network({
49
+ slug: "monad-testnet",
50
+ name: "Monad Testnet",
51
+ chainId: 10143,
52
+ rpcUrl: "https://testnet-rpc.monad.xyz",
53
+ nativeCurrency: { name: "Monad", symbol: "MON", decimals: 18 },
54
+ color: "#7e22ce",
55
+ vm: "evm",
56
+ }));
57
+
58
+ await ws.lock();
59
+ ws = await Workspace.open({ path: root, password: "wsp-pwd" });
60
+ const monad = ws.networks.bySlug("monad-testnet");
61
+ assert.ok(monad);
62
+ assert.strictEqual(monad.chainId, 10143);
63
+ assert.strictEqual(monad.rpcUrl, "https://testnet-rpc.monad.xyz");
64
+ assert.strictEqual(monad.nativeCurrency.symbol, "MON");
65
+ assert.strictEqual(monad.color, "#7e22ce");
66
+
67
+ await ws.lock();
68
+ });
69
+
70
+ test("persistence — custom user asset round-trips", async () => {
71
+ const root = mkdtempSync(join(tmpdir(), "wative-persist-asset-"));
72
+ let ws = await Workspace.open({ path: root, password: "wsp-pwd" });
73
+
74
+ await ws.addAsset(new Asset({
75
+ id: 250,
76
+ symbol: "PEPE",
77
+ name: "Pepe",
78
+ decimals: 18,
79
+ contractAddress: "0x6982508145454Ce325dDbE47a25d4ec3d2311933",
80
+ network: Network.Ethereum,
81
+ }));
82
+
83
+ await ws.lock();
84
+ ws = await Workspace.open({ path: root, password: "wsp-pwd" });
85
+ const ethAssets = await ws.assets(Network.Ethereum);
86
+ const pepe = ethAssets.find((a) => a.id === 250);
87
+ assert.ok(pepe);
88
+ assert.strictEqual(pepe.symbol, "PEPE");
89
+ assert.strictEqual(pepe.decimals, 18);
90
+
91
+ await ws.lock();
92
+ });
93
+
94
+ test("persistence — custom logger config round-trips", async () => {
95
+ const root = mkdtempSync(join(tmpdir(), "wative-persist-log-"));
96
+ let ws = await Workspace.open({ path: root, password: "wsp-pwd" });
97
+
98
+ await ws.logger.setLevel("debug");
99
+
100
+ await ws.lock();
101
+ ws = await Workspace.open({ path: root, password: "wsp-pwd" });
102
+ assert.strictEqual(ws.logger.config.minLevel, "debug");
103
+
104
+ await ws.lock();
105
+ });
@@ -0,0 +1,84 @@
1
+ // 09 — Workspace search. `filter(query, objective)` returns the live object
2
+ // matching `query` for the given objective: Wallet, Address, Account, or Asset.
3
+
4
+ const test = require("node:test");
5
+ const assert = require("node:assert");
6
+ const { mkdtempSync } = require("node:fs");
7
+ const { tmpdir } = require("node:os");
8
+ const { join } = require("node:path");
9
+
10
+ const { Workspace } = require("wative-core");
11
+
12
+ const MNEMONIC =
13
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
14
+
15
+ test("filter — find an Account by displayName or slug", async () => {
16
+ const root = mkdtempSync(join(tmpdir(), "wative-filter-acc-"));
17
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
18
+
19
+ const acc = await ws.accounts.create("Trading Desk", "wsp-pwd", MNEMONIC);
20
+
21
+ const byName = await ws.filter("Trading Desk", "Account");
22
+ const bySlug = await ws.filter(acc.slug, "Account");
23
+ assert.ok(byName && byName.slug === acc.slug);
24
+ assert.ok(bySlug && bySlug.slug === acc.slug);
25
+
26
+ const missing = await ws.filter("nonexistent", "Account");
27
+ assert.strictEqual(missing, null);
28
+
29
+ await ws.lock();
30
+ });
31
+
32
+ test("filter — find a Wallet by id or by tag", async () => {
33
+ const root = mkdtempSync(join(tmpdir(), "wative-filter-wallet-"));
34
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
35
+
36
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
37
+ await acc.deriveWallets(2);
38
+
39
+ await acc.wallets[1].addTag("alpha-strategy");
40
+
41
+ const byId = await ws.filter("1", "Wallet");
42
+ assert.ok(byId);
43
+ assert.strictEqual(byId.id, 1);
44
+
45
+ const byTag = await ws.filter("alpha-strategy", "Wallet");
46
+ assert.ok(byTag);
47
+ assert.strictEqual(byTag.id, 1);
48
+
49
+ await ws.lock();
50
+ });
51
+
52
+ test("filter — find an Address by its public key (case-insensitive for EVM)", async () => {
53
+ const root = mkdtempSync(join(tmpdir(), "wative-filter-addr-"));
54
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
55
+
56
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
57
+ const evm = acc.wallets[0].addresses.find((a) => a.vm === "evm");
58
+
59
+ const found = await ws.filter(evm.publicKey, "Address");
60
+ assert.ok(found);
61
+ assert.strictEqual(found.publicKey, evm.publicKey);
62
+
63
+ const foundLower = await ws.filter(evm.publicKey.toLowerCase(), "Address");
64
+ assert.ok(foundLower);
65
+ assert.strictEqual(foundLower.publicKey, evm.publicKey);
66
+
67
+ await ws.lock();
68
+ });
69
+
70
+ test("filter — find an Asset; returns null for unknown query", async () => {
71
+ const root = mkdtempSync(join(tmpdir(), "wative-filter-asset-"));
72
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
73
+
74
+ const usdc = await ws.filter("USDC", "Asset");
75
+ assert.ok(usdc && usdc.symbol === "USDC");
76
+
77
+ const usdt = await ws.filter("0xdAC17F958D2ee523a2206206994597C13D831ec7", "Asset");
78
+ assert.ok(usdt && usdt.symbol === "USDT");
79
+
80
+ const missing = await ws.filter("DOES-NOT-EXIST", "Asset");
81
+ assert.strictEqual(missing, null);
82
+
83
+ await ws.lock();
84
+ });
@@ -0,0 +1,70 @@
1
+ // 10 — Account default network. `account.setDefaultNetwork(value)` accepts
2
+ // a slug, a chain id, a hex chain id, or a Network instance — and the choice
3
+ // persists across reopen.
4
+
5
+ const test = require("node:test");
6
+ const assert = require("node:assert");
7
+ const { mkdtempSync } = require("node:fs");
8
+ const { tmpdir } = require("node:os");
9
+ const { join } = require("node:path");
10
+
11
+ const { Workspace, Network } = require("wative-core");
12
+
13
+ const MNEMONIC =
14
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
15
+
16
+ test("setDefaultNetwork — accepts slug, chainId, hex chainId, and Network instance", async () => {
17
+ const root = mkdtempSync(join(tmpdir(), "wative-default-net-forms-"));
18
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
19
+
20
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
21
+
22
+ await acc.setDefaultNetwork("base");
23
+ assert.strictEqual(acc.defaultNetwork, "base");
24
+
25
+ await acc.setDefaultNetwork(8453);
26
+ assert.strictEqual(acc.defaultNetwork, "base");
27
+
28
+ await acc.setDefaultNetwork("0xa");
29
+ assert.strictEqual(acc.defaultNetwork, "optimism");
30
+
31
+ await acc.setDefaultNetwork(Network.Arbitrum);
32
+ assert.strictEqual(acc.defaultNetwork, "arbitrum");
33
+
34
+ await ws.lock();
35
+ });
36
+
37
+ test("setDefaultNetwork — rejects an unknown network", async () => {
38
+ const root = mkdtempSync(join(tmpdir(), "wative-default-net-bad-"));
39
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
40
+
41
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
42
+
43
+ await assert.rejects(
44
+ acc.setDefaultNetwork("does-not-exist"),
45
+ (err) => err.code === "UNSUPPORTED_NETWORK",
46
+ );
47
+ await assert.rejects(
48
+ acc.setDefaultNetwork(99999999),
49
+ (err) => err.code === "UNSUPPORTED_NETWORK",
50
+ );
51
+
52
+ await ws.lock();
53
+ });
54
+
55
+ test("setDefaultNetwork — choice persists across lock + reopen", async () => {
56
+ const root = mkdtempSync(join(tmpdir(), "wative-default-net-persist-"));
57
+ let ws = await Workspace.open({ path: root, password: "wsp-pwd" });
58
+
59
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
60
+ await acc.setDefaultNetwork(Network.Solana);
61
+ assert.strictEqual(acc.defaultNetwork, "solana");
62
+
63
+ await ws.lock();
64
+ ws = await Workspace.open({ path: root, password: "wsp-pwd" });
65
+ const reopened = ws.accounts.bySlug(acc.slug);
66
+ assert.ok(reopened);
67
+ assert.strictEqual(reopened.defaultNetwork, "solana");
68
+
69
+ await ws.lock();
70
+ });
@@ -0,0 +1,24 @@
1
+ // 11 — Subpath imports. Heavier chain helpers ship under `wative-core/artifacts/evm`
2
+ // and `wative-core/artifacts/svm` so they're only pulled in when used. In your
3
+ // own project: require("wative-core/artifacts/evm") / require("wative-core/artifacts/svm").
4
+
5
+ const test = require("node:test");
6
+ const assert = require("node:assert");
7
+
8
+ // Imported by SUBPATH, which is the whole point of this example — and what a
9
+ // consumer writes. A relative path into dist/ would resolve here but nowhere
10
+ // else, so it would pass in-repo while failing for every real installation.
11
+ const evmArtifacts = require("wative-core/artifacts/evm");
12
+ const svmArtifacts = require("wative-core/artifacts/svm");
13
+
14
+ test("subpath import — evm artifacts expose a usable ERC20 contract", () => {
15
+ assert.ok(evmArtifacts.ERC20);
16
+ assert.strictEqual(typeof evmArtifacts.ERC20.encode, "function");
17
+ });
18
+
19
+ test("subpath import — svm artifacts expose TokenProgram and Token2022Program", () => {
20
+ assert.ok(svmArtifacts.TokenProgram);
21
+ assert.ok(svmArtifacts.Token2022Program);
22
+ assert.strictEqual(typeof svmArtifacts.TokenProgram.encodeInstruction, "function");
23
+ assert.strictEqual(typeof svmArtifacts.Token2022Program.encodeInstruction, "function");
24
+ });
@@ -0,0 +1,44 @@
1
+ // 12 — Logger. Workspace exposes `ws.logger` after unlock. Write structured
2
+ // logs at any level, change the level via `ws.logger.setLevel(...)`, and the
3
+ // configuration persists across reopen.
4
+
5
+ const test = require("node:test");
6
+ const assert = require("node:assert");
7
+ const { mkdtempSync } = require("node:fs");
8
+ const { tmpdir } = require("node:os");
9
+ const { join } = require("node:path");
10
+
11
+ const { Workspace } = require("wative-core");
12
+
13
+ test("logger — write at every level + change minLevel at runtime", async () => {
14
+ const root = mkdtempSync(join(tmpdir(), "wative-logger-write-"));
15
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
16
+
17
+ ws.logger.trace("trace event", { phase: "init" });
18
+ ws.logger.debug("debug event", { phase: "init" });
19
+ ws.logger.info("info event", { phase: "init" });
20
+ ws.logger.warn("warn event", { phase: "init" });
21
+ ws.logger.error("error event", { phase: "init" });
22
+
23
+ await ws.logger.setLevel("warn");
24
+ assert.strictEqual(ws.logger.config.minLevel, "warn");
25
+
26
+ ws.logger.warn("still works");
27
+ ws.logger.info("filtered out below minLevel");
28
+
29
+ await ws.lock();
30
+ });
31
+
32
+ test("logger — config persists across reopen", async () => {
33
+ const root = mkdtempSync(join(tmpdir(), "wative-logger-persist-"));
34
+ let ws = await Workspace.open({ path: root, password: "wsp-pwd" });
35
+
36
+ await ws.logger.setLevel("error");
37
+ assert.strictEqual(ws.logger.config.minLevel, "error");
38
+
39
+ await ws.lock();
40
+ ws = await Workspace.open({ path: root, password: "wsp-pwd" });
41
+ assert.strictEqual(ws.logger.config.minLevel, "error");
42
+
43
+ await ws.lock();
44
+ });
@@ -0,0 +1,43 @@
1
+ // 13 — Workspace config public API. Consumers should never need Provider
2
+ // record access to update business-wide settings.
3
+
4
+ const test = require("node:test");
5
+ const assert = require("node:assert");
6
+ const { mkdtempSync } = require("node:fs");
7
+ const { tmpdir } = require("node:os");
8
+ const { join } = require("node:path");
9
+
10
+ const { Workspace } = require("wative-core");
11
+
12
+ test("workspace config — setBusinessTimezone/getConfig work from release dist", async () => {
13
+ const root = mkdtempSync(join(tmpdir(), "wative-config-"));
14
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
15
+
16
+ await ws.setBusinessTimezone("UTC");
17
+ assert.deepStrictEqual(await ws.getConfig(), { businessTimezone: "UTC" });
18
+
19
+ await ws.setBusinessTimezone("Etc/UTC");
20
+ assert.deepStrictEqual(await ws.getConfig(), { businessTimezone: "Etc/UTC" });
21
+
22
+ await assert.rejects(ws.setBusinessTimezone("Not/AZone"), (err) => (
23
+ err.code === "PARAMETER_ERROR"
24
+ && /INVALID_BUSINESS_TIMEZONE/.test(err.message)
25
+ && !err.message.includes("Not/AZone")
26
+ ));
27
+
28
+ // `config` hands back a frozen snapshot, not the live object. Assigning to it
29
+ // does not change the workspace — read config through `config` / `getConfig()`
30
+ // and change it through the setters.
31
+ const snapshot = ws.config;
32
+ assert.strictEqual(Object.isFrozen(snapshot), true);
33
+ snapshot.settlementWindow = "ny-close";
34
+ assert.strictEqual(snapshot.settlementWindow, undefined);
35
+
36
+ await ws.setBusinessTimezone("America/New_York");
37
+ assert.deepStrictEqual(await ws.getConfig(), { businessTimezone: "America/New_York" });
38
+
39
+ assert.strictEqual(Object.prototype.hasOwnProperty.call(ws, "_provider"), false);
40
+ assert.strictEqual("_provider" in ws, false);
41
+
42
+ await ws.lock();
43
+ });
@@ -0,0 +1,81 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtemp } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const here = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ async function importDist() {
11
+ // Import by PACKAGE SPECIFIER, not by file path. The root entry is
12
+ // condition-split: a raw path to dist/index.js bypasses the exports map and
13
+ // lands on the browser build, which deliberately has no filesystem backend.
14
+ // "wative-core" resolves through the node condition, which is what an ESM
15
+ // consumer actually gets — and still loads real ESM, so this keeps testing
16
+ // that the dist needs no CJS require shims.
17
+ return import("wative-core");
18
+ }
19
+
20
+ const PASSWORD = "wsp-pwd";
21
+ const MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
22
+ const EVM_PK = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
23
+
24
+ test("ESM dist consumer creates HD and PK accounts without global require shims", async () => {
25
+ const { Workspace } = await importDist();
26
+ assert.equal(globalThis.require, undefined);
27
+ const root = await mkdtemp(path.join(tmpdir(), "wative-core-esm-runtime-"));
28
+ const ws = await Workspace.open({ path: root, password: PASSWORD });
29
+
30
+ const hd = await ws.accounts.create("ESM HD", PASSWORD, MNEMONIC, undefined, { kind: "HD" });
31
+ const pk = await ws.accounts.create("ESM PK", PASSWORD, EVM_PK, undefined, { kind: "PK" });
32
+
33
+ assert.equal(hd.organizationType, "HD");
34
+ assert.equal(pk.organizationType, "PK");
35
+ });
36
+
37
+ test("ESM dist consumer signs EVM transactions without global require shims", async () => {
38
+ const { Workspace } = await importDist();
39
+ assert.equal(globalThis.require, undefined);
40
+ const root = await mkdtemp(path.join(tmpdir(), "wative-core-esm-tx-sign-"));
41
+ const ws = await Workspace.open({ path: root, password: PASSWORD });
42
+ const account = await ws.accounts.create("ESM TX PK", PASSWORD, EVM_PK, undefined, { kind: "PK" });
43
+ await account.tryUnlock(PASSWORD);
44
+ const address = account.wallets[0].addresses.find((candidate) => candidate.vm === "evm");
45
+
46
+ const tx = address.buildTransaction({
47
+ to: "0x000000000000000000000000000000000000dEaD",
48
+ value: "0",
49
+ nonce: 0,
50
+ chainId: 1,
51
+ gasLimit: "21000",
52
+ maxFeePerGas: "2000000000",
53
+ maxPriorityFeePerGas: "1000000000",
54
+ });
55
+ await tx.sign();
56
+
57
+ assert.match(tx.rawTransaction, /^0x[0-9a-fA-F]+$/);
58
+ });
59
+
60
+ test("ESM dist consumer signs SVM transactions without global require shims", async () => {
61
+ // Regression: the SVM signing path used require() for @solana/web3.js, bs58
62
+ // and tweetnacl, which throws "Dynamic require ... is not supported" under
63
+ // native ESM. These are now static imports like web3 on the EVM side.
64
+ const { Workspace } = await importDist();
65
+ assert.equal(globalThis.require, undefined);
66
+ const root = await mkdtemp(path.join(tmpdir(), "wative-core-esm-svm-sign-"));
67
+ const ws = await Workspace.open({ path: root, password: PASSWORD });
68
+ const account = await ws.accounts.create("ESM SVM HD", PASSWORD, MNEMONIC, undefined, { kind: "HD" });
69
+ await account.tryUnlock(PASSWORD);
70
+ const svm = account.wallets[0].addresses.find((candidate) => candidate.vm === "svm");
71
+ assert.ok(svm, "wallet exposes an SVM address");
72
+
73
+ const tx = svm.buildTransaction({
74
+ recipient: "11111111111111111111111111111111",
75
+ amount: "1000000",
76
+ recentBlockhash: "11111111111111111111111111111111",
77
+ });
78
+ await tx.sign();
79
+ assert.equal(tx.status, "signed");
80
+ assert.ok(typeof tx.hash === "string" && tx.hash.length > 0);
81
+ });
@@ -0,0 +1,76 @@
1
+ // 15 — Package resolution. Every other example imports `../dist/index.cjs`
2
+ // directly, which bypasses the `exports` map in package.json entirely. This
3
+ // file is the only one that resolves the package the way a real consumer does:
4
+ // by its bare name. It therefore covers the export conditions (`import` vs
5
+ // `require`), the declared subpaths, and the parity between the two builds.
6
+ //
7
+ // It works without installing anything because Node resolves a package's own
8
+ // name from inside that package ("self-reference"), which requires `name` and
9
+ // `exports` to be present — so a broken exports map fails this file outright.
10
+
11
+ const test = require("node:test");
12
+ const assert = require("node:assert");
13
+
14
+ const SUBPATHS = ["wative-core", "wative-core/artifacts/evm", "wative-core/artifacts/svm"];
15
+
16
+ test("every declared subpath resolves by bare name under require(), to the CJS build", () => {
17
+ for (const specifier of SUBPATHS) {
18
+ const resolved = require.resolve(specifier);
19
+ assert.ok(
20
+ resolved.endsWith(".cjs"),
21
+ `${specifier} should resolve to the CJS build via the "require" condition, got ${resolved}`,
22
+ );
23
+ const mod = require(specifier);
24
+ assert.ok(mod && typeof mod === "object", `${specifier} exported nothing`);
25
+ }
26
+ });
27
+
28
+ test("bare-name require() exposes the documented entry points", () => {
29
+ const core = require("wative-core");
30
+ for (const name of ["Workspace", "Network", "Asset", "Provider", "WativeError"]) {
31
+ assert.ok(core[name], `wative-core should export ${name}`);
32
+ }
33
+ const evm = require("wative-core/artifacts/evm");
34
+ assert.strictEqual(typeof evm.ERC20.encode, "function");
35
+ const svm = require("wative-core/artifacts/svm");
36
+ assert.strictEqual(typeof svm.TokenProgram.encodeInstruction, "function");
37
+ });
38
+
39
+ test("every declared subpath resolves by bare name under import(), to the ESM build", async () => {
40
+ for (const specifier of SUBPATHS) {
41
+ const mod = await import(specifier);
42
+ assert.ok(mod && typeof mod === "object", `${specifier} exported nothing via import()`);
43
+ }
44
+ });
45
+
46
+ test("the CJS and ESM builds expose an identical export surface", async () => {
47
+ const cjs = require("wative-core");
48
+ const esm = await import("wative-core");
49
+
50
+ const cjsKeys = Object.keys(cjs).sort();
51
+ // The ESM namespace carries a `default` interop key that the CJS object does not.
52
+ const esmKeys = Object.keys(esm).filter((k) => k !== "default").sort();
53
+
54
+ assert.deepStrictEqual(
55
+ esmKeys,
56
+ cjsKeys,
57
+ "the two builds must expose the same named exports, or consumers get different APIs depending on how they import",
58
+ );
59
+ assert.ok(cjsKeys.length > 0, "expected a non-empty export surface");
60
+ });
61
+
62
+ test("the package advertises a Node version that the current runtime satisfies", () => {
63
+ const { engines } = require("wative-core/package.json");
64
+ assert.ok(engines && engines.node, "package.json should declare engines.node");
65
+
66
+ const required = engines.node.replace(/^[^\d]*/, "").split(".").map(Number);
67
+ const actual = process.versions.node.split(".").map(Number);
68
+ const satisfies =
69
+ actual[0] > required[0] ||
70
+ (actual[0] === required[0] && (actual[1] ?? 0) >= (required[1] ?? 0));
71
+
72
+ assert.ok(
73
+ satisfies,
74
+ `these examples are running on Node ${process.versions.node} but the package requires ${engines.node}`,
75
+ );
76
+ });
@@ -0,0 +1,141 @@
1
+ // 16 — Known-answer vectors. The other examples check that operations succeed
2
+ // and that results look plausible ("is a string", "starts with 0x"). That kind
3
+ // of assertion still passes if a key is derived from the wrong path, or if a
4
+ // signature is produced by the wrong key — the two failures most likely to lose
5
+ // someone's funds.
6
+ //
7
+ // This file pins exact expected values instead. Every EVM value below was taken
8
+ // from the published BIP-39 / BIP-44 test vectors for the canonical "abandon …
9
+ // about" mnemonic, not from this library's own output, so the assertions are
10
+ // independent ground truth rather than a recording of current behaviour.
11
+
12
+ const test = require("node:test");
13
+ const assert = require("node:assert");
14
+ const { mkdtempSync } = require("node:fs");
15
+ const { tmpdir } = require("node:os");
16
+ const { join } = require("node:path");
17
+
18
+ const { Workspace } = require("wative-core");
19
+
20
+ // The canonical all-zero BIP-39 entropy phrase. Publicly known — never fund it.
21
+ const MNEMONIC =
22
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
23
+
24
+ // m/44'/60'/0'/0/{0,1,2} for the mnemonic above. These are the standard vectors
25
+ // reproduced by every BIP-44 implementation; a mismatch means the derivation
26
+ // path or the seed derivation changed.
27
+ const EVM_ADDRESSES = [
28
+ "0x9858EfFD232B4033E47d90003D41EC34EcaEda94",
29
+ "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0",
30
+ "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A",
31
+ ];
32
+
33
+ // Solana derivation for the same mnemonic. Pinned as a regression vector: these
34
+ // are this library's values, so they guard against silent drift in the SVM
35
+ // derivation path rather than asserting an external standard.
36
+ const SVM_ADDRESSES = [
37
+ "HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk",
38
+ "Hh8QwFUA6MtVu1qAoq12ucvFHNwCcVTV7hpWjeY1Hztb",
39
+ "7WktogJEd2wQ9eH2oWusmcoFTgeYi6rS632UviTBJ2jm",
40
+ ];
41
+
42
+ // EIP-191 personal_sign of "hello" by the first EVM address. ECDSA here is
43
+ // deterministic (RFC 6979), so the signature is a fixed value, and it verifies
44
+ // externally as recovering to EVM_ADDRESSES[0].
45
+ const EVM_SIGNATURE_HELLO =
46
+ "0x22f6b9cd7ff4f321e11181c4fe64adeea9469908fb514fbb6001fe022002dfda" +
47
+ "1f4ec9ea436bad14a7823806487d3aeb39b22e2556590922d6a8308971a17e991c";
48
+
49
+ // Well-known test private key and the address it must produce.
50
+ const EVM_PRIVATE_KEY = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
51
+ const EVM_PRIVATE_KEY_ADDRESS = "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1";
52
+
53
+ function newWorkspaceRoot(tag) {
54
+ return mkdtempSync(join(tmpdir(), `wative-kat-${tag}-`));
55
+ }
56
+
57
+ function addressFor(wallet, vm) {
58
+ const found = wallet.addresses.find((a) => a.vm === vm);
59
+ assert.ok(found, `wallet has no ${vm} address`);
60
+ return found;
61
+ }
62
+
63
+ test("HD derivation reproduces the published BIP-44 vectors for EVM", async () => {
64
+ const ws = await Workspace.open({ path: newWorkspaceRoot("hd"), password: "wsp-pwd" });
65
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
66
+ await acc.deriveWallets(EVM_ADDRESSES.length);
67
+
68
+ EVM_ADDRESSES.forEach((expected, i) => {
69
+ assert.strictEqual(
70
+ addressFor(acc.wallets[i], "evm").publicKey,
71
+ expected,
72
+ `m/44'/60'/0'/0/${i} derived the wrong address`,
73
+ );
74
+ });
75
+
76
+ await ws.lock();
77
+ });
78
+
79
+ test("HD derivation stays stable for Solana", async () => {
80
+ const ws = await Workspace.open({ path: newWorkspaceRoot("svm"), password: "wsp-pwd" });
81
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
82
+ await acc.deriveWallets(SVM_ADDRESSES.length);
83
+
84
+ SVM_ADDRESSES.forEach((expected, i) => {
85
+ assert.strictEqual(
86
+ addressFor(acc.wallets[i], "svm").publicKey,
87
+ expected,
88
+ `Solana derivation slot ${i} changed`,
89
+ );
90
+ });
91
+
92
+ await ws.lock();
93
+ });
94
+
95
+ test("an imported private key produces its known address", async () => {
96
+ const ws = await Workspace.open({ path: newWorkspaceRoot("pk"), password: "wsp-pwd" });
97
+ const acc = await ws.accounts.create("HotWallet", "wsp-pwd", EVM_PRIVATE_KEY, undefined, {
98
+ kind: "PK",
99
+ });
100
+
101
+ assert.strictEqual(
102
+ addressFor(acc.wallets[0], "evm").publicKey,
103
+ EVM_PRIVATE_KEY_ADDRESS,
104
+ "the private key produced an address it does not own",
105
+ );
106
+
107
+ await ws.lock();
108
+ });
109
+
110
+ test("personal-message signing produces the exact expected signature", async () => {
111
+ const ws = await Workspace.open({ path: newWorkspaceRoot("sign"), password: "wsp-pwd" });
112
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
113
+
114
+ const signature = addressFor(acc.wallets[0], "evm").signMessage("hello");
115
+
116
+ assert.strictEqual(
117
+ signature,
118
+ EVM_SIGNATURE_HELLO,
119
+ "signing 'hello' no longer yields the signature that recovers to " + EVM_ADDRESSES[0],
120
+ );
121
+
122
+ await ws.lock();
123
+ });
124
+
125
+ test("signatures are bound to the message and to the signing key", async () => {
126
+ const ws = await Workspace.open({ path: newWorkspaceRoot("bind"), password: "wsp-pwd" });
127
+ const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
128
+ await acc.deriveWallets(2);
129
+
130
+ const first = addressFor(acc.wallets[0], "evm");
131
+ const second = addressFor(acc.wallets[1], "evm");
132
+
133
+ // A different message under the same key must not reuse the signature.
134
+ assert.notStrictEqual(first.signMessage("hello!"), EVM_SIGNATURE_HELLO);
135
+ // The same message under a different key must not either.
136
+ assert.notStrictEqual(second.signMessage("hello"), EVM_SIGNATURE_HELLO);
137
+ // And signing is repeatable for a given key and message.
138
+ assert.strictEqual(first.signMessage("hello"), EVM_SIGNATURE_HELLO);
139
+
140
+ await ws.lock();
141
+ });