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.
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.
MEV Shield Encrypted Transaction Flow
Key Pair
next_key
NextKey
Inner Tx
encrypted
Submission
Commitment
The protocol has three phases:
- Key Announcement: A validator generates an ML-KEM-768 key pair and publishes
the public key via
announce_next_key. The key is stored inNextKey. - Encrypt & Submit: You read
NextKey, encrypt your inner transaction, and callsubmit_encryptedwith the ciphertext and a commitment hash. The submission is queued inSubmissions. - 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 6Alice 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:
- A validator calls
announce_next_keywith their ML-KEM-768 public key. The key is stored inNextKey. - At the next block boundary,
NextKeyautomatically rotates intoCurrentKey. A newNextKeymay be announced by the next block author. - Users read
NextKey(notCurrentKey!) and use it to encrypt their inner transaction. - The user calls
submit_encryptedwith the ciphertext and commitment hash. TheEncryptedSubmittedevent fires and the submission is queued. - The block author (who holds the private key matching
CurrentKey) decrypts the submission, verifies the commitment matches, and dispatches the inner transaction. - On success,
DecryptedExecutedfires. If the inner transaction decrypts but fails during dispatch,DecryptedRejectedfires with the dispatch error. If decryption itself fails,DecryptionFailedfires and the inner transaction never executes.
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
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:
# 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 extrinsic6. Failure Modes
The MevShield pallet defines 7 errors covering three outcome paths. Understanding these helps diagnose issues:
| Error | When | Who Sees It |
|---|---|---|
| BadEncKeyLen | Encryption key length is invalid | Validator (announce_next_key fails) |
| Unreachable | Internal error that should never occur | Block author (inherent) |
The three outcome paths are:
- Success: Decryption succeeds, commitment matches, signature valid, inner tx
dispatches successfully. The
DecryptedExecutedevent fires. - Dispatch Failed: Decryption and verification succeed, but the inner
transaction fails during dispatch (e.g., insufficient balance). The
DecryptedRejectedevent fires with the dispatch error details. - Decryption/Verification Failed: The block author cannot decrypt or the
commitment doesn't match. The author calls
mark_decryption_failedand theDecryptionFailedevent fires. The inner transaction never executes.
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