For the complete documentation index, see llms.txt. This page is also available as Markdown.

75330 bc low mempool deadline after successful publish can abandon the next required nonce

Submitted on Apr 28th 2026 at 14:45:24 UTC by @y4y for Audit Comp | Base Azul

  • Report ID: #75330

  • Report Type: Blockchain/DLT

  • Report severity: Low

  • Target: https://github.com/base/base/tree/v0.8.0-rc.28

  • Impacts:

    • Temporary freezing of network transactions by delaying one block by 500% or more of the average block time of the preceding 24 hours beyond standard difficulty adjustments

Description

Brief/Intro

The tx manager sets tx_not_in_mempool_timeout before the first publish attempt, records a successful publish when send_raw_transaction returns, but still treats the expired mempool deadline as a terminal send failure if no receipt was observed in time. In the send_async() path that nonce was already pre-reserved and is not returned once publication succeeded. The next send therefore allocates the next nonce even though the lower nonce may still be live in the txpool or later mineable.

In the batcher path this becomes an L1 publication stall:

  1. a batch publication tx is accepted with nonce N;

  2. the tx manager returns MempoolDeadlineExpired;

  3. the batcher requeues the same work and retries it;

  4. send_async() pre-reserves nonce N+1;

  5. the lower nonce N is still unresolved on L1;

  6. unsafe_l2 can continue advancing, but the client safe view remains stale until the lower L1 nonces are finally resolved.

Vulnerability Details

send_tx() arms the mempool deadline before the transaction is ever published:

The initial send path publishes first, then enters the event loop:

publish_tx() records a successful publish:

But critical_error() still aborts on the mempool deadline even after has_published = true:

When the send started through send_async(), the nonce was already consumed before the task was spawned:

After a failed send_async(), the reserved nonce is returned only if no transaction was ever published:

Once the batcher sees that error, it treats it as a normal failed submission, requeues the frames, and tries again:

Concrete runtime example from the verified compose-backed PoC:

  1. the batcher published L1 nonces 11 and 12;

  2. the chain nonce was still 11, but the pending nonce had already advanced to 13;

  3. txpool_content still contained live batcher entries at nonces 11 and 12;

  4. unsafe_l2 advanced from 23 to 24 while safe_l2 stayed at 0;

  5. the previously abandoned lower nonces later mined in L1 block 28;

  6. only after those lower nonces resolved did the client safe head advance to 10.

The logical mismatch is:

even though nonce N is still unresolved and still gates later nonce execution.

Pre-conditions

  • tx_not_in_mempool_timeout is non-zero. The default config is 120s.

  • The send path uses send_async() or another caller that pre-reserves the nonce before the send task runs.

  • The transaction is accepted by RPC and recorded as published.

  • No receipt is observed before the mempool deadline expires.

  • The caller retries the work after the failed send, as the batcher does.

Impact Details

The compose-backed PoC demonstrates the protocol consequence directly:

  • the batcher sender had live L1 txs at nonces 11 and 12;

  • the batcher pending nonce had already advanced to 13;

  • unsafe_l2 continued to advance;

  • the L1-backed safe_l2 remained stale until those lower L1 nonces later mined.

The system can continue producing unsafe L2 blocks while the L1-backed safe view lags behind because publication is stranded behind abandoned lower nonces. The PoC prints this directly as:

Operational recovery is possible by eventual inclusion, explicit nonce repair, or restart, but forward safe progress is still lost once this path is hit.

References

  • base/crates/utilities/tx-manager/src/manager.rs:906-968, 1010-1022, 1606-1629

  • base/crates/utilities/tx-manager/src/send_state.rs:138-177

  • base/crates/batcher/core/src/submissions.rs:145-220

  • base/crates/utilities/tx-manager/src/config.rs:128-142

https://gist.github.com/brandonshiyay/0d683896ee228a3269b9e78a3b392f35

Proof of Concept

Add the following file under devnet/examples/stale_l1_publications.rs:

For more instruction/reproduction steps, please see attached gist.

Was this helpful?