How Nautilus Wallet Handles Transaction Mempool and Unconfirmed Transactions

Nautilus Wallet tracks unconfirmed transactions through a zero-confirmation setting that queries both the blockchain and mempool, automatically cleaning up stale pending UTXOs when transactions leave the mempool.

Nautilus Wallet is an open-source browser extension wallet for the Ergo blockchain. To provide accurate balance displays while transactions await confirmation, the wallet implements a sophisticated mempool handling system that optionally includes unconfirmed transactions in balance calculations and actively monitors pending transaction status.

Zero-Confirmation Settings and Mempool Integration

Enabling Zero-Confirmation Mode

The wallet exposes a user-configurable zeroConf flag in the application settings. When enabled, the wallet treats mempool transactions as valid for balance calculations and UTXO selection.

In src/stores/appStore.ts, the settings type definition includes:

export type Settings = {
  …
  zeroConf: boolean;               // toggle for mempool inclusion
  …
};

The default value is defined in src/constants/settings.ts as false, ensuring conservative behavior for new users.

Fetching Boxes from Blockchain and Mempool

When the wallet needs to retrieve UTXOs (boxes), the fetchBoxes() function in src/chains/ergo/boxFetcher.ts dynamically selects the data source based on the zero-confirmation setting:

const from: BoxSource = includeUnconf ? "blockchain+mempool" : "blockchain";

let boxes = await graphQLService.getBoxes({ where: { addresses }, from });

The includeUnconf parameter is passed from ergoHandlers.ts based on settings.zeroConf. This ensures that when zero-confirmation mode is active, the wallet sees unconfirmed incoming transactions and excludes spent outputs that are waiting for confirmation.

Tracking Pending Transactions in the Mempool

Mempool Transaction Lookup

To determine whether a pending transaction is still waiting for confirmation or has been included in a block (or dropped), Nautilus uses graphQLService.mempoolTransactionsLookup(). This method, located in src/chains/ergo/services/graphQlService.ts, queries the GraphQL backend for the presence of specific transaction IDs in the mempool:

async mempoolTransactionsLookup(txIds: string[]): Promise<Set<string>> {
  const set = new Set<string>();
  const chunks = chunk(txIds, MAX_PARAMS_PER_REQUEST);

  for (const txIds of chunks) {
    const { data } = await this.#checkMempoolTxs({ transactionIds: txIds });
    if (data?.mempool?.transactions?.length === 0) continue;
    for (const tx of data.mempool.transactions) set.add(tx.transactionId);
  }
  return set;
}

The function returns a Set<string> containing only the transaction IDs still present in the mempool. This powers the wallet's ability to distinguish between transactions that are still pending versus those that have been confirmed or evicted.

Merging Local Unconfirmed Boxes

When a user creates a transaction, the resulting output boxes are stored locally in utxosDbService before the transaction is confirmed. The boxFetcher.ts file merges these local unconfirmed boxes with the remote fetched boxes:

if (localUnconfirmedBoxes.length > 0) {
  const lockedIds = localUnconfirmedBoxes.filter((x) => x.locked).map((x) => x.id);
  const unconfirmed = localUnconfirmedBoxes
    .filter((b) => !b.locked && b.content)
    .map((b) => b.content!);
  …
  if (unconfirmed.length > 0) boxes = unionBy(boxes, unconfirmed, (b) => b.boxId);
}

These local entries remain valid only while the parent transaction exists in the mempool. Once the transaction is confirmed or dropped, checkPendingBoxes() removes them from the local database.

Cleaning Up Stale Pending UTXOs

Automatic Cleanup on New Blocks

Nautilus implements an automatic cleanup mechanism to prevent stale pending UTXOs from accumulating. In src/stores/appStore.ts, the application watches for blockchain height changes and triggers checkPendingBoxes():

watch(() => chain.height, checkPendingBoxes);

