UNPKG

@bigmi/core

Version:

TypeScript library for Bitcoin apps.

187 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitForTransaction = waitForTransaction; const bitcoinjs_lib_1 = require("bitcoinjs-lib"); const transaction_js_1 = require("../errors/transaction.js"); const getAction_js_1 = require("../utils/getAction.js"); const observe_js_1 = require("../utils/observe.js"); const stringify_js_1 = require("../utils/stringify.js"); const withRetry_js_1 = require("../utils/withRetry.js"); const getBlock_js_1 = require("./getBlock.js"); const getBlockStats_js_1 = require("./getBlockStats.js"); const getUTXOTransaction_js_1 = require("./getUTXOTransaction.js"); const watchBlockNumber_js_1 = require("./watchBlockNumber.js"); async function waitForTransaction(client, { confirmations = 1, txId, txHex, senderAddress, onReplaced, pollingInterval = client.pollingInterval, retryCount = 10, retryDelay = 3000, timeout, }) { const observerId = (0, stringify_js_1.stringify)(['waitForTransaction', client.uid, txId]); let count = 0; let transaction; let replacedTransaction; let retrying = false; return new Promise((resolve, reject) => { if (timeout) { setTimeout(() => reject(new transaction_js_1.WaitForTransactionReceiptTimeoutError({ hash: txId })), timeout); } const _unobserve = (0, observe_js_1.observe)(observerId, { onReplaced, resolve, reject }, (emit) => { const _unwatch = (0, getAction_js_1.getAction)(client, watchBlockNumber_js_1.watchBlockNumber, 'watchBlockNumber')({ emitMissed: true, emitOnBegin: true, pollingInterval, async onBlockNumber(blockNumber_) { const done = (fn) => { _unwatch(); fn(); _unobserve(); }; let blockNumber = blockNumber_; if (retrying) { return; } if (count > retryCount) { done(() => emit.reject(new transaction_js_1.WaitForTransactionReceiptTimeoutError({ hash: txId, }))); } try { if (transaction?.blockhash) { const blockStats = await (0, getAction_js_1.getAction)(client, getBlockStats_js_1.getBlockStats, 'getBlockStats')({ blockHash: transaction.blockhash, stats: ['height'], }); if (confirmations > 1 && (!blockStats.height || blockNumber - blockStats.height + 1 < confirmations)) { return; } done(() => emit.resolve(transaction)); return; } retrying = true; transaction = await (0, withRetry_js_1.withRetry)(() => (0, getAction_js_1.getAction)(client, getUTXOTransaction_js_1.getUTXOTransaction, 'getUTXOTransaction')({ txId: transaction?.txid || txId }), { delay: retryDelay, retryCount, }); if (transaction.blockhash) { const blockStats = await (0, getAction_js_1.getAction)(client, getBlockStats_js_1.getBlockStats, 'getBlockStats')({ blockHash: transaction.blockhash, stats: ['height'], }); if (blockStats.height) { blockNumber = blockStats.height; } } retrying = false; if (!transaction?.confirmations) { throw new transaction_js_1.TransactionReceiptNotFoundError({ hash: txId, }); } if (transaction.confirmations < confirmations) { return; } done(() => emit.resolve(transaction)); } catch (err) { if (err instanceof transaction_js_1.TransactionNotFoundError || err instanceof transaction_js_1.TransactionReceiptNotFoundError) { try { replacedTransaction = bitcoinjs_lib_1.Transaction.fromHex(transaction?.hex || txHex); retrying = true; const block = await (0, withRetry_js_1.withRetry)(() => (0, getAction_js_1.getAction)(client, getBlock_js_1.getBlock, 'getBlock')({ blockNumber, }), { delay: retryDelay, retryCount, }); retrying = false; const replacedTransactionInputs = new Set(); for (const input of replacedTransaction.ins) { const txid = Array.from(input.hash) .reverse() .map((byte) => `00${byte.toString(16)}`.slice(-2)) .join(''); const vout = input.index; const inputId = `${txid}:${vout}`; replacedTransactionInputs.add(inputId); } let replacementTransaction; for (const tx of block.transactions) { if (tx.isCoinbase()) { continue; } for (const input of tx.ins) { const txid = Array.from(input.hash) .reverse() .map((byte) => `00${byte.toString(16)}`.slice(-2)) .join(''); const vout = input.index; const inputId = `${txid}:${vout}`; if (replacedTransactionInputs.has(inputId)) { replacementTransaction = tx; break; } } if (replacementTransaction) { break; } } if (!replacementTransaction) { return; } transaction = await (0, getAction_js_1.getAction)(client, getUTXOTransaction_js_1.getUTXOTransaction, 'getUTXOTransaction')({ txId: replacementTransaction.getId(), }); if (transaction.confirmations && transaction.confirmations < confirmations) { return; } let reason = 'replaced'; function getOutputAddresses(tx) { const addresses = []; for (const output of tx.outs) { try { const outputAddress = bitcoinjs_lib_1.address.fromOutputScript(output.script); addresses.push(outputAddress); } catch (_e) { } } return addresses; } const originalOutputAddresses = getOutputAddresses(replacedTransaction); const replacementOutputAddresses = getOutputAddresses(replacementTransaction); if (originalOutputAddresses.length === replacementOutputAddresses.length && originalOutputAddresses.every((address) => replacementOutputAddresses.includes(address))) { reason = 'repriced'; } else if (senderAddress && replacementOutputAddresses.length === 1 && replacementOutputAddresses.includes(senderAddress)) { reason = 'cancelled'; } done(() => { emit.onReplaced?.({ reason, replacedTransaction: replacedTransaction, transaction: transaction, }); emit.resolve(transaction); }); } catch (err_) { done(() => emit.reject(err_)); } } else { done(() => emit.reject(err)); } } finally { count++; } }, }); }); }); } //# sourceMappingURL=waitForTransaction.js.map