wative-core 1.2.1 → 2.2.0

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 +136 -0
  2. package/LICENSE +55 -0
  3. package/README.md +496 -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/package.json +85 -37
  35. package/tests/01-quick-start.test.cjs +37 -0
  36. package/tests/02-hd-account.test.cjs +71 -0
  37. package/tests/03-pk-account.test.cjs +59 -0
  38. package/tests/04-network-management.test.cjs +94 -0
  39. package/tests/05-asset-management.test.cjs +117 -0
  40. package/tests/06-address-signing.test.cjs +55 -0
  41. package/tests/07-custom-provider.test.cjs +164 -0
  42. package/tests/08-persistence.test.cjs +105 -0
  43. package/tests/09-workspace-filter.test.cjs +84 -0
  44. package/tests/10-default-network.test.cjs +70 -0
  45. package/tests/11-subpath-imports.test.cjs +21 -0
  46. package/tests/12-logger.test.cjs +44 -0
  47. package/tests/13-workspace-config.test.cjs +43 -0
  48. package/tests/14-esm-runtime.test.mjs +81 -0
  49. package/tests/15-package-resolution.test.cjs +76 -0
  50. package/tests/16-known-answer-vectors.test.cjs +141 -0
  51. package/tests/17-locked-state.test.cjs +162 -0
  52. package/tests/README.md +72 -0
  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,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
