Front running is when an attacker sees your pending transaction in the mempool (transaction pool) and submits their own transaction with a higher gas price to get executed first, profiting from your transaction.
YOUR TRANSACTION (Pending):
"Buy token for 1 ETH"
Gas Price: 20 gwei
ATTACKER SEES IT:
"I'll buy before them!"
Gas Price: 100 gwei ← Gets mined first!
RESULT:
1. Attacker buys token (pushes price up)
2. Your transaction executes (buys at higher price)
3. Attacker sells token (profits from price increase)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/*
A simple game where you need to find the correct number to win.
First person to submit the correct answer wins 10 ETH.
*/
contract FindThisHash {
bytes32 public constant hash = 0x564ccaf7594d66b1eaaea24fe01f0585bf52ee70852af4eac0cc4b04711cd0e2;
constructor() payable {}
function solve(string memory solution) public {
require(hash == keccak256(abi.encodePacked(solution)), "Incorrect answer");
(bool sent,) = msg.sender.call{value: 10 ether}("");
require(sent, "Failed to send Ether");
}
}
STEP 1: Alice finds the solution
├─ Solution: "Ethereum"
├─ Creates transaction: solve("Ethereum")
└─ Gas Price: 20 gwei
STEP 2: Transaction enters mempool (not mined yet)
├─ Visible to everyone
└─ Waiting to be mined...
STEP 3: Bob (attacker) monitors mempool
├─ Sees: solve("Ethereum")
├─ Copies the solution!
├─ Creates his own transaction: solve("Ethereum")
└─ Gas Price: 100 gwei ← HIGHER!
STEP 4: Miner picks transactions
├─ Higher gas = higher priority
├─ Bob's transaction mined first ✅
└─ Alice's transaction mined second ❌
STEP 5: Results
├─ Bob gets 10 ETH 💰
└─ Alice gets nothing (solution already used) 😭
// Alice's transaction (slower)
const tx1 = await contract.solve("Ethereum", {
gasPrice: ethers.utils.parseUnits('20', 'gwei')
});
// Bob watches mempool and sees the solution
// Bob's transaction (faster - front runs Alice)
const tx2 = await contract.solve("Ethereum", {
gasPrice: ethers.utils.parseUnits('100', 'gwei') // Higher gas!
});
// Mining order:
// 1. tx2 (Bob) - executed first ✅
// 2. tx1 (Alice) - reverts (solution already claimed) ❌
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract SimpleSwap {
uint public tokenPrice = 1 ether; // 1 token = 1 ETH
uint public totalTokens = 100;
mapping(address => uint) public balances;
function buyToken() public payable {
uint amount = msg.value / tokenPrice;
require(amount <= totalTokens, "Not enough tokens");
totalTokens -= amount;
balances[msg.sender] += amount;
// Price increases as supply decreases
tokenPrice = tokenPrice * 110 / 100; // +10% per purchase
}
function sellToken(uint amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
totalTokens += amount;
uint ethAmount = amount * tokenPrice;
payable(msg.sender).transfer(ethAmount);
// Price decreases as supply increases
tokenPrice = tokenPrice * 90 / 100; // -10% per sale
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract FrontRunAttack {
SimpleSwap public swap;
constructor(address _swap) {
swap = SimpleSwap(_swap);
}
/*
Attack scenario:
1. Current price: 1 ETH per token
2. Alice submits: buyToken() with 10 ETH (gas: 20 gwei)
3. Attacker sees this in mempool
4. Attacker front-runs with higher gas
*/
function attack() external payable {
// Step 1: Buy tokens BEFORE Alice (higher gas price)
swap.buyToken{value: 5 ether}();
// Price is now 1.1 ETH per token
// Step 2: Alice's transaction executes
// She buys at higher price (1.1 ETH)
// Step 3: Attacker sells immediately after (back-running)
uint balance = swap.balances(address(this));
swap.sellToken(balance);
// Profits from the price difference!
}
receive() external payable {}
}
INITIAL STATE:
├─ Token Price: 1 ETH
└─ Tokens Available: 100
ALICE'S TRANSACTION (Pending in mempool):
├─ Action: Buy 10 tokens
├─ Cost: 10 ETH
├─ Gas: 20 gwei
└─ Status: Waiting...
ATTACKER SEES ALICE'S TX AND FRONT-RUNS:
┌─────────────────────────────────────┐
│ TX #1: Attacker buys 5 tokens │
│ ├─ Cost: 5 ETH │
│ ├─ Gas: 100 gwei ← HIGHER! │
│ └─ Price after: 1.5 ETH per token │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ TX #2: Alice's buy executes │
│ ├─ Cost: 10 ETH │
│ ├─ Gets: 6.6 tokens (not 10!) │
│ └─ Price after: 2.14 ETH per token │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ TX #3: Attacker sells 5 tokens │
│ ├─ Revenue: 10.7 ETH │
│ ├─ Profit: 5.7 ETH 💰 │
│ └─ Alice: Lost money 😭 │
└─────────────────────────────────────┘
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract NFTAuction {
address public highestBidder;
uint public highestBid;
bool public ended;
function bid() public payable {
require(!ended, "Auction ended");
require(msg.value > highestBid, "Bid too low");
// Refund previous bidder
if (highestBidder != address(0)) {
payable(highestBidder).transfer(highestBid);
}
highestBidder = msg.sender;
highestBid = msg.value;
}
function endAuction() public {
ended = true;
// Transfer NFT to highestBidder
}
}