otx-btc-wallet-connectors 0.1.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 (142) hide show
  1. package/README.md +554 -0
  2. package/dist/base-IAFq7sd8.d.mts +53 -0
  3. package/dist/base-IAFq7sd8.d.ts +53 -0
  4. package/dist/binance/index.d.mts +81 -0
  5. package/dist/binance/index.d.ts +81 -0
  6. package/dist/binance/index.js +13 -0
  7. package/dist/binance/index.js.map +1 -0
  8. package/dist/binance/index.mjs +4 -0
  9. package/dist/binance/index.mjs.map +1 -0
  10. package/dist/bitget/index.d.mts +84 -0
  11. package/dist/bitget/index.d.ts +84 -0
  12. package/dist/bitget/index.js +13 -0
  13. package/dist/bitget/index.js.map +1 -0
  14. package/dist/bitget/index.mjs +4 -0
  15. package/dist/bitget/index.mjs.map +1 -0
  16. package/dist/chunk-5Z5Q2Y75.mjs +91 -0
  17. package/dist/chunk-5Z5Q2Y75.mjs.map +1 -0
  18. package/dist/chunk-7KK2LZLZ.mjs +208 -0
  19. package/dist/chunk-7KK2LZLZ.mjs.map +1 -0
  20. package/dist/chunk-AW2JZIHR.mjs +753 -0
  21. package/dist/chunk-AW2JZIHR.mjs.map +1 -0
  22. package/dist/chunk-EIJOSZXZ.js +331 -0
  23. package/dist/chunk-EIJOSZXZ.js.map +1 -0
  24. package/dist/chunk-EQHR7P7G.js +541 -0
  25. package/dist/chunk-EQHR7P7G.js.map +1 -0
  26. package/dist/chunk-EWRXLZO4.mjs +539 -0
  27. package/dist/chunk-EWRXLZO4.mjs.map +1 -0
  28. package/dist/chunk-FISNQZZ7.js +802 -0
  29. package/dist/chunk-FISNQZZ7.js.map +1 -0
  30. package/dist/chunk-HL4WDMGS.js +200 -0
  31. package/dist/chunk-HL4WDMGS.js.map +1 -0
  32. package/dist/chunk-IPYWR76I.js +314 -0
  33. package/dist/chunk-IPYWR76I.js.map +1 -0
  34. package/dist/chunk-JYYNWR5G.js +142 -0
  35. package/dist/chunk-JYYNWR5G.js.map +1 -0
  36. package/dist/chunk-LNKTYZJM.js +701 -0
  37. package/dist/chunk-LNKTYZJM.js.map +1 -0
  38. package/dist/chunk-LVZMONQL.mjs +699 -0
  39. package/dist/chunk-LVZMONQL.mjs.map +1 -0
  40. package/dist/chunk-MFXLQWOE.js +93 -0
  41. package/dist/chunk-MFXLQWOE.js.map +1 -0
  42. package/dist/chunk-NBIA4TTE.mjs +204 -0
  43. package/dist/chunk-NBIA4TTE.mjs.map +1 -0
  44. package/dist/chunk-O4DD2XJ2.js +206 -0
  45. package/dist/chunk-O4DD2XJ2.js.map +1 -0
  46. package/dist/chunk-P7HVBU2B.mjs +140 -0
  47. package/dist/chunk-P7HVBU2B.mjs.map +1 -0
  48. package/dist/chunk-Q7QVQYEB.js +210 -0
  49. package/dist/chunk-Q7QVQYEB.js.map +1 -0
  50. package/dist/chunk-RLZEG6KL.mjs +329 -0
  51. package/dist/chunk-RLZEG6KL.mjs.map +1 -0
  52. package/dist/chunk-SYLDBJ75.mjs +246 -0
  53. package/dist/chunk-SYLDBJ75.mjs.map +1 -0
  54. package/dist/chunk-TTEUU3CI.mjs +198 -0
  55. package/dist/chunk-TTEUU3CI.mjs.map +1 -0
  56. package/dist/chunk-V66BXDTR.mjs +292 -0
  57. package/dist/chunk-V66BXDTR.mjs.map +1 -0
  58. package/dist/chunk-X77ZT4OI.js +268 -0
  59. package/dist/chunk-X77ZT4OI.js.map +1 -0
  60. package/dist/imtoken/index.d.mts +116 -0
  61. package/dist/imtoken/index.d.ts +116 -0
  62. package/dist/imtoken/index.js +14 -0
  63. package/dist/imtoken/index.js.map +1 -0
  64. package/dist/imtoken/index.mjs +5 -0
  65. package/dist/imtoken/index.mjs.map +1 -0
  66. package/dist/index.d.mts +14 -0
  67. package/dist/index.d.ts +14 -0
  68. package/dist/index.js +170 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/index.mjs +13 -0
  71. package/dist/index.mjs.map +1 -0
  72. package/dist/ledger/index.d.mts +290 -0
  73. package/dist/ledger/index.d.ts +290 -0
  74. package/dist/ledger/index.js +14 -0
  75. package/dist/ledger/index.js.map +1 -0
  76. package/dist/ledger/index.mjs +5 -0
  77. package/dist/ledger/index.mjs.map +1 -0
  78. package/dist/okx/index.d.mts +88 -0
  79. package/dist/okx/index.d.ts +88 -0
  80. package/dist/okx/index.js +13 -0
  81. package/dist/okx/index.js.map +1 -0
  82. package/dist/okx/index.mjs +4 -0
  83. package/dist/okx/index.mjs.map +1 -0
  84. package/dist/phantom/index.d.mts +96 -0
  85. package/dist/phantom/index.d.ts +96 -0
  86. package/dist/phantom/index.js +14 -0
  87. package/dist/phantom/index.js.map +1 -0
  88. package/dist/phantom/index.mjs +5 -0
  89. package/dist/phantom/index.mjs.map +1 -0
  90. package/dist/psbt-builder-CFOs69Z5.d.mts +131 -0
  91. package/dist/psbt-builder-CFOs69Z5.d.ts +131 -0
  92. package/dist/trezor/index.d.mts +155 -0
  93. package/dist/trezor/index.d.ts +155 -0
  94. package/dist/trezor/index.js +14 -0
  95. package/dist/trezor/index.js.map +1 -0
  96. package/dist/trezor/index.mjs +5 -0
  97. package/dist/trezor/index.mjs.map +1 -0
  98. package/dist/unisat/index.d.mts +75 -0
  99. package/dist/unisat/index.d.ts +75 -0
  100. package/dist/unisat/index.js +13 -0
  101. package/dist/unisat/index.js.map +1 -0
  102. package/dist/unisat/index.mjs +4 -0
  103. package/dist/unisat/index.mjs.map +1 -0
  104. package/dist/utils/index.d.mts +398 -0
  105. package/dist/utils/index.d.ts +398 -0
  106. package/dist/utils/index.js +120 -0
  107. package/dist/utils/index.js.map +1 -0
  108. package/dist/utils/index.mjs +3 -0
  109. package/dist/utils/index.mjs.map +1 -0
  110. package/dist/xverse/index.d.mts +79 -0
  111. package/dist/xverse/index.d.ts +79 -0
  112. package/dist/xverse/index.js +13 -0
  113. package/dist/xverse/index.js.map +1 -0
  114. package/dist/xverse/index.mjs +4 -0
  115. package/dist/xverse/index.mjs.map +1 -0
  116. package/package.json +108 -0
  117. package/src/base.ts +132 -0
  118. package/src/binance/BinanceConnector.ts +307 -0
  119. package/src/binance/index.ts +1 -0
  120. package/src/bitget/BitgetConnector.ts +301 -0
  121. package/src/bitget/index.ts +1 -0
  122. package/src/imtoken/ImTokenConnector.ts +420 -0
  123. package/src/imtoken/index.ts +2 -0
  124. package/src/index.ts +78 -0
  125. package/src/ledger/LedgerConnector.ts +1019 -0
  126. package/src/ledger/index.ts +8 -0
  127. package/src/okx/OKXConnector.ts +230 -0
  128. package/src/okx/index.ts +1 -0
  129. package/src/phantom/PhantomConnector.ts +381 -0
  130. package/src/phantom/index.ts +2 -0
  131. package/src/trezor/TrezorConnector.ts +824 -0
  132. package/src/trezor/index.ts +6 -0
  133. package/src/unisat/UnisatConnector.ts +312 -0
  134. package/src/unisat/index.ts +1 -0
  135. package/src/utils/blockstream.ts +230 -0
  136. package/src/utils/btc-service.ts +364 -0
  137. package/src/utils/index.ts +56 -0
  138. package/src/utils/mempool.ts +232 -0
  139. package/src/utils/psbt-builder.ts +492 -0
  140. package/src/utils/types.ts +183 -0
  141. package/src/xverse/XverseConnector.ts +479 -0
  142. package/src/xverse/index.ts +1 -0