This function retrieves all pending UTXOs from the local database, filters for those older than UTXO_CHECK_INTERVAL, queries the mempool to see which transactions are still pending, and removes entries for transactions that no longer exist in the mempool:

async function checkPendingBoxes() {
  const dbBoxes = await utxosDbService.getAllPending();
  const boxesToCheck = dbBoxes.filter(
    (b) => b.spentTimestamp && Date.now() - b.spentTimestamp >= UTXO_CHECK_INTERVAL
  );
  if (boxesToCheck.length === 0) return;

  const txIds = uniq(boxesToCheck.map((b) => b.spentTxId));
  const mempool = await graphQLService.mempoolTransactionsLookup(txIds);
  await utxosDbService.removeByTxIds(
    txIds.filter((id) => !mempool.has(id))
  );
}

The Pending UTXO Check Interval

The cleanup interval is defined in src/constants/intervals.ts as UTXO_CHECK_INTERVAL (default 30 seconds). This prevents excessive GraphQL queries while ensuring that dropped or confirmed transactions are cleaned up promptly.

Balance Queries and Zero-Confirmation Display

The background handlers in src/extension/background/ergoHandlers.ts respect the zero-confirmation setting when responding to balance and UTXO queries from the extension UI:

// In background handler
const boxes = await fetchBoxes(walletId, settings.zeroConf);
…
return settings.zeroConf ? getRemoteBalance(walletId, tokenId) : getLocalBalance(walletId, tokenId);

When settings.zeroConf is true, getRemoteBalance queries the GraphQL backend with from: "blockchain+mempool", ensuring that unconfirmed incoming and outgoing transactions are reflected in the displayed balance.

Key Implementation Files

File Primary Responsibility
src/stores/appStore.ts Holds settings.zeroConf; watches chain height → checkPendingBoxes()
src/chains/ergo/services/graphQlService.ts GraphQL wrapper; implements mempoolTransactionsLookup
src/chains/ergo/boxFetcher.ts Retrieves boxes from "blockchain+mempool" and merges local unconfirmed boxes
src/extension/background/ergoHandlers.ts Public API for UTXO & balance queries; respects zero‑conf
src/constants/settings.ts Default zeroConf: false
src/constants/intervals.ts UTXO_CHECK_INTERVAL (time before a pending box is re‑checked)

Summary

Frequently Asked Questions

How does Nautilus Wallet determine if a pending transaction is still valid?

Nautilus Wallet uses the mempoolTransactionsLookup() method in src/chains/ergo/services/graphQlService.ts to query the GraphQL backend for specific transaction IDs. This returns a Set of transactions still present in the mempool. If a transaction ID is not in this set, the wallet assumes it has been confirmed or dropped and removes the associated pending UTXO from the local database via checkPendingBoxes().

What is the zero-confirmation setting and how does it affect my balance?

The zeroConf setting, defined in src/stores/appStore.ts and defaulting to false in src/constants/settings.ts, controls whether the wallet includes mempool transactions in balance calculations and UTXO selection. When enabled, src/chains/ergo/boxFetcher.ts queries the GraphQL backend with from: "blockchain+mempool", allowing you to see incoming funds immediately and spend change outputs before confirmation.

How often does Nautilus Wallet clean up stale pending transactions?

The wallet checks for stale pending UTXOs every time the blockchain height changes, as implemented in src/stores/appStore.ts via watch(() => chain.height, checkPendingBoxes). Additionally, individual UTXOs are only checked against the mempool if they have been pending for longer than UTXO_CHECK_INTERVAL (30 seconds by default), defined in src/constants/intervals.ts. This prevents excessive API calls while ensuring timely cleanup.

Can I manually verify if my transaction is still in the mempool?

Yes, you can use the internal graphQLService.mempoolTransactionsLookup() method to check specific transaction IDs. While the wallet automatically handles this in checkPendingBoxes(), developers can call this method directly with an array of transaction IDs to receive a Set of those still present in the mempool, as shown in the implementation in src/chains/ergo/services/graphQlService.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →