This chapter builds on: Ch 14 Block Execution

MEV Shield

MEV Shield protects your transactions from front-running and sandwich attacks by encrypting them before they enter the mempool. This deep-dive covers the complete protocol: the cryptographic scheme, key lifecycle, nonce management gotcha, and every failure mode.

Introduced in Runtime v361

MEV Shield was added at block 7,063,679. The MevShield pallet contains 3 calls, 4 events, 4 storage items, and 7 errors. See the pallet reference for full details.

1. The Threat: What is MEV?

MEV (Miner/Maximal Extractable Value) is the profit a block producer can extract by manipulating transaction ordering. In traditional blockchain mempools, pending transactions are visible to everyone, including the block producer who decides their order.

The two most common MEV attacks are:

  • Front-running: A bot sees your large buy order, places its own buy first (driving up the price), then profits when your order executes at the higher price.
  • Sandwich attacks: A bot places a buy before and a sell after your trade, capturing the price impact you create.

On Bittensor, MEV risks are especially significant for staking swaps and Alpha pool operations. When you swap a large amount of TAO for Alpha on a subnet, the price impact is visible in the open mempool. A front-running bot can buy Alpha first, raise the price, and sell after your trade, extracting value directly from you.

2. How MEV Shield Works

MEV Shield implements an encrypted mempool. Instead of submitting transactions in plaintext, you encrypt your transaction with a public key announced by the next block author. The block author decrypts and executes your transaction during block production, but they committed to their key before seeing your encrypted submission, so they cannot selectively front-run based on content.

The protocol has three phases:

  1. Key Announcement: A validator generates an ML-KEM-768 key pair and publishes the public key via announce_next_key. The key is stored in NextKey.
  2. Encrypt & Submit: You read NextKey, encrypt your inner transaction, and call submit_encrypted with the ciphertext and a commitment hash. The submission is queued in Submissions.
  3. Decrypt & Execute: The block author decrypts your submission using the matching private key, verifies the commitment, and either executes the inner transaction or rejects it.

Scenario: Alice Protects Her Stake from Front-Running

Step 1 of 6

Alice wants to stake 50,000 TAO on Subnet 3

Alice plans a large stake swap: 50,000 TAO for SN3's alpha tokens. At this size, the swap will move the alpha price noticeably. If she submits this in plaintext, a front-running bot will see it in the mempool and sandwich her trade.

3. Key Lifecycle

Understanding the key rotation is critical to using MEV Shield correctly. The protocol uses two storage items:

  • NextKey: The key users should read for encrypting new submissions.
  • CurrentKey: The decryption-side key. The block author who announced this key holds the matching private key.

The lifecycle proceeds as follows:

  1. A validator calls announce_next_key with their ML-KEM-768 public key. The key is stored in NextKey.
  2. At the next block boundary, NextKey automatically rotates into CurrentKey. A new NextKey may be announced by the next block author.
  3. Users read NextKey (not CurrentKey!) and use it to encrypt their inner transaction.
  4. The user calls submit_encrypted with the ciphertext and commitment hash. The EncryptedSubmitted event fires and the submission is queued.
  5. The block author (who holds the private key matching CurrentKey) decrypts the submission, verifies the commitment matches, and dispatches the inner transaction.
  6. On success, DecryptedExecuted fires. If the inner transaction decrypts but fails during dispatch, DecryptedRejected fires with the dispatch error. If decryption itself fails, DecryptionFailed fires and the inner transaction never executes.
Always use NextKey, not CurrentKey

CurrentKey is the decryption-side reference: it tells the block author which private key to use. By the time a key is in CurrentKey, new submissions should use the newer NextKey instead.

4. The Nonce Trap

Common Pitfall

The outer submit_encrypted call and the inner transaction use different nonces. Getting this wrong means your inner transaction will be rejected with an invalid nonce error after decryption.

When you submit an encrypted transaction, you're sending two signed extrinsics – the outer submit_encrypted wrapper and the inner transaction that will be decrypted and executed later. Each requires its own nonce.

The nonce assignment must be:

  • Outer submit_encrypted: uses nonce N (the account's current nonce)
  • Inner transaction: uses nonce N+1 (because executing the outer call increments the account nonce first)

Step by step for direct callers:

python
# Manual nonce management for MEV Shield
# 1. Query current nonce
nonce = substrate.get_account_nonce(coldkey.ss58_address)  # N

# 2. Sign inner extrinsic with nonce N+1
inner_tx = substrate.create_signed_extrinsic(
    call=swap_call,
    keypair=coldkey,
    nonce=nonce + 1  # Inner tx uses N+1
)

# 3. Hash inner tx to create commitment
commitment = blake2b(inner_tx.encode())

# 4. Encrypt inner tx with NextKey
ciphertext = encrypt_with_ml_kem(next_key, inner_tx.encode())

# 5. Sign submit_encrypted with nonce N
outer_tx = substrate.create_signed_extrinsic(
    call=substrate.compose_call(
        "MevShield", "submit_encrypted",
        {"commitment": commitment, "encrypted": ciphertext}
    ),
    keypair=coldkey,
    nonce=nonce  # Outer tx uses N
)

The nonce management code above is required for all callers of submit_encrypted. Always query the current nonce before constructing the inner and outer extrinsics.

5. Cryptography

MEV Shield uses a hybrid encryption scheme combining two primitives:

ML-KEM-768 (Key Encapsulation)

ML-KEM-768 is a post-quantum key encapsulation mechanism standardized as FIPS 203. It generates a shared secret from the recipient's public key using lattice-based cryptography that resists both classical and quantum attacks. The "768" refers to the security parameter, roughly equivalent to 192-bit classical security.

Why post-quantum? Encrypted submissions are stored on-chain permanently. If a future quantum computer could break the key encapsulation, it could retroactively decrypt all historical submissions. ML-KEM provides forward security against this scenario.

XChaCha20-Poly1305 (Authenticated Encryption)

The shared secret from ML-KEM is used as the key for XChaCha20-Poly1305, an authenticated encryption scheme with a 24-byte nonce. "Authenticated" means the ciphertext includes a MAC (message authentication code) that detects tampering. If anyone modifies the ciphertext, decryption will fail rather than producing corrupted data.

Ciphertext Format

The final ciphertext submitted on-chain has this structure:

[u16 kem_len] || kem_ciphertext || nonce24 || aead_ciphertext

Field           Size              Description
kem_len         2 bytes           Length of the KEM ciphertext (u16 LE)
kem_ciphertext  kem_len bytes     ML-KEM-768 encapsulated shared secret
nonce24         24 bytes          XChaCha20-Poly1305 nonce
aead_ciphertext variable          Encrypted + authenticated inner extrinsic

6. Failure Modes

The MevShield pallet defines 7 errors covering three outcome paths. Understanding these helps diagnose issues:

ErrorWhenWho Sees It
BadEncKeyLenEncryption key length is invalidValidator (announce_next_key fails)
UnreachableInternal error that should never occurBlock author (inherent)

The three outcome paths are:

  1. Success: Decryption succeeds, commitment matches, signature valid, inner tx dispatches successfully. The DecryptedExecuted event fires.
  2. Dispatch Failed: Decryption and verification succeed, but the inner transaction fails during dispatch (e.g., insufficient balance). The DecryptedRejected event fires with the dispatch error details.
  3. Decryption/Verification Failed: The block author cannot decrypt or the commitment doesn't match. The author calls mark_decryption_failed and the DecryptionFailed event fires. The inner transaction never executes.
CommitmentMismatch and SignatureInvalid

These errors are thrown during block processing (as an inherent), not by user-callable dispatchables. You won't see them as extrinsic errors; they appear during the block author's internal processing of encrypted submissions.

7. Coldkey Requirement

Both the outer submit_encrypted call and the inner transaction must be signed by a coldkey . The official Bittensor documentation states:

"MEV shield should not be used for transactions that are signed by a hotkey" as it "may result in unintended consequences that could result in asset loss."

MEV Shield is designed for coldkey operations like staking swaps – not for hotkey-signed operations like setting weights.

Source Code

The MEV Shield implementation lives in the Subtensor repository:

  • pallets/shield/src/lib.rs: Pallet definition, calls, events, errors

What's next?

You've completed the learning path. Here's where to go from here.