@@ -0,0 +1,364 @@
1
+ import type { BitcoinNetwork } from 'otx-btc-wallet-core';
2
+ import type {
3
+ IBtcService,
4
+ Utxo,
5
+ UtxoWithTx,
6
+ FeeRates,
7
+ Transaction,
8
+ FullTransaction,
9
+ AddressBalance,
10
+ BtcServiceConfig,
11
+ } from './types';
12
+ import { MempoolService } from './mempool';
13
+ import { BlockstreamService } from './blockstream';
14
+
15
+ /**
16
+ * Global configuration for BtcService
17
+ * This is applied to all new BtcService instances and standalone functions
18
+ */
19
+ let globalConfig: BtcServiceConfig = {};
20
+
21
+ /**
22
+ * Configure global BtcService settings
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { configureBtcService } from 'otx-btc-wallet-connectors';
27
+ *
28
+ * // Use custom mempool instance
29
+ * configureBtcService({
30
+ * mempool: {
31
+ * mainnet: 'https://my-mempool.com/api',
32
+ * testnet: 'https://my-mempool.com/testnet/api',
33
+ * }
34
+ * });
35
+ *
36
+ * // Use only mempool provider (no race)
37
+ * configureBtcService({
38
+ * preferredProvider: 'mempool',
39
+ * mempool: {
40
+ * mainnet: 'https://custom-api.example.com/api',
41
+ * }
42
+ * });
43
+ *
44
+ * // Reset to defaults
45
+ * configureBtcService({});
46
+ * ```
47
+ */
48
+ export function configureBtcService(config: BtcServiceConfig): void {
49
+ globalConfig = config;
50
+ // Reset default service so it picks up new config
51
+ defaultService = null;
52
+ }
53
+
54
+ /**
55
+ * Get current global BtcService configuration
56
+ */
57
+ export function getBtcServiceConfig(): BtcServiceConfig {
58
+ return { ...globalConfig };
59
+ }
60
+
61
+ /**
62
+ * Race promises and return first result
63
+ */
64
+ function race<T>(promises: Promise<T>[]): Promise<T> {
65
+ return Promise.race(promises);
66
+ }
67
+
68
+ /**
69
+ * Bitcoin Service that combines Mempool and Blockstream APIs
70
+ * Uses Promise.race for reliability and speed
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // Default usage
75
+ * const btcService = new BtcService('mainnet');
76
+ *
77
+ * // With custom configuration
78
+ * const btcService = new BtcService('mainnet', {
79
+ * mempool: {
80
+ * mainnet: 'https://my-mempool-proxy.com/api',
81
+ * },
82
+ * preferredProvider: 'mempool', // Only use mempool
83
+ * });
84
+ *
85
+ * // Get balance (uses fastest responding API if preferredProvider is 'race')
86
+ * const balance = await btcService.getBalance('bc1q...');
87
+ *
88
+ * // Get UTXOs with tx hex (for Ledger signing)
89
+ * const utxos = await btcService.getUtxosWithTxHex('bc1q...');
90
+ *
91
+ * // Broadcast transaction
92
+ * const txid = await btcService.broadcastTransaction(signedTxHex);
93
+ * ```
94
+ */
95
+ export class BtcService implements IBtcService {
96
+ readonly name = 'btc-service';
97
+ private _network: BitcoinNetwork;
98
+ private _mempool: MempoolService;
99
+ private _blockstream: BlockstreamService;
100
+ private _config: BtcServiceConfig;
101
+
102
+ constructor(network: BitcoinNetwork = 'mainnet', config?: BtcServiceConfig) {
103
+ this._network = network;
104
+ // Merge with global config, instance config takes precedence
105
+ this._config = { ...globalConfig, ...config };
106
+ this._mempool = new MempoolService(network, this._config.mempool);
107
+ this._blockstream = new BlockstreamService(network, this._config.blockstream);
108
+ }
109
+
110
+ get network(): BitcoinNetwork {
111
+ return this._network;
112
+ }
113
+
114
+ /**
115
+ * Get the underlying Mempool service for direct access
116
+ */
117
+ get mempool(): MempoolService {
118
+ return this._mempool;
119
+ }
120
+
121
+ /**
122
+ * Get the underlying Blockstream service for direct access
123
+ */
124
+ get blockstream(): BlockstreamService {
125
+ return this._blockstream;
126
+ }
127
+
128
+ /**
129
+ * Get current configuration
130
+ */
131
+ get config(): BtcServiceConfig {
132
+ return { ...this._config };
133
+ }
134
+
135
+ setNetwork(network: BitcoinNetwork): void {
136
+ this._network = network;
137
+ this._mempool.setNetwork(network);
138
+ this._blockstream.setNetwork(network);
139
+ }
140
+
141
+ /**
142
+ * Update configuration
143
+ */
144
+ setConfig(config: BtcServiceConfig): void {
145
+ this._config = { ...this._config, ...config };
146
+ if (config.mempool) {
147
+ this._mempool.setCustomEndpoints(config.mempool);
148
+ }
149
+ if (config.blockstream) {
150
+ this._blockstream.setCustomEndpoints(config.blockstream);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Execute method based on preferred provider setting
156
+ */
157
+ private async executeWithProvider<T>(
158
+ mempoolFn: () => Promise<T>,
159
+ blockstreamFn: () => Promise<T>
160
+ ): Promise<T> {
161
+ const provider = this._config.preferredProvider ?? 'race';
162
+
163
+ switch (provider) {
164
+ case 'mempool':
165
+ return mempoolFn();
166
+ case 'blockstream':
167
+ return blockstreamFn();
168
+ case 'race':
169
+ default:
170
+ return race([mempoolFn(), blockstreamFn()]);
171
+ }
172
+ }
173
+
174
+ // ============ UTXO Methods ============
175
+
176
+ async getUtxos(address: string): Promise<Utxo[]> {
177
+ return this.executeWithProvider(
178
+ () => this._mempool.getUtxos(address),
179
+ () => this._blockstream.getUtxos(address)
180
+ );
181
+ }
182
+
183
+ async getUtxosWithTxHex(address: string): Promise<UtxoWithTx[]> {
184
+ return this.executeWithProvider(
185
+ () => this._mempool.getUtxosWithTxHex(address),
186
+ () => this._blockstream.getUtxosWithTxHex(address)
187
+ );
188
+ }
189
+
190
+ // ============ Transaction Methods ============
191
+
192
+ async getTxHex(txid: string): Promise<string> {
193
+ return this.executeWithProvider(
194
+ () => this._mempool.getTxHex(txid),
195
+ () => this._blockstream.getTxHex(txid)
196
+ );
197
+ }
198
+
199
+ async getTransaction(txid: string): Promise<Transaction> {
200
+ return this.executeWithProvider(
201
+ () => this._mempool.getTransaction(txid),
202
+ () => this._blockstream.getTransaction(txid)
203
+ );
204
+ }
205
+
206
+ async getFullTransaction(txid: string): Promise<FullTransaction> {
207
+ return this.executeWithProvider(
208
+ () => this._mempool.getFullTransaction(txid),
209
+ () => this._blockstream.getFullTransaction(txid)
210
+ );
211
+ }
212
+
213
+ async broadcastTransaction(txHex: string): Promise<string> {
214
+ return this.executeWithProvider(
215
+ () => this._mempool.broadcastTransaction(txHex),
216
+ () => this._blockstream.broadcastTransaction(txHex)
217
+ );
218
+ }
219
+
220
+ // ============ Address Methods ============
221
+
222
+ async getAddressInfo(address: string): Promise<AddressBalance> {
223
+ return this.executeWithProvider(
224
+ () => this._mempool.getAddressInfo(address),
225
+ () => this._blockstream.getAddressInfo(address)
226
+ );
227
+ }
228
+
229
+ async getBalance(address: string): Promise<number> {
230
+ return this.executeWithProvider(
231
+ () => this._mempool.getBalance(address),
232
+ () => this._blockstream.getBalance(address)
233
+ );
234
+ }
235
+
236
+ async getConfirmedBalance(address: string): Promise<number> {
237
+ return this.executeWithProvider(
238
+ () => this._mempool.getConfirmedBalance(address),
239
+ () => this._blockstream.getConfirmedBalance(address)
240
+ );
241
+ }
242
+
243
+ // ============ Fee Methods ============
244
+
245
+ async getFeeRates(): Promise<FeeRates> {
246
+ return this.executeWithProvider(
247
+ () => this._mempool.getFeeRates(),
248
+ () => this._blockstream.getFeeRates()
249
+ );
250
+ }
251
+ }
252
+
253
+ // ============ Standalone Functions ============
254
+
255
+ /**
256
+ * Default service instance (mainnet)
257
+ */
258
+ let defaultService: BtcService | null = null;
259
+
260
+ function getDefaultService(network: BitcoinNetwork = 'mainnet'): BtcService {
261
+ if (!defaultService || defaultService.network !== network) {
262
+ defaultService = new BtcService(network);
263
+ }
264
+ return defaultService;
265
+ }
266
+
267
+ /**
268
+ * Get UTXOs for an address
269
+ */
270
+ export async function getUtxos(
271
+ address: string,
272
+ network: BitcoinNetwork = 'mainnet'
273
+ ): Promise<Utxo[]> {
274
+ return getDefaultService(network).getUtxos(address);
275
+ }
276
+
277
+ /**
278
+ * Get UTXOs with raw transaction hex
279
+ */
280
+ export async function getUtxosWithTxHex(
281
+ address: string,
282
+ network: BitcoinNetwork = 'mainnet'
283
+ ): Promise<UtxoWithTx[]> {
284
+ return getDefaultService(network).getUtxosWithTxHex(address);
285
+ }
286
+
287
+ /**
288
+ * Get raw transaction hex
289
+ */
290
+ export async function getTxHex(
291
+ txid: string,
292
+ network: BitcoinNetwork = 'mainnet'
293
+ ): Promise<string> {
294
+ return getDefaultService(network).getTxHex(txid);
295
+ }
296
+
297
+ /**
298
+ * Get transaction details
299
+ */
300
+ export async function getTransaction(
301
+ txid: string,
302
+ network: BitcoinNetwork = 'mainnet'
303
+ ): Promise<Transaction> {
304
+ return getDefaultService(network).getTransaction(txid);
305
+ }
306
+
307
+ /**
308
+ * Get full transaction with inputs and outputs (for Trezor refTxs)
309
+ */
310
+ export async function getFullTransaction(
311
+ txid: string,
312
+ network: BitcoinNetwork = 'mainnet'
313
+ ): Promise<FullTransaction> {
314
+ return getDefaultService(network).getFullTransaction(txid);
315
+ }
316
+
317
+ /**
318
+ * Broadcast a signed transaction
319
+ */
320
+ export async function broadcastTransaction(
321
+ txHex: string,
322
+ network: BitcoinNetwork = 'mainnet'
323
+ ): Promise<string> {
324
+ return getDefaultService(network).broadcastTransaction(txHex);
325
+ }
326
+
327
+ /**
328
+ * Get address info
329
+ */
330
+ export async function getAddressInfo(
331
+ address: string,
332
+ network: BitcoinNetwork = 'mainnet'
333
+ ): Promise<AddressBalance> {
334
+ return getDefaultService(network).getAddressInfo(address);
335
+ }
336
+
337
+ /**
338
+ * Get balance for an address
339
+ */
340
+ export async function getBalance(
341
+ address: string,
342
+ network: BitcoinNetwork = 'mainnet'
343
+ ): Promise<number> {
344
+ return getDefaultService(network).getBalance(address);
345
+ }
346
+
347
+ /**
348
+ * Get confirmed balance for an address
349
+ */
350
+ export async function getConfirmedBalance(
351
+ address: string,
352
+ network: BitcoinNetwork = 'mainnet'
353
+ ): Promise<number> {
354
+ return getDefaultService(network).getConfirmedBalance(address);
355
+ }
356
+
357
+ /**
358
+ * Get current fee rates
359
+ */
360
+ export async function getFeeRates(
361
+ network: BitcoinNetwork = 'mainnet'
362
+ ): Promise<FeeRates> {
363
+ return getDefaultService(network).getFeeRates();
364
+ }
@@ -0,0 +1,56 @@
1
+ // Types
2
+ export type {
3
+ Utxo,
4
+ UtxoWithTx,
5
+ FeeRates,
6
+ Transaction,
7
+ FullTransaction,
8
+ AddressBalance,
9
+ IBtcService,
10
+ BtcServiceConfig,
11
+ NetworkEndpoints,
12
+ } from './types';
13
+
14
+ // Services
15
+ export { MempoolService } from './mempool';
16
+ export { BlockstreamService } from './blockstream';
17
+ export { BtcService } from './btc-service';
18
+
19
+ // Configuration functions
20
+ export { configureBtcService, getBtcServiceConfig } from './btc-service';
21
+
22
+ // Standalone functions
23
+ export {
24
+ getUtxos,
25
+ getUtxosWithTxHex,
26
+ getTxHex,
27
+ getTransaction,
28
+ getFullTransaction,
29
+ broadcastTransaction,
30
+ getAddressInfo,
31
+ getBalance,
32
+ getConfirmedBalance,
33
+ getFeeRates,
34
+ } from './btc-service';
35
+
36
+ // PSBT Builder utilities
37
+ export {
38
+ generatePsbtForSend,
39
+ finalizeAllInputs,
40
+ finalizeAndBroadcast,
41
+ selectUtxos,
42
+ hexToBytes,
43
+ bytesToHex,
44
+ toXOnly,
45
+ getAddressType,
46
+ getBitcoinJsNetwork,
47
+ getInputVBytes,
48
+ getOutputVBytes,
49
+ getDustThreshold,
50
+ deriveAddressFromPublicKey,
51
+ } from './psbt-builder';
52
+
53
+ export type {
54
+ GeneratePsbtOptions,
55
+ GeneratePsbtResult,
56
+ } from './psbt-builder';
@@ -0,0 +1,232 @@
1
+ import type { BitcoinNetwork } from 'otx-btc-wallet-core';
2
+ import type {
3
+ IBtcService,
4
+ Utxo,
5
+ UtxoWithTx,
6
+ FeeRates,
7
+ Transaction,
8
+ FullTransaction,
9
+ AddressBalance,
10
+ NetworkEndpoints,
11
+ } from './types';
12
+
13
+ /**
14
+ * Default API endpoints for mempool.space
15
+ */
16
+ const DEFAULT_MEMPOOL_ENDPOINTS: Record<BitcoinNetwork, string> = {
17
+ mainnet: 'https://mempool.space/api',
18
+ testnet: 'https://mempool.space/testnet/api',
19
+ testnet4: 'https://mempool.space/testnet4/api',
20
+ signet: 'https://mempool.space/signet/api',
21
+ };
22
+
23
+ /**
24
+ * Fetch with timeout helper
25
+ */
26
+ async function fetchWithTimeout(
27
+ url: string,
28
+ options?: RequestInit,
29
+ timeout: number = 10000
30
+ ): Promise<Response> {
31
+ const controller = new AbortController();
32
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
33
+
34
+ try {
35
+ const response = await fetch(url, {
36
+ ...options,
37
+ signal: controller.signal,
38
+ });
39
+ clearTimeout(timeoutId);
40
+ return response;
41
+ } catch (error) {
42
+ clearTimeout(timeoutId);
43
+ throw error;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Mempool.space Bitcoin Service
49
+ *
50
+ * @see https://mempool.space/docs/api
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Default usage
55
+ * const service = new MempoolService('mainnet');
56
+ *
57
+ * // With custom endpoints
58
+ * const service = new MempoolService('mainnet', {
59
+ * mainnet: 'https://my-mempool-proxy.com/api',
60
+ * testnet: 'https://my-mempool-proxy.com/testnet/api',
61
+ * });
62
+ * ```
63
+ */
64
+ export class MempoolService implements IBtcService {
65
+ readonly name = 'mempool';
66
+ private _network: BitcoinNetwork;
67
+ private _customEndpoints: NetworkEndpoints;
68
+
69
+ constructor(network: BitcoinNetwork = 'mainnet', customEndpoints?: NetworkEndpoints) {
70
+ this._network = network;
71
+ this._customEndpoints = customEndpoints ?? {};
72
+ }
73
+
74
+ get network(): BitcoinNetwork {
75
+ return this._network;
76
+ }
77
+
78
+ private get baseUrl(): string {
79
+ // Use custom endpoint if provided, otherwise use default
80
+ return this._customEndpoints[this._network] ?? DEFAULT_MEMPOOL_ENDPOINTS[this._network];
81
+ }
82
+
83
+ setNetwork(network: BitcoinNetwork): void {
84
+ this._network = network;
85
+ }
86
+
87
+ /**
88
+ * Set custom endpoints for this service
89
+ */
90
+ setCustomEndpoints(endpoints: NetworkEndpoints): void {
91
+ this._customEndpoints = { ...this._customEndpoints, ...endpoints };
92
+ }
93
+
94
+ /**
95
+ * Get current endpoints configuration
96
+ */
97
+ getEndpoints(): Record<BitcoinNetwork, string> {
98
+ return {
99
+ mainnet: this._customEndpoints.mainnet ?? DEFAULT_MEMPOOL_ENDPOINTS.mainnet,
100
+ testnet: this._customEndpoints.testnet ?? DEFAULT_MEMPOOL_ENDPOINTS.testnet,
101
+ testnet4: this._customEndpoints.testnet4 ?? DEFAULT_MEMPOOL_ENDPOINTS.testnet4,
102
+ signet: this._customEndpoints.signet ?? DEFAULT_MEMPOOL_ENDPOINTS.signet,
103
+ };
104
+ }
105
+
106
+ // ============ UTXO Methods ============
107
+
108
+ async getUtxos(address: string): Promise<Utxo[]> {
109
+ const response = await fetchWithTimeout(`${this.baseUrl}/address/${address}/utxo`);
110
+
111
+ if (!response.ok) {
112
+ throw new Error(`Mempool API error: ${response.status} ${response.statusText}`);
113
+ }
114
+
115
+ return response.json() as Promise<Utxo[]>;
116
+ }
117
+
118
+ async getUtxosWithTxHex(address: string): Promise<UtxoWithTx[]> {
119
+ const utxos = await this.getUtxos(address);
120
+
121
+ const utxosWithTx = await Promise.all(
122
+ utxos.map(async (utxo): Promise<UtxoWithTx> => {
123
+ const txHex = await this.getTxHex(utxo.txid);
124
+ return { ...utxo, txHex };
125
+ })
126
+ );
127
+
128
+ return utxosWithTx;
129
+ }
130
+
131
+ // ============ Transaction Methods ============
132
+
133
+ async getTxHex(txid: string): Promise<string> {
134
+ const response = await fetchWithTimeout(`${this.baseUrl}/tx/${txid}/hex`);
135
+
136
+ if (!response.ok) {
137
+ throw new Error(`Mempool API error: ${response.status} ${response.statusText}`);
138
+ }
139
+
140
+ return response.text();
141
+ }
142
+
143
+ async getTransaction(txid: string): Promise<Transaction> {
144
+ const response = await fetchWithTimeout(`${this.baseUrl}/tx/${txid}`);
145
+
146
+ if (!response.ok) {
147
+ throw new Error(`Mempool API error: ${response.status} ${response.statusText}`);
148
+ }
149
+
150
+ return response.json() as Promise<Transaction>;
151
+ }
152
+
153
+ async getFullTransaction(txid: string): Promise<FullTransaction> {
154
+ const response = await fetchWithTimeout(`${this.baseUrl}/tx/${txid}`);
155
+
156
+ if (!response.ok) {
157
+ throw new Error(`Mempool API error: ${response.status} ${response.statusText}`);
158
+ }
159
+
160
+ return response.json() as Promise<FullTransaction>;
161
+ }
162
+
163
+ async broadcastTransaction(txHex: string): Promise<string> {
164
+ const response = await fetchWithTimeout(
165
+ `${this.baseUrl}/tx`,
166
+ {
167
+ method: 'POST',
168
+ body: txHex,
169
+ headers: { 'Content-Type': 'text/plain' },
170
+ },
171
+ 30000
172
+ );
173
+
174
+ if (!response.ok) {
175
+ const error = await response.text();
176
+ throw new Error(`Mempool broadcast error: ${error}`);
177
+ }
178
+
179
+ return response.text();
180
+ }
181
+
182
+ // ============ Address Methods ============
183
+
184
+ async getAddressInfo(address: string): Promise<AddressBalance> {
185
+ const response = await fetchWithTimeout(`${this.baseUrl}/address/${address}`);
186
+
187
+ if (!response.ok) {
188
+ throw new Error(`Mempool API error: ${response.status} ${response.statusText}`);
189
+ }
190
+
191
+ return response.json() as Promise<AddressBalance>;
192
+ }
193
+
194
+ async getBalance(address: string): Promise<number> {
195
+ const info = await this.getAddressInfo(address);
196
+ const confirmedBalance =
197
+ info.chain_stats.funded_txo_sum - info.chain_stats.spent_txo_sum;
198
+ const unconfirmedBalance =
199
+ info.mempool_stats.funded_txo_sum - info.mempool_stats.spent_txo_sum;
200
+ return confirmedBalance + unconfirmedBalance;
201
+ }
202
+
203
+ async getConfirmedBalance(address: string): Promise<number> {
204
+ const info = await this.getAddressInfo(address);
205
+ return info.chain_stats.funded_txo_sum - info.chain_stats.spent_txo_sum;
206
+ }
207
+
208
+ // ============ Fee Methods ============
209
+
210
+ async getFeeRates(): Promise<FeeRates> {
211
+ const response = await fetchWithTimeout(`${this.baseUrl}/v1/fees/recommended`);
212
+
213
+ if (!response.ok) {
214
+ throw new Error(`Mempool API error: ${response.status} ${response.statusText}`);
215
+ }
216
+
217
+ const fees = (await response.json()) as {
218
+ fastestFee: number;
219
+ halfHourFee: number;
220
+ hourFee: number;
221
+ economyFee: number;
222
+ minimumFee: number;
223
+ };
224
+ return {
225
+ fastest: fees.fastestFee,
226
+ halfHour: fees.halfHourFee,
227
+ hour: fees.hourFee,
228
+ economy: fees.economyFee,
229
+ minimum: fees.minimumFee,
230
+ };
231
+ }
232
+ }