@unlink-xyz/core 0.1.3-canary.fd5dddf → 0.1.4

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 (213) hide show
  1. package/README.md +9 -0
  2. package/dist/account/account.d.ts +31 -2
  3. package/dist/account/account.d.ts.map +1 -1
  4. package/dist/account/accounts.d.ts +42 -0
  5. package/dist/account/accounts.d.ts.map +1 -0
  6. package/dist/account/seed.d.ts +45 -0
  7. package/dist/account/seed.d.ts.map +1 -0
  8. package/dist/account/serialization.d.ts +6 -0
  9. package/dist/account/serialization.d.ts.map +1 -0
  10. package/dist/browser/index.js +34424 -86406
  11. package/dist/browser/index.js.map +1 -1
  12. package/dist/browser/wallet/index.js +55942 -0
  13. package/dist/browser/wallet/index.js.map +1 -0
  14. package/dist/clients/broadcaster.d.ts +1 -0
  15. package/dist/clients/broadcaster.d.ts.map +1 -1
  16. package/dist/clients/indexer.d.ts +5 -0
  17. package/dist/clients/indexer.d.ts.map +1 -1
  18. package/dist/config.d.ts +6 -4
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/core.d.ts.map +1 -1
  21. package/dist/crypto/adapters/index.d.ts +17 -0
  22. package/dist/crypto/adapters/index.d.ts.map +1 -0
  23. package/dist/crypto/adapters/polyfills.d.ts +5 -0
  24. package/dist/crypto/adapters/polyfills.d.ts.map +1 -0
  25. package/dist/crypto/encrypt.d.ts +33 -0
  26. package/dist/crypto/encrypt.d.ts.map +1 -0
  27. package/dist/crypto/secure-memory.d.ts.map +1 -0
  28. package/dist/errors.d.ts +8 -0
  29. package/dist/errors.d.ts.map +1 -1
  30. package/dist/index.d.ts +6 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +6721 -23
  33. package/dist/index.js.map +1 -0
  34. package/dist/keys/derive.d.ts +2 -2
  35. package/dist/keys/derive.d.ts.map +1 -1
  36. package/dist/keys/hex.d.ts +1 -4
  37. package/dist/keys/hex.d.ts.map +1 -1
  38. package/dist/keys/mnemonic.d.ts +0 -2
  39. package/dist/keys/mnemonic.d.ts.map +1 -1
  40. package/dist/keys.d.ts +1 -0
  41. package/dist/keys.d.ts.map +1 -1
  42. package/dist/prover/config.d.ts +54 -9
  43. package/dist/prover/config.d.ts.map +1 -1
  44. package/dist/prover/integrity.d.ts +20 -0
  45. package/dist/prover/integrity.d.ts.map +1 -0
  46. package/dist/prover/prover.d.ts +16 -31
  47. package/dist/prover/prover.d.ts.map +1 -1
  48. package/dist/state/merkle/hydrator.d.ts +21 -19
  49. package/dist/state/merkle/hydrator.d.ts.map +1 -1
  50. package/dist/state/merkle/index.d.ts +1 -1
  51. package/dist/state/merkle/index.d.ts.map +1 -1
  52. package/dist/state/store/ciphertext-store.d.ts +7 -0
  53. package/dist/state/store/ciphertext-store.d.ts.map +1 -1
  54. package/dist/state/store/index.d.ts +1 -1
  55. package/dist/state/store/index.d.ts.map +1 -1
  56. package/dist/state/store/job-store.d.ts.map +1 -1
  57. package/dist/state/store/jobs.d.ts +14 -16
  58. package/dist/state/store/jobs.d.ts.map +1 -1
  59. package/dist/state/store/leaf-store.d.ts +4 -0
  60. package/dist/state/store/leaf-store.d.ts.map +1 -1
  61. package/dist/state/store/nullifier-store.d.ts.map +1 -1
  62. package/dist/state/store/records.d.ts +8 -0
  63. package/dist/state/store/records.d.ts.map +1 -1
  64. package/dist/state/store/store.d.ts +18 -0
  65. package/dist/state/store/store.d.ts.map +1 -1
  66. package/dist/storage/indexeddb.d.ts.map +1 -1
  67. package/dist/storage/memory.d.ts.map +1 -1
  68. package/dist/transactions/adapter.d.ts +31 -0
  69. package/dist/transactions/adapter.d.ts.map +1 -0
  70. package/dist/transactions/deposit.d.ts +1 -1
  71. package/dist/transactions/deposit.d.ts.map +1 -1
  72. package/dist/transactions/index.d.ts +4 -2
  73. package/dist/transactions/index.d.ts.map +1 -1
  74. package/dist/transactions/note-sync.d.ts +3 -3
  75. package/dist/transactions/note-sync.d.ts.map +1 -1
  76. package/dist/transactions/reconcile.d.ts +1 -1
  77. package/dist/transactions/reconcile.d.ts.map +1 -1
  78. package/dist/transactions/transact.d.ts +21 -2
  79. package/dist/transactions/transact.d.ts.map +1 -1
  80. package/dist/transactions/transaction-planner.d.ts +1 -1
  81. package/dist/transactions/transaction-planner.d.ts.map +1 -1
  82. package/dist/transactions/transfer-planner.d.ts +2 -1
  83. package/dist/transactions/transfer-planner.d.ts.map +1 -1
  84. package/dist/transactions/types/deposit.d.ts +3 -3
  85. package/dist/transactions/types/deposit.d.ts.map +1 -1
  86. package/dist/transactions/types/domain.d.ts +3 -0
  87. package/dist/transactions/types/domain.d.ts.map +1 -1
  88. package/dist/transactions/types/options.d.ts +14 -5
  89. package/dist/transactions/types/options.d.ts.map +1 -1
  90. package/dist/transactions/types/planning.d.ts +2 -0
  91. package/dist/transactions/types/planning.d.ts.map +1 -1
  92. package/dist/transactions/types/state-stores.d.ts +53 -5
  93. package/dist/transactions/types/state-stores.d.ts.map +1 -1
  94. package/dist/transactions/types/transact.d.ts +10 -3
  95. package/dist/transactions/types/transact.d.ts.map +1 -1
  96. package/dist/transactions/withdrawal-planner.d.ts +1 -1
  97. package/dist/transactions/withdrawal-planner.d.ts.map +1 -1
  98. package/dist/tsconfig.tsbuildinfo +1 -1
  99. package/dist/tsup.config.d.ts +8 -0
  100. package/dist/tsup.config.d.ts.map +1 -0
  101. package/dist/types.d.ts +1 -0
  102. package/dist/types.d.ts.map +1 -1
  103. package/dist/utils/amounts.d.ts +0 -13
  104. package/dist/utils/amounts.d.ts.map +1 -1
  105. package/dist/utils/async.js +37 -34
  106. package/dist/utils/async.js.map +1 -0
  107. package/dist/utils/bigint.d.ts +0 -2
  108. package/dist/utils/bigint.d.ts.map +1 -1
  109. package/dist/utils/random.d.ts +5 -0
  110. package/dist/utils/random.d.ts.map +1 -1
  111. package/dist/utils/validators.d.ts.map +1 -1
  112. package/dist/vitest.config.d.ts.map +1 -1
  113. package/dist/wallet/adapter.d.ts +21 -0
  114. package/dist/wallet/adapter.d.ts.map +1 -0
  115. package/dist/wallet/burner/service.d.ts +32 -0
  116. package/dist/wallet/burner/service.d.ts.map +1 -0
  117. package/dist/wallet/burner/types.d.ts +47 -0
  118. package/dist/wallet/burner/types.d.ts.map +1 -0
  119. package/dist/wallet/index.d.ts +20 -0
  120. package/dist/wallet/index.d.ts.map +1 -0
  121. package/dist/wallet/index.js +6462 -0
  122. package/dist/wallet/index.js.map +1 -0
  123. package/dist/wallet/sdk.d.ts +48 -0
  124. package/dist/wallet/sdk.d.ts.map +1 -0
  125. package/dist/wallet/types.d.ts +457 -0
  126. package/dist/wallet/types.d.ts.map +1 -0
  127. package/dist/wallet/unlink-wallet.d.ts +187 -0
  128. package/dist/wallet/unlink-wallet.d.ts.map +1 -0
  129. package/package.json +16 -6
  130. package/dist/account/account.js +0 -142
  131. package/dist/circuits.json +0 -74
  132. package/dist/clients/broadcaster.js +0 -30
  133. package/dist/clients/http.js +0 -72
  134. package/dist/clients/indexer.js +0 -94
  135. package/dist/config.js +0 -36
  136. package/dist/constants.js +0 -5
  137. package/dist/core.js +0 -15
  138. package/dist/crypto-adapters/auto-init.d.ts +0 -2
  139. package/dist/crypto-adapters/auto-init.d.ts.map +0 -1
  140. package/dist/crypto-adapters/auto-init.js +0 -7
  141. package/dist/crypto-adapters/index.d.ts +0 -22
  142. package/dist/crypto-adapters/index.d.ts.map +0 -1
  143. package/dist/crypto-adapters/index.js +0 -47
  144. package/dist/crypto-adapters/polyfills.d.ts +0 -5
  145. package/dist/crypto-adapters/polyfills.d.ts.map +0 -1
  146. package/dist/crypto-adapters/polyfills.js +0 -8
  147. package/dist/errors.js +0 -36
  148. package/dist/history/index.js +0 -2
  149. package/dist/history/service.js +0 -354
  150. package/dist/history/types.js +0 -1
  151. package/dist/keys/address.js +0 -55
  152. package/dist/keys/derive.js +0 -112
  153. package/dist/keys/hex.js +0 -66
  154. package/dist/keys/index.js +0 -4
  155. package/dist/keys/mnemonic.js +0 -23
  156. package/dist/keys.js +0 -45
  157. package/dist/prover/config.js +0 -70
  158. package/dist/prover/index.js +0 -1
  159. package/dist/prover/prover.js +0 -291
  160. package/dist/prover/registry.js +0 -18
  161. package/dist/schema.js +0 -14
  162. package/dist/state/index.js +0 -2
  163. package/dist/state/merkle/hydrator.js +0 -37
  164. package/dist/state/merkle/index.js +0 -2
  165. package/dist/state/merkle/merkle-tree.js +0 -113
  166. package/dist/state/store/ciphertext-store.js +0 -37
  167. package/dist/state/store/history-store.js +0 -53
  168. package/dist/state/store/index.js +0 -9
  169. package/dist/state/store/job-store.js +0 -144
  170. package/dist/state/store/jobs.js +0 -1
  171. package/dist/state/store/leaf-store.js +0 -32
  172. package/dist/state/store/note-store.js +0 -146
  173. package/dist/state/store/nullifier-store.js +0 -60
  174. package/dist/state/store/records.js +0 -1
  175. package/dist/state/store/root-store.js +0 -26
  176. package/dist/state/store/store.js +0 -113
  177. package/dist/storage/index.js +0 -2
  178. package/dist/storage/indexeddb.js +0 -205
  179. package/dist/storage/memory.js +0 -91
  180. package/dist/transactions/deposit.js +0 -220
  181. package/dist/transactions/index.js +0 -9
  182. package/dist/transactions/note-selection.js +0 -201
  183. package/dist/transactions/note-sync.js +0 -485
  184. package/dist/transactions/reconcile.js +0 -85
  185. package/dist/transactions/transact.js +0 -450
  186. package/dist/transactions/transaction-planner.js +0 -116
  187. package/dist/transactions/transfer-planner.js +0 -85
  188. package/dist/transactions/types/deposit.js +0 -1
  189. package/dist/transactions/types/domain.js +0 -4
  190. package/dist/transactions/types/index.js +0 -17
  191. package/dist/transactions/types/options.js +0 -1
  192. package/dist/transactions/types/planning.js +0 -1
  193. package/dist/transactions/types/state-stores.js +0 -1
  194. package/dist/transactions/types/transact.js +0 -1
  195. package/dist/transactions/withdrawal-planner.js +0 -128
  196. package/dist/tsup.browser.config.js +0 -34
  197. package/dist/types.js +0 -1
  198. package/dist/utils/amounts.js +0 -89
  199. package/dist/utils/bigint.js +0 -29
  200. package/dist/utils/crypto.d.ts +0 -18
  201. package/dist/utils/crypto.d.ts.map +0 -1
  202. package/dist/utils/crypto.js +0 -45
  203. package/dist/utils/format.js +0 -33
  204. package/dist/utils/json-codec.js +0 -25
  205. package/dist/utils/notes.js +0 -14
  206. package/dist/utils/polling.js +0 -11
  207. package/dist/utils/random.js +0 -27
  208. package/dist/utils/secure-memory.d.ts.map +0 -1
  209. package/dist/utils/secure-memory.js +0 -28
  210. package/dist/utils/signature.js +0 -14
  211. package/dist/utils/validators.js +0 -96
  212. package/dist/vitest.config.js +0 -13
  213. /package/dist/{utils → crypto}/secure-memory.d.ts +0 -0
