Every transaction your users send sits in a queue before it executes, and the people who decide the order of that queue are paid to make money from it. They can see a user's swap before it lands, put their own trade in front of it and another behind it, or watch your oracle's price update enter the mempool and act on the new price before your contract does. None of this requires a bug in your code. It requires only that your mechanism assigns value to position in a block, and that someone else controls the position.

Maximal extractable value is therefore a property of the design, not an edge case to patch after launch. A lending protocol decides, in its liquidation formula, how much of a borrower's collateral goes to whoever wins a race. An AMM decides, in its curve, how much a sandwich attacker earns per unit of slippage. These choices are made whether or not the designer thinks about them. This article is for the architects making them, the engineers implementing them, and the auditors reviewing the result.

Assume the adversary orders your transactions

The useful mental model is simple. For every transaction that touches your contracts, assume an adversary can read it before execution, insert any number of their own transactions before and after it, and delay it for a bounded time. Then ask what that adversary earns.

Every weaker assumption has been violated in practice. Public mempools expose transactions to everyone, private relays to the relay operator, centralised sequencers to the sequencer. Even where the ordering party is honest today, your design should not depend on that persisting, because the ordering party can change and your contracts cannot.

The adversary is not one person; it is a market. Searchers run bots that scan pending transactions for profitable patterns. Builders assemble blocks from those bundles, optimising for total payment. Proposers accept the most valuable block offered. Each layer is competitive, so any value your mechanism leaves on the table will be found, priced, and extracted within a few blocks of being discovered.

The classic extractions

Each of the patterns below has a precondition in the design. Recognising it is the first step to removing it.

Sandwiches

A user submits an AMM swap with a slippage tolerance. The attacker buys the same asset first to push the price up, lets the user's swap execute at the worse price, then sells back into the pool the user just moved. Profit is bounded by the user's tolerance minus fees and gas, which is why wide default slippage in a front-end is, from our point of view, a direct subsidy to searchers. The precondition is an execution price that depends on state the attacker can move within the block, combined with an acceptable outcome wider than the real market.

Back-running

After a large trade moves a pool away from other venues, a searcher arbitrages it back. After a liquidation frees collateral, a searcher buys it. After an oracle update, a searcher trades on the new price. The back-runner takes nothing directly from the preceding transaction; they take the value it created and that the protocol chose not to capture. The design question is whether that value should go to the protocol, to the user who created it, or to whoever paid the most for the next position. By default it goes to the third group.

Liquidation races

A position becomes liquidatable the moment an oracle update pushes its health factor below one. Whoever liquidates first takes the bonus, and the prize is a fixed percentage of the collateral regardless of how much competition there was. The borrower loses the full bonus even when a smaller one would have found a willing liquidator, and the surplus flows to builders and validators as searchers bid for position. Dynamic bonuses and explicit liquidation auctions exist to fix exactly that.

Just-in-time liquidity

On concentrated-liquidity AMMs, a searcher can see a large pending swap, add a tight band of liquidity at the current price in the same block, collect most of that swap's fees, and withdraw immediately. Passive providers who bore inventory risk through every quiet block are diluted at the one moment they would have earned. The precondition is instantaneous, fee-free entry and exit for liquidity with fees paid at the moment of the trade; minimum durations and vesting fee accrual change the economics.

Oracle-update front-running

Push-based oracles publish a price as a transaction. Between its appearance in the mempool and its execution, everyone can see the new price while every contract still uses the old one. A searcher opens a position that will be instantly profitable at the new price, or closes one that would be instantly underwater. Mitigations include settling against a price committed in a later block, fees or delays on position changes that make the window unprofitable, and pull-based oracles verified inside the user's own transaction so that no gap exists; see securing oracles and price feeds.

How builders, searchers and proposer-builder separation change the model

The early picture of MEV was a validator reordering its own mempool. Proposer-builder separation changed the roles. Searchers find opportunities and package them into bundles with a bid. Builders compete to assemble the most profitable block, paying searchers for bundles and the proposer for inclusion. The proposer commits to a header without seeing the contents.

For a designer this has three consequences. The ordering adversary is now a specialised, well-capitalised firm, so assume the best possible extraction rather than an average one. Atomic bundles are the norm, so an attacker can guarantee that their front-run, your user's transaction, and their back-run execute together or not at all, removing most of the risk that used to limit sandwiching. And the builder simulates the whole block before anyone else sees it, so any defence that relies on the attacker not knowing what other transactions will do is weaker than it looks.

The same structure creates options: because ordering is an explicit market, a protocol can auction the right to back-run its own transactions and redistribute the proceeds.

Private order flow and its trust trade-offs

If the mempool is the problem, one answer is to avoid it. Private relays accept a transaction directly, hide it from searchers, and deliver it to a builder, sometimes sharing back-running profit with the user.

This removes the sandwich by introducing a trusted party. The relay sees every transaction it carries. It can front-run them itself, sell the information, or simply fail and leave the transaction unsent, and its promises are enforced by reputation rather than by protocol. If your front-end routes users through a relay by default, you have moved trust from a public market everyone can observe to a single operator few can audit, and your threat model should say so. We do not think this makes private order flow wrong. It is a trade-off to choose deliberately, with a fallback when the relay is unavailable and no user funds dependent on its good behaviour.

