Keyed nonces and recent roots, removing two obstacles to private transactions

Strawmap Hegotá (2027) · keyed nonces & recent roots

This adds two mechanisms to the frame transaction (EIP-8141), a new transaction format. A single sender address gets multiple ticket lines so that unrelated transactions do not have to wait. It also lets an app's recently recorded summary value, a Merkle root, be checked in the mempool validation stage.

Why it is needed

The proposed frame transaction uses only one nonce (a transaction sequence number) per sending address, so if an earlier transaction is delayed, all later transactions from the same address must wait. Privacy apps have multiple users share a single common sender address so that activity is not tied to a particular address, and with only one nonce this address becomes a bottleneck. When one user's transaction is included in a block, the pending withdrawals of other, unrelated users all become invalid. In addition, when a frame transaction is validated in the public mempool (the transaction queue), it cannot read the storage of other apps, because those values can change at any time. Yet privacy apps must prove spends against a recent commitment tree root. Keyed nonces solve the first problem by splitting the ticket line into several, and recent roots solve the second by placing the root in a standard storage that changes only on a reorg (chain reorganization). The two proposals were accepted as Hegotá candidates at the July 2026 ACDE, moved up to Considered for Inclusion (CFI) in September, and in October it was decided to include them in bogota-devnet-1. The Strawmap's Hegotá column is only a projection and not a commitment.

How it works

  1. Split the ticket line into several In the nonce field of a transaction, a list of nonce keys (up to 16) and a sequence number are placed. Transactions whose keys do not overlap do not wait for each other's order. If the key list is empty, the account nonce is used as today. The per-key sequence numbers are recorded by the protocol directly in a dedicated contract's storage.
  2. One-time keys are supported too If the sequence number is sent as the maximum value, all selected keys must be unused, and once used, those keys are exhausted. This form is tailored to values that must be used only once, such as a nullifier, which must leave a mark that it was used. This check is done before any frame is executed.
  3. The app writes recent roots into a standard contract The app sends the standard contract a salt (a distinguishing value) and a root, and the contract records it in a cell for that slot (the consensus layer's unit of time). The writer is identified by the calling address and the salt, and each writer cycles through 8192 cells. The stored value includes both the writer and the slot, so an old root cannot pass.
  4. Transactions only point to roots from past slots A root written in slot S can be referenced from S+1 onward. The window that can be referenced is 8191 slots, excluding the current slot. A dedicated VERIFY frame checks this. Unless there is a reorg the value does not change, so it is safe for nodes to check and index it ahead of time in the mempool.

Related EIPs

← Roadmap