@utxopia/sdk 0.1.0-alpha.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 (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/package.json +108 -0
  4. package/packages/btc-client/src/esplora-client.ts +153 -0
  5. package/packages/btc-client/src/index.ts +3 -0
  6. package/packages/btc-client/src/op-return.ts +93 -0
  7. package/packages/btc-client/src/types.ts +112 -0
  8. package/packages/sdk/README.md +277 -0
  9. package/packages/sdk/dist/announcement-client.d.ts +64 -0
  10. package/packages/sdk/dist/announcement-client.js +337 -0
  11. package/packages/sdk/dist/auditor-ciphertext.d.ts +72 -0
  12. package/packages/sdk/dist/auditor-ciphertext.js +135 -0
  13. package/packages/sdk/dist/auditor.d.ts +115 -0
  14. package/packages/sdk/dist/auditor.js +292 -0
  15. package/packages/sdk/dist/bitcoin/ika.d.ts +47 -0
  16. package/packages/sdk/dist/bitcoin/ika.js +74 -0
  17. package/packages/sdk/dist/bitcoin/index.d.ts +2 -0
  18. package/packages/sdk/dist/bitcoin/index.js +1 -0
  19. package/packages/sdk/dist/bound-params.d.ts +96 -0
  20. package/packages/sdk/dist/bound-params.js +249 -0
  21. package/packages/sdk/dist/chadbuffer.d.ts +124 -0
  22. package/packages/sdk/dist/chadbuffer.js +405 -0
  23. package/packages/sdk/dist/claim-link.d.ts +30 -0
  24. package/packages/sdk/dist/claim-link.js +50 -0
  25. package/packages/sdk/dist/client.d.ts +233 -0
  26. package/packages/sdk/dist/client.js +416 -0
  27. package/packages/sdk/dist/commitment-tree.d.ts +268 -0
  28. package/packages/sdk/dist/commitment-tree.js +564 -0
  29. package/packages/sdk/dist/config.d.ts +177 -0
  30. package/packages/sdk/dist/config.js +426 -0
  31. package/packages/sdk/dist/core/esplora.d.ts +110 -0
  32. package/packages/sdk/dist/core/esplora.js +187 -0
  33. package/packages/sdk/dist/core/mempool.d.ts +60 -0
  34. package/packages/sdk/dist/core/mempool.js +107 -0
  35. package/packages/sdk/dist/crypto-babyjub.d.ts +105 -0
  36. package/packages/sdk/dist/crypto-babyjub.js +312 -0
  37. package/packages/sdk/dist/crypto-ed25519.d.ts +116 -0
  38. package/packages/sdk/dist/crypto-ed25519.js +235 -0
  39. package/packages/sdk/dist/crypto.d.ts +59 -0
  40. package/packages/sdk/dist/crypto.js +140 -0
  41. package/packages/sdk/dist/event-client.d.ts +64 -0
  42. package/packages/sdk/dist/event-client.js +165 -0
  43. package/packages/sdk/dist/events.d.ts +127 -0
  44. package/packages/sdk/dist/events.js +305 -0
  45. package/packages/sdk/dist/explorer.d.ts +86 -0
  46. package/packages/sdk/dist/explorer.js +197 -0
  47. package/packages/sdk/dist/index.d.ts +61 -0
  48. package/packages/sdk/dist/index.js +216 -0
  49. package/packages/sdk/dist/instructions.d.ts +1125 -0
  50. package/packages/sdk/dist/instructions.js +1760 -0
  51. package/packages/sdk/dist/keys.d.ts +467 -0
  52. package/packages/sdk/dist/keys.js +799 -0
  53. package/packages/sdk/dist/logger.d.ts +10 -0
  54. package/packages/sdk/dist/logger.js +40 -0
  55. package/packages/sdk/dist/magicblock.d.ts +90 -0
  56. package/packages/sdk/dist/magicblock.js +164 -0
  57. package/packages/sdk/dist/merkle.d.ts +82 -0
  58. package/packages/sdk/dist/merkle.js +141 -0
  59. package/packages/sdk/dist/note.d.ts +348 -0
  60. package/packages/sdk/dist/note.js +483 -0
  61. package/packages/sdk/dist/pda.d.ts +160 -0
  62. package/packages/sdk/dist/pda.js +384 -0
  63. package/packages/sdk/dist/pool-state.d.ts +100 -0
  64. package/packages/sdk/dist/pool-state.js +126 -0
  65. package/packages/sdk/dist/poseidon.d.ts +53 -0
  66. package/packages/sdk/dist/poseidon.js +136 -0
  67. package/packages/sdk/dist/prover/index.d.ts +10 -0
  68. package/packages/sdk/dist/prover/index.js +10 -0
  69. package/packages/sdk/dist/prover/mobile.d.ts +45 -0
  70. package/packages/sdk/dist/prover/mobile.js +227 -0
  71. package/packages/sdk/dist/prover/web.d.ts +136 -0
  72. package/packages/sdk/dist/prover/web.js +597 -0
  73. package/packages/sdk/dist/psbt.d.ts +73 -0
  74. package/packages/sdk/dist/psbt.js +202 -0
  75. package/packages/sdk/dist/selective-disclosure.d.ts +181 -0
  76. package/packages/sdk/dist/selective-disclosure.js +172 -0
  77. package/packages/sdk/dist/sender-memo.d.ts +149 -0
  78. package/packages/sdk/dist/sender-memo.js +250 -0
  79. package/packages/sdk/dist/sns-resolver.d.ts +121 -0
  80. package/packages/sdk/dist/sns-resolver.js +229 -0
  81. package/packages/sdk/dist/solana/connection.d.ts +77 -0
  82. package/packages/sdk/dist/solana/connection.js +133 -0
  83. package/packages/sdk/dist/solana/priority-fee.d.ts +78 -0
  84. package/packages/sdk/dist/solana/priority-fee.js +137 -0
  85. package/packages/sdk/dist/stealth.d.ts +429 -0
  86. package/packages/sdk/dist/stealth.js +703 -0
  87. package/packages/sdk/dist/taproot.d.ts +172 -0
  88. package/packages/sdk/dist/taproot.js +494 -0
  89. package/packages/sdk/dist/token-registry.d.ts +96 -0
  90. package/packages/sdk/dist/token-registry.js +122 -0
  91. package/packages/sdk/dist/utils/encoding.d.ts +19 -0
  92. package/packages/sdk/dist/utils/encoding.js +29 -0
  93. package/packages/sdk/dist/vk-registry.d.ts +100 -0
  94. package/packages/sdk/dist/vk-registry.js +212 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Cryptographic utilities for UTXOpia
3
+ *
4
+ * This module provides:
5
+ * - Field constants (BN254)
6
+ * - Byte conversion utilities
7
+ * - SHA-256 and tagged hashing
8
+ * - Re-exports from Baby Jubjub (spending keys) and Ed25519 (viewing keys)
9
+ *
10
+ * Baby Jubjub: Twisted Edwards curve for SNARK-friendly spending key operations
11
+ * Ed25519/X25519: Fast standard curve for viewing key ECDH (off-chain only)
12
+ *
13
+ * @module crypto
14
+ */
15
+ /** BN254 field prime (used by circom/snarkjs, also Baby Jubjub base field) */
16
+ export declare const BN254_FIELD_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
17
+ export { BABYJUB_FIELD_PRIME, BABYJUB_A, BABYJUB_D, BABYJUB_ORDER, BABYJUB_BASE8, BABYJUB_IDENTITY, babyJubAdd, babyJubDouble, babyJubMul, babyJubNegate, isOnBabyJubCurve, isIdentity, babyJubCompress, babyJubDecompress, generateBabyJubKeyPair, deriveBabyJubKeyFromSeed, babyJubScalarFromBytes, babyJubScalarToBytes, type BabyJubPoint, } from "./crypto-babyjub";
18
+ export { ed25519GenerateKeyPair, ed25519GetPublicKey, ed25519DeriveKeyFromSeed, ed25519PubToX25519, ed25519PrivToX25519, x25519Ecdh, x25519PubFromPriv, x25519EcdhRaw, deriveAmountKey, encryptAmountEd25519, decryptAmountEd25519, encryptNoteData, decryptNoteData, } from "./crypto-ed25519";
19
+ /**
20
+ * Generate a random field element (< BN254 prime)
21
+ */
22
+ export declare function randomFieldElement(): bigint;
23
+ /**
24
+ * Convert bigint to 32-byte Uint8Array (big-endian)
25
+ */
26
+ export declare function bigintToBytes(value: bigint): Uint8Array;
27
+ /**
28
+ * Convert Uint8Array to bigint (big-endian)
29
+ */
30
+ export declare function bytesToBigint(bytes: Uint8Array): bigint;
31
+ /**
32
+ * Convert hex string to Uint8Array
33
+ */
34
+ export declare function hexToBytes(hex: string): Uint8Array;
35
+ /**
36
+ * Convert Uint8Array to hex string
37
+ */
38
+ export declare function bytesToHex(bytes: Uint8Array): string;
39
+ /**
40
+ * SHA-256 hash using @noble/hashes
41
+ */
42
+ export declare function sha256Hash(data: Uint8Array): Uint8Array;
43
+ /**
44
+ * Double SHA256 hash (Bitcoin standard)
45
+ */
46
+ export declare function doubleSha256(data: Uint8Array): Uint8Array;
47
+ /**
48
+ * Tagged hash as used in BIP-340/341 (Taproot)
49
+ * H_tag(x) = SHA256(SHA256(tag) || SHA256(tag) || x)
50
+ */
51
+ export declare function taggedHash(tag: string, data: Uint8Array): Uint8Array;
52
+ /**
53
+ * Derive a scalar from bytes (reduces modulo Baby Jubjub subgroup order)
54
+ */
55
+ export declare function scalarFromBytes(bytes: Uint8Array): bigint;
56
+ /**
57
+ * Convert a bigint scalar to 32 bytes (big-endian)
58
+ */
59
+ export declare function scalarToBytes(scalar: bigint): Uint8Array;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Cryptographic utilities for UTXOpia
3
+ *
4
+ * This module provides:
5
+ * - Field constants (BN254)
6
+ * - Byte conversion utilities
7
+ * - SHA-256 and tagged hashing
8
+ * - Re-exports from Baby Jubjub (spending keys) and Ed25519 (viewing keys)
9
+ *
10
+ * Baby Jubjub: Twisted Edwards curve for SNARK-friendly spending key operations
11
+ * Ed25519/X25519: Fast standard curve for viewing key ECDH (off-chain only)
12
+ *
13
+ * @module crypto
14
+ */
15
+ import { sha256 } from "@noble/hashes/sha2.js";
16
+ // =============================================================================
17
+ // Field Constants
18
+ // =============================================================================
19
+ /** BN254 field prime (used by circom/snarkjs, also Baby Jubjub base field) */
20
+ export const BN254_FIELD_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
21
+ // =============================================================================
22
+ // Re-exports from Baby Jubjub (spending keys)
23
+ // =============================================================================
24
+ export { BABYJUB_FIELD_PRIME, BABYJUB_A, BABYJUB_D, BABYJUB_ORDER, BABYJUB_BASE8, BABYJUB_IDENTITY, babyJubAdd, babyJubDouble, babyJubMul, babyJubNegate, isOnBabyJubCurve, isIdentity, babyJubCompress, babyJubDecompress, generateBabyJubKeyPair, deriveBabyJubKeyFromSeed, babyJubScalarFromBytes, babyJubScalarToBytes, } from "./crypto-babyjub";
25
+ // =============================================================================
26
+ // Re-exports from Ed25519 (viewing keys)
27
+ // =============================================================================
28
+ export { ed25519GenerateKeyPair, ed25519GetPublicKey, ed25519DeriveKeyFromSeed, ed25519PubToX25519, ed25519PrivToX25519, x25519Ecdh, x25519PubFromPriv, x25519EcdhRaw, deriveAmountKey, encryptAmountEd25519, decryptAmountEd25519, encryptNoteData, decryptNoteData, } from "./crypto-ed25519";
29
+ // =============================================================================
30
+ // Byte Conversion Utilities
31
+ // =============================================================================
32
+ /**
33
+ * Generate a random field element (< BN254 prime)
34
+ */
35
+ export function randomFieldElement() {
36
+ const bytes = new Uint8Array(32);
37
+ crypto.getRandomValues(bytes);
38
+ return bytesToBigint(bytes) % BN254_FIELD_PRIME;
39
+ }
40
+ /**
41
+ * Convert bigint to 32-byte Uint8Array (big-endian)
42
+ */
43
+ export function bigintToBytes(value) {
44
+ const bytes = new Uint8Array(32);
45
+ let temp = value;
46
+ for (let i = 31; i >= 0; i--) {
47
+ bytes[i] = Number(temp & 0xffn);
48
+ temp = temp >> 8n;
49
+ }
50
+ return bytes;
51
+ }
52
+ /**
53
+ * Convert Uint8Array to bigint (big-endian)
54
+ */
55
+ export function bytesToBigint(bytes) {
56
+ let result = 0n;
57
+ for (let i = 0; i < bytes.length; i++) {
58
+ result = (result << 8n) | BigInt(bytes[i]);
59
+ }
60
+ return result;
61
+ }
62
+ /**
63
+ * Convert hex string to Uint8Array
64
+ */
65
+ export function hexToBytes(hex) {
66
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
67
+ const bytes = new Uint8Array(cleanHex.length / 2);
68
+ for (let i = 0; i < cleanHex.length; i += 2) {
69
+ bytes[i / 2] = parseInt(cleanHex.substr(i, 2), 16);
70
+ }
71
+ return bytes;
72
+ }
73
+ /**
74
+ * Convert Uint8Array to hex string
75
+ */
76
+ export function bytesToHex(bytes) {
77
+ return Array.from(bytes)
78
+ .map((b) => b.toString(16).padStart(2, "0"))
79
+ .join("");
80
+ }
81
+ // =============================================================================
82
+ // Hashing Utilities
83
+ // =============================================================================
84
+ /**
85
+ * SHA-256 hash using @noble/hashes
86
+ */
87
+ export function sha256Hash(data) {
88
+ return sha256(data);
89
+ }
90
+ /**
91
+ * Double SHA256 hash (Bitcoin standard)
92
+ */
93
+ export function doubleSha256(data) {
94
+ return sha256(sha256(data));
95
+ }
96
+ /**
97
+ * Tagged hash as used in BIP-340/341 (Taproot)
98
+ * H_tag(x) = SHA256(SHA256(tag) || SHA256(tag) || x)
99
+ */
100
+ export function taggedHash(tag, data) {
101
+ const encoder = new TextEncoder();
102
+ const tagBytes = encoder.encode(tag);
103
+ const tagHash = sha256(tagBytes);
104
+ // Concatenate: SHA256(tag) || SHA256(tag) || data
105
+ const combined = new Uint8Array(64 + data.length);
106
+ combined.set(tagHash, 0);
107
+ combined.set(tagHash, 32);
108
+ combined.set(data, 64);
109
+ return sha256(combined);
110
+ }
111
+ // =============================================================================
112
+ // Scalar Utilities (curve-agnostic, uses Baby Jubjub order)
113
+ // =============================================================================
114
+ import { BABYJUB_ORDER } from "./crypto-babyjub";
115
+ function mod(n, p) {
116
+ const result = n % p;
117
+ return result >= 0n ? result : result + p;
118
+ }
119
+ /**
120
+ * Derive a scalar from bytes (reduces modulo Baby Jubjub subgroup order)
121
+ */
122
+ export function scalarFromBytes(bytes) {
123
+ let result = 0n;
124
+ for (let i = 0; i < bytes.length; i++) {
125
+ result = (result << 8n) | BigInt(bytes[i]);
126
+ }
127
+ return mod(result, BABYJUB_ORDER);
128
+ }
129
+ /**
130
+ * Convert a bigint scalar to 32 bytes (big-endian)
131
+ */
132
+ export function scalarToBytes(scalar) {
133
+ const bytes = new Uint8Array(32);
134
+ let temp = mod(scalar, BABYJUB_ORDER);
135
+ for (let i = 31; i >= 0; i--) {
136
+ bytes[i] = Number(temp & 0xffn);
137
+ temp = temp >> 8n;
138
+ }
139
+ return bytes;
140
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * EventClient — Unified event stream client extending AnnouncementClient
3
+ *
4
+ * Handles all three backend event types via /ws/events:
5
+ * - leaf_inserted (tree updates)
6
+ * - nullifier_spent
7
+ * - stealth_announcement (delegated to parent AnnouncementClient)
8
+ *
9
+ * Also provides REST helpers for tree status and nullifier fetching.
10
+ */
11
+ import { AnnouncementClient } from "./announcement-client";
12
+ export interface LeafInsertedEvent {
13
+ type: "leaf_inserted";
14
+ leaf_index: number;
15
+ commitment: string;
16
+ new_root: string;
17
+ }
18
+ export interface NullifierSpentEvent {
19
+ type: "nullifier_spent";
20
+ nullifier_hash: string;
21
+ slot: number;
22
+ }
23
+ export interface AnnouncementEvent {
24
+ type: "stealth_announcement";
25
+ announcement_type: number;
26
+ ephemeral_pub: string;
27
+ encrypted_amount: string;
28
+ commitment: string;
29
+ leaf_index: number;
30
+ block_time?: number;
31
+ }
32
+ export type ServerEvent = LeafInsertedEvent | NullifierSpentEvent | AnnouncementEvent;
33
+ export type EventListener<T> = (event: T) => void;
34
+ export interface TreeStatusResponse {
35
+ root: string;
36
+ next_index: number;
37
+ size: number;
38
+ }
39
+ export interface NullifierPdasResponse {
40
+ pdas: string[];
41
+ total: number;
42
+ latest_slot: number;
43
+ }
44
+ export declare class EventClient extends AnnouncementClient {
45
+ private treeListeners;
46
+ private nullifierListeners;
47
+ private spentNullifiers;
48
+ private nullifierLatestSlot;
49
+ /** Subscribe to tree (leaf_inserted) events */
50
+ onTreeUpdate(listener: EventListener<LeafInsertedEvent>): () => void;
51
+ /** Subscribe to nullifier (nullifier_spent) events */
52
+ onNullifierSpent(listener: EventListener<NullifierSpentEvent>): () => void;
53
+ /** Fetch tree status from backend */
54
+ fetchTreeStatus(): Promise<TreeStatusResponse | null>;
55
+ /** Fetch spent nullifier PDAs with incremental caching */
56
+ fetchSpentNullifiers(): Promise<Set<string>>;
57
+ /** Check if a specific nullifier PDA is in the spent set */
58
+ isNullifierSpent(pda: string): boolean;
59
+ /** Stop client, close WS, clear extra listeners */
60
+ close(): void;
61
+ protected connectWs(): void;
62
+ private dispatchEvent;
63
+ private handleAnnouncementEvent;
64
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * EventClient — Unified event stream client extending AnnouncementClient
3
+ *
4
+ * Handles all three backend event types via /ws/events:
5
+ * - leaf_inserted (tree updates)
6
+ * - nullifier_spent
7
+ * - stealth_announcement (delegated to parent AnnouncementClient)
8
+ *
9
+ * Also provides REST helpers for tree status and nullifier fetching.
10
+ */
11
+ import { AnnouncementClient, } from "./announcement-client";
12
+ import { hexToBytes } from "./crypto";
13
+ // ---------------------------------------------------------------------------
14
+ // EventClient
15
+ // ---------------------------------------------------------------------------
16
+ export class EventClient extends AnnouncementClient {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.treeListeners = new Set();
20
+ this.nullifierListeners = new Set();
21
+ this.spentNullifiers = new Set();
22
+ this.nullifierLatestSlot = 0;
23
+ }
24
+ /** Subscribe to tree (leaf_inserted) events */
25
+ onTreeUpdate(listener) {
26
+ this.treeListeners.add(listener);
27
+ return () => this.treeListeners.delete(listener);
28
+ }
29
+ /** Subscribe to nullifier (nullifier_spent) events */
30
+ onNullifierSpent(listener) {
31
+ this.nullifierListeners.add(listener);
32
+ return () => this.nullifierListeners.delete(listener);
33
+ }
34
+ /** Fetch tree status from backend */
35
+ async fetchTreeStatus() {
36
+ try {
37
+ const resp = await fetch(`${this.config.backendUrl}/api/tree/status`, { signal: AbortSignal.timeout(this.restTimeout) });
38
+ if (!resp.ok)
39
+ return null;
40
+ return (await resp.json());
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ /** Fetch spent nullifier PDAs with incremental caching */
47
+ async fetchSpentNullifiers() {
48
+ try {
49
+ const since = this.nullifierLatestSlot > 0
50
+ ? `?since=${this.nullifierLatestSlot}`
51
+ : "";
52
+ const resp = await fetch(`${this.config.backendUrl}/api/nullifiers${since}`, { signal: AbortSignal.timeout(this.restTimeout) });
53
+ if (!resp.ok)
54
+ return this.spentNullifiers;
55
+ const data = await resp.json();
56
+ for (const pda of data.pdas || [])
57
+ this.spentNullifiers.add(pda);
58
+ if (data.latest_slot > this.nullifierLatestSlot) {
59
+ this.nullifierLatestSlot = data.latest_slot;
60
+ }
61
+ return this.spentNullifiers;
62
+ }
63
+ catch {
64
+ return this.spentNullifiers;
65
+ }
66
+ }
67
+ /** Check if a specific nullifier PDA is in the spent set */
68
+ isNullifierSpent(pda) {
69
+ return this.spentNullifiers.has(pda);
70
+ }
71
+ /** Stop client, close WS, clear extra listeners */
72
+ close() {
73
+ super.close();
74
+ this.treeListeners.clear();
75
+ this.nullifierListeners.clear();
76
+ }
77
+ // -----------------------------------------------------------------------
78
+ // Override: connect to /ws/events instead of /ws/announcements
79
+ // -----------------------------------------------------------------------
80
+ connectWs() {
81
+ if (this.closed)
82
+ return;
83
+ try {
84
+ const url = `${this.wsUrl}/ws/events`;
85
+ this.ws = new WebSocket(url);
86
+ this.ws.onopen = () => {
87
+ this.wsConnected = true;
88
+ this.wsReconnectDelay = 1000;
89
+ };
90
+ this.ws.onmessage = (event) => {
91
+ try {
92
+ const data = JSON.parse(typeof event.data === "string" ? event.data : "");
93
+ this.dispatchEvent(data);
94
+ }
95
+ catch {
96
+ // Ignore malformed messages
97
+ }
98
+ };
99
+ this.ws.onclose = () => {
100
+ this.wsConnected = false;
101
+ this.ws = null;
102
+ this.scheduleReconnect();
103
+ };
104
+ this.ws.onerror = () => {
105
+ // onclose will fire after onerror
106
+ };
107
+ }
108
+ catch {
109
+ this.wsConnected = false;
110
+ this.scheduleReconnect();
111
+ }
112
+ }
113
+ // -----------------------------------------------------------------------
114
+ // Internal: route events to appropriate listeners
115
+ // -----------------------------------------------------------------------
116
+ dispatchEvent(event) {
117
+ switch (event.type) {
118
+ case "stealth_announcement":
119
+ this.handleAnnouncementEvent(event);
120
+ break;
121
+ case "leaf_inserted":
122
+ for (const listener of this.treeListeners) {
123
+ try {
124
+ listener(event);
125
+ }
126
+ catch { /* ignore */ }
127
+ }
128
+ break;
129
+ case "nullifier_spent":
130
+ for (const listener of this.nullifierListeners) {
131
+ try {
132
+ listener(event);
133
+ }
134
+ catch { /* ignore */ }
135
+ }
136
+ break;
137
+ }
138
+ }
139
+ handleAnnouncementEvent(event) {
140
+ const announcement = {
141
+ announcementType: event.announcement_type,
142
+ ephemeralPub: hexToBytes(event.ephemeral_pub),
143
+ encryptedAmount: hexToBytes(event.encrypted_amount),
144
+ commitment: hexToBytes(event.commitment),
145
+ leafIndex: event.leaf_index,
146
+ blockTime: event.block_time ?? 0,
147
+ };
148
+ // Update cache (same logic as parent AnnouncementClient.connectWs)
149
+ if (announcement.leafIndex > this.latestLeafIndex) {
150
+ this.latestLeafIndex = announcement.leafIndex;
151
+ }
152
+ if (!this.cachedAnnouncements.some((a) => a.leafIndex === announcement.leafIndex)) {
153
+ this.cachedAnnouncements.push(announcement);
154
+ }
155
+ // Notify announcement listeners
156
+ for (const listener of this.listeners) {
157
+ try {
158
+ listener([announcement]);
159
+ }
160
+ catch {
161
+ // Listener errors shouldn't crash the client
162
+ }
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Event parser for UTXOpia sol_log_data events
3
+ *
4
+ * Events are emitted by the on-chain program as base64-encoded log data.
5
+ * Transaction logs contain lines like: "Program data: <base64>"
6
+ * Each base64 segment decodes to one slice from sol_log_data.
7
+ *
8
+ * ## Events
9
+ *
10
+ * - 0x02 NullifierSpent: disc(1) + hash(32) + op_type(1) = 34 bytes
11
+ * - 0x03 StealthAnnouncement: disc(1) + type(1) + ephemeral(32) + amount(8) + commitment(32) + leaf_index(4) = 78 bytes
12
+ * - 0x0B NullifiersBatch: flat payload in single segment
13
+ * - 0x0C AnnouncementsBatch: flat payload in single segment
14
+ */
15
+ /** Event discriminators matching contracts/programs/utxopia/src/utils/events.rs */
16
+ export declare const EVENT_NULLIFIER_SPENT = 2;
17
+ export declare const EVENT_STEALTH_ANNOUNCEMENT = 3;
18
+ export declare const EVENT_NULLIFIERS_BATCH = 11;
19
+ export declare const EVENT_ANNOUNCEMENTS_BATCH = 12;
20
+ /** Phase 2: sender memo (XChaCha20-Poly1305 AEAD payload). */
21
+ export declare const EVENT_SENDER_MEMO = 18;
22
+ export declare const EVENT_BTC_ORIGIN_ATTESTATION = 21;
23
+ /** Method-Y: auditor ciphertext emitted alongside every shielded deposit into a permissioned pool. */
24
+ export declare const EVENT_AUDITOR_CIPHERTEXT = 22;
25
+ /** Parsed nullifier spent event */
26
+ export interface NullifierSpentEvent {
27
+ type: "nullifier_spent";
28
+ nullifierHash: Uint8Array;
29
+ operationType: number;
30
+ }
31
+ /** Parsed stealth announcement event (includes token_id) */
32
+ export interface StealthAnnouncementEvent {
33
+ type: "stealth_announcement";
34
+ announcementType: number;
35
+ ephemeralPub: Uint8Array;
36
+ encryptedAmount: Uint8Array;
37
+ commitment: Uint8Array;
38
+ leafIndex: number;
39
+ tokenId?: Uint8Array;
40
+ }
41
+ /** Parsed sender memo event (Phase 2). */
42
+ export interface SenderMemoEvent {
43
+ type: "sender_memo";
44
+ /** 24-byte XChaCha20 nonce. */
45
+ nonce: Uint8Array;
46
+ /** 56-byte ChaCha20 ciphertext + Poly1305 tag. */
47
+ ciphertextWithTag: Uint8Array;
48
+ /** Commitment of the output this memo covers (also AAD). */
49
+ commitment: Uint8Array;
50
+ /** Leaf index of the covered output (also AAD). */
51
+ leafIndex: number;
52
+ }
53
+ /**
54
+ * BTC origin attestation, emitted alongside every SPV-verified deposit.
55
+ * Lets third-party auditors anchor commitments to their on-chain BTC
56
+ * origin without trusting our backend.
57
+ *
58
+ * Layout matches the Rust `emit_btc_origin_attestation` in
59
+ * `contracts/programs/utxopia/src/utils/events.rs`.
60
+ */
61
+ export interface BtcOriginAttestationEvent {
62
+ type: "btc_origin_attestation";
63
+ blockHeight: bigint;
64
+ /** Bitcoin deposit txid in internal byte order (same as `complete_deposit` instruction data). */
65
+ depositTxid: Uint8Array;
66
+ /** Sweep transaction's output index that paid the pool. */
67
+ sweepVout: number;
68
+ /** Commitment inserted into the JoinSplit tree for this deposit. */
69
+ commitment: Uint8Array;
70
+ /** Pool-received amount in satoshis (after sweep fees). */
71
+ amountSats: bigint;
72
+ }
73
+ /**
74
+ * Auditor ciphertext event (Method-Y permissioned pools).
75
+ *
76
+ * Emitted alongside every shielded deposit into a permissioned pool so that
77
+ * a designated auditor can decrypt note viewing data off-chain.
78
+ */
79
+ export interface AuditorCiphertextEvent {
80
+ type: "auditor_ciphertext";
81
+ /** 32-byte Poseidon commitment of the shielded note. */
82
+ commitment: Uint8Array;
83
+ /** 112-byte encrypted blob: eph_pub(32) || nonce(24) || ciphertextWithTag(56). */
84
+ blob: Uint8Array;
85
+ }
86
+ export type ProgramEvent = NullifierSpentEvent | StealthAnnouncementEvent | SenderMemoEvent | BtcOriginAttestationEvent | AuditorCiphertextEvent;
87
+ /**
88
+ * Parse a nullifier spent event from decoded sol_log_data segments.
89
+ * Expected: disc(1) + nullifier_hash(32) + op_type(1)
90
+ */
91
+ export declare function parseNullifierSpentEvent(segments: Uint8Array[]): NullifierSpentEvent | null;
92
+ /**
93
+ * Parse a stealth announcement event from decoded sol_log_data segments.
94
+ * v1: disc(1) + type(1) + ephemeral_pub(32) + encrypted_amount(8) + commitment(32) + leaf_index(4) = 6 segments
95
+ * v2: + token_id(32) = 7 segments
96
+ */
97
+ export declare function parseStealthAnnouncementEvent(segments: Uint8Array[]): StealthAnnouncementEvent | null;
98
+ /**
99
+ * Parse an association-set update event (Phase 3) from decoded sol_log_data segments.
100
+ * Layout: disc(1) + new_root(32) + status(1) + version_le(8)
101
+ */
102
+ /**
103
+ * Parse a BTC origin attestation event from decoded sol_log_data segments.
104
+ * Layout: disc(1) + block_height(8 LE) + deposit_txid(32) + sweep_vout(4 LE)
105
+ * + commitment(32) + amount_sats(8 LE)
106
+ */
107
+ export declare function parseBtcOriginAttestationEvent(segments: Uint8Array[]): BtcOriginAttestationEvent | null;
108
+ /**
109
+ * Parse a sender memo event (Phase 2) from decoded sol_log_data segments.
110
+ * Layout: disc(1) + nonce(24) + ciphertext_and_tag(56) + commitment(32) + leaf_index(4)
111
+ */
112
+ export declare function parseSenderMemoEvent(segments: Uint8Array[]): SenderMemoEvent | null;
113
+ /**
114
+ * Parse an auditor ciphertext event (Method-Y) from decoded sol_log_data segments.
115
+ * Layout: disc(1) + commitment(32) + blob(112)
116
+ */
117
+ export declare function parseAuditorCiphertextEvent(segments: Uint8Array[]): AuditorCiphertextEvent | null;
118
+ /**
119
+ * Parse program events from Solana transaction log messages.
120
+ *
121
+ * sol_log_data emits log lines in the format:
122
+ * "Program data: <base64_segment1> <base64_segment2> ..."
123
+ *
124
+ * @param logs - Array of log message strings from a transaction
125
+ * @param programId - Optional program ID to filter events (matches "Program <id> invoke" blocks)
126
+ */
127
+ export declare function parseProgramEvents(logs: string[], programId?: string): ProgramEvent[];