Skip to content

Frontrunning

Every transaction on a blockchain network undergoes a period of visibility in the mempool before execution. That transparency lets network participants see and respond to a transaction before its inclusion in a block, and attackers use the resulting information leak to influence how the transaction executes. On a decentralized exchange, a visible buy order is preempted by broadcasting a competing transaction that reaches the block first.

Protection is not trivial, because the attacks are tailored to the target code base. A taxonomy splits them into three categories, which structures both the discussion and the search for solutions: Displacement, Insertion, and Suppression.

Displacement

In a displacement attack, the attacker relegates the user's transaction to a lower position in the block. The user's function is then orphaned, executed without significant effect, or reverted for running against an outdated state. The attack works by amplifying the gas price beyond the original transaction's, often by ten times or more. A simple number-guessing game shows the mechanism:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pragma solidity ^0.8.17;

contract GuessTheNumberChallenge {
    bytes32 challenge;

    constructor(bytes32 _challenge) payable {
        require(msg.value == 1 ether);
        challenge = _challenge;
    }

    function isComplete() public view returns (bool) {
        return address(this).balance == 0;
    }

    // @audit the guess travels through the mempool in plaintext
    function guess(uint256 number) public payable {
        require(msg.value == 1 ether, "Submission fee required");
        uint256 balance = address(this).balance;
        require(balance != 0, "Game has ended");

        bytes32 userChallenge = keccak256(abi.encode(number));
        if (userChallenge == challenge) {
            (bool success, ) = msg.sender.call{value: balance}("");
            require(success, "Transfer failed");
        }
    }
}

The premise is simple. A player guesses a number that has been hashed and stored in the contract. The deployer submits 1 ETH in the constructor along with the hash of the challenge, derived from a number calculated off-chain, and the pre-image of that hash is the secret. A correct guess makes the guess function produce a hash matching the original challenge, and the contract pays out its entire balance as a reward.

The design flaw sits in the submission itself. Calling guess passes the number as a plain parameter, and unless special precautions are taken the transaction becomes visible in the mempool before it is mined. Any observer copies the transaction data, function signature and parameters included.

Generalized frontrunners copy that data and simulate the transaction on a forked network. Where the simulation shows a profit, they resubmit the copied payload at a higher gas price than the original, and the gas price auction favors their transaction.

The consequence is the defining feature of displacement frontrunning. Once the copied transaction is mined, the original becomes irrelevant to the contract. The guessing game checks for a non-zero balance before executing, and a correct guess, copied or not, drops the balance to zero and ends the game. Every subsequent call to guess fails on that balance check, so the user who first found the correct number sees their transaction reverted despite being the rightful winner.

Insertion

An insertion attack requires the original user's function call to succeed after the adversary's transaction. The adversary modifies the contract state first, so that the user's call executes in the altered context.

Slippage skimming is the prime example. A user bidding on a decentralized exchange offers an asking price and a slippage range, three percent for instance, within which price deviation remains acceptable. The adversary brackets that trade with two transactions. The first, placed ahead of the user, purchases the same asset and inflates its price by the sum of the asking price and the maximum allowed slippage.

The user's transaction then executes at the highest feasible price. The second adversarial transaction follows it, offloading the assets and deflating the price again. The adversary keeps a profit equal to the user's maximum slippage, an amount that grows significant for large order sizes.

The pattern is called a sandwiching attack, after the placement of the user's transaction between two adversarial ones. Positioning a transaction after the original is called backrunning.

Suppression

A malicious actor delays the execution of other transactions by launching a Block Stuffing or suppression attack, issuing a series of high gas price transactions that keep other transactions from being processed for multiple blocks.

The target of a suppression attack is the delay itself. The Fomo3d game-winner case demonstrated it, blocking transactions that followed the adversary's. The attacker generates multiple transactions with high gasPrice and gasLimit and directs them at custom smart contracts, which consume all available gas through failing assert statements and fill the block gas limit.

Ethereum Improvement Proposal 1559 addressed the block gas exhaustion technique by changing how transaction fees work and burning the base fee, which reduces the incentive for miners to manipulate the block gas limit.

The risk is not eliminated. Malicious actors still spam transactions at a high gas price, consume a significant portion of the block's capacity, and delay other transactions.

Such attacks are most damaging against crucial transactions such as oracle updates. Oracles supply the price information that decentralized finance depends on, and a delay in those updates produces market inefficiencies, inaccurate valuations, and market manipulation, all of which threaten the stability of the surrounding infrastructure.

Mitigations

Frontrunning is a widespread problem on public blockchains such as Ethereum. The primary remediation removes the benefit of frontrunning from the application, mainly by minimizing the relevance of transaction ordering or timing. Batch auctions offer that for markets and address high-frequency trading concerns at the same time. A pre-commit scheme with details submitted later is a second approach. Defining a maximum or minimum acceptable price range on a trade constrains price slippage and reduces the payoff of frontrunning.

Restricting the visibility of transactions through a commit-and-reveal scheme is the other common strategy. An elementary implementation stores the Keccak256 hash of the data in a first transaction, then reveals the data and verifies it against the hash in a second. Intention and collateralization value still leak through the transaction itself. More secure but more complex schemes, such as submarine sends, require additional transactions to operate.