"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.waitForTransactionReceipt = waitForTransactionReceipt;
const block_js_1 = require("../../errors/block.js");
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 withResolvers_js_1 = require("../../utils/promise/withResolvers.js");
const withRetry_js_1 = require("../../utils/promise/withRetry.js");
const stringify_js_1 = require("../../utils/stringify.js");
const getBlock_js_1 = require("./getBlock.js");
const getTransaction_js_1 = require("./getTransaction.js");
const getTransactionReceipt_js_1 = require("./getTransactionReceipt.js");
const watchBlockNumber_js_1 = require("./watchBlockNumber.js");
async function waitForTransactionReceipt(client, parameters) {
    const { checkReplacement = true, confirmations = 1, hash, onReplaced, retryCount = 6, retryDelay = ({ count }) => ~~(1 << count) * 200, timeout = 180_000, } = parameters;
    const observerId = (0, stringify_js_1.stringify)(['waitForTransactionReceipt', client.uid, hash]);
    const pollingInterval = (() => {
        if (parameters.pollingInterval)
            return parameters.pollingInterval;
        if (client.chain?.experimental_preconfirmationTime)
            return client.chain.experimental_preconfirmationTime;
        return client.pollingInterval;
    })();
    let transaction;
    let replacedTransaction;
    let receipt;
    let retrying = false;
    let _unobserve;
    let _unwatch;
    const { promise, resolve, reject } = (0, withResolvers_js_1.withResolvers)();
    const timer = timeout
        ? setTimeout(() => {
            _unwatch?.();
            _unobserve?.();
            reject(new transaction_js_1.WaitForTransactionReceiptTimeoutError({ hash }));
        }, timeout)
        : undefined;
    _unobserve = (0, observe_js_1.observe)(observerId, { onReplaced, resolve, reject }, async (emit) => {
        receipt = await (0, getAction_js_1.getAction)(client, getTransactionReceipt_js_1.getTransactionReceipt, 'getTransactionReceipt')({ hash }).catch(() => undefined);
        if (receipt && confirmations <= 1) {
            clearTimeout(timer);
            emit.resolve(receipt);
            _unobserve?.();
            return;
        }
        _unwatch = (0, getAction_js_1.getAction)(client, watchBlockNumber_js_1.watchBlockNumber, 'watchBlockNumber')({
            emitMissed: true,
            emitOnBegin: true,
            poll: true,
            pollingInterval,
            async onBlockNumber(blockNumber_) {
                const done = (fn) => {
                    clearTimeout(timer);
                    _unwatch?.();
                    fn();
                    _unobserve?.();
                };
                let blockNumber = blockNumber_;
                if (retrying)
                    return;
                try {
                    if (receipt) {
                        if (confirmations > 1 &&
                            (!receipt.blockNumber ||
                                blockNumber - receipt.blockNumber + 1n < confirmations))
                            return;
                        done(() => emit.resolve(receipt));
                        return;
                    }
                    if (checkReplacement && !transaction) {
                        retrying = true;
                        await (0, withRetry_js_1.withRetry)(async () => {
                            transaction = (await (0, getAction_js_1.getAction)(client, getTransaction_js_1.getTransaction, 'getTransaction')({ hash }));
                            if (transaction.blockNumber)
                                blockNumber = transaction.blockNumber;
                        }, {
                            delay: retryDelay,
                            retryCount,
                        });
                        retrying = false;
                    }
                    receipt = await (0, getAction_js_1.getAction)(client, getTransactionReceipt_js_1.getTransactionReceipt, 'getTransactionReceipt')({ hash });
                    if (confirmations > 1 &&
                        (!receipt.blockNumber ||
                            blockNumber - receipt.blockNumber + 1n < confirmations))
                        return;
                    done(() => emit.resolve(receipt));
                }
                catch (err) {
                    if (err instanceof transaction_js_1.TransactionNotFoundError ||
                        err instanceof transaction_js_1.TransactionReceiptNotFoundError) {
                        if (!transaction) {
                            retrying = false;
                            return;
                        }
                        try {
                            replacedTransaction = transaction;
                            retrying = true;
                            const block = await (0, withRetry_js_1.withRetry)(() => (0, getAction_js_1.getAction)(client, getBlock_js_1.getBlock, 'getBlock')({
                                blockNumber,
                                includeTransactions: true,
                            }), {
                                delay: retryDelay,
                                retryCount,
                                shouldRetry: ({ error }) => error instanceof block_js_1.BlockNotFoundError,
                            });
                            retrying = false;
                            const replacementTransaction = block.transactions.find(({ from, nonce }) => from === replacedTransaction.from &&
                                nonce === replacedTransaction.nonce);
                            if (!replacementTransaction)
                                return;
                            receipt = await (0, getAction_js_1.getAction)(client, getTransactionReceipt_js_1.getTransactionReceipt, 'getTransactionReceipt')({
                                hash: replacementTransaction.hash,
                            });
                            if (confirmations > 1 &&
                                (!receipt.blockNumber ||
                                    blockNumber - receipt.blockNumber + 1n < confirmations))
                                return;
                            let reason = 'replaced';
                            if (replacementTransaction.to === replacedTransaction.to &&
                                replacementTransaction.value === replacedTransaction.value &&
                                replacementTransaction.input === replacedTransaction.input) {
                                reason = 'repriced';
                            }
                            else if (replacementTransaction.from === replacementTransaction.to &&
                                replacementTransaction.value === 0n) {
                                reason = 'cancelled';
                            }
                            done(() => {
                                emit.onReplaced?.({
                                    reason,
                                    replacedTransaction: replacedTransaction,
                                    transaction: replacementTransaction,
                                    transactionReceipt: receipt,
                                });
                                emit.resolve(receipt);
                            });
                        }
                        catch (err_) {
                            done(() => emit.reject(err_));
                        }
                    }
                    else {
                        done(() => emit.reject(err));
                    }
                }
            },
        });
    });
    return promise;
}
//# sourceMappingURL=waitForTransactionReceipt.js.map