Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to the sixth lesson in our module on smart contract security.
In our previous lesson, we implemented a Pausable contract, giving us a reactive "emergency stop" button. This is a crucial tool for responding to unexpected bugs. However, the most robust security posture is proactive, not just reactive. It involves designing contracts that are resilient to the fundamental properties of the blockchain itself.
Today, we'll dive into one of the most well-known and inherent risks of public blockchains like Ethereum: front-running. We will explore how attackers exploit the public nature of pending transactions for their own gain. Your goal for this lesson is to explain the risks of front-running and discuss basic mitigation strategies. Understanding this is essential for building a token that your users can trust.
This topic might remind you of race conditions in traditional backend systems, where the outcome of an operation depends on the non-deterministic order of concurrent processes. Here, the "race" is for a transaction's position within a block, and it's a race that attackers can pay to win.
1. What is Front-Running and How Does It Work?
At its core, front-running is the act of observing a pending transaction in a public system and submitting your own transaction to be executed before it, profiting from the knowledge of the impending transaction.
In the context of Ethereum, this "public system" is the mempool (memory pool). When you submit a transaction, it doesn't go directly into a block. Instead, it enters the mempool, a public waiting area where it sits until a validator picks it up. Attackers, often using automated bots, constantly monitor the mempool for profitable opportunities.
So, how do they ensure their transaction gets processed first? By paying a higher gas fee, specifically a higher priority fee. Validators are economically incentivized to include transactions that pay them more. An attacker can see the gas fee on a target transaction and simply submit their own with a slightly higher fee to cut in line.
To get a clear and detailed overview of this mechanism, let's watch a video that breaks down the role of the mempool and gas fees in enabling front-running attacks.
Front-Running Attacks | Web3 Exploits 101
This video from Owen Thurm provides an excellent explanation of the core mechanics behind front-running, starting with the mempool and transaction ordering.
Watch the first part of the video (from 00:00 to 07:05). Focus on understanding: What the mempool is. How transactions are ordered within a block. How an attacker can manipulate this ordering by adjusting the priority gas fee.
This diagram visually summarizes the process described in the video:

