@utxopia/sdk 0.1.0-alpha.2 → 0.1.0-alpha.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 (142) hide show
  1. package/README.md +214 -108
  2. package/{packages/sdk/dist → dist}/client.d.ts +25 -1
  3. package/{packages/sdk/dist → dist}/client.js +36 -1
  4. package/{packages/sdk/dist → dist}/crypto-ed25519.d.ts +14 -0
  5. package/{packages/sdk/dist → dist}/crypto-ed25519.js +14 -0
  6. package/{packages/sdk/dist → dist}/index.d.ts +3 -3
  7. package/{packages/sdk/dist → dist}/index.js +3 -3
  8. package/{packages/sdk/dist → dist}/instructions.d.ts +28 -11
  9. package/{packages/sdk/dist → dist}/instructions.js +45 -15
  10. package/{packages/sdk/dist → dist}/psbt.d.ts +12 -2
  11. package/{packages/sdk/dist → dist}/psbt.js +17 -12
  12. package/{packages/sdk/dist → dist}/stealth.d.ts +104 -9
  13. package/{packages/sdk/dist → dist}/stealth.js +146 -14
  14. package/{packages/sdk/dist → dist}/taproot.d.ts +39 -2
  15. package/{packages/sdk/dist → dist}/taproot.js +54 -2
  16. package/package.json +86 -63
  17. package/src/announcement-client.ts +457 -0
  18. package/src/auditor-ciphertext.ts +181 -0
  19. package/src/auditor.ts +409 -0
  20. package/src/bitcoin/ika.ts +103 -0
  21. package/src/bitcoin/index.ts +5 -0
  22. package/src/bound-params.ts +322 -0
  23. package/src/chadbuffer.ts +603 -0
  24. package/src/circomlibjs.d.ts +51 -0
  25. package/src/claim-link.ts +53 -0
  26. package/src/client.ts +638 -0
  27. package/src/commitment-tree.ts +736 -0
  28. package/src/config.ts +772 -0
  29. package/src/core/esplora.ts +332 -0
  30. package/src/core/mempool.ts +159 -0
  31. package/src/crypto-babyjub.ts +385 -0
  32. package/src/crypto-ed25519.ts +297 -0
  33. package/src/crypto.ts +199 -0
  34. package/src/event-client.ts +231 -0
  35. package/src/events.ts +384 -0
  36. package/src/explorer.ts +300 -0
  37. package/src/index.ts +902 -0
  38. package/src/instructions.ts +2820 -0
  39. package/src/keys.ts +1228 -0
  40. package/src/logger.ts +41 -0
  41. package/src/magicblock.ts +278 -0
  42. package/src/merkle.ts +197 -0
  43. package/src/note.ts +754 -0
  44. package/src/pda.ts +516 -0
  45. package/src/pool-state.ts +176 -0
  46. package/src/poseidon.ts +175 -0
  47. package/src/prover/index.ts +19 -0
  48. package/src/prover/mobile.ts +303 -0
  49. package/src/prover/web.ts +771 -0
  50. package/src/psbt.ts +345 -0
  51. package/src/selective-disclosure.ts +284 -0
  52. package/src/sender-memo.ts +343 -0
  53. package/src/snarkjs.d.ts +19 -0
  54. package/src/sns-resolver.ts +333 -0
  55. package/src/solana/connection.ts +189 -0
  56. package/src/solana/priority-fee.ts +201 -0
  57. package/src/spend-doc.ts +163 -0
  58. package/src/stealth.ts +1477 -0
  59. package/src/taproot.ts +707 -0
  60. package/src/token-registry.ts +207 -0
  61. package/src/utils/encoding.ts +33 -0
  62. package/src/vk-registry.ts +295 -0
  63. package/LICENSE +0 -21
  64. package/packages/btc-client/src/esplora-client.ts +0 -153
  65. package/packages/btc-client/src/index.ts +0 -3
  66. package/packages/btc-client/src/op-return.ts +0 -93
  67. package/packages/btc-client/src/types.ts +0 -112
  68. package/packages/sdk/README.md +0 -277
  69. /package/{packages/sdk/dist → dist}/announcement-client.d.ts +0 -0
  70. /package/{packages/sdk/dist → dist}/announcement-client.js +0 -0
  71. /package/{packages/sdk/dist → dist}/auditor-ciphertext.d.ts +0 -0
  72. /package/{packages/sdk/dist → dist}/auditor-ciphertext.js +0 -0
  73. /package/{packages/sdk/dist → dist}/auditor.d.ts +0 -0
  74. /package/{packages/sdk/dist → dist}/auditor.js +0 -0
  75. /package/{packages/sdk/dist → dist}/bitcoin/ika.d.ts +0 -0
  76. /package/{packages/sdk/dist → dist}/bitcoin/ika.js +0 -0
  77. /package/{packages/sdk/dist → dist}/bitcoin/index.d.ts +0 -0
  78. /package/{packages/sdk/dist → dist}/bitcoin/index.js +0 -0
  79. /package/{packages/sdk/dist → dist}/bound-params.d.ts +0 -0
  80. /package/{packages/sdk/dist → dist}/bound-params.js +0 -0
  81. /package/{packages/sdk/dist → dist}/chadbuffer.d.ts +0 -0
  82. /package/{packages/sdk/dist → dist}/chadbuffer.js +0 -0
  83. /package/{packages/sdk/dist → dist}/claim-link.d.ts +0 -0
  84. /package/{packages/sdk/dist → dist}/claim-link.js +0 -0
  85. /package/{packages/sdk/dist → dist}/commitment-tree.d.ts +0 -0
  86. /package/{packages/sdk/dist → dist}/commitment-tree.js +0 -0
  87. /package/{packages/sdk/dist → dist}/config.d.ts +0 -0
  88. /package/{packages/sdk/dist → dist}/config.js +0 -0
  89. /package/{packages/sdk/dist → dist}/core/esplora.d.ts +0 -0
  90. /package/{packages/sdk/dist → dist}/core/esplora.js +0 -0
  91. /package/{packages/sdk/dist → dist}/core/mempool.d.ts +0 -0
  92. /package/{packages/sdk/dist → dist}/core/mempool.js +0 -0
  93. /package/{packages/sdk/dist → dist}/crypto-babyjub.d.ts +0 -0
  94. /package/{packages/sdk/dist → dist}/crypto-babyjub.js +0 -0
  95. /package/{packages/sdk/dist → dist}/crypto.d.ts +0 -0
  96. /package/{packages/sdk/dist → dist}/crypto.js +0 -0
  97. /package/{packages/sdk/dist → dist}/event-client.d.ts +0 -0
  98. /package/{packages/sdk/dist → dist}/event-client.js +0 -0
  99. /package/{packages/sdk/dist → dist}/events.d.ts +0 -0
  100. /package/{packages/sdk/dist → dist}/events.js +0 -0
  101. /package/{packages/sdk/dist → dist}/explorer.d.ts +0 -0
  102. /package/{packages/sdk/dist → dist}/explorer.js +0 -0
  103. /package/{packages/sdk/dist → dist}/keys.d.ts +0 -0
  104. /package/{packages/sdk/dist → dist}/keys.js +0 -0
  105. /package/{packages/sdk/dist → dist}/logger.d.ts +0 -0
  106. /package/{packages/sdk/dist → dist}/logger.js +0 -0
  107. /package/{packages/sdk/dist → dist}/magicblock.d.ts +0 -0
  108. /package/{packages/sdk/dist → dist}/magicblock.js +0 -0
  109. /package/{packages/sdk/dist → dist}/merkle.d.ts +0 -0
  110. /package/{packages/sdk/dist → dist}/merkle.js +0 -0
  111. /package/{packages/sdk/dist → dist}/note.d.ts +0 -0
  112. /package/{packages/sdk/dist → dist}/note.js +0 -0
  113. /package/{packages/sdk/dist → dist}/pda.d.ts +0 -0
  114. /package/{packages/sdk/dist → dist}/pda.js +0 -0
  115. /package/{packages/sdk/dist → dist}/pool-state.d.ts +0 -0
  116. /package/{packages/sdk/dist → dist}/pool-state.js +0 -0
  117. /package/{packages/sdk/dist → dist}/poseidon.d.ts +0 -0
  118. /package/{packages/sdk/dist → dist}/poseidon.js +0 -0
  119. /package/{packages/sdk/dist → dist}/prover/index.d.ts +0 -0
  120. /package/{packages/sdk/dist → dist}/prover/index.js +0 -0
  121. /package/{packages/sdk/dist → dist}/prover/mobile.d.ts +0 -0
  122. /package/{packages/sdk/dist → dist}/prover/mobile.js +0 -0
  123. /package/{packages/sdk/dist → dist}/prover/web.d.ts +0 -0
  124. /package/{packages/sdk/dist → dist}/prover/web.js +0 -0
  125. /package/{packages/sdk/dist → dist}/selective-disclosure.d.ts +0 -0
  126. /package/{packages/sdk/dist → dist}/selective-disclosure.js +0 -0
  127. /package/{packages/sdk/dist → dist}/sender-memo.d.ts +0 -0
  128. /package/{packages/sdk/dist → dist}/sender-memo.js +0 -0
  129. /package/{packages/sdk/dist → dist}/sns-resolver.d.ts +0 -0
  130. /package/{packages/sdk/dist → dist}/sns-resolver.js +0 -0
  131. /package/{packages/sdk/dist → dist}/solana/connection.d.ts +0 -0
  132. /package/{packages/sdk/dist → dist}/solana/connection.js +0 -0
  133. /package/{packages/sdk/dist → dist}/solana/priority-fee.d.ts +0 -0
  134. /package/{packages/sdk/dist → dist}/solana/priority-fee.js +0 -0
  135. /package/{packages/sdk/dist → dist}/spend-doc.d.ts +0 -0
  136. /package/{packages/sdk/dist → dist}/spend-doc.js +0 -0
  137. /package/{packages/sdk/dist → dist}/token-registry.d.ts +0 -0
  138. /package/{packages/sdk/dist → dist}/token-registry.js +0 -0
  139. /package/{packages/sdk/dist → dist}/utils/encoding.d.ts +0 -0
  140. /package/{packages/sdk/dist → dist}/utils/encoding.js +0 -0
  141. /package/{packages/sdk/dist → dist}/vk-registry.d.ts +0 -0
  142. /package/{packages/sdk/dist → dist}/vk-registry.js +0 -0
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Esplora Client - Platform-agnostic HTTP client for Bitcoin blockchain queries
3
+ *
4
+ * Uses standard fetch API (works in browser, Node.js 18+, React Native)
5
+ * Supports mempool.space API for testnet/mainnet
6
+ */
7
+
8
+ import { hexToBytes } from "../crypto";
9
+
10
+ export interface EsploraTransaction {
11
+ txid: string;
12
+ version: number;
13
+ locktime: number;
14
+ vin: EsploraVin[];
15
+ vout: EsploraVout[];
16
+ size: number;
17
+ weight: number;
18
+ fee: number;
19
+ status: EsploraStatus;
20
+ }
21
+
22
+ export interface EsploraVin {
23
+ txid: string;
24
+ vout: number;
25
+ prevout: EsploraVout | null;
26
+ scriptsig: string;
27
+ scriptsig_asm: string;
28
+ witness?: string[];
29
+ is_coinbase: boolean;
30
+ sequence: number;
31
+ }
32
+
33
+ export interface EsploraVout {
34
+ scriptpubkey: string;
35
+ scriptpubkey_asm: string;
36
+ scriptpubkey_type: string;
37
+ scriptpubkey_address?: string;
38
+ value: number;
39
+ }
40
+
41
+ export interface EsploraStatus {
42
+ confirmed: boolean;
43
+ block_height?: number;
44
+ block_hash?: string;
45
+ block_time?: number;
46
+ }
47
+
48
+ export interface EsploraAddressInfo {
49
+ address: string;
50
+ chain_stats: {
51
+ funded_txo_count: number;
52
+ funded_txo_sum: number;
53
+ spent_txo_count: number;
54
+ spent_txo_sum: number;
55
+ tx_count: number;
56
+ };
57
+ mempool_stats: {
58
+ funded_txo_count: number;
59
+ funded_txo_sum: number;
60
+ spent_txo_count: number;
61
+ spent_txo_sum: number;
62
+ tx_count: number;
63
+ };
64
+ }
65
+
66
+ export interface EsploraUtxo {
67
+ txid: string;
68
+ vout: number;
69
+ status: EsploraStatus;
70
+ value: number;
71
+ }
72
+
73
+ export interface EsploraMerkleProof {
74
+ block_height: number;
75
+ merkle: string[];
76
+ pos: number;
77
+ }
78
+
79
+ export type EsploraNetwork = "mainnet" | "testnet" | "testnet4" | "signet" | "regtest";
80
+
81
+ const NETWORK_URLS: Record<EsploraNetwork, string> = {
82
+ mainnet: "https://mempool.space/api",
83
+ testnet: "https://mempool.space/testnet/api",
84
+ testnet4: "https://mempool.space/testnet4/api",
85
+ signet: "https://mempool.space/signet/api",
86
+ regtest: "http://localhost:2140",
87
+ };
88
+
89
+ export class EsploraClient {
90
+ private baseUrl: string;
91
+ private network: EsploraNetwork;
92
+
93
+ constructor(
94
+ networkOrUrl: EsploraNetwork | string = "testnet",
95
+ customBaseUrl?: string
96
+ ) {
97
+ if (customBaseUrl) {
98
+ this.baseUrl = customBaseUrl.replace(/\/$/, "");
99
+ this.network = networkOrUrl as EsploraNetwork;
100
+ } else if (networkOrUrl in NETWORK_URLS) {
101
+ this.network = networkOrUrl as EsploraNetwork;
102
+ this.baseUrl = NETWORK_URLS[this.network];
103
+ } else {
104
+ // Assume it's a custom URL
105
+ this.baseUrl = networkOrUrl.replace(/\/$/, "");
106
+ this.network = "testnet";
107
+ }
108
+ }
109
+
110
+ private async fetch<T>(endpoint: string): Promise<T> {
111
+ const url = `${this.baseUrl}${endpoint}`;
112
+ const res = await fetch(url);
113
+
114
+ if (!res.ok) {
115
+ throw new Error(`Esplora API error: ${res.status} ${res.statusText}`);
116
+ }
117
+
118
+ const contentType = res.headers.get("content-type");
119
+ if (contentType?.includes("application/json")) {
120
+ return res.json() as Promise<T>;
121
+ }
122
+
123
+ // For text responses (like block height)
124
+ const text = await res.text();
125
+ return text as T;
126
+ }
127
+
128
+ // =========================================================================
129
+ // Address endpoints
130
+ // =========================================================================
131
+
132
+ async getAddress(address: string): Promise<EsploraAddressInfo> {
133
+ return this.fetch<EsploraAddressInfo>(`/address/${address}`);
134
+ }
135
+
136
+ async getAddressTxs(
137
+ address: string,
138
+ lastSeenTxid?: string
139
+ ): Promise<EsploraTransaction[]> {
140
+ const endpoint = lastSeenTxid
141
+ ? `/address/${address}/txs/chain/${lastSeenTxid}`
142
+ : `/address/${address}/txs`;
143
+ return this.fetch<EsploraTransaction[]>(endpoint);
144
+ }
145
+
146
+ async getAddressTxsMempool(address: string): Promise<EsploraTransaction[]> {
147
+ return this.fetch<EsploraTransaction[]>(`/address/${address}/txs/mempool`);
148
+ }
149
+
150
+ async getAddressUtxos(address: string): Promise<EsploraUtxo[]> {
151
+ return this.fetch<EsploraUtxo[]>(`/address/${address}/utxo`);
152
+ }
153
+
154
+ // =========================================================================
155
+ // Transaction endpoints
156
+ // =========================================================================
157
+
158
+ async getTransaction(txid: string): Promise<EsploraTransaction> {
159
+ return this.fetch<EsploraTransaction>(`/tx/${txid}`);
160
+ }
161
+
162
+ async getTxStatus(txid: string): Promise<EsploraStatus> {
163
+ return this.fetch<EsploraStatus>(`/tx/${txid}/status`);
164
+ }
165
+
166
+ async getTxHex(txid: string): Promise<string> {
167
+ return this.fetch<string>(`/tx/${txid}/hex`);
168
+ }
169
+
170
+ async getTxRaw(txid: string): Promise<Uint8Array> {
171
+ const hex = await this.getTxHex(txid);
172
+ return hexToBytes(hex);
173
+ }
174
+
175
+ async getTxMerkleProof(txid: string): Promise<EsploraMerkleProof> {
176
+ return this.fetch<EsploraMerkleProof>(`/tx/${txid}/merkle-proof`);
177
+ }
178
+
179
+ async getTxOutspend(
180
+ txid: string,
181
+ vout: number
182
+ ): Promise<{
183
+ spent: boolean;
184
+ txid?: string;
185
+ vin?: number;
186
+ status?: EsploraStatus;
187
+ }> {
188
+ return this.fetch(`/tx/${txid}/outspend/${vout}`);
189
+ }
190
+
191
+ // =========================================================================
192
+ // Block endpoints
193
+ // =========================================================================
194
+
195
+ async getBlockHeight(): Promise<number> {
196
+ const height = await this.fetch<string>("/blocks/tip/height");
197
+ return parseInt(height, 10);
198
+ }
199
+
200
+ async getBlockHash(height: number): Promise<string> {
201
+ return this.fetch<string>(`/block-height/${height}`);
202
+ }
203
+
204
+ async getBlockHeader(hash: string): Promise<string> {
205
+ return this.fetch<string>(`/block/${hash}/header`);
206
+ }
207
+
208
+ async getBlockTxids(hash: string): Promise<string[]> {
209
+ return this.fetch<string[]>(`/block/${hash}/txids`);
210
+ }
211
+
212
+ // =========================================================================
213
+ // Helper methods
214
+ // =========================================================================
215
+
216
+ async getConfirmations(txid: string): Promise<number> {
217
+ const status = await this.getTxStatus(txid);
218
+ if (!status.confirmed || status.block_height === undefined) {
219
+ return 0;
220
+ }
221
+ const tipHeight = await this.getBlockHeight();
222
+ return tipHeight - status.block_height + 1;
223
+ }
224
+
225
+ async waitForTransaction(
226
+ address: string,
227
+ timeoutMs: number = 600000,
228
+ pollIntervalMs: number = 5000
229
+ ): Promise<EsploraTransaction | null> {
230
+ const startTime = Date.now();
231
+
232
+ while (Date.now() - startTime < timeoutMs) {
233
+ // Check mempool first
234
+ const mempoolTxs = await this.getAddressTxsMempool(address);
235
+ if (mempoolTxs.length > 0) {
236
+ return mempoolTxs[0];
237
+ }
238
+
239
+ // Check confirmed
240
+ const confirmedTxs = await this.getAddressTxs(address);
241
+ if (confirmedTxs.length > 0) {
242
+ return confirmedTxs[0];
243
+ }
244
+
245
+ await sleep(pollIntervalMs);
246
+ }
247
+
248
+ return null;
249
+ }
250
+
251
+ async waitForConfirmations(
252
+ txid: string,
253
+ requiredConfirmations: number = 6,
254
+ timeoutMs: number = 3600000,
255
+ pollIntervalMs: number = 30000
256
+ ): Promise<number> {
257
+ const startTime = Date.now();
258
+
259
+ while (Date.now() - startTime < timeoutMs) {
260
+ const confirmations = await this.getConfirmations(txid);
261
+ if (confirmations >= requiredConfirmations) {
262
+ return confirmations;
263
+ }
264
+ await sleep(pollIntervalMs);
265
+ }
266
+
267
+ return await this.getConfirmations(txid);
268
+ }
269
+
270
+ /**
271
+ * Find a deposit to a specific address matching the expected amount
272
+ */
273
+ async findDeposit(
274
+ address: string,
275
+ expectedAmount?: bigint
276
+ ): Promise<{ tx: EsploraTransaction; vout: number; amount: number } | null> {
277
+ // Check mempool first
278
+ const mempoolTxs = await this.getAddressTxsMempool(address);
279
+ for (const tx of mempoolTxs) {
280
+ const result = this.findMatchingOutput(tx, address, expectedAmount);
281
+ if (result) return result;
282
+ }
283
+
284
+ // Check confirmed transactions
285
+ const confirmedTxs = await this.getAddressTxs(address);
286
+ for (const tx of confirmedTxs) {
287
+ const result = this.findMatchingOutput(tx, address, expectedAmount);
288
+ if (result) return result;
289
+ }
290
+
291
+ return null;
292
+ }
293
+
294
+ private findMatchingOutput(
295
+ tx: EsploraTransaction,
296
+ address: string,
297
+ expectedAmount?: bigint
298
+ ): { tx: EsploraTransaction; vout: number; amount: number } | null {
299
+ for (let i = 0; i < tx.vout.length; i++) {
300
+ const output = tx.vout[i];
301
+ if (output.scriptpubkey_address === address) {
302
+ if (
303
+ expectedAmount === undefined ||
304
+ BigInt(output.value) === expectedAmount
305
+ ) {
306
+ return { tx, vout: i, amount: output.value };
307
+ }
308
+ }
309
+ }
310
+ return null;
311
+ }
312
+
313
+ getNetwork(): EsploraNetwork {
314
+ return this.network;
315
+ }
316
+
317
+ getBaseUrl(): string {
318
+ return this.baseUrl;
319
+ }
320
+ }
321
+
322
+ // =========================================================================
323
+ // Utility functions
324
+ // =========================================================================
325
+
326
+ function sleep(ms: number): Promise<void> {
327
+ return new Promise((resolve) => setTimeout(resolve, ms));
328
+ }
329
+
330
+ // Default client for testnet
331
+ export const esploraTestnet = new EsploraClient("testnet");
332
+ export const esploraMainnet = new EsploraClient("mainnet");
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Mempool.space API Client for SPV Verification
3
+ *
4
+ * Extended client for fetching Bitcoin SPV proof data.
5
+ * Builds on EsploraClient with additional SPV-specific methods.
6
+ */
7
+
8
+ import { EsploraClient, type EsploraNetwork, type EsploraMerkleProof } from "./esplora";
9
+
10
+ // =============================================================================
11
+ // Types
12
+ // =============================================================================
13
+
14
+ export interface BlockHeader {
15
+ height: number;
16
+ hash: string;
17
+ version: number;
18
+ previousBlockHash: string;
19
+ merkleRoot: string;
20
+ timestamp: number;
21
+ bits: number;
22
+ nonce: number;
23
+ /** Raw 80-byte header in hex */
24
+ rawHeader: string;
25
+ }
26
+
27
+ export interface TransactionInfo {
28
+ txid: string;
29
+ confirmed: boolean;
30
+ blockHeight?: number;
31
+ blockHash?: string;
32
+ blockTime?: number;
33
+ }
34
+
35
+ export interface SPVProofData {
36
+ txInfo: TransactionInfo;
37
+ blockHeader: BlockHeader;
38
+ merkleProof: EsploraMerkleProof & { blockHash: string };
39
+ confirmations: number;
40
+ }
41
+
42
+ // =============================================================================
43
+ // MempoolClient (extends EsploraClient with SPV methods)
44
+ // =============================================================================
45
+
46
+ export class MempoolClient extends EsploraClient {
47
+ constructor(network: EsploraNetwork = "testnet") {
48
+ super(network);
49
+ }
50
+
51
+ /**
52
+ * Get transaction info including block hash
53
+ */
54
+ async getTransactionInfo(txid: string): Promise<TransactionInfo> {
55
+ const tx = await this.getTransaction(txid);
56
+ return {
57
+ txid: tx.txid,
58
+ confirmed: tx.status.confirmed,
59
+ blockHeight: tx.status.block_height,
60
+ blockHash: tx.status.block_hash,
61
+ blockTime: tx.status.block_time,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Get block header with raw 80-byte header
67
+ */
68
+ async getBlockHeaderFull(blockHash: string): Promise<BlockHeader> {
69
+ const baseUrl = this.getBaseUrl();
70
+
71
+ // Fetch block info
72
+ const blockRes = await fetch(`${baseUrl}/block/${blockHash}`);
73
+ if (!blockRes.ok) {
74
+ throw new Error(`Failed to fetch block: ${blockRes.statusText}`);
75
+ }
76
+ const blockInfo = await blockRes.json();
77
+
78
+ // Fetch raw header (80 bytes hex)
79
+ const rawHeader = await this.getBlockHeader(blockHash);
80
+
81
+ return {
82
+ height: blockInfo.height,
83
+ hash: blockHash,
84
+ version: blockInfo.version,
85
+ previousBlockHash: blockInfo.previousblockhash,
86
+ merkleRoot: blockInfo.merkle_root,
87
+ timestamp: blockInfo.timestamp,
88
+ bits: blockInfo.bits,
89
+ nonce: blockInfo.nonce,
90
+ rawHeader,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Get block header by height
96
+ */
97
+ async getBlockHeaderByHeight(height: number): Promise<BlockHeader> {
98
+ const blockHash = await this.getBlockHash(height);
99
+ return this.getBlockHeaderFull(blockHash);
100
+ }
101
+
102
+ /**
103
+ * Get all data needed for SPV verification
104
+ */
105
+ async getSPVProofData(txid: string): Promise<SPVProofData> {
106
+ // Get transaction info
107
+ const txInfo = await this.getTransactionInfo(txid);
108
+
109
+ if (!txInfo.confirmed || !txInfo.blockHash) {
110
+ throw new Error("Transaction not confirmed yet");
111
+ }
112
+
113
+ // Get block header
114
+ const blockHeader = await this.getBlockHeaderFull(txInfo.blockHash);
115
+
116
+ // Get merkle proof
117
+ const merkleProof = await this.getTxMerkleProof(txid);
118
+
119
+ // Get confirmations
120
+ const tipHeight = await this.getBlockHeight();
121
+ const confirmations = tipHeight - txInfo.blockHeight! + 1;
122
+
123
+ return {
124
+ txInfo,
125
+ blockHeader,
126
+ merkleProof: {
127
+ ...merkleProof,
128
+ blockHash: txInfo.blockHash,
129
+ },
130
+ confirmations,
131
+ };
132
+ }
133
+ }
134
+
135
+ // =============================================================================
136
+ // Utility Functions
137
+ // =============================================================================
138
+
139
+ /**
140
+ * Reverse bytes (for Bitcoin internal byte order)
141
+ */
142
+ export function reverseBytes(bytes: Uint8Array): Uint8Array {
143
+ const reversed = new Uint8Array(bytes.length);
144
+ for (let i = 0; i < bytes.length; i++) {
145
+ reversed[i] = bytes[bytes.length - 1 - i];
146
+ }
147
+ return reversed;
148
+ }
149
+
150
+ // hexToBytes / bytesToHex live in ../crypto (single source); re-exported here
151
+ // to preserve this module's public surface.
152
+ export { hexToBytes, bytesToHex } from "../crypto";
153
+
154
+ // =============================================================================
155
+ // Default Instances
156
+ // =============================================================================
157
+
158
+ export const mempoolTestnet = new MempoolClient("testnet");
159
+ export const mempoolMainnet = new MempoolClient("mainnet");