+ });
@@ -0,0 +1,162 @@
1
+ // 17 — Locked-state contract. Locking is the library's core safety promise:
2
+ // once locked, nothing that touches key material may succeed. The other
3
+ // examples lock at the end of a test but never assert what locking actually
4
+ // enforces, so a regression that left a decrypted key in memory would not fail
5
+ // any of them.
6
+ //
7
+ // This file pins the contract from the outside: which operations refuse, which
8
+ // error code they refuse with, that they hand back no secret material while
9
+ // refusing, and that unlocking restores exactly the previous behaviour.
10
+
11
+ const test = require("node:test");
12
+ const assert = require("node:assert");
13
+ const { mkdtempSync } = require("node:fs");
14
+ const { tmpdir } = require("node:os");
15
+ const { join } = require("node:path");
16
+
17
+ const { Workspace } = require("wative-core");
18
+
19
+ const MNEMONIC =
20
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
21
+
22
+ // Same vector as 16 — reused here to prove unlock restores identical behaviour.
23
+ const EVM_SIGNATURE_HELLO =
24
+ "0x22f6b9cd7ff4f321e11181c4fe64adeea9469908fb514fbb6001fe022002dfda" +
25
+ "1f4ec9ea436bad14a7823806487d3aeb39b22e2556590922d6a8308971a17e991c";
26
+
27
+ const TYPED_DATA = {
28
+ domain: { name: "Test", version: "1", chainId: 1 },
29
+ types: { Msg: [{ name: "text", type: "string" }] },
30
+ primaryType: "Msg",
31
+ message: { text: "hello" },
32
+ };
33
+
34
+ async function freshAccount(tag) {
35
+ const ws = await Workspace.open({
36
+ path: mkdtempSync(join(tmpdir(), `wative-locked-${tag}-`)),
37
+ password: "wsp-pwd",
38
+ });
39
+ const account = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
40
+ const wallet = account.wallets[0];
41
+ const address = wallet.addresses.find((a) => a.vm === "evm");
42
+ return { ws, account, wallet, address };
43
+ }
44
+
45
+ /**
46
+ * Assert an operation refuses with the given code.
47
+ *
48
+ * The API deliberately mixes shapes: signing and key export are synchronous
49
+ * (they only touch already-decrypted material), while anything that persists or
50
+ * decrypts is async. Wrapping the call in an async function normalises a
51
+ * synchronous throw and a rejected promise into one thing, so these assertions
52
+ * describe the security contract rather than the call shape.
53
+ */
54
+ async function refuses(code, label, operation) {
55
+ await assert.rejects(
56
+ async () => operation(),
57
+ (err) => {
58
+ assert.strictEqual(err.code, code, `${label} should refuse with ${code}, got ${err.code}`);
59
+ return true;
60
+ },
61
+ `${label} succeeded while locked`,
62
+ );
63
+ }
64
+
65
+ test("a locked account refuses every operation that needs key material", async () => {
66
+ const { ws, account, wallet, address } = await freshAccount("account");
67
+
68
+ await account.lock();
69
+ assert.strictEqual(account.locked, true, "account should report itself locked");
70
+
71
+ await refuses("ACCOUNT_LOCKED", "dumpMnemonic", () => account.dumpMnemonic());
72
+ await refuses("ACCOUNT_LOCKED", "dumpPrivateKey", () => wallet.dumpPrivateKey("evm"));
73
+ await refuses("ACCOUNT_LOCKED", "signMessage", () => address.signMessage("hello"));
74
+ await refuses("ACCOUNT_LOCKED", "signTypedData", () => address.signTypedData(TYPED_DATA));
75
+ await refuses("ACCOUNT_LOCKED", "deriveWallets", () => account.deriveWallets(1));
76
+
77
+ // Refusing with the correct password supplied is the point: possession of the
78
+ // password must not be enough to bypass the lock.
79
+ await ws.unlock("wsp-pwd").catch(() => {});
80
+ });
81
+
82
+ test("locking the workspace blocks workspace-level mutation too", async () => {
83
+ const { ws, address } = await freshAccount("workspace");
84
+
85
+ await ws.lock();
86
+ assert.strictEqual(ws.locked, true, "workspace should report itself locked");
87
+
88
+ await refuses("WORKSPACE_LOCKED", "accounts.create", () =>
89
+ ws.accounts.create("Other", "wsp-pwd", MNEMONIC),
90
+ );
91
+ // Accounts held from before the lock must not keep signing.
92
+ await refuses("ACCOUNT_LOCKED", "signMessage on a stale handle", () =>
93
+ address.signMessage("hello"),
94
+ );
95
+ });
96
+
97
+ test("a locked account exposes no plaintext secret through its own surface", async () => {
98
+ const { ws, account } = await freshAccount("leak");
99
+
100
+ const beforeLock = account.dumpMnemonic();
101
+ assert.strictEqual(beforeLock, MNEMONIC, "sanity: the mnemonic is retrievable while unlocked");
102
+
103
+ await account.lock();
104
+
105
+ // Walk everything reachable from the locked account — the object graph is
106
+ // cyclic (workspace ⇄ account ⇄ wallet), so track visited objects.
107
+ const strings = [];
108
+ const seen = new Set();
109
+ const walk = (value) => {
110
+ if (typeof value === "string") return strings.push(value);
111
+ if (value === null || typeof value !== "object" || seen.has(value)) return;
112
+ seen.add(value);
113
+ for (const key of Object.keys(value)) walk(value[key]);
114
+ };
115
+ walk(account);
116
+
117
+ const leak = strings.find((s) => s.includes(MNEMONIC) || s.includes("abandon abandon"));
118
+ assert.strictEqual(
119
+ leak,
120
+ undefined,
121
+ "a locked account still holds mnemonic material in the clear",
122
+ );
123
+
124
+ await ws.lock();
125
+ });
126
+
127
+ test("unlocking restores exactly the previous behaviour", async () => {
128
+ const { ws, account, address } = await freshAccount("unlock");
129
+
130
+ const before = address.signMessage("hello");
131
+ assert.strictEqual(before, EVM_SIGNATURE_HELLO, "sanity: known signature before locking");
132
+
133
+ await account.lock();
134
+ await refuses("ACCOUNT_LOCKED", "signMessage", () => address.signMessage("hello"));
135
+
136
+ await account.tryUnlock("wsp-pwd");
137
+ assert.strictEqual(account.locked, false, "account should report itself unlocked");
138
+
139
+ assert.strictEqual(
140
+ address.signMessage("hello"),
141
+ EVM_SIGNATURE_HELLO,
142
+ "signing after unlock must reproduce the same signature as before locking",
143
+ );
144
+
145
+ await ws.lock();
146
+ });
147
+
148
+ test("unlocking with the wrong password fails and leaves the account locked", async () => {
149
+ const { ws, account, address } = await freshAccount("badpwd");
150
+
151
+ await account.lock();
152
+ await refuses("BAD_PASSWORD", "tryUnlock with a wrong password", () =>
153
+ account.tryUnlock("not-the-password"),
154
+ );
155
+
156
+ assert.strictEqual(account.locked, true, "a failed unlock must leave the account locked");
157
+ await refuses("ACCOUNT_LOCKED", "signMessage after a failed unlock", () =>
158
+ address.signMessage("hello"),
159
+ );
160
+
161
+ await ws.lock();
162
+ });
@@ -0,0 +1,72 @@
1
+ # Examples & integration tests
2
+
3
+ Runnable JavaScript demos covering every domain in the library. Each file is both:
4
+
5
+ - **A use-case example** — read it top-to-bottom for a self-contained walkthrough.
6
+ - **An integration test** — run it via `node:test` to verify the library works in your environment.
7
+
8
+ Zero external dependencies beyond the package itself. Uses Node's built-in test runner (`node --test`), available since Node 18.
9
+
10
+ Most files are written as CommonJS (`.test.cjs`) because the CJS build is the most-tested path. The ESM entry is exercised too — [14-esm-runtime.test.mjs](14-esm-runtime.test.mjs) runs the same flows through `import`, and [15-package-resolution.test.cjs](15-package-resolution.test.cjs) checks that both builds resolve by bare package name and expose the same API.
11
+
12
+ ## Run them
13
+
14
+ From the repo root:
15
+
16
+ ```bash
17
+ node --test tests/
18
+ ```
19
+
20
+ Or run a single file:
21
+
22
+ ```bash
23
+ node --test tests/01-quick-start.test.cjs
24
+ ```
25
+
26
+ If you are reading this inside `node_modules` after installing the package, copy the folder out before running `--test` — Node's test runner skips anything under `node_modules`. A single file can still be run in place with plain `node <file>`.
27
+
28
+ All tests work fully offline — no RPC calls, no real networks. Each test creates its own temporary workspace under your OS tmp dir; nothing persists between runs.
29
+
30
+ ## What each example demonstrates
31
+
32
+ | File | Demonstrates |
33
+ |---|---|
34
+ | [01-quick-start.test.cjs](01-quick-start.test.cjs) | Open a workspace, create an HD account, derive 5 wallets, inspect EVM + Solana addresses, lock. |
35
+ | [02-hd-account.test.cjs](02-hd-account.test.cjs) | HD-specific flows — deriving / slicing wallets, dumping the mnemonic, account-level passwords with `resetPassword`, round-trip across lock + reopen. |
36
+ | [03-pk-account.test.cjs](03-pk-account.test.cjs) | PK accounts — creating from a private key, importing more keys (mix EVM + Solana under one account), duplicate-import rejection, dropping wallets. |
37
+ | [04-network-management.test.cjs](04-network-management.test.cjs) | The 10 pre-loaded networks, adding a brand-new network, overriding a pre-loaded RPC URL via `update()`, dropping user networks, the built-in protection guard. |
38
+ | [05-asset-management.test.cjs](05-asset-management.test.cjs) | The 25 pre-loaded tokens, adding a custom user token, id collision rejection, contract-address collision rejection, dropping user tokens, `workspace.filter(q, "Asset")` search. |
39
+ | [06-address-signing.test.cjs](06-address-signing.test.cjs) | Personal-message signing on EVM and Solana, building a transaction fully offline (no RPC). |
40
+ | [07-custom-provider.test.cjs](07-custom-provider.test.cjs) | Plugging your own storage backend by extending the `Provider` base class. The example uses an in-memory key-value store but the same shape works for databases, cloud storage, and browser storage like IndexedDB. |
41
+ | [08-persistence.test.cjs](08-persistence.test.cjs) | Round-trip across `lock()` + reopen — multiple accounts, user networks, custom assets, and logger configuration. |
42
+ | [09-workspace-filter.test.cjs](09-workspace-filter.test.cjs) | `workspace.filter(query, objective)` for all four objectives — Account (by name or slug), Wallet (by id or tag), Address (by public key), Asset (by symbol or contract address). |
43
+ | [10-default-network.test.cjs](10-default-network.test.cjs) | `account.setDefaultNetwork(value)` accepts a slug, a chain id, a hex chain id, or a `Network` instance. The choice persists across reopen. Unknown networks are rejected. |
44
+ | [11-subpath-imports.test.cjs](11-subpath-imports.test.cjs) | Heavier chain helpers via `wative-core/artifacts/evm` (`ERC20`) and `wative-core/artifacts/svm` (`TokenProgram`, `Token2022Program`). |
45
+ | [12-logger.test.cjs](12-logger.test.cjs) | The workspace logger — six log levels, runtime `setLevel`, and config persistence across reopen. |
46
+ | [13-workspace-config.test.cjs](13-workspace-config.test.cjs) | Workspace config public API — `setBusinessTimezone()` / `getConfig()` from the release dist without provider record access. |
47
+ | [14-esm-runtime.test.mjs](14-esm-runtime.test.mjs) | The same core flows driven through the ESM build via `import`, proving the package works from an ESM-flagged project. |
48
+ | [15-package-resolution.test.cjs](15-package-resolution.test.cjs) | Resolution by bare package name — every declared subpath under both `require()` and `import()`, identical export surfaces from the two builds, and the declared Node version. |
49
+ | [16-known-answer-vectors.test.cjs](16-known-answer-vectors.test.cjs) | Exact expected values — published BIP-44 addresses for the canonical mnemonic, a known address from an imported private key, and a fixed EIP-191 signature. Catches derivation from the wrong path or signing with the wrong key. |
50
+ | [17-locked-state.test.cjs](17-locked-state.test.cjs) | What locking enforces — which operations refuse and with which error code, that a locked account holds no mnemonic in the clear, and that unlocking restores identical behaviour. |
51
+
52
+ ## Adapting for your project
53
+
54
+ In each file, replace:
55
+
56
+ ```js
57
+ const { Workspace } = require("../dist/index.cjs");
58
+ ```
59
+
60
+ with:
61
+
62
+ ```js
63
+ const { Workspace } = require("wative-core");
64
+ ```
65
+
66
+ once you've done `pnpm add wative-core` (or `npm i wative-core`) in your own project. Or, if your project is ESM-flagged, use `import` instead — both syntax styles resolve to the right entry via the package's `exports` map.
67
+
68
+ ## Notes
69
+
70
+ - **No real money.** The mnemonics and private keys used in these tests are well-known testing values. Never fund them.
71
+ - **No network calls.** Tests sign offline only. To actually broadcast a transaction, configure a real RPC URL on the network and call `address.sendTransaction(tx)` — see the README for the full lifecycle.
72
+ - **Custom Provider example** uses a deliberately simple cipher (XOR). Real Providers should seal data with an authenticated cipher — see the bundled `HybridProvider` for a reference implementation.
package/lib/account.d.ts DELETED
@@ -1,69 +0,0 @@
1
- import { Result } from "./utils";
2
- export declare class Account {
3
- private setting_path;
4
- private account_labels;
5
- private account_passwords;
6
- private account_ciphertexts;
7
- private disable_address;
8
- private account_signatures;
9
- constructor(setting_path: string, account_labels: string[], account_passwords?: string[], disable_address?: boolean);
10
- isLogin(account_label: string): boolean;
11
- reloadAccount(): void;
12
- getAccountLabel(account_address: string): {
13
- account_address: any;
14
- account_label: any;
15
- } | null;
16
- login(account_label: string, password: string): {
17
- status: boolean;
18
- output: string;
19
- };
20
- getAccounts(account_chain_type: string): string[];
21
- checkAccountLabelIsExist(account_label: string): boolean;
22
- checkAccountIsExist(account_address: string): boolean;
23
- checkPassword(account_label: string, password: string): boolean;
24
- showPrivateKey(account_address: string): string;
25
- getAccountChainType(account_address: string): string;
26
- generatePKAccount(account_label: string, pk: string, password: string, chain_id: string, generate_account_label: boolean): {
27
- status: boolean;
28
- output: any;
29
- };
30
- generatePPAccount(account_label: string, pp: string, password: string): {
31
- status: boolean;
32
- output: any;
33
- };
34
- generateSubAccount(account_label: string, account_index: number): {
35
- status: boolean;
36
- output: any;
37
- };
38
- resetPassword(account_label: string, new_password: string): {
39
- status: boolean;
40
- output: string;
41
- };
42
- signTransaction(account_address: string, transaction: any, rpc_url: string): Promise<Result> | {
43
- status: boolean;
44
- output: string;
45
- };
46
- signTransactionOffline(account_address: string, transaction: any, chainId: number): Promise<Result>;
47
- sendSignedTransaction(account_address: string, signed_transaction: any, rpc_url: string): Promise<Result> | {
48
- status: boolean;
49
- output: string;
50
- };
51
- signMessage(account_address: string, message: string): {
52
- status: boolean;
53
- output: string;
54
- };
55
- simulateTransaction(account_address: string, rpc_url: string, transaction: any): Promise<Result>;
56
- private signEvmTransaction;
57
- private sendEvmTransaction;
58
- private signSolanaTransaction;
59
- private sendSolanaTransaction;
60
- private signEvmMessage;
61
- private signSolanaMessage;
62
- checkIsDisableAddress(disabled_slots: any, account_index: number): boolean;
63
- private reload;
64
- private getAccountChecksumAddress;
65
- private getAccountAddressByPrivateKey;
66
- private getNetworkTypeByChainId;
67
- private getSubAccount;
68
- private getAccountSignature;
69
- }
package/lib/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { Account } from "./account";
2
- export declare class WativeCore {
3
- static version: any;
4
- account: Account;
5
- constructor(setting_path: string, account_labels: string[], account_passwords?: string[], disable_address?: boolean);
6
- }
package/lib/index.esm.js DELETED
@@ -1 +0,0 @@
1
- import*as e from"fs";import*as t from"path";import a from"web3";import*as s from"tweetnacl";import*as r from"bs58";import*as o from"bip39";import{Connection as n,Keypair as i}from"@solana/web3.js";import{Wallet as c}from"@coral-xyz/anchor";import*as l from"crypto-js";import{hdkey as u}from"ethereumjs-wallet";import{HDKey as d}from"micro-ed25519-hdkey";const h=async(e,t)=>{let a;try{a={status:!0,output:await e(...t)}}catch(e){a={status:!1,output:e.toString()}}return a};class p{constructor(e){this.web3=new a(e)}async getEvmGasPrice(){return await h(this.web3.eth.getGasPrice,[])}async getEvmGasLimit(e,t){return await h(this.web3.eth.estimateGas,[e,t])}}const m=t=>(t=t.trim(),e.existsSync(t)),g=(e,a)=>{const s=a.length;if(0===s)return!0;for(let r=0;r<s;r++){const s=t.join(e,`accounts/${a[r]}.json`);if(!m(s))return!1}return!0},b=e=>e=(e=(e=(e=e.trim()).toLocaleLowerCase()).replace(/\s+/g,"-").trim()).replace(/\-+/g,"-").trim(),f=(e,a)=>{let s=[];const r=t.join(e,"network.json");if(!m(r))throw new Error("network.json not found");let o=(e=>{let t=[];for(let a=0;a<e.length;a++)t.push(b(e[a]));return t})(y(r).accounts);for(let e=0;e<a.length;e++)o.includes(a[e])&&s.push(a[e]);return s},y=t=>{const a=e.readFileSync(t).toString();return JSON.parse(a)},w=(t,a)=>{const s=JSON.stringify(t);e.writeFileSync(a,s)},v=(e,t)=>l.AES.encrypt(e,t).toString(),k=(e,t)=>l.AES.decrypt(e,t).toString(l.enc.Utf8),x=["abandon","ability","able","about","above","absent","absorb","abstract","absurd","abuse","access","accident","account","accuse","achieve","acid","acoustic","acquire","across","act","action","actor","actress","actual","adapt","add","addict","address","adjust","admit","adult","advance","advice","aerobic","affair","afford","afraid","again","age","agent","agree","ahead","aim","air","airport","aisle","alarm","album","alcohol","alert","alien","all","alley","allow","almost","alone","alpha","already","also","alter","always","amateur","amazing","among","amount","amused","analyst","anchor","ancient","anger","angle","angry","animal","ankle","announce","annual","another","answer","antenna","antique","anxiety","any","apart","apology","appear","apple","approve","april","arch","arctic","area","arena","argue","arm","armed","armor","army","around","arrange","arrest","arrive","arrow","art","artefact","artist","artwork","ask","aspect","assault","asset","assist","assume","asthma","athlete","atom","attack","attend","attitude","attract","auction","audit","august","aunt","author","auto","autumn","average","avocado","avoid","awake","aware","away","awesome","awful","awkward","axis","baby","bachelor","bacon","badge","bag","balance","balcony","ball","bamboo","banana","banner","bar","barely","bargain","barrel","base","basic","basket","battle","beach","bean","beauty","because","become","beef","before","begin","behave","behind","believe","below","belt","bench","benefit","best","betray","better","between","beyond","bicycle","bid","bike","bind","biology","bird","birth","bitter","black","blade","blame","blanket","blast","bleak","bless","blind","blood","blossom","blouse","blue","blur","blush","board","boat","body","boil","bomb","bone","bonus","book","boost","border","boring","borrow","boss","bottom","bounce","box","boy","bracket","brain","brand","brass","brave","bread","breeze","brick","bridge","brief","bright","bring","brisk","broccoli","broken","bronze","broom","brother","brown","brush","bubble","buddy","budget","buffalo","build","bulb","bulk","bullet","bundle","bunker","burden","burger","burst","bus","business","busy","butter","buyer","buzz","cabbage","cabin","cable","cactus","cage","cake","call","calm","camera","camp","can","canal","cancel","candy","cannon","canoe","canvas","canyon","capable","capital","captain","car","carbon","card","cargo","carpet","carry","cart","case","cash","casino","castle","casual","cat","catalog","catch","category","cattle","caught","cause","caution","cave","ceiling","celery","cement","census","century","cereal","certain","chair","chalk","champion","change","chaos","chapter","charge","chase","chat","cheap","check","cheese","chef","cherry","chest","chicken","chief","child","chimney","choice","choose","chronic","chuckle","chunk","churn","cigar","cinnamon","circle","citizen","city","civil","claim","clap","clarify","claw","clay","clean","clerk","clever","click","client","cliff","climb","clinic","clip","clock","clog","close","cloth","cloud","clown","club","clump","cluster","clutch","coach","coast","coconut","code","coffee","coil","coin","collect","color","column","combine","come","comfort","comic","common","company","concert","conduct","confirm","congress","connect","consider","control","convince","cook","cool","copper","copy","coral","core","corn","correct","cost","cotton","couch","country","couple","course","cousin","cover","coyote","crack","cradle","craft","cram","crane","crash","crater","crawl","crazy","cream","credit","creek","crew","cricket","crime","crisp","critic","crop","cross","crouch","crowd","crucial","cruel","cruise","crumble","crunch","crush","cry","crystal","cube","culture","cup","cupboard","curious","current","curtain","curve","cushion","custom","cute","cycle","dad","damage","damp","dance","danger","daring","dash","daughter","dawn","day","deal","debate","debris","decade","december","decide","decline","decorate","decrease","deer","defense","define","defy","degree","delay","deliver","demand","demise","denial","dentist","deny","depart","depend","deposit","depth","deputy","derive","describe","desert","design","desk","despair","destroy","detail","detect","develop","device","devote","diagram","dial","diamond","diary","dice","diesel","diet","differ","digital","dignity","dilemma","dinner","dinosaur","direct","dirt","disagree","discover","disease","dish","dismiss","disorder","display","distance","divert","divide","divorce","dizzy","doctor","document","dog","doll","dolphin","domain","donate","donkey","donor","door","dose","double","dove","draft","dragon","drama","drastic","draw","dream","dress","drift","drill","drink","drip","drive","drop","drum","dry","duck","dumb","dune","during","dust","dutch","duty","dwarf","dynamic","eager","eagle","early","earn","earth","easily","east","easy","echo","ecology","economy","edge","edit","educate","effort","egg","eight","either","elbow","elder","electric","elegant","element","elephant","elevator","elite","else","embark","embody","embrace","emerge","emotion","employ","empower","empty","enable","enact","end","endless","endorse","enemy","energy","enforce","engage","engine","enhance","enjoy","enlist","enough","enrich","enroll","ensure","enter","entire","entry","envelope","episode","equal","equip","era","erase","erode","erosion","error","erupt","escape","essay","essence","estate","eternal","ethics","evidence","evil","evoke","evolve","exact","example","excess","exchange","excite","exclude","excuse","execute","exercise","exhaust","exhibit","exile","exist","exit","exotic","expand","expect","expire","explain","expose","express","extend","extra","eye","eyebrow","fabric","face","faculty","fade","faint","faith","fall","false","fame","family","famous","fan","fancy","fantasy","farm","fashion","fat","fatal","father","fatigue","fault","favorite","feature","february","federal","fee","feed","feel","female","fence","festival","fetch","fever","few","fiber","fiction","field","figure","file","film","filter","final","find","fine","finger","finish","fire","firm","first","fiscal","fish","fit","fitness","fix","flag","flame","flash","flat","flavor","flee","flight","flip","float","flock","floor","flower","fluid","flush","fly","foam","focus","fog","foil","fold","follow","food","foot","force","forest","forget","fork","fortune","forum","forward","fossil","foster","found","fox","fragile","frame","frequent","fresh","friend","fringe","frog","front","frost","frown","frozen","fruit","fuel","fun","funny","furnace","fury","future","gadget","gain","galaxy","gallery","game","gap","garage","garbage","garden","garlic","garment","gas","gasp","gate","gather","gauge","gaze","general","genius","genre","gentle","genuine","gesture","ghost","giant","gift","giggle","ginger","giraffe","girl","give","glad","glance","glare","glass","glide","glimpse","globe","gloom","glory","glove","glow","glue","goat","goddess","gold","good","goose","gorilla","gospel","gossip","govern","gown","grab","grace","grain","grant","grape","grass","gravity","great","green","grid","grief","grit","grocery","group","grow","grunt","guard","guess","guide","guilt","guitar","gun","gym","habit","hair","half","hammer","hamster","hand","happy","harbor","hard","harsh","harvest","hat","have","hawk","hazard","head","health","heart","heavy","hedgehog","height","hello","helmet","help","hen","hero","hidden","high","hill","hint","hip","hire","history","hobby","hockey","hold","hole","holiday","hollow","home","honey","hood","hope","horn","horror","horse","hospital","host","hotel","hour","hover","hub","huge","human","humble","humor","hundred","hungry","hunt","hurdle","hurry","hurt","husband","hybrid","ice","icon","idea","identify","idle","ignore","ill","illegal","illness","image","imitate","immense","immune","impact","impose","improve","impulse","inch","include","income","increase","index","indicate","indoor","industry","infant","inflict","inform","inhale","inherit","initial","inject","injury","inmate","inner","innocent","input","inquiry","insane","insect","inside","inspire","install","intact","interest","into","invest","invite","involve","iron","island","isolate","issue","item","ivory","jacket","jaguar","jar","jazz","jealous","jeans","jelly","jewel","job","join","joke","journey","joy","judge","juice","jump","jungle","junior","junk","just","kangaroo","keen","keep","ketchup","key","kick","kid","kidney","kind","kingdom","kiss","kit","kitchen","kite","kitten","kiwi","knee","knife","knock","know","lab","label","labor","ladder","lady","lake","lamp","language","laptop","large","later","latin","laugh","laundry","lava","law","lawn","lawsuit","layer","lazy","leader","leaf","learn","leave","lecture","left","leg","legal","legend","leisure","lemon","lend","length","lens","leopard","lesson","letter","level","liar","liberty","library","license","life","lift","light","like","limb","limit","link","lion","liquid","list","little","live","lizard","load","loan","lobster","local","lock","logic","lonely","long","loop","lottery","loud","lounge","love","loyal","lucky","luggage","lumber","lunar","lunch","luxury","lyrics","machine","mad","magic","magnet","maid","mail","main","major","make","mammal","man","manage","mandate","mango","mansion","manual","maple","marble","march","margin","marine","market","marriage","mask","mass","master","match","material","math","matrix","matter","maximum","maze","meadow","mean","measure","meat","mechanic","medal","media","melody","melt","member","memory","mention","menu","mercy","merge","merit","merry","mesh","message","metal","method","middle","midnight","milk","million","mimic","mind","minimum","minor","minute","miracle","mirror","misery","miss","mistake","mix","mixed","mixture","mobile","model","modify","mom","moment","monitor","monkey","monster","month","moon","moral","more","morning","mosquito","mother","motion","motor","mountain","mouse","move","movie","much","muffin","mule","multiply","muscle","museum","mushroom","music","must","mutual","myself","mystery","myth","naive","name","napkin","narrow","nasty","nation","nature","near","neck","need","negative","neglect","neither","nephew","nerve","nest","net","network","neutral","never","news","next","nice","night","noble","noise","nominee","noodle","normal","north","nose","notable","note","nothing","notice","novel","now","nuclear","number","nurse","nut","oak","obey","object","oblige","obscure","observe","obtain","obvious","occur","ocean","october","odor","off","offer","office","often","oil","okay","old","olive","olympic","omit","once","one","onion","online","only","open","opera","opinion","oppose","option","orange","orbit","orchard","order","ordinary","organ","orient","original","orphan","ostrich","other","outdoor","outer","output","outside","oval","oven","over","own","owner","oxygen","oyster","ozone","pact","paddle","page","pair","palace","palm","panda","panel","panic","panther","paper","parade","parent","park","parrot","party","pass","patch","path","patient","patrol","pattern","pause","pave","payment","peace","peanut","pear","peasant","pelican","pen","penalty","pencil","people","pepper","perfect","permit","person","pet","phone","photo","phrase","physical","piano","picnic","picture","piece","pig","pigeon","pill","pilot","pink","pioneer","pipe","pistol","pitch","pizza","place","planet","plastic","plate","play","please","pledge","pluck","plug","plunge","poem","poet","point","polar","pole","police","pond","pony","pool","popular","portion","position","possible","post","potato","pottery","poverty","powder","power","practice","praise","predict","prefer","prepare","present","pretty","prevent","price","pride","primary","print","priority","prison","private","prize","problem","process","produce","profit","program","project","promote","proof","property","prosper","protect","proud","provide","public","pudding","pull","pulp","pulse","pumpkin","punch","pupil","puppy","purchase","purity","purpose","purse","push","put","puzzle","pyramid","quality","quantum","quarter","question","quick","quit","quiz","quote","rabbit","raccoon","race","rack","radar","radio","rail","rain","raise","rally","ramp","ranch","random","range","rapid","rare","rate","rather","raven","raw","razor","ready","real","reason","rebel","rebuild","recall","receive","recipe","record","recycle","reduce","reflect","reform","refuse","region","regret","regular","reject","relax","release","relief","rely","remain","remember","remind","remove","render","renew","rent","reopen","repair","repeat","replace","report","require","rescue","resemble","resist","resource","response","result","retire","retreat","return","reunion","reveal","review","reward","rhythm","rib","ribbon","rice","rich","ride","ridge","rifle","right","rigid","ring","riot","ripple","risk","ritual","rival","river","road","roast","robot","robust","rocket","romance","roof","rookie","room","rose","rotate","rough","round","route","royal","rubber","rude","rug","rule","run","runway","rural","sad","saddle","sadness","safe","sail","salad","salmon","salon","salt","salute","same","sample","sand","satisfy","satoshi","sauce","sausage","save","say","scale","scan","scare","scatter","scene","scheme","school","science","scissors","scorpion","scout","scrap","screen","script","scrub","sea","search","season","seat","second","secret","section","security","seed","seek","segment","select","sell","seminar","senior","sense","sentence","series","service","session","settle","setup","seven","shadow","shaft","shallow","share","shed","shell","sheriff","shield","shift","shine","ship","shiver","shock","shoe","shoot","shop","short","shoulder","shove","shrimp","shrug","shuffle","shy","sibling","sick","side","siege","sight","sign","silent","silk","silly","silver","similar","simple","since","sing","siren","sister","situate","six","size","skate","sketch","ski","skill","skin","skirt","skull","slab","slam","sleep","slender","slice","slide","slight","slim","slogan","slot","slow","slush","small","smart","smile","smoke","smooth","snack","snake","snap","sniff","snow","soap","soccer","social","sock","soda","soft","solar","soldier","solid","solution","solve","someone","song","soon","sorry","sort","soul","sound","soup","source","south","space","spare","spatial","spawn","speak","special","speed","spell","spend","sphere","spice","spider","spike","spin","spirit","split","spoil","sponsor","spoon","sport","spot","spray","spread","spring","spy","square","squeeze","squirrel","stable","stadium","staff","stage","stairs","stamp","stand","start","state","stay","steak","steel","stem","step","stereo","stick","still","sting","stock","stomach","stone","stool","story","stove","strategy","street","strike","strong","struggle","student","stuff","stumble","style","subject","submit","subway","success","such","sudden","suffer","sugar","suggest","suit","summer","sun","sunny","sunset","super","supply","supreme","sure","surface","surge","surprise","surround","survey","suspect","sustain","swallow","swamp","swap","swarm","swear","sweet","swift","swim","swing","switch","sword","symbol","symptom","syrup","system","table","tackle","tag","tail","talent","talk","tank","tape","target","task","taste","tattoo","taxi","teach","team","tell","ten","tenant","tennis","tent","term","test","text","thank","that","theme","then","theory","there","they","thing","this","thought","three","thrive","throw","thumb","thunder","ticket","tide","tiger","tilt","timber","time","tiny","tip","tired","tissue","title","toast","tobacco","today","toddler","toe","together","toilet","token","tomato","tomorrow","tone","tongue","tonight","tool","tooth","top","topic","topple","torch","tornado","tortoise","toss","total","tourist","toward","tower","town","toy","track","trade","traffic","tragic","train","transfer","trap","trash","travel","tray","treat","tree","trend","trial","tribe","trick","trigger","trim","trip","trophy","trouble","truck","true","truly","trumpet","trust","truth","try","tube","tuition","tumble","tuna","tunnel","turkey","turn","turtle","twelve","twenty","twice","twin","twist","two","type","typical","ugly","umbrella","unable","unaware","uncle","uncover","under","undo","unfair","unfold","unhappy","uniform","unique","unit","universe","unknown","unlock","until","unusual","unveil","update","upgrade","uphold","upon","upper","upset","urban","urge","usage","use","used","useful","useless","usual","utility","vacant","vacuum","vague","valid","valley","valve","van","vanish","vapor","various","vast","vault","vehicle","velvet","vendor","venture","venue","verb","verify","version","very","vessel","veteran","viable","vibrant","vicious","victory","video","view","village","vintage","violin","virtual","virus","visa","visit","visual","vital","vivid","vocal","voice","void","volcano","volume","vote","voyage","wage","wagon","wait","walk","wall","walnut","want","warfare","warm","warrior","wash","wasp","waste","water","wave","way","wealth","weapon","wear","weasel","weather","web","wedding","weekend","weird","welcome","west","wet","whale","what","wheat","wheel","when","where","whip","whisper","wide","width","wife","wild","will","win","window","wine","wing","wink","winner","winter","wire","wisdom","wise","wish","witness","wolf","woman","wonder","wood","wool","word","work","world","worry","worth","wrap","wreck","wrestle","wrist","write","wrong","yard","year","yellow","you","young","youth","zebra","zero","zone","zoo"],_=new a(a.givenProvider);class A{constructor(e,t,a,s){if(this.account_passwords={},this.account_ciphertexts={},this.disable_address=!1,this.account_signatures="",this.setting_path=e,this.account_labels=t,0!==t.length){if(a){if(t.length!==a.length)throw new Error("account_labels should have same length for account_passwords");for(let e=0;e<t.length;e++){if(!this.checkPassword(t[e],a[e].trim()))throw new Error("account_passwords is not correct");this.account_passwords[t[e]]=a[e].trim()}}s&&(this.disable_address=s),this.reload()}}isLogin(e){return e in this.account_passwords}reloadAccount(){this.reload()}getAccountLabel(e){let t=null;for(let a in this.account_ciphertexts)a.toLocaleLowerCase().includes(e.toLocaleLowerCase())&&(t=a);return null===t?null:{account_address:t,account_label:this.account_ciphertexts[t].account_label}}login(e,t){return this.checkPassword(e,t)?(this.account_passwords[e]=t,this.account_labels.push(e),this.reload(),{status:!0,output:"Login success"}):{status:!1,output:"Password is not correct"}}getAccounts(e){let t=this.getAccountSignature();t!==this.account_signatures&&(this.account_signatures=t,this.reload());let a=Object.keys(this.account_ciphertexts),s=[];for(let t=0;t<a.length;t++)this.getAccountChainType(a[t])===e&&s.push(a[t]);return s}checkAccountLabelIsExist(e){return g(this.setting_path,[e])}checkAccountIsExist(e){return(e=this.getAccountChecksumAddress(e))in this.account_ciphertexts||(this.reload(),e in this.account_ciphertexts)}checkPassword(e,a){let s=t.join(this.setting_path,`accounts/${e}.json`);if(!m(s))return!1;let r,o=y(s);r="PK"===o.account_type?{salt:a,...o.data[0].ciphertexts}:{slat:a,data:o.pp_ciphertexts};let n=l.SHA256(JSON.stringify(r)).toString();return o.signature===n}showPrivateKey(e){if(!this.checkAccountIsExist(e))throw new Error("Account is not exist");e=this.getAccountChecksumAddress(e);let t=this.account_ciphertexts[e],a=this.account_passwords[t.account_label];return k(t.data,a)}getAccountChainType(e){return e.startsWith("0x")&&42===e.length?"evm":/^[A-HJ-NP-Za-km-z1-9]*$/.test(e)?"solana":"unknown"}generatePKAccount(e,t,a,s,r){if(r&&this.checkAccountLabelIsExist(e))throw new Error("Account labels already exist");if(!r&&!this.account_passwords[e])throw new Error("Password is not correct");r||(a=this.account_passwords[e]);let o,n=v(t,a);try{o=this.getAccountAddressByPrivateKey(t,s)}catch(e){return{status:!1,output:e.message}}let i={address:o,data:n},c={salt:a,...i},u={signature:l.SHA256(JSON.stringify(c)).toString(),account_type:"PK",tag:"",ciphertexts:i};return r||(this.account_passwords[e]=a,this.account_labels.push(e)),{status:!0,output:u}}generatePPAccount(e,t,a){if(this.checkAccountLabelIsExist(e))return{status:!1,output:"Account labels already exist"};let s=v(t,a),r={slat:a,data:s},o=l.SHA256(JSON.stringify(r)).toString(),n=this.getSubAccount("evm",t,0);if(!1===n.status)return{status:!1,output:n.output};let i=this.getSubAccount("solana",t,0);if(!1===i.status)return{status:!1,output:i.output};let c={signature:o,account_type:"PP",pp_ciphertexts:s,tag:"",data:[{ciphertexts:{evm:{address:n.output.address,data:v(n.output.pk,a)},solana:{address:i.output.address,data:v(i.output.pk,a)}}}]};return this.account_passwords[e]=a,this.account_labels.push(e),{status:!0,output:c}}generateSubAccount(e,a){if(!this.checkAccountLabelIsExist(e))return{status:!1,output:"Account labels not exist"};let s=t.join(this.setting_path,`accounts/${e}.json`),r=y(s).pp_ciphertexts,o=k(r,this.account_passwords[e]),n=this.getSubAccount("evm",o,a);if(!1===n.status)return{status:!1,output:n.output};let i=this.getSubAccount("solana",o,a);return!1===i.status?{status:!1,output:i.output}:{status:!0,output:{evm:{address:n.output.address,data:v(n.output.pk,this.account_passwords[e])},solana:{address:i.output.address,data:v(i.output.pk,this.account_passwords[e])}}}}resetPassword(e,a){if(!this.checkAccountLabelIsExist(e))return{status:!1,output:"Account labels not exist"};if(!this.checkPassword(e,this.account_passwords[e]))return{status:!1,output:"account passwords is not correct"};let s=t.join(this.setting_path,`accounts/${e}.json`),r=y(s);if("PK"===r.account_type){let t=r.data;for(let s=0;s<t.length;s++){let r=t[s].ciphertexts.data,o=k(r,this.account_passwords[e]);t[s].ciphertexts.data=v(o,a)}let o={salt:a,...r.data[0].ciphertexts};return r.signature=l.SHA256(JSON.stringify(o)).toString(),w(r,s),delete this.account_passwords[e],{status:!0,output:"Password reset successfully"}}let o=k(r.pp_ciphertexts,this.account_passwords[e]);r.pp_ciphertexts=v(o,a);let n={slat:a,data:r.pp_ciphertexts};r.signature=l.SHA256(JSON.stringify(n)).toString();let i=r.data;for(let t=0;t<i.length;t++){let s=k(i[t].ciphertexts.evm.data,this.account_passwords[e]);i[t].ciphertexts.evm.data=v(s,a);let r=k(i[t].ciphertexts.solana.data,this.account_passwords[e]);i[t].ciphertexts.solana.data=v(r,a)}return w(r,s),delete this.account_passwords[e],{status:!0,output:"Password reset successfully"}}signTransaction(e,t,a){return e=this.getAccountChecksumAddress(e),this.checkAccountIsExist(e)?"unknown"===this.getAccountChainType(e)?{status:!1,output:"Chain type unknown"}:"evm"===this.getAccountChainType(e)?this.signEvmTransaction(t,a):this.signSolanaTransaction(e,a,t):{status:!1,output:"Account is not exist"}}signTransactionOffline(e,t,s){"chainId"in t||(t.chainId=s);const r=this.showPrivateKey(e),o=new a(a.givenProvider);return h(o.eth.accounts.signTransaction.bind(o.eth.accounts),[t,r])}sendSignedTransaction(e,t,a){return e=this.getAccountChecksumAddress(e),this.checkAccountIsExist(e)?"unknown"===this.getAccountChainType(e)?{status:!1,output:"Chain type unknown"}:"evm"===this.getAccountChainType(e)?this.sendEvmTransaction(t,a):this.sendSolanaTransaction(t,a):{status:!1,output:"Account is not exist"}}signMessage(e,t){return e=this.getAccountChecksumAddress(e),this.checkAccountIsExist(e)?"unknown"===this.getAccountChainType(e)?{status:!1,output:"Chain type unknown"}:"evm"===this.getAccountChainType(e)?{status:!0,output:this.signEvmMessage(t,e)}:{status:!0,output:this.signSolanaMessage(t,e)}:{status:!1,output:"Account is not exist"}}async simulateTransaction(e,t,a){const s=new n(t),o=this.showPrivateKey(e),c=i.fromSecretKey(r.decode(o));return await h(s.simulateTransaction.bind(s),[a,[c]])}async signEvmTransaction(e,t){if(!this.account_passwords)return{status:!1,output:"Please set password first"};let s=e.from;if(!e.gas){let a=new p(t),s=await a.getEvmGasLimit(e,t);if(!s.status)return s;e.gas=s.output}if(!e.gasPrice){let a=new p(t),s=await a.getEvmGasPrice();if(!s.status)return s;e.gasPrice=s.output}const r=this.showPrivateKey(s),o=new a(t);return await h(o.eth.accounts.signTransaction.bind(o.eth.accounts),[e,r])}async sendEvmTransaction(e,t){const s=new a(t);return await h(s.eth.sendSignedTransaction.bind(s.eth),[e])}async signSolanaTransaction(e,t,a){const s=this.showPrivateKey(e),o=i.fromSecretKey(r.decode(s)),l=new c(o);if(!("recentBlockhash"in a)||!a.recentBlockhash){const e=new n(t);let s=await h(e.getLatestBlockhash.bind(e),[]);if(!s.status)return{status:!1,output:"Failed to get latest blockhash"};a.recentBlockhash=s.output.blockhash}return a.feePayer=l.publicKey,await h(l.signTransaction.bind(l),[a])}async sendSolanaTransaction(e,t){const a=new n(t);return await h(a.sendRawTransaction.bind(a),[e.serialize()])}signEvmMessage(e,t){const a=this.showPrivateKey(t);return _.eth.accounts.sign(e,a).signature}signSolanaMessage(e,t){const a=this.showPrivateKey(t),o=s.sign.detached(new Uint8Array(e.match(/.{1,2}/g).map(e=>parseInt(e,16))),r.decode(a));return r.encode(o)}checkIsDisableAddress(e,t){if(!e||!this.disable_address)return!1;let a=e.length;for(let s=0;s<a;s++){let a=e[s].from,r=e[s].to;if(t>=a&&t<=r)return!0}return!1}reload(){if(this.account_labels=f(this.setting_path,this.account_labels),!g(this.setting_path,this.account_labels))throw new Error("Account labels not exist");let a={},s=this.account_labels.length;for(let r=0;r<s;r++){let s=t.join(this.setting_path,`accounts/${this.account_labels[r]}.json`);if(!e.existsSync(s))continue;let o=JSON.parse(e.readFileSync(s,"utf8"));if("PK"===o.account_type){let e=o.data;if(!e)continue;let t=e.length;for(let s=0;s<t;s++)a[e[s].ciphertexts.address]={account_label:this.account_labels[r],data:e[s].ciphertexts.data};continue}let n=o.data.length,i=o.disabled_slots;for(let e=0;e<n;e++)this.checkIsDisableAddress(i,e)||(a[o.data[e].ciphertexts.evm.address]={account_label:this.account_labels[r],data:o.data[e].ciphertexts.evm.data},a[o.data[e].ciphertexts.solana.address]={account_label:this.account_labels[r],data:o.data[e].ciphertexts.solana.data})}this.account_ciphertexts=a}getAccountChecksumAddress(e){return"evm"===this.getAccountChainType(e)&&(e=_.utils.toChecksumAddress(e)),e}getAccountAddressByPrivateKey(e,t){return"evm"===this.getNetworkTypeByChainId(t)?_.eth.accounts.privateKeyToAccount(e).address:i.fromSecretKey(r.decode(e)).publicKey.toBase58()}getNetworkTypeByChainId(e){return"901"===e||"902"===e||"903"===e?"solana":"evm"}getSubAccount(e,t,a){if(!(e=>{const t=e.split(/\s+/g),a=t.length;if(12!==a&&24!==a)return!1;for(let e=0;e<a;e++)if(!x.includes(t[e]))return!1;return!0})(t))return{status:!1,output:"Please set correct mnemonic"};if("solana"===e){const e=o.mnemonicToSeedSync(t);let s=d.fromMasterSeed(e),n=i.fromSeed(s.derive(`m/44'/501'/${a}'/0'`).privateKey);return{status:!0,output:{address:n.publicKey.toBase58(),pk:r.encode(n.secretKey)}}}if("evm"===e){const e=o.mnemonicToSeedSync(t),s=u.fromMasterSeed(e).derivePath("m/44'/60'/0'/0").deriveChild(a).getWallet();return{status:!0,output:{address:_.utils.toChecksumAddress(s.getAddressString()),pk:s.getPrivateKeyString()}}}return{status:!1,output:"Unknown chain type"}}getAccountSignature(){let a=this.account_labels.length,s=l.algo.MD5.create();for(let r=0;r<a;r++){let a=t.join(this.setting_path,`accounts/${this.account_labels[r]}.json`);const o=65536,n=e.openSync(a,"r"),i=Buffer.alloc(o);let c=0;try{for(;;){const t=e.readSync(n,i,0,o,c);if(0===t)break;const a=i.subarray(0,t).toString("utf8");s.update(a),c+=t}}finally{e.closeSync(n)}s.update("|SEPARATOR|")}return s.finalize().toString()}}const S=require("../package.json");class j{constructor(e,t,a,s){this.account=new A(e,t,a,s)}}j.version=S.version;export{j as WativeCore};