package/dist/config.js DELETED
@@ -1,36 +0,0 @@
1
- /**
2
- * Environment types for SDK configuration.
3
- */
4
- import { InitializationError } from "./errors.js";
5
- const CONFIG_URL = "https://api.unlink.xyz/config/networks.json";
6
- /**
7
- * Fetch environment configuration from the hosted network config file.
8
- *
9
- * @param env - The environment to fetch config for
10
- * @returns Promise resolving to the environment configuration
11
- * @throws If fetch fails or environment is not found
12
- */
13
- export async function fetchEnvironmentConfig(env) {
14
- const res = await fetch(CONFIG_URL);
15
- if (!res.ok) {
16
- throw new InitializationError(`Failed to fetch SDK config: ${res.status}`);
17
- }
18
- const config = (await res.json());
19
- if (!config[env]) {
20
- throw new InitializationError(`Unknown environment: ${env}`);
21
- }
22
- return config[env];
23
- }
24
- /**
25
- * Create service configuration from an RPC URL.
26
- *
27
- * @param rpcUrl - The base RPC URL
28
- * @returns Service configuration with broadcaster and indexer URLs
29
- */
30
- export function createServiceConfig(rpcUrl) {
31
- const baseUrl = rpcUrl.replace(/\/+$/, "");
32
- return {
33
- broadcasterBaseUrl: baseUrl + "/broadcaster",
34
- indexerBaseUrl: baseUrl + "/indexer",
35
- };
36
- }
package/dist/constants.js DELETED
@@ -1,5 +0,0 @@
1
- /**
2
- * Sentinel address for native ETH, matching the Solidity Constants.ETH_TOKEN.
3
- * Used as the token address in notes to indicate a native ETH deposit/withdrawal.
4
- */
5
- export const ETH_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
package/dist/core.js DELETED
@@ -1,15 +0,0 @@
1
- import { initCrypto } from "./crypto-adapters/index.js";
2
- import { InitializationError } from "./errors.js";
3
- import { migrate } from "./schema.js";
4
- export async function initCore(deps) {
5
- if (!deps?.storage)
6
- throw new InitializationError("storage dep required");
7
- if (!deps?.rng)
8
- throw new InitializationError("rng dep required");
9
- await initCrypto();
10
- await migrate(deps.storage);
11
- return {
12
- storage: deps.storage,
13
- rng: deps.rng,
14
- };
15
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=auto-init.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auto-init.d.ts","sourceRoot":"","sources":["../../crypto-adapters/auto-init.ts"],"names":[],"mappings":""}
@@ -1,7 +0,0 @@
1
- /**
2
- * Auto-initialization module for tests.
3
- * Import this at the top of test files to automatically initialize crypto, not in production code.
4
- */
5
- import { initCrypto } from "./index.js";
6
- await initCrypto();
7
- console.log("[crypto] Auto-initialized for tests");
@@ -1,22 +0,0 @@
1
- /**
2
- * Crypto adapters for browser-compatible cryptographic operations.
3
- *
4
- * Exports:
5
- * - poseidon: Poseidon hash from circomlibjs (iden3)
6
- * - deriveSpendingPublicKey, signMessage, verifySignature: EdDSA from @zk-kit/eddsa-poseidon
7
- * - initCrypto: Async function to initialize crypto primitives (must be called before using poseidon)
8
- */
9
- import "./polyfills.js";
10
- /**
11
- * Initialize the crypto primitives (Poseidon hash function).
12
- * This must be called before using the poseidon function.
13
- * Safe to call multiple times - subsequent calls return the same promise.
14
- */
15
- export declare function initCrypto(): Promise<void>;
16
- /**
17
- * Poseidon hash function compatible with circomlib circuits.
18
- * Wraps circomlibjs buildPoseidon to return bigint directly.
19
- */
20
- export declare const poseidon: (inputs: (bigint | number)[]) => bigint;
21
- export { derivePublicKey as deriveSpendingPublicKey, signMessage, verifySignature, } from "@zk-kit/eddsa-poseidon";
22
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../crypto-adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,gBAAgB,CAAC;AAWxB;;;;GAIG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAehD;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAAG,MAQtD,CAAC;AAEF,OAAO,EACL,eAAe,IAAI,uBAAuB,EAC1C,WAAW,EACX,eAAe,GAChB,MAAM,wBAAwB,CAAC"}
@@ -1,47 +0,0 @@
1
- /**
2
- * Crypto adapters for browser-compatible cryptographic operations.
3
- *
4
- * Exports:
5
- * - poseidon: Poseidon hash from circomlibjs (iden3)
6
- * - deriveSpendingPublicKey, signMessage, verifySignature: EdDSA from @zk-kit/eddsa-poseidon
7
- * - initCrypto: Async function to initialize crypto primitives (must be called before using poseidon)
8
- */
9
- // Polyfill must be the first import
10
- import "./polyfills.js";
11
- import { InitializationError } from "../errors.js";
12
- let _poseidon = null;
13
- let _initPromise = null;
14
- /**
15
- * Initialize the crypto primitives (Poseidon hash function).
16
- * This must be called before using the poseidon function.
17
- * Safe to call multiple times - subsequent calls return the same promise.
18
- */
19
- export async function initCrypto() {
20
- if (_poseidon)
21
- return; // Already initialized
22
- if (_initPromise)
23
- return _initPromise; // Initialization in progress
24
- _initPromise = (async () => {
25
- try {
26
- const { buildPoseidon } = await import("circomlibjs");
27
- _poseidon = await buildPoseidon();
28
- }
29
- catch (error) {
30
- _initPromise = null; // Reset to allow retry
31
- throw error;
32
- }
33
- })();
34
- return _initPromise;
35
- }
36
- /**
37
- * Poseidon hash function compatible with circomlib circuits.
38
- * Wraps circomlibjs buildPoseidon to return bigint directly.
39
- */
40
- export const poseidon = (inputs) => {
41
- if (!_poseidon) {
42
- throw new InitializationError("Crypto not initialized. Call initCrypto() before using poseidon().");
43
- }
44
- const hash = _poseidon(inputs);
45
- return _poseidon.F.toObject(hash);
46
- };
47
- export { derivePublicKey as deriveSpendingPublicKey, signMessage, verifySignature, } from "@zk-kit/eddsa-poseidon";
@@ -1,5 +0,0 @@
1
- /**
2
- * Browser polyfills for Node.js-specific APIs used by circomlibjs.
3
- */
4
- export {};
5
- //# sourceMappingURL=polyfills.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"polyfills.d.ts","sourceRoot":"","sources":["../../crypto-adapters/polyfills.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -1,8 +0,0 @@
1
- /**
2
- * Browser polyfills for Node.js-specific APIs used by circomlibjs.
3
- */
4
- import { Buffer } from "buffer";
5
- // Make Buffer available globally for circomlibjs
6
- if (typeof globalThis.Buffer === "undefined") {
7
- globalThis.Buffer = Buffer;
8
- }
package/dist/errors.js DELETED
@@ -1,36 +0,0 @@
1
- export class CoreError extends Error {
2
- constructor(message) {
3
- super(message);
4
- this.name = "CoreError";
5
- }
6
- }
7
- export class KeyValidationError extends CoreError {
8
- constructor(message) {
9
- super(message);
10
- this.name = "KeyValidationError";
11
- }
12
- }
13
- export class SchemaMismatchError extends CoreError {
14
- constructor(current, expected) {
15
- super(`schema mismatch (current ${current}, expected ${expected})`);
16
- this.name = "SchemaMismatchError";
17
- }
18
- }
19
- export class ValidationError extends CoreError {
20
- constructor(message) {
21
- super(message);
22
- this.name = "ValidationError";
23
- }
24
- }
25
- export class ProofError extends CoreError {
26
- constructor(message) {
27
- super(message);
28
- this.name = "ProofError";
29
- }
30
- }
31
- export class InitializationError extends CoreError {
32
- constructor(message) {
33
- super(message);
34
- this.name = "InitializationError";
35
- }
36
- }
@@ -1,2 +0,0 @@
1
- export * from "./types.js";
2
- export * from "./service.js";
@@ -1,354 +0,0 @@
1
- import { ensureChainId, ensureMpk } from "../utils/validators.js";
2
- function addByToken(totals, token, delta) {
3
- totals.set(token, (totals.get(token) ?? 0n) + delta);
4
- }
5
- function maxTimestamp(notes, field) {
6
- let max = 0;
7
- for (const note of notes) {
8
- const timestamp = note[field];
9
- if (timestamp !== undefined)
10
- max = Math.max(max, timestamp);
11
- }
12
- return max > 0 ? max : undefined;
13
- }
14
- const STATUS_PRIORITY = {
15
- failed: 3,
16
- pending: 2,
17
- confirmed: 1,
18
- };
19
- function convertJobStatus(jobStatus) {
20
- if (jobStatus === "failed" || jobStatus === "dead")
21
- return "failed";
22
- if (jobStatus === "pending" ||
23
- jobStatus === "submitted" ||
24
- jobStatus === "broadcasting")
25
- return "pending";
26
- return "confirmed";
27
- }
28
- function mergeStatus(prev, next) {
29
- if (!prev)
30
- return next;
31
- return STATUS_PRIORITY[next] > STATUS_PRIORITY[prev] ? next : prev;
32
- }
33
- function computeEntryId(params) {
34
- return `${params.chainId}:${params.mpk}:${params.txHash}:${params.kind}`;
35
- }
36
- function groupNotesByTransaction(notes) {
37
- const byTx = new Map();
38
- const ensureGroup = (txHash) => {
39
- const existing = byTx.get(txHash);
40
- if (existing)
41
- return existing;
42
- const next = {
43
- created: [],
44
- spent: [],
45
- createdEventType: undefined,
46
- };
47
- byTx.set(txHash, next);
48
- return next;
49
- };
50
- for (const note of notes) {
51
- if (note.createdTxHash) {
52
- const group = ensureGroup(note.createdTxHash);
53
- group.created.push(note);
54
- group.createdEventType ??= note.createdEventType;
55
- }
56
- if (note.spentTxHash) {
57
- const group = ensureGroup(note.spentTxHash);
58
- group.spent.push(note);
59
- }
60
- }
61
- return byTx;
62
- }
63
- function resolveJobStatuses(jobs, chainId, mpk) {
64
- const statusByTxHash = new Map();
65
- for (const job of jobs) {
66
- if (job.chainId !== chainId)
67
- continue;
68
- if (job.mpk && job.mpk !== mpk)
69
- continue;
70
- const txHash = job.txHash ?? undefined;
71
- if (!txHash)
72
- continue;
73
- const status = convertJobStatus(job.status);
74
- statusByTxHash.set(txHash, mergeStatus(statusByTxHash.get(txHash), status));
75
- }
76
- return statusByTxHash;
77
- }
78
- function aggregateAmounts(group) {
79
- const createdTotals = new Map();
80
- const spentTotals = new Map();
81
- for (const note of group.created) {
82
- addByToken(createdTotals, note.token, BigInt(note.value));
83
- }
84
- for (const note of group.spent) {
85
- addByToken(spentTotals, note.token, BigInt(note.value));
86
- }
87
- const tokens = new Set([
88
- ...createdTotals.keys(),
89
- ...spentTotals.keys(),
90
- ]);
91
- const netByToken = new Map();
92
- let hasNetChange = false;
93
- for (const token of tokens) {
94
- const created = createdTotals.get(token) ?? 0n;
95
- const spent = spentTotals.get(token) ?? 0n;
96
- const net = created - spent;
97
- netByToken.set(token, net);
98
- if (net !== 0n)
99
- hasNetChange = true;
100
- }
101
- return { createdTotals, spentTotals, netByToken, hasNetChange };
102
- }
103
- function classifyTransaction(group, amounts) {
104
- const isSelfSend = group.spent.length > 0 && !amounts.hasNetChange;
105
- if (isSelfSend) {
106
- return "SelfSend";
107
- }
108
- else if (group.spent.length > 0) {
109
- return "Send";
110
- }
111
- else if (group.created.length > 0) {
112
- return group.createdEventType === "deposit" ? "Deposit" : "Receive";
113
- }
114
- return null;
115
- }
116
- function computeFinalAmounts(kind, amounts) {
117
- const finalTotals = new Map();
118
- const tokens = new Set([
119
- ...amounts.createdTotals.keys(),
120
- ...amounts.spentTotals.keys(),
121
- ]);
122
- if (kind === "SelfSend") {
123
- for (const token of tokens)
124
- finalTotals.set(token, 0n);
125
- }
126
- else if (kind === "Send" || kind === "Withdraw") {
127
- for (const [token, net] of amounts.netByToken.entries()) {
128
- if (net < 0n)
129
- finalTotals.set(token, net);
130
- }
131
- }
132
- else {
133
- for (const [token, net] of amounts.netByToken.entries()) {
134
- if (net > 0n)
135
- finalTotals.set(token, net);
136
- }
137
- }
138
- return finalTotals;
139
- }
140
- function selectTimestamp(kind, group) {
141
- if (kind === "Deposit" || kind === "Receive") {
142
- return maxTimestamp(group.created, "createdAt");
143
- }
144
- else if (kind === "Send" || kind === "SelfSend" || kind === "Withdraw") {
145
- return (maxTimestamp(group.spent, "spentAt") ??
146
- maxTimestamp(group.created, "createdAt"));
147
- }
148
- return undefined;
149
- }
150
- function buildHistoryEntry(params) {
151
- return {
152
- id: computeEntryId({
153
- chainId: params.chainId,
154
- mpk: params.mpk,
155
- txHash: params.txHash,
156
- kind: params.kind,
157
- }),
158
- chainId: params.chainId,
159
- mpk: params.mpk,
160
- txHash: params.txHash,
161
- kind: params.kind,
162
- status: params.status,
163
- timestamp: params.timestamp,
164
- amounts: [...params.finalAmounts.entries()].map(([token, delta]) => ({
165
- token,
166
- delta: delta.toString(),
167
- })),
168
- details: {
169
- createdNoteCount: params.group.created.length,
170
- spentNoteCount: params.group.spent.length,
171
- createdEventType: params.group.createdEventType ?? "unknown",
172
- },
173
- };
174
- }
175
- export function createHistoryService(stateStore) {
176
- // Track pending rebuilds to prevent concurrent execution
177
- const pendingRebuilds = new Map();
178
- async function computeHistory(params) {
179
- ensureChainId(params.chainId);
180
- ensureMpk(params.mpk);
181
- // Fetch data
182
- const notes = await stateStore.listNotes({
183
- chainId: params.chainId,
184
- mpk: params.mpk,
185
- includeSpent: true,
186
- });
187
- const jobs = await stateStore.listJobs({
188
- statuses: [
189
- "pending",
190
- "submitted",
191
- "broadcasting",
192
- "failed",
193
- "dead",
194
- "succeeded",
195
- ],
196
- });
197
- // Group notes by transaction hash
198
- const byTx = groupNotesByTransaction(notes);
199
- // Resolve job statuses for each transaction
200
- const statusByTxHash = resolveJobStatuses(jobs, params.chainId, params.mpk);
201
- const entries = [];
202
- const confirmedTxHashes = new Set();
203
- // Process each transaction group
204
- for (const [txHash, group] of byTx.entries()) {
205
- if (group.created.length === 0 && group.spent.length === 0)
206
- continue;
207
- // Aggregate amounts and compute net deltas
208
- const amounts = aggregateAmounts(group);
209
- // Classify transaction type
210
- const kind = classifyTransaction(group, amounts);
211
- if (!kind)
212
- continue;
213
- // Compute final amounts to display
214
- const finalAmounts = computeFinalAmounts(kind, amounts);
215
- if (finalAmounts.size === 0)
216
- continue;
217
- // Select appropriate timestamp
218
- const timestamp = selectTimestamp(kind, group);
219
- // Build history entry
220
- const entry = buildHistoryEntry({
221
- chainId: params.chainId,
222
- mpk: params.mpk,
223
- txHash,
224
- kind,
225
- status: statusByTxHash.get(txHash) ?? "confirmed",
226
- timestamp,
227
- finalAmounts,
228
- group,
229
- });
230
- confirmedTxHashes.add(txHash);
231
- entries.push(entry);
232
- }
233
- // Add pending job entries for transactions not yet indexed
234
- for (const job of jobs) {
235
- if (job.chainId !== params.chainId)
236
- continue;
237
- if (job.mpk && job.mpk !== params.mpk)
238
- continue;
239
- const txHash = job.txHash ?? undefined;
240
- if (!txHash)
241
- continue;
242
- if (confirmedTxHashes.has(txHash))
243
- continue;
244
- if (!job.historyPreview)
245
- continue;
246
- if (job.status === "succeeded")
247
- continue;
248
- const status = job.status === "failed" || job.status === "dead" ? "failed" : "pending";
249
- entries.push({
250
- id: computeEntryId({
251
- chainId: params.chainId,
252
- mpk: params.mpk,
253
- txHash,
254
- kind: job.historyPreview.kind,
255
- }),
256
- chainId: params.chainId,
257
- mpk: params.mpk,
258
- txHash,
259
- kind: job.historyPreview.kind,
260
- status,
261
- timestamp: job.createdAt,
262
- amounts: job.historyPreview.amounts,
263
- details: {
264
- source: "job",
265
- jobKind: job.kind,
266
- },
267
- });
268
- }
269
- // Sort by timestamp descending
270
- entries.sort((a, b) => {
271
- const at = a.timestamp ?? 0;
272
- const bt = b.timestamp ?? 0;
273
- if (at !== bt)
274
- return bt - at;
275
- return b.txHash.localeCompare(a.txHash);
276
- });
277
- // Filter self-sends if needed (unified filtering point)
278
- return params.includeSelfSends
279
- ? entries
280
- : entries.filter((e) => e.kind !== "SelfSend");
281
- }
282
- async function computeAndPersist(params) {
283
- // Always compute with self-sends included for complete storage
284
- const allEntries = await computeHistory({
285
- chainId: params.chainId,
286
- mpk: params.mpk,
287
- includeSelfSends: true,
288
- });
289
- await stateStore.clearHistoryEntries({
290
- chainId: params.chainId,
291
- mpk: params.mpk,
292
- });
293
- await Promise.all(allEntries.map((entry) => stateStore.putHistoryEntry(entry)));
294
- await stateStore.commitHistorySnapshot({
295
- chainId: params.chainId,
296
- mpk: params.mpk,
297
- });
298
- }
299
- return {
300
- async rebuildHistory(params) {
301
- const rebuildKey = `${params.chainId}:${params.mpk}`;
302
- // If a rebuild is already in progress, wait for it instead of starting a new one
303
- const existing = pendingRebuilds.get(rebuildKey);
304
- if (existing) {
305
- return existing;
306
- }
307
- // Start a new rebuild and track it
308
- const rebuildPromise = computeAndPersist(params).finally(() => {
309
- // Clean up when done
310
- pendingRebuilds.delete(rebuildKey);
311
- });
312
- pendingRebuilds.set(rebuildKey, rebuildPromise);
313
- return rebuildPromise;
314
- },
315
- async getHistory(params) {
316
- ensureChainId(params.chainId);
317
- ensureMpk(params.mpk);
318
- // If rebuild in progress, wait for it
319
- const rebuildKey = `${params.chainId}:${params.mpk}`;
320
- const pendingRebuild = pendingRebuilds.get(rebuildKey);
321
- if (pendingRebuild) {
322
- await pendingRebuild;
323
- }
324
- if (params.force) {
325
- await computeAndPersist({
326
- chainId: params.chainId,
327
- mpk: params.mpk,
328
- });
329
- }
330
- else {
331
- // Check if we have persisted history
332
- const hasSnapshot = await stateStore.hasHistorySnapshot({
333
- chainId: params.chainId,
334
- mpk: params.mpk,
335
- });
336
- if (!hasSnapshot) {
337
- // First time - compute and persist
338
- await computeAndPersist({
339
- chainId: params.chainId,
340
- mpk: params.mpk,
341
- });
342
- }
343
- }
344
- // Always read from storage
345
- const stored = await stateStore.listHistoryEntries({
346
- chainId: params.chainId,
347
- mpk: params.mpk,
348
- });
349
- return params.includeSelfSends
350
- ? stored
351
- : stored.filter((e) => e.kind !== "SelfSend");
352
- },
353
- };
354
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,55 +0,0 @@
1
- import { bech32m } from "@scure/base";
2
- import { ValidationError } from "../errors.js";
3
- import { FieldSize, Hex } from "./hex.js";
4
- const VERSION = 1;
5
- const LIMIT = 127;
6
- const ALL_CHAINS = "ffffffffffffffff";
7
- const PREFIX = "0zk";
8
- const SALT = new TextEncoder().encode("unlink");
9
- function xorWithSalt(hex) {
10
- const bytes = Hex.toBytes(hex);
11
- const result = new Uint8Array(bytes.length);
12
- for (let i = 0; i < bytes.length; i++)
13
- result[i] = bytes[i] ^ SALT[i % SALT.length];
14
- return Hex.fromBytes(result);
15
- }
16
- function chainToHex(chain) {
17
- if (!chain)
18
- return ALL_CHAINS;
19
- return ((chain.type & 0xff).toString(16).padStart(2, "0") +
20
- BigInt(chain.id).toString(16).padStart(14, "0"));
21
- }
22
- function hexToChain(hex) {
23
- if (hex === ALL_CHAINS)
24
- return undefined;
25
- return {
26
- type: parseInt(hex.slice(0, 2), 16),
27
- id: parseInt(hex.slice(2), 16),
28
- };
29
- }
30
- export function encodeAddress(data) {
31
- const ver = (data.version ?? VERSION).toString(16).padStart(2, "0");
32
- const mpk = Hex.fromBigInt(data.masterPublicKey, FieldSize.SCALAR);
33
- const vpk = Hex.padToSize(data.viewingPublicKey, FieldSize.SCALAR);
34
- const net = xorWithSalt(chainToHex(data.chain));
35
- return bech32m.encode(PREFIX, bech32m.toWords(Hex.toBytes(`${ver}${mpk}${net}${vpk}`)), LIMIT);
36
- }
37
- export function decodeAddress(address) {
38
- if (!address)
39
- throw new ValidationError("No address to decode");
40
- const decoded = bech32m.decode(address, LIMIT);
41
- if (decoded.prefix !== PREFIX)
42
- throw new ValidationError("Invalid address prefix");
43
- const hex = Hex.encode(bech32m.fromWords(decoded.words));
44
- if (hex.length !== 146)
45
- throw new ValidationError("Incorrect address payload length");
46
- const version = parseInt(hex.slice(0, 2), 16);
47
- if (version !== VERSION)
48
- throw new ValidationError("Incorrect address version");
49
- return {
50
- version,
51
- masterPublicKey: Hex.toBigInt(hex.slice(2, 66)),
52
- viewingPublicKey: Hex.toBytes(hex.slice(82, 146)),
53
- chain: hexToChain(xorWithSalt(hex.slice(66, 82))),
54
- };
55
- }