@atomiqlabs/btc-mempool 1.0.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.
@@ -0,0 +1,418 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MempoolBitcoinRpc = void 0;
4
+ const base_1 = require("@atomiqlabs/base");
5
+ const MempoolBitcoinBlock_1 = require("./MempoolBitcoinBlock");
6
+ const MempoolApi_1 = require("./MempoolApi");
7
+ const buffer_1 = require("buffer");
8
+ const btc_signer_1 = require("@scure/btc-signer");
9
+ const sha2_1 = require("@noble/hashes/sha2");
10
+ const BITCOIN_BLOCKTIME = 600 * 1000;
11
+ const BITCOIN_BLOCKSIZE = 1024 * 1024;
12
+ function bitcoinTxToBtcTx(btcTx) {
13
+ return {
14
+ locktime: btcTx.lockTime,
15
+ version: btcTx.version,
16
+ confirmations: 0,
17
+ txid: buffer_1.Buffer.from((0, sha2_1.sha256)((0, sha2_1.sha256)(btcTx.toBytes(true, false)))).reverse().toString("hex"),
18
+ hex: buffer_1.Buffer.from(btcTx.toBytes(true, false)).toString("hex"),
19
+ raw: buffer_1.Buffer.from(btcTx.toBytes(true, true)).toString("hex"),
20
+ vsize: btcTx.isFinal ? btcTx.vsize : NaN,
21
+ outs: Array.from({ length: btcTx.outputsLength }, (_, i) => i).map((index) => {
22
+ const output = btcTx.getOutput(index);
23
+ return {
24
+ value: Number(output.amount),
25
+ n: index,
26
+ scriptPubKey: {
27
+ asm: btc_signer_1.Script.decode(output.script).map(val => typeof (val) === "object" ? buffer_1.Buffer.from(val).toString("hex") : val.toString()).join(" "),
28
+ hex: buffer_1.Buffer.from(output.script).toString("hex")
29
+ }
30
+ };
31
+ }),
32
+ ins: Array.from({ length: btcTx.inputsLength }, (_, i) => i).map(index => {
33
+ const input = btcTx.getInput(index);
34
+ return {
35
+ txid: buffer_1.Buffer.from(input.txid).toString("hex"),
36
+ vout: input.index,
37
+ scriptSig: {
38
+ asm: btc_signer_1.Script.decode(input.finalScriptSig).map(val => typeof (val) === "object" ? buffer_1.Buffer.from(val).toString("hex") : val.toString()).join(" "),
39
+ hex: buffer_1.Buffer.from(input.finalScriptSig).toString("hex")
40
+ },
41
+ sequence: input.sequence,
42
+ txinwitness: input.finalScriptWitness == null ? [] : input.finalScriptWitness.map(witness => buffer_1.Buffer.from(witness).toString("hex"))
43
+ };
44
+ })
45
+ };
46
+ }
47
+ /**
48
+ * Bitcoin RPC implementation via Mempool.space API
49
+ *
50
+ * @category Bitcoin
51
+ */
52
+ class MempoolBitcoinRpc {
53
+ constructor(urlOrMempoolApi) {
54
+ this.api = urlOrMempoolApi instanceof MempoolApi_1.MempoolApi ? urlOrMempoolApi : new MempoolApi_1.MempoolApi(urlOrMempoolApi);
55
+ }
56
+ /**
57
+ * Returns a txo hash for a specific transaction vout
58
+ *
59
+ * @param vout
60
+ * @private
61
+ */
62
+ static getTxoHash(vout) {
63
+ return buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.concat([
64
+ base_1.BigIntBufferUtils.toBuffer(BigInt(vout.value), "le", 8),
65
+ buffer_1.Buffer.from(vout.scriptpubkey, "hex")
66
+ ])));
67
+ }
68
+ /**
69
+ * Returns delay in milliseconds till an unconfirmed transaction is expected to confirm, returns -1
70
+ * if the transaction won't confirm any time soon
71
+ *
72
+ * @param feeRate
73
+ * @private
74
+ */
75
+ async getTimeTillConfirmation(feeRate) {
76
+ const mempoolBlocks = await this.api.getPendingBlocks();
77
+ const mempoolBlockIndex = mempoolBlocks.findIndex(block => block.feeRange[0] <= feeRate);
78
+ if (mempoolBlockIndex === -1)
79
+ return -1;
80
+ //Last returned block is usually an aggregate (or a stack) of multiple btc blocks, if tx falls in this block
81
+ // and the last returned block really is an aggregate one (size bigger than BITCOIN_BLOCKSIZE) we return -1
82
+ if (mempoolBlockIndex + 1 === mempoolBlocks.length &&
83
+ mempoolBlocks[mempoolBlocks.length - 1].blockVSize > BITCOIN_BLOCKSIZE)
84
+ return -1;
85
+ return (mempoolBlockIndex + 1) * BITCOIN_BLOCKTIME;
86
+ }
87
+ /**
88
+ * @inheritDoc
89
+ */
90
+ async getConfirmationDelay(tx, requiredConfirmations) {
91
+ if (tx.confirmations == null || tx.confirmations === 0) {
92
+ //Get CPFP data
93
+ const cpfpData = await this.api.getCPFPData(tx.txid);
94
+ if (cpfpData == null) {
95
+ //Transaction is either confirmed in the meantime, or replaced
96
+ return null;
97
+ }
98
+ let confirmationDelay = (await this.getTimeTillConfirmation(cpfpData.effectiveFeePerVsize));
99
+ if (confirmationDelay !== -1)
100
+ confirmationDelay += (requiredConfirmations - 1) * BITCOIN_BLOCKTIME;
101
+ return confirmationDelay;
102
+ }
103
+ if (tx.confirmations > requiredConfirmations)
104
+ return 0;
105
+ return ((requiredConfirmations - tx.confirmations) * BITCOIN_BLOCKTIME);
106
+ }
107
+ /**
108
+ * Converts mempool API's transaction to BtcTx object while fetching the raw tx separately
109
+ * @param tx Transaction to convert
110
+ * @private
111
+ */
112
+ async toBtcTx(tx) {
113
+ const base = await this.toBtcTxWithoutRawData(tx);
114
+ if (base == null)
115
+ return null;
116
+ const rawTx = await this.api.getRawTransaction(tx.txid);
117
+ if (rawTx == null)
118
+ return null;
119
+ //Strip witness data
120
+ const btcTx = btc_signer_1.Transaction.fromRaw(rawTx, {
121
+ allowLegacyWitnessUtxo: true,
122
+ allowUnknownInputs: true,
123
+ allowUnknownOutputs: true,
124
+ disableScriptCheck: true
125
+ });
126
+ const strippedRawTx = buffer_1.Buffer.from(btcTx.toBytes(true, false)).toString("hex");
127
+ return {
128
+ ...base,
129
+ hex: strippedRawTx,
130
+ raw: rawTx.toString("hex")
131
+ };
132
+ }
133
+ /**
134
+ * Converts mempool API's transaction to BtcTx object, doesn't populate raw and hex fields
135
+ * @param tx Transaction to convert
136
+ * @private
137
+ */
138
+ async toBtcTxWithoutRawData(tx) {
139
+ let confirmations = 0;
140
+ if (tx.status != null && tx.status.confirmed) {
141
+ const blockheight = await this.api.getTipBlockHeight();
142
+ confirmations = blockheight - tx.status.block_height + 1;
143
+ }
144
+ return {
145
+ locktime: tx.locktime,
146
+ version: tx.version,
147
+ blockheight: tx.status?.block_height,
148
+ blockhash: tx.status?.block_hash,
149
+ confirmations,
150
+ txid: tx.txid,
151
+ vsize: tx.weight / 4,
152
+ outs: tx.vout.map((e, index) => {
153
+ return {
154
+ value: e.value,
155
+ n: index,
156
+ scriptPubKey: {
157
+ hex: e.scriptpubkey,
158
+ asm: e.scriptpubkey_asm
159
+ }
160
+ };
161
+ }),
162
+ ins: tx.vin.map(e => {
163
+ return {
164
+ txid: e.txid,
165
+ vout: e.vout,
166
+ scriptSig: {
167
+ hex: e.scriptsig,
168
+ asm: e.scriptsig_asm
169
+ },
170
+ sequence: e.sequence,
171
+ txinwitness: e.witness
172
+ };
173
+ }),
174
+ inputAddresses: tx.vin.map(e => e.prevout.scriptpubkey_address)
175
+ };
176
+ }
177
+ /**
178
+ * @inheritDoc
179
+ */
180
+ getTipHeight() {
181
+ return this.api.getTipBlockHeight();
182
+ }
183
+ /**
184
+ * @inheritDoc
185
+ */
186
+ async getBlockHeader(blockhash) {
187
+ return new MempoolBitcoinBlock_1.MempoolBitcoinBlock(await this.api.getBlockHeader(blockhash));
188
+ }
189
+ /**
190
+ * @inheritDoc
191
+ */
192
+ async getMerkleProof(txId, blockhash) {
193
+ const proof = await this.api.getTransactionProof(txId);
194
+ return {
195
+ reversedTxId: buffer_1.Buffer.from(txId, "hex").reverse(),
196
+ pos: proof.pos,
197
+ merkle: proof.merkle.map(e => buffer_1.Buffer.from(e, "hex").reverse()),
198
+ blockheight: proof.block_height
199
+ };
200
+ }
201
+ /**
202
+ * @inheritDoc
203
+ */
204
+ async getTransaction(txId) {
205
+ const tx = await this.api.getTransaction(txId);
206
+ if (tx == null)
207
+ return null;
208
+ return await this.toBtcTx(tx);
209
+ }
210
+ /**
211
+ * @inheritDoc
212
+ */
213
+ async isInMainChain(blockhash) {
214
+ const blockStatus = await this.api.getBlockStatus(blockhash);
215
+ return blockStatus.in_best_chain;
216
+ }
217
+ /**
218
+ * @inheritDoc
219
+ */
220
+ getBlockhash(height) {
221
+ return this.api.getBlockHash(height);
222
+ }
223
+ /**
224
+ * @inheritDoc
225
+ */
226
+ getBlockWithTransactions(blockhash) {
227
+ throw new Error("Unsupported.");
228
+ }
229
+ /**
230
+ * @inheritDoc
231
+ */
232
+ async getSyncInfo() {
233
+ const tipHeight = await this.api.getTipBlockHeight();
234
+ return {
235
+ verificationProgress: 1,
236
+ blocks: tipHeight,
237
+ headers: tipHeight,
238
+ ibd: false
239
+ };
240
+ }
241
+ /**
242
+ * @private
243
+ */
244
+ async getPast15Blocks(height) {
245
+ return (await this.api.getPast15BlockHeaders(height)).map(blockHeader => new MempoolBitcoinBlock_1.MempoolBitcoinBlock(blockHeader));
246
+ }
247
+ /**
248
+ * @inheritDoc
249
+ */
250
+ async checkAddressTxos(address, txoHash) {
251
+ const allTxs = await this.api.getAddressTransactions(address);
252
+ const relevantTxs = allTxs
253
+ .map(tx => {
254
+ return {
255
+ tx,
256
+ vout: tx.vout.findIndex(vout => MempoolBitcoinRpc.getTxoHash(vout).equals(txoHash))
257
+ };
258
+ })
259
+ .filter(obj => obj.vout >= 0)
260
+ .sort((a, b) => {
261
+ if (a.tx.status.confirmed && !b.tx.status.confirmed)
262
+ return -1;
263
+ if (!a.tx.status.confirmed && b.tx.status.confirmed)
264
+ return 1;
265
+ if (a.tx.status.confirmed && b.tx.status.confirmed)
266
+ return a.tx.status.block_height - b.tx.status.block_height;
267
+ return 0;
268
+ });
269
+ if (relevantTxs.length === 0)
270
+ return null;
271
+ return {
272
+ tx: await this.toBtcTxWithoutRawData(relevantTxs[0].tx),
273
+ vout: relevantTxs[0].vout
274
+ };
275
+ }
276
+ /**
277
+ * @inheritDoc
278
+ */
279
+ async waitForAddressTxo(address, txoHash, requiredConfirmations, stateUpdateCbk, abortSignal, intervalSeconds) {
280
+ if (abortSignal != null)
281
+ abortSignal.throwIfAborted();
282
+ while (abortSignal == null || !abortSignal.aborted) {
283
+ await (0, base_1.timeoutPromise)((intervalSeconds || 5) * 1000, abortSignal);
284
+ const result = await this.checkAddressTxos(address, txoHash);
285
+ if (result == null) {
286
+ stateUpdateCbk();
287
+ continue;
288
+ }
289
+ const confirmationDelay = await this.getConfirmationDelay(result.tx, requiredConfirmations);
290
+ if (confirmationDelay == null)
291
+ continue;
292
+ if (stateUpdateCbk != null)
293
+ stateUpdateCbk(result.tx, result.vout, confirmationDelay);
294
+ if (confirmationDelay === 0)
295
+ return result;
296
+ }
297
+ throw abortSignal.reason;
298
+ }
299
+ /**
300
+ * @inheritDoc
301
+ */
302
+ async waitForTransaction(txId, requiredConfirmations, stateUpdateCbk, abortSignal, intervalSeconds) {
303
+ if (abortSignal != null)
304
+ abortSignal.throwIfAborted();
305
+ while (abortSignal == null || !abortSignal.aborted) {
306
+ await (0, base_1.timeoutPromise)((intervalSeconds || 5) * 1000, abortSignal);
307
+ const result = await this.getTransaction(txId);
308
+ if (result == null) {
309
+ stateUpdateCbk();
310
+ continue;
311
+ }
312
+ const confirmationDelay = await this.getConfirmationDelay(result, requiredConfirmations);
313
+ if (confirmationDelay == null)
314
+ continue;
315
+ if (stateUpdateCbk != null)
316
+ stateUpdateCbk(result, confirmationDelay);
317
+ if (confirmationDelay === 0)
318
+ return result;
319
+ }
320
+ throw abortSignal.reason;
321
+ }
322
+ /**
323
+ * @inheritDoc
324
+ */
325
+ async getLNNodeLiquidity(pubkey) {
326
+ const nodeInfo = await this.api.getLNNodeInfo(pubkey);
327
+ if (nodeInfo == null)
328
+ return null;
329
+ return {
330
+ publicKey: nodeInfo.public_key,
331
+ capacity: BigInt(nodeInfo.capacity),
332
+ numChannels: nodeInfo.active_channel_count
333
+ };
334
+ }
335
+ /**
336
+ * @inheritDoc
337
+ */
338
+ sendRawTransaction(rawTx) {
339
+ return this.api.sendTransaction(rawTx);
340
+ }
341
+ /**
342
+ * @inheritDoc
343
+ */
344
+ sendRawPackage(rawTx) {
345
+ throw new Error("Unsupported");
346
+ }
347
+ /**
348
+ * @inheritDoc
349
+ */
350
+ async isSpent(utxo, confirmed) {
351
+ const [txId, voutStr] = utxo.split(":");
352
+ const vout = parseInt(voutStr);
353
+ const outspends = await this.api.getOutspends(txId);
354
+ if (outspends[vout] == null)
355
+ return true;
356
+ if (confirmed) {
357
+ return outspends[vout].spent && outspends[vout].status.confirmed;
358
+ }
359
+ return outspends[vout].spent;
360
+ }
361
+ /**
362
+ * @inheritDoc
363
+ */
364
+ parseTransaction(rawTx) {
365
+ const btcTx = btc_signer_1.Transaction.fromRaw(buffer_1.Buffer.from(rawTx, "hex"), {
366
+ allowLegacyWitnessUtxo: true,
367
+ allowUnknownInputs: true,
368
+ allowUnknownOutputs: true,
369
+ disableScriptCheck: true
370
+ });
371
+ return Promise.resolve(bitcoinTxToBtcTx(btcTx));
372
+ }
373
+ /**
374
+ * @inheritDoc
375
+ */
376
+ getEffectiveFeeRate(btcTx) {
377
+ throw new Error("Unsupported.");
378
+ }
379
+ /**
380
+ * @inheritDoc
381
+ */
382
+ async getFeeRate() {
383
+ return (await this.api.getFees()).fastestFee;
384
+ }
385
+ /**
386
+ * @inheritDoc
387
+ */
388
+ getAddressBalances(address) {
389
+ return this.api.getAddressBalances(address);
390
+ }
391
+ /**
392
+ * @inheritDoc
393
+ */
394
+ async getAddressUTXOs(address) {
395
+ return (await this.api.getAddressUTXOs(address)).map(val => ({
396
+ txid: val.txid,
397
+ vout: val.vout,
398
+ confirmed: val.status.confirmed,
399
+ block_height: val.status.block_height,
400
+ block_hash: val.status.block_hash,
401
+ block_time: val.status.block_time,
402
+ value: val.value
403
+ }));
404
+ }
405
+ /**
406
+ * @inheritDoc
407
+ */
408
+ async getCPFPData(txId) {
409
+ const cpfpData = await this.api.getCPFPData(txId);
410
+ if (cpfpData == null || cpfpData.effectiveFeePerVsize == null)
411
+ return null;
412
+ return cpfpData;
413
+ }
414
+ outputScriptToAddress(outputScriptHex) {
415
+ return Promise.resolve((0, btc_signer_1.Address)().encode(btc_signer_1.OutScript.decode(buffer_1.Buffer.from(outputScriptHex, "hex"))));
416
+ }
417
+ }
418
+ exports.MempoolBitcoinRpc = MempoolBitcoinRpc;
@@ -0,0 +1,30 @@
1
+ import { BtcRelay, BtcStoredHeader, RelaySynchronizer } from "@atomiqlabs/base";
2
+ import { MempoolBitcoinBlock } from "../mempool/MempoolBitcoinBlock";
3
+ import { MempoolBitcoinRpc } from "../mempool/MempoolBitcoinRpc";
4
+ /**
5
+ * Mempool.space API based bitcoin relay synchronizer
6
+ *
7
+ * @category Bitcoin
8
+ */
9
+ export declare class MempoolBtcRelaySynchronizer<B extends BtcStoredHeader<any>, TX> implements RelaySynchronizer<B, TX, MempoolBitcoinBlock> {
10
+ bitcoinRpc: MempoolBitcoinRpc;
11
+ btcRelay: BtcRelay<B, TX, MempoolBitcoinBlock>;
12
+ constructor(btcRelay: BtcRelay<B, TX, MempoolBitcoinBlock>, bitcoinRpc: MempoolBitcoinRpc);
13
+ /**
14
+ * @inheritDoc
15
+ */
16
+ syncToLatestTxs(signer: string, feeRate?: string): Promise<{
17
+ txs: TX[];
18
+ targetCommitedHeader: B;
19
+ computedHeaderMap: {
20
+ [blockheight: number]: B;
21
+ };
22
+ blockHeaderMap: {
23
+ [blockheight: number]: MempoolBitcoinBlock;
24
+ };
25
+ btcRelayTipCommitedHeader: B;
26
+ btcRelayTipBlockHeader: MempoolBitcoinBlock;
27
+ latestBlockHeader: MempoolBitcoinBlock;
28
+ startForkId?: number;
29
+ }>;
30
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MempoolBtcRelaySynchronizer = void 0;
4
+ const base_1 = require("@atomiqlabs/base");
5
+ /**
6
+ * Mempool.space API based bitcoin relay synchronizer
7
+ *
8
+ * @category Bitcoin
9
+ */
10
+ class MempoolBtcRelaySynchronizer {
11
+ constructor(btcRelay, bitcoinRpc) {
12
+ this.btcRelay = btcRelay;
13
+ this.bitcoinRpc = bitcoinRpc;
14
+ }
15
+ /**
16
+ * @inheritDoc
17
+ */
18
+ async syncToLatestTxs(signer, feeRate) {
19
+ const tipData = await this.btcRelay.getTipData();
20
+ if (tipData == null)
21
+ throw new Error("BtcRelay tip data not found - probably not initialized?");
22
+ const latestKnownBlockLogData = await this.btcRelay.retrieveLatestKnownBlockLog();
23
+ if (latestKnownBlockLogData == null)
24
+ throw new Error("Failed to get latest known block log");
25
+ const { resultStoredHeader, resultBitcoinHeader } = latestKnownBlockLogData;
26
+ let cacheData = {
27
+ forkId: resultStoredHeader.getBlockheight() < tipData.blockheight ? -1 : 0,
28
+ lastStoredHeader: resultStoredHeader,
29
+ computedCommitedHeaders: []
30
+ };
31
+ let spvTipBlockHeader = latestKnownBlockLogData.resultBitcoinHeader;
32
+ let spvTipBlockHeight = spvTipBlockHeader.height;
33
+ const txsList = [];
34
+ const blockHeaderMap = {
35
+ [resultBitcoinHeader.getHeight()]: resultBitcoinHeader
36
+ };
37
+ const computedHeaderMap = {
38
+ [resultStoredHeader.getBlockheight()]: resultStoredHeader
39
+ };
40
+ let startForkId = undefined;
41
+ let forkFee = feeRate;
42
+ let mainFee = feeRate;
43
+ const saveHeaders = async (headerCache) => {
44
+ if (cacheData.forkId === -1) {
45
+ if (mainFee == null)
46
+ mainFee = await this.btcRelay.getMainFeeRate(signer);
47
+ cacheData = await this.btcRelay.saveNewForkHeaders(signer, headerCache, cacheData.lastStoredHeader, tipData.chainWork, mainFee);
48
+ }
49
+ else if (cacheData.forkId === 0) {
50
+ if (mainFee == null)
51
+ mainFee = await this.btcRelay.getMainFeeRate(signer);
52
+ cacheData = await this.btcRelay.saveMainHeaders(signer, headerCache, cacheData.lastStoredHeader, mainFee);
53
+ }
54
+ else {
55
+ if (forkFee == null)
56
+ forkFee = await this.btcRelay.getForkFeeRate(signer, cacheData.forkId);
57
+ cacheData = await this.btcRelay.saveForkHeaders(signer, headerCache, cacheData.lastStoredHeader, cacheData.forkId, tipData.chainWork, forkFee);
58
+ }
59
+ if (cacheData.forkId !== -1 && cacheData.forkId !== 0)
60
+ startForkId = cacheData.forkId;
61
+ txsList.push(cacheData.tx);
62
+ for (let storedHeader of cacheData.computedCommitedHeaders) {
63
+ computedHeaderMap[storedHeader.getBlockheight()] = storedHeader;
64
+ }
65
+ };
66
+ let headerCache = [];
67
+ while (true) {
68
+ const retrievedHeaders = await this.bitcoinRpc.getPast15Blocks(spvTipBlockHeight + 15);
69
+ let startIndex = retrievedHeaders.findIndex(val => val.height === spvTipBlockHeight);
70
+ if (startIndex === -1)
71
+ startIndex = retrievedHeaders.length; //Start from the last block
72
+ for (let i = startIndex - 1; i >= 0; i--) {
73
+ const header = retrievedHeaders[i];
74
+ blockHeaderMap[header.height] = header;
75
+ headerCache.push(header);
76
+ if (cacheData.forkId === 0 ?
77
+ headerCache.length >= this.btcRelay.maxHeadersPerTx :
78
+ headerCache.length >= this.btcRelay.maxForkHeadersPerTx) {
79
+ await saveHeaders(headerCache);
80
+ headerCache = [];
81
+ }
82
+ }
83
+ if (retrievedHeaders.length > 0) {
84
+ if (spvTipBlockHeight === retrievedHeaders[0].height)
85
+ break; //Already at the tip
86
+ spvTipBlockHeight = retrievedHeaders[0].height;
87
+ await (0, base_1.timeoutPromise)(1000);
88
+ }
89
+ else
90
+ break;
91
+ }
92
+ if (headerCache.length > 0)
93
+ await saveHeaders(headerCache);
94
+ if (cacheData.forkId !== 0) {
95
+ throw new Error("Unable to synchronize on-chain bitcoin light client! Not enough chainwork at connected RPC.");
96
+ }
97
+ return {
98
+ txs: txsList,
99
+ targetCommitedHeader: cacheData.lastStoredHeader,
100
+ blockHeaderMap,
101
+ computedHeaderMap,
102
+ btcRelayTipCommitedHeader: resultStoredHeader,
103
+ btcRelayTipBlockHeader: resultBitcoinHeader,
104
+ latestBlockHeader: spvTipBlockHeader,
105
+ startForkId
106
+ };
107
+ }
108
+ }
109
+ exports.MempoolBtcRelaySynchronizer = MempoolBtcRelaySynchronizer;
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@atomiqlabs/btc-mempool",
3
+ "version": "1.0.1",
4
+ "description": "Connector and synchronizer using mempool.space API for bitcoin",
5
+ "main": "./dist/index.js",
6
+ "types:": "./dist/index.d.ts",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "build:ts4": "npx -p typescript@4.9 tsc --noEmit",
10
+ "build:ts5": "npx -p typescript@5.9 tsc --noEmit"
11
+ },
12
+ "files": [
13
+ "/dist",
14
+ "/src"
15
+ ],
16
+ "keywords": [
17
+ "Solana",
18
+ "Bitcoin",
19
+ "Cross-chain",
20
+ "Cryptocurrency",
21
+ "Bridge",
22
+ "Trustless"
23
+ ],
24
+ "author": "adambor",
25
+ "license": "ISC",
26
+ "dependencies": {
27
+ "@atomiqlabs/base": "github:atomiqlabs/atomiq-base#develop",
28
+ "@scure/btc-signer": "1.6.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^22.19.7",
32
+ "typescript": ">=4"
33
+ }
34
+ }
@@ -0,0 +1,18 @@
1
+
2
+ /**
3
+ * An error returned by the mempool api in a http response
4
+ *
5
+ * @category Errors
6
+ */
7
+ export class MempoolApiError extends Error {
8
+
9
+ httpCode: number;
10
+
11
+ constructor(msg: string, httpCode: number) {
12
+ super(`MempoolApiError(${httpCode}): `+msg);
13
+ // Set the prototype explicitly.
14
+ Object.setPrototypeOf(this, MempoolApiError.prototype);
15
+ this.httpCode = httpCode;
16
+ }
17
+
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+
2
+ export * from "./errors/MempoolApiError";
3
+
4
+ export * from "./mempool/MempoolApi";
5
+ export * from "./mempool/MempoolBitcoinBlock";
6
+ export * from "./mempool/MempoolBitcoinRpc";
7
+
8
+ export * from "./synchronizer/MempoolBtcRelaySynchronizer";