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.
- package/README.md +554 -0
- package/dist/base-IAFq7sd8.d.mts +53 -0
- package/dist/base-IAFq7sd8.d.ts +53 -0
- package/dist/binance/index.d.mts +81 -0
- package/dist/binance/index.d.ts +81 -0
- package/dist/binance/index.js +13 -0
- package/dist/binance/index.js.map +1 -0
- package/dist/binance/index.mjs +4 -0
- package/dist/binance/index.mjs.map +1 -0
- package/dist/bitget/index.d.mts +84 -0
- package/dist/bitget/index.d.ts +84 -0
- package/dist/bitget/index.js +13 -0
- package/dist/bitget/index.js.map +1 -0
- package/dist/bitget/index.mjs +4 -0
- package/dist/bitget/index.mjs.map +1 -0
- package/dist/chunk-5Z5Q2Y75.mjs +91 -0
- package/dist/chunk-5Z5Q2Y75.mjs.map +1 -0
- package/dist/chunk-7KK2LZLZ.mjs +208 -0
- package/dist/chunk-7KK2LZLZ.mjs.map +1 -0
- package/dist/chunk-AW2JZIHR.mjs +753 -0
- package/dist/chunk-AW2JZIHR.mjs.map +1 -0
- package/dist/chunk-EIJOSZXZ.js +331 -0
- package/dist/chunk-EIJOSZXZ.js.map +1 -0
- package/dist/chunk-EQHR7P7G.js +541 -0
- package/dist/chunk-EQHR7P7G.js.map +1 -0
- package/dist/chunk-EWRXLZO4.mjs +539 -0
- package/dist/chunk-EWRXLZO4.mjs.map +1 -0
- package/dist/chunk-FISNQZZ7.js +802 -0
- package/dist/chunk-FISNQZZ7.js.map +1 -0
- package/dist/chunk-HL4WDMGS.js +200 -0
- package/dist/chunk-HL4WDMGS.js.map +1 -0
- package/dist/chunk-IPYWR76I.js +314 -0
- package/dist/chunk-IPYWR76I.js.map +1 -0
- package/dist/chunk-JYYNWR5G.js +142 -0
- package/dist/chunk-JYYNWR5G.js.map +1 -0
- package/dist/chunk-LNKTYZJM.js +701 -0
- package/dist/chunk-LNKTYZJM.js.map +1 -0
- package/dist/chunk-LVZMONQL.mjs +699 -0
- package/dist/chunk-LVZMONQL.mjs.map +1 -0
- package/dist/chunk-MFXLQWOE.js +93 -0
- package/dist/chunk-MFXLQWOE.js.map +1 -0
- package/dist/chunk-NBIA4TTE.mjs +204 -0
- package/dist/chunk-NBIA4TTE.mjs.map +1 -0
- package/dist/chunk-O4DD2XJ2.js +206 -0
- package/dist/chunk-O4DD2XJ2.js.map +1 -0
- package/dist/chunk-P7HVBU2B.mjs +140 -0
- package/dist/chunk-P7HVBU2B.mjs.map +1 -0
- package/dist/chunk-Q7QVQYEB.js +210 -0
- package/dist/chunk-Q7QVQYEB.js.map +1 -0
- package/dist/chunk-RLZEG6KL.mjs +329 -0
- package/dist/chunk-RLZEG6KL.mjs.map +1 -0
- package/dist/chunk-SYLDBJ75.mjs +246 -0
- package/dist/chunk-SYLDBJ75.mjs.map +1 -0
- package/dist/chunk-TTEUU3CI.mjs +198 -0
- package/dist/chunk-TTEUU3CI.mjs.map +1 -0
- package/dist/chunk-V66BXDTR.mjs +292 -0
- package/dist/chunk-V66BXDTR.mjs.map +1 -0
- package/dist/chunk-X77ZT4OI.js +268 -0
- package/dist/chunk-X77ZT4OI.js.map +1 -0
- package/dist/imtoken/index.d.mts +116 -0
- package/dist/imtoken/index.d.ts +116 -0
- package/dist/imtoken/index.js +14 -0
- package/dist/imtoken/index.js.map +1 -0
- package/dist/imtoken/index.mjs +5 -0
- package/dist/imtoken/index.mjs.map +1 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +170 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/ledger/index.d.mts +290 -0
- package/dist/ledger/index.d.ts +290 -0
- package/dist/ledger/index.js +14 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/index.mjs +5 -0
- package/dist/ledger/index.mjs.map +1 -0
- package/dist/okx/index.d.mts +88 -0
- package/dist/okx/index.d.ts +88 -0
- package/dist/okx/index.js +13 -0
- package/dist/okx/index.js.map +1 -0
- package/dist/okx/index.mjs +4 -0
- package/dist/okx/index.mjs.map +1 -0
- package/dist/phantom/index.d.mts +96 -0
- package/dist/phantom/index.d.ts +96 -0
- package/dist/phantom/index.js +14 -0
- package/dist/phantom/index.js.map +1 -0
- package/dist/phantom/index.mjs +5 -0
- package/dist/phantom/index.mjs.map +1 -0
- package/dist/psbt-builder-CFOs69Z5.d.mts +131 -0
- package/dist/psbt-builder-CFOs69Z5.d.ts +131 -0
- package/dist/trezor/index.d.mts +155 -0
- package/dist/trezor/index.d.ts +155 -0
- package/dist/trezor/index.js +14 -0
- package/dist/trezor/index.js.map +1 -0
- package/dist/trezor/index.mjs +5 -0
- package/dist/trezor/index.mjs.map +1 -0
- package/dist/unisat/index.d.mts +75 -0
- package/dist/unisat/index.d.ts +75 -0
- package/dist/unisat/index.js +13 -0
- package/dist/unisat/index.js.map +1 -0
- package/dist/unisat/index.mjs +4 -0
- package/dist/unisat/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +398 -0
- package/dist/utils/index.d.ts +398 -0
- package/dist/utils/index.js +120 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +3 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/xverse/index.d.mts +79 -0
- package/dist/xverse/index.d.ts +79 -0
- package/dist/xverse/index.js +13 -0
- package/dist/xverse/index.js.map +1 -0
- package/dist/xverse/index.mjs +4 -0
- package/dist/xverse/index.mjs.map +1 -0
- package/package.json +108 -0
- package/src/base.ts +132 -0
- package/src/binance/BinanceConnector.ts +307 -0
- package/src/binance/index.ts +1 -0
- package/src/bitget/BitgetConnector.ts +301 -0
- package/src/bitget/index.ts +1 -0
- package/src/imtoken/ImTokenConnector.ts +420 -0
- package/src/imtoken/index.ts +2 -0
- package/src/index.ts +78 -0
- package/src/ledger/LedgerConnector.ts +1019 -0
- package/src/ledger/index.ts +8 -0
- package/src/okx/OKXConnector.ts +230 -0
- package/src/okx/index.ts +1 -0
- package/src/phantom/PhantomConnector.ts +381 -0
- package/src/phantom/index.ts +2 -0
- package/src/trezor/TrezorConnector.ts +824 -0
- package/src/trezor/index.ts +6 -0
- package/src/unisat/UnisatConnector.ts +312 -0
- package/src/unisat/index.ts +1 -0
- package/src/utils/blockstream.ts +230 -0
- package/src/utils/btc-service.ts +364 -0
- package/src/utils/index.ts +56 -0
- package/src/utils/mempool.ts +232 -0
- package/src/utils/psbt-builder.ts +492 -0
- package/src/utils/types.ts +183 -0
- package/src/xverse/XverseConnector.ts +479 -0
- 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
|
+
}
|