Free snapshot slots open this week info@inversez.com
DetectionVulnerabilitiesToolsAuditsPricingBlogFree monitoring assessmentContact
Home/Vulnerabilities/Front-running and MEV
SWC-114 · Typically medium

Front-running and MEV

Every pending transaction is visible before it is mined. Anyone can read yours, submit their own with a higher fee, and have it execute first. For a swap, that means being sandwiched: a buy before yours pushes the price up, and a sell after yours captures the difference.

Vulnerable code

Do not ship this
function swap(uint256 amountIn) external {
    uint256 out = _calculateOut(amountIn);
    _executeSwap(amountIn, out);   // no minimum, no deadline
}

function claimReward(bytes32 answer) external {
    require(answer == secretAnswer, "wrong");
    _payReward(msg.sender);      // answer visible in mempool
}

Why it breaks

The swap accepts any output amount, so a sandwich can extract an unbounded share of the trade. The reward function is worse: the correct answer is in your transaction's calldata, sitting in the public mempool. A bot reads it, submits the same answer with a higher gas price, and collects the reward before your transaction is mined.

Watch the attack run

The same bug as a stepped sequence. Play it, or walk through with the arrow keys.

The fix

Corrected
function swap(
    uint256 amountIn,
    uint256 minOut,
    uint256 deadline
) external {
    require(block.timestamp <= deadline, "expired");
    uint256 out = _calculateOut(amountIn);
    require(out >= minOut, "slippage");
    _executeSwap(amountIn, out);
}

// Commit-reveal for anything secret:
function commit(bytes32 hash) external {
    commits[msg.sender] = Commit(hash, block.number);
}

function reveal(bytes32 answer, bytes32 salt) external {
    Commit memory c = commits[msg.sender];
    require(block.number > c.blockNumber, "same block");
    require(keccak256(abi.encodePacked(answer, salt, msg.sender)) == c.hash);
    _payReward(msg.sender);
}

The invariant

The fix above is what an audit gives you. This is what monitoring gives you: the thing that should always be true once the contract is live, stated precisely enough to check every block.

InvariantEVM · front running

Executed price sits inside the slippage bound that was quoted when the transaction was signed.

invariant  executed_out >= quoted_out * (1 - max_slippage)
watch  same-block opposing swaps from one address
Backtest: not yet run. This entry carries the reasoning, not the evidence. When the invariant has been replayed against the real transactions around the exploit, the firing block, how far ahead it fired, and its false-positive rate over a clean control period will be published here, along with the recording of the run.

How to test for it

A finding you cannot reproduce is an opinion. Write the test before you write the fix, watch it fail, then make it pass.

function testSandwichBoundedBySlippage() public {
    // attacker front-runs
    vm.prank(mev); dex.swap(500 ether, 0, block.timestamp);

    // victim's trade must revert rather than execute at a bad price
    vm.prank(victim);
    vm.expectRevert("slippage");
    dex.swap(1 ether, expectedOut * 99 / 100, block.timestamp);
}

Where it has caused real losses

Sandwich attacks extract value from ordinary users continuously rather than in single dramatic incidents. The cumulative total across DeFi runs to hundreds of millions of dollars, and unlike most vulnerability classes it requires no bug at all, only a transaction that fails to bound its own outcome.

What we check during review

Front-running cannot be eliminated on a public chain, only bounded. Practical mitigations: always require a user-supplied minOut and deadline rather than computing them on-chain; use commit-reveal for auctions, games and anything with a secret; and consider private transaction relays for high-value operations. Beware the anti-pattern of passing block.timestamp as the deadline. It makes the parameter meaningless.

Want us to check your contract for this?

A free monitoring assessment covers one contract up to 200 lines, manually reviewed, findings back within 72 hours. This class is on the checklist for every review we run.

Free monitoring assessment

Found this useful?

We publish these because the alternative, asking you to trust us, is worth less. Send a contract and we’ll apply the same thinking to your code.

  • Reply written by the auditor who read your code
  • No sales sequence, no drip campaign, no retargeting
  • We’ll tell you if you don’t need a paid audit yet
  • Report published only with your written permission

Request your snapshot

We read every submission. If it doesn’t fit the free tier we’ll say so and quote you instead, no obligation.