2. The Risks: Types of Front-Running Attacks
Front-running isn't a single attack but a category of exploits with different objectives. Let's look at the most common types.
A. Financial Exploitation: The Sandwich Attack
This is a classic front-running attack, especially common on Decentralized Exchanges (DEXs). Imagine a user wants to make a large purchase of your token on a DEX like Uniswap. An attacker sees this in the mempool and executes the following steps in rapid succession:
- Front-run: The attacker places a
buyorder for your token just before the victim's large buy order. - Victim's Trade: The victim's large buy order executes, pushing the price of your token up.
- Back-run: The attacker immediately places a
sellorder for the tokens they just bought, selling them at the new, higher price for a quick, risk-free profit.
The victim's trade is "sandwiched" between the attacker's buy and sell orders. The result is that the victim gets a worse price (higher slippage) than they expected, and the value is extracted by the attacker.
This next video provides a clear, animated explanation of sandwich attacks.
What is MEV? Front-running, Sandwich Attacks, and Slippage Explained
This video from Whiteboard Crypto explains Maximal Extractable Value (MEV), a broader category that includes front-running. It gives a very clear definition of front-running, back-running, and the combination of the two in a sandwich attack.
Watch the segment from 02:21 to 03:35. Pay close attention to how the sandwich attack is constructed by combining a front-running and a back-running transaction.
B. Value Theft: Exploiting Stepwise Value Changes
Some contracts have functions that cause a sudden, significant increase in the value of a shared pool. For example, a function that distributes weekly staking rewards or adds profits to a vault. These "stepwise jumps" in value are prime targets.
An attacker can:
- Front-run: Observe the transaction that will add value to the pool. Just before it executes, they deposit a large amount of capital into the pool.
- Value Added: The reward distribution or profit-sharing transaction executes, and the value of the pool increases.
- Back-run: The attacker immediately withdraws their capital plus a large portion of the newly added value that was intended for long-term stakers.
This next video segment gives a fantastic walkthrough of this exact scenario.
Front-Running Attacks | Web3 Exploits 101
Let's return to the video from Owen Thurm. He now details a real-world example of how a staking pool with stepwise reward accrual can be exploited.
Watch the section from 10:29 to 16:22. Focus on understanding why a sudden, discrete jump in a pool's value is a major red flag for front-running vulnerabilities.
C. Griefing: Causing Transactions to Revert
Not all front-running attacks are for direct financial gain. Sometimes, the goal is simply to cause another user's transaction to fail (a "griefing" or denial-of-service attack).
This can happen if a function's logic depends on a state that can be easily changed. For example, if a function requires a user to provide a unique orderId, an attacker can see the ID in the mempool, copy it, and submit their own transaction using that same ID first. When the victim's transaction arrives, the orderId is no longer unique, and the transaction reverts.
This article from Cyfrin provides several excellent, code-based examples of different front-running vulnerabilities, including griefing attacks. We will use it as a reference for the rest of the lesson.
Solodit Checklist Explained: Front-Running Attacks
This article from Cyfrin's Solodit Checklist series is a practical guide for developers. It details common front-running patterns with Solidity code examples and tests.
For now, just bookmark this article. We will refer to specific sections as we discuss mitigation strategies. It's a valuable resource to keep for your future work.
3. Basic Mitigation Strategies
Now for the most important part: how do we defend against these attacks? While it's impossible to completely eliminate front-running on a public blockchain, you can design your contracts to be far more resilient.
A. Slippage Protection for DEX Interactions
As we saw, sandwich attacks exploit price changes. The primary defense here is slippage tolerance. When swapping tokens, a user should be able to specify the worst possible price they are willing to accept.
From a smart contract developer's perspective, this means that any function you write that performs a swap on a DEX should not just take the amount to swap, but also a parameter like minAmountOut (for selling) or maxAmountIn (for buying). Your function then passes this value to the DEX router, which will cause the transaction to revert if the price moves unfavorably beyond this limit.
The swapBNBForCatgirl function in the Hacken.io article on "Front-Running In Blockchain" is a perfect example of what not to do. It hardcodes the amountOutMin parameter to 0, offering zero protection to the user.
B. Commit-Reveal Schemes
To prevent attackers from knowing your intentions, you can use a commit-reveal scheme. This is a two-step process:
- Commit Phase: A user submits a transaction containing a hash of their intended action plus a secret value (a "salt"). The contract stores this hash. The hash reveals nothing about the underlying data. It's like submitting a bid in a sealed envelope.
commitment = keccak256(abi.encode(msg.sender, bidAmount, salt))
- Reveal Phase: After a certain period, users submit their original data (e.g.,
bidAmountandsalt). The contract hashes this data and checks if it matches the commitment stored earlier. If it matches, the action is executed.
This prevents front-running because no one can see the content of the action during the commit phase.
However, a common mistake is not binding the commitment to the user. Let's study a concrete example of this pattern and its potential pitfall from the Cyfrin article.
Solodit Checklist Explained: Front-Running Attacks
Referencing the Cyfrin article, let's analyze how commit-reveal schemes work and how they can be implemented incorrectly.
Read the section 'SOL-AM-FrA-4: Is the protocol using a properly user-bound commit-reveal scheme?'. Pay close attention to the vulnerability in the reveal function and how the test case exploits it. The key is to understand why including msg.sender in the hash is critical for security.
Test your understanding!
Based on the Cyfrin article you just read, imagine you have a VulnerableAuction contract with the following reveal function:
function reveal(uint256 bid, bytes32 salt) public {
// ... checks ...
bytes32 expectedCommitment = keccak256(abi.encode(bid, salt));
require(commitments[msg.sender] == expectedCommitment, "Invalid commitment");
// ... set winner logic ...
}
An attacker sees a victim's commit transaction in the mempool. They don't know the bid and salt yet. How can they still potentially win the auction by front-running the victim's reveal transaction later? What single change would you make to the keccak256 hashing to prevent this?
Show answer
The vulnerability is that the commitment keccak256(abi.encode(bid, salt)) is not tied to a specific user.
Here's the attack:
- The victim commits a hash.
- Later, when the reveal phase starts, the victim submits their
reveal(bid, salt)transaction to the mempool. - The attacker sees this pending transaction, which contains the now-public
bidandsaltvalues. - The attacker copies these values and front-runs the victim by submitting their own
reveal(bid, salt)transaction with a higher gas fee. - Because the attacker's
revealis executed first, and the commitment check doesn't care who created the original values, the attacker becomes the winner.
The Fix:
You must include the committer's address in the hash. This binds the commitment to a specific user.
// In both the commit and reveal functions:
bytes32 expectedCommitment = keccak256(abi.encode(msg.sender, bid, salt));
Now, if the attacker tries to reveal the victim's bid, the hash won't match because msg.sender will be the attacker's address, not the victim's.
C. Broader Solutions and Advanced Concepts
While slippage control and commit-reveal are direct patterns you can implement, it's also good to be aware of other solutions in the ecosystem:
- Time-Weighted Average Prices (TWAPs): To defend against attacks on "stepwise jumps" in value, protocols can use a TWAP instead of a spot price. A TWAP calculates the average price over a period (e.g., the last 30 minutes), which smooths out sudden price manipulations from a single transaction, making front-running unprofitable.
- Private Mempools / Transaction Relays: Services like Flashbots allow users to send transactions directly to validators, bypassing the public mempool. This hides the transaction from front-running bots, though it doesn't prevent the validator themselves from extracting value.
- Fair Sequencing: Some L2s like Arbitrum use a "first-come, first-served" sequencer model, which largely mitigates traditional gas-based front-running.
Conclusion
You've now explored the shadowy world of front-running and MEV. It's a complex and adversarial environment, but by understanding the mechanics, you can build much stronger and safer applications.
Key Takeaways:
- Front-running is possible because of the public mempool and the ability to influence transaction order with gas fees.
- The risks range from users getting worse prices (sandwich attacks) to outright theft (staking exploits) and denial of service (griefing).
- Your primary lines of defense in your own code are implementing slippage controls for DEX interactions and using properly designed, user-bound commit-reveal schemes for sensitive actions.
- Always maintain an adversarial mindset: assume any pending transaction will be seen, and design your logic to be resilient to reordering and manipulation.
Preview of the Next Lesson:
Our discussion of sandwich attacks highlighted the risks of interacting with external contracts like DEXs. In the next lesson, we will continue this theme by focusing on another aspect of external calls: using OpenZeppelin's SafeERC20 library to interact safely with other tokens. You'll learn why a simple token.transfer() can sometimes be risky and how to protect your contract from non-standard or malicious token implementations.