wevm / viem

TypeScript Interface for Ethereum
https://viem.sh
Other
2.48k stars 756 forks source link

bug: Cannot mix BigInt and other types, use explicit conversions on waitForTransaction receipt on zksync #1192

Closed prevostc closed 1 year ago

prevostc commented 1 year ago

Is there an existing issue for this?

Package Version

1.10.9

Current Behavior

On zksync, we had this error:

TypeError: Cannot mix BigInt and other types, use explicit conversions
    at Object.onBlockNumber (/app/node_modules/viem/dist/cjs/actions/public/waitForTransactionReceipt.js:57:41)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

I know that this error happens when bigints are used in arithmetic operations with null, undefined or numbers, but unfortunately I don't know which values were returned and I can't reproduce anymore.

The code this error points to is this (full transpile output below)

  if (confirmations > 0 &&
      blockNumber - receipt.blockNumber + 1n < confirmations)

So my guess is that somehow receipt.blockNumber gets an undefined value somehow but I'm not sure.

Full transpile output of waitForTransactionReceipt.js is this:


"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.waitForTransactionReceipt = void 0;
const transaction_js_1 = require("../../errors/transaction.js");
const observe_js_1 = require("../../utils/observe.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, { confirmations = 1, hash, onReplaced, pollingInterval = client.pollingInterval, timeout, }) {
    const observerId = (0, stringify_js_1.stringify)(['waitForTransactionReceipt', client.uid, hash]);
    let transaction;
    let replacedTransaction;
    let receipt;
    let retrying = false;
    return new Promise((resolve, reject) => {
        if (timeout)
            setTimeout(() => reject(new transaction_js_1.WaitForTransactionReceiptTimeoutError({ hash })), timeout);
        const _unobserve = (0, observe_js_1.observe)(observerId, { onReplaced, resolve, reject }, (emit) => {
            const _unwatch = (0, watchBlockNumber_js_1.watchBlockNumber)(client, {
                emitMissed: true,
                emitOnBegin: true,
                poll: true,
                pollingInterval,
                async onBlockNumber(blockNumber_) {
                    if (retrying)
                        return;
                    let blockNumber = blockNumber_;
                    const done = (fn) => {
                        _unwatch();
                        fn();
                        _unobserve();
                    };
                    try {
                        if (receipt) {
                            if (blockNumber - receipt.blockNumber + 1n < confirmations)
                                return;
                            done(() => emit.resolve(receipt));
                            return;
                        }
                        if (!transaction) {
                            retrying = true;
                            await (0, withRetry_js_1.withRetry)(async () => {
                                transaction = await (0, getTransaction_js_1.getTransaction)(client, { hash });
                                if (transaction.blockNumber)
                                    blockNumber = transaction.blockNumber;
                            }, {
                                delay: ({ count }) => ~~(1 << count) * 200,
                                retryCount: 6,
                            });
                            retrying = false;
                        }
                        receipt = await (0, getTransactionReceipt_js_1.getTransactionReceipt)(client, { hash });
                        if (confirmations > 0 &&
                            blockNumber - receipt.blockNumber + 1n < confirmations)
                            return;
                        done(() => emit.resolve(receipt));
                    }
                    catch (err) {
                        if (transaction &&
                            (err instanceof transaction_js_1.TransactionNotFoundError ||
                                err instanceof transaction_js_1.TransactionReceiptNotFoundError)) {
                            try {
                                replacedTransaction = transaction;
                                const block = await (0, getBlock_js_1.getBlock)(client, {
                                    blockNumber,
                                    includeTransactions: true,
                                });
                                const replacementTransaction = block.transactions.find(({ from, nonce }) => from === replacedTransaction.from &&
                                    nonce === replacedTransaction.nonce);
                                if (!replacementTransaction)
                                    return;
                                receipt = await (0, getTransactionReceipt_js_1.getTransactionReceipt)(client, {
                                    hash: replacementTransaction.hash,
                                });
                                if (blockNumber - receipt.blockNumber + 1n < confirmations)
                                    return;
                                let reason = 'replaced';
                                if (replacementTransaction.to === replacedTransaction.to &&
                                    replacementTransaction.value === replacedTransaction.value) {
                                    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));
                        }
                    }
                },
            });
        });
    });
}
exports.waitForTransactionReceipt = waitForTransactionReceipt;

Expected Behavior

If the blockNumber gets an undefined or null value, we probably want to throw an error like InvalidSerializableTransactionError or ParseRpcError?

Steps To Reproduce

I was not able to reproduce.

Link to Minimal Reproducible Example (StackBlitz, CodeSandbox, GitHub repo etc.)

No response

Anything else?

No response

github-actions[bot] commented 4 months ago

This issue has been locked since it has been closed for more than 14 days.

If you found a concrete bug or regression related to it, please open a new bug report with a reproduction against the latest Viem version. If you have any questions or comments you can create a new discussion thread.