Design mechanisms that do not leak value to ordering

The durable fix is to make position in the block worth less. Three families of mechanism do this, and they combine.

Batch auctions

Collect all orders arriving in an interval and settle them together at one clearing price. Within a batch there is no earlier and later, so there is nothing to sandwich. The cost is latency and a residual exposure at the batch boundary, where whoever settles the batch still decides what is included. Batching suits mechanisms where a few seconds of delay is acceptable and the value per trade justifies waiting for a better price.

Intents and solvers

An intent is a signed statement of the outcome the user wants rather than a transaction specifying how to get there. Solvers compete to fulfil it, and the signature authorises only outcomes within the stated constraints. The user never exposes a predictable on-chain path, so there is nothing to sandwich; in a competitive solver market most of the extraction returns to the user as price improvement. The caveat is that solvers are now the ordering party, and a solver market with two participants is a cartel with extra steps. Solver competitiveness is an operational property you must keep measuring after launch.

Commit-reveal

Where the content of a transaction is what gets exploited, split it in two: commit a hash of the action, then reveal it in a later block. A searcher who sees the commit learns nothing, and by the time the reveal is visible the ordering is fixed. This is the standard defence for on-chain auctions. It costs two transactions, needs a deadline after which an unrevealed commit is void, and must handle a censored reveal; a scheme where the attacker can profitably suppress someone else's reveal has only moved the problem.

Protect your users, not just your contracts

Most MEV losses are borne by users, and most are mitigated by two parameters that live in the front-end and the calldata rather than in the contract.

Slippage tolerance should be computed from current liquidity and volatility, not set to a fixed percentage. A default of several percent on a deep stablecoin pool is an invitation; a default too tight on a thin pool causes reverts that cost gas and push users to loosen it by hand. The contract must enforce the minimum output as a hard check, never as advice from the interface.

Deadlines bound how long a transaction stays valid. Without one, a transaction that was unprofitable to include when sent can sit in a searcher's inventory until the market moves far enough to make it profitable for them and bad for the user. Every swap, liquidity change, and signed order should carry an expiry measured in seconds or minutes, enforced on-chain.

Beyond the parameters, the front-end should show how much a trade of this size will move the price and whether a batch or private route is available. None of this is Solidity, and all of it is part of the protocol's security.

MEV on L2s with centralised sequencers

Most rollups today run a single sequencer operated by the rollup team. It receives transactions, orders them, and posts batches to the base chain. There is no public mempool in the base-chain sense, so searchers cannot see pending transactions and the classic sandwich is mostly absent.

That is not the absence of MEV; it is its concentration in one party. The sequencer sees everything, orders everything, and is bound only by its published policy. A first-come-first-served promise is only as good as the sequencer's clock, and if an insider extracted, no competitive market would notice. When the sequencer is later decentralised or shared, the public-mempool adversary returns. There is a cross-domain dimension as well: a searcher who sees a price move on the base chain can act on the rollup before the rollup's oracle updates. We treat a centralised sequencer as a trusted party, write down what it is trusted not to do, and ask what the exposure is on the day that trust is withdrawn.

How we model MEV exposure in an audit

MEV review runs through the economic analysis of every DeFi audit we perform, alongside the items in our audit checklist. The method is consistent.

We enumerate every transaction type the protocol accepts and ask, for each, what an adversary earns by executing something before it, after it, or both. Swaps, deposits, withdrawals, liquidations, oracle updates, governance executions and reward claims each get the question. Where the answer is non-zero we quantify the precondition: how much capital, how much tolerance, how large a price move, how long a window.

We then list the state the protocol reads that someone else can write in the same block: spot prices, pool reserves, the contract's own token balances, and other protocols' view functions. Each is a lever, and we check what moving it does to the protocol's decisions.

We check that defensive parameters are enforced on-chain. Minimum outputs checked only in the front-end are findings. Optional deadlines are findings. Oracle reads with no staleness check are findings with a direct MEV angle, because stale prices are what the oracle front-runner works with.

For anything rated High or above we write a proof of concept, usually a forked-mainnet test that builds the adversary's bundle around a realistic user transaction and reports the extracted amount. The test turns an opinion into a number the team can weigh against the cost of the fix.

Finally we document the trusted ordering parties: sequencers, relays, solvers and keepers, with what each can see, what it can reorder, and what happens if it misbehaves. This section most often surprises teams, because it covers the part of the system they did not build.

Practical next steps

  • Write the ordering adversary into your threat model with its three powers: read ahead, insert around, delay.
  • For every transaction type, name who profits from its position and decide whether that is acceptable. If not, change the mechanism, not the front-end.
  • Enforce minimum outputs and deadlines on-chain, and derive default slippage from live conditions.
  • Prefer batching, intents or commit-reveal where latency is tolerable, and measure solver or relay competitiveness after launch.
  • Test extraction on a fork before launch and after every change to pricing, liquidation or oracle logic.

Our article on key management covers the other side of the trust question. If you want an MEV exposure review as part of an audit, the scope is on the services page, and you can reach the team on Telegram.