How to Build an On-Chain MEV Monitor: Detecting Sandwich Attacks and Arbitrage in Real-Time with Python
Maximal Extractable Value (MEV) is one of the most fascinating and controversial topics in crypto today. Whether you're a DeFi developer, a security researcher, or just crypto-curious, understanding how MEV bots operate makes you a sharper builder. In this guide, we'll write a Python tool that connects to the Ethereum mempool and detects common MEV patterns — sandwich attacks and arbitrage opportunities — in real time.
What You'll Learn
- How the Ethereum mempool works and why it matters for MEV
- How to stream pending transactions with Web3.py
- Pattern-matching techniques for sandwich attacks
- Detecting cross-DEX arbitrage opportunities
- Logging findings to a structured output
Prerequisites
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting web3
Downloading web3-7.16.0-py3-none-any.whl (1.4 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 1.5 MB/s 0:00:00
Collecting hexbytes
Downloading hexbytes-1.3.1-py3-none-any.whl (5.1 kB)
Collecting eth-abi>=5.0.1 (from web3)
Downloading eth_abi-5.2.0-py3-none-any.whl (28 kB)
Collecting eth-account>=0.13.6 (from web3)
Downloading eth_account-0.13.7-py3-none-any.whl (587 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 587.5/587.5 kB 1.3 MB/s 0:00:00
Collecting eth-hash>=0.5.1 (from eth-hash[pycryptodome]>=0.5.1->web3)
Downloading eth_hash-0.8.0-py3-none-any.whl (8.0 kB)
Collecting eth-typing>=5.0.0 (from web3)
Downloading eth_typing-6.0.0-py3-none-any.whl (19 kB)
Collecting eth-utils>=5.0.0 (from web3)
Downloading eth_utils-6.0.0-py3-none-any.whl (102 kB)
Requirement already satisfied: aiohttp>=3.7.4.post0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from web3) (3.14.2)
Requirement already satisfied: pydantic>=2.4.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from web3) (2.13.4)
Requirement already satisfied: requests>=2.23.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from web3) (2.33.0)
Requirement already satisfied: typing-extensions>=4.0.1 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from web3) (4.16.0)
Collecting types-requests>=2.0.0 (from web3)
Downloading types_requests-2.33.0.20260712-py3-none-any.whl (21 kB)
Collecting websockets<16.0.0,>=10.0.0 (from web3)
Downloading websockets-15.0.1-py3-none-any.whl (169 kB)
Collecting pyunormalize>=15.0.0 (from web3)
Downloading pyunormalize-17.0.0-py3-none-any.whl (51 kB)
Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (2.7.1)
Requirement already satisfied: aiosignal>=1.4.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (1.4.0)
Requirement already satisfied: attrs>=17.3.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (26.1.0)
Requirement already satisfied: frozenlist>=1.1.1 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (1.8.0)
Requirement already satisfied: multidict<7.0,>=4.5 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (6.7.1)
Requirement already satisfied: propcache>=0.2.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (0.5.2)
Requirement already satisfied: yarl<2.0,>=1.17.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from aiohttp>=3.7.4.post0->web3) (1.24.5)
Requirement already satisfied: idna>=2.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from yarl<2.0,>=1.17.0->aiohttp>=3.7.4.post0->web3) (3.18)
Collecting parsimonious<0.11.0,>=0.10.0 (from eth-abi>=5.0.1->web3)
Using cached parsimonious-0.10.0-py3-none-any.whl (48 kB)
Collecting regex>=2022.3.15 (from parsimonious<0.11.0,>=0.10.0->eth-abi>=5.0.1->web3)
Downloading regex-2026.7.19.tar.gz (416 kB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting bitarray>=2.4.0 (from eth-account>=0.13.6->web3)
Downloading bitarray-3.9.2.tar.gz (160 kB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting eth-abi>=5.0.1 (from web3)
Downloading eth_abi-6.0.0b1-py3-none-any.whl (28 kB)
Collecting eth-keyfile<0.9.0,>=0.7.0 (from eth-account>=0.13.6->web3)
Using cached eth_keyfile-0.8.1-py3-none-any.whl (7.5 kB)
Collecting eth-keys>=0.4.0 (from eth-account>=0.13.6->web3)
Downloading eth_keys-0.7.0-py3-none-any.whl (20 kB)
Collecting eth-rlp>=2.1.0 (from eth-account>=0.13.6->web3)
Downloading eth_rlp-2.2.0-py3-none-any.whl (4.4 kB)
Collecting rlp>=1.0.0 (from eth-account>=0.13.6->web3)
Downloading rlp-4.1.0-py3-none-any.whl (19 kB)
Collecting ckzg>=2.0.0 (from eth-account>=0.13.6->web3)
Downloading ckzg-2.1.8.tar.gz (1.1 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 1.1 MB/s 0:00:00
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting pycryptodome<4,>=3.6.6 (from eth-keyfile<0.9.0,>=0.7.0->eth-account>=0.13.6->web3)
Downloading pycryptodome-3.23.0.tar.gz (4.9 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.9/4.9 MB 1.7 MB/s 0:00:02
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Collecting cytoolz>=0.10.1 (from eth-utils>=5.0.0->web3)
Downloading cytoolz-1.1.0.tar.gz (642 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 642.5/642.5 kB 1.5 MB/s 0:00:00
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Requirement already satisfied: annotated-types>=0.6.0 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from pydantic>=2.4.0->web3) (0.7.0)
Requirement already satisfied: pydantic-core==2.46.4 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from pydantic>=2.4.0->web3) (2.46.4)
Requirement already satisfied: typing-inspection>=0.4.2 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from pydantic>=2.4.0->web3) (0.4.2)
Collecting toolz>=0.8.0 (from cytoolz>=0.10.1->eth-utils>=5.0.0->web3)
Downloading toolz-1.1.0-py3-none-any.whl (58 kB)
Requirement already satisfied: charset_normalizer<4,>=2 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from requests>=2.23.0->web3) (3.4.9)
Requirement already satisfied: urllib3<3,>=1.26 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from requests>=2.23.0->web3) (2.7.0)
Requirement already satisfied: certifi>=2023.5.7 in /data/data/com.termux/files/home/.hermes/hermes-agent/venv/lib/python3.14/site-packages (from requests>=2.23.0->web3) (2026.5.20)
Building wheels for collected packages: pycryptodome, bitarray, ckzg, cytoolz, regex
Building wheel for pycryptodome (pyproject.toml): started
Building wheel for pycryptodome (pyproject.toml): finished with status 'done'
Created wheel for pycryptodome: filename=pycryptodome-3.23.0-cp37-abi3-android_24_arm64_v8a.whl size=1987435 sha256=1455f4764c3f8b2f7af703df3fafdca3c7bb9ede30a5d2fa49614660a97be4f9
Stored in directory: /data/data/com.termux/files/home/.cache/pip/wheels/3c/f9/be/e50ed8bfe5e8d0a6c994cf85138b1122021ffab04eeb7097f3
Building wheel for bitarray (pyproject.toml): started
Building wheel for bitarray (pyproject.toml): finished with status 'done'
Created wheel for bitarray: filename=bitarray-3.9.2-cp314-cp314-android_24_arm64_v8a.whl size=262292 sha256=e965d4d79b85aa20a227ef36285e03bc0bd3a16627888a36e385833576bfc30b
Stored in directory: /data/data/com.termux/files/home/.cache/pip/wheels/46/a7/de/cb4da8faba0eddb791924885c1c3504487965f35540c8b248c
Building wheel for ckzg (pyproject.toml): started
Building wheel for ckzg (pyproject.toml): finished with status 'done'
Created wheel for ckzg: filename=ckzg-2.1.8-cp314-cp314-android_24_arm64_v8a.whl size=162777 sha256=5397935f743343503b1dc9a8e9c7c852fd1aefe6e8920e9ee5d7592a0b9e744c
Stored in directory: /data/data/com.termux/files/home/.cache/pip/wheels/29/ff/76/a9fe38fd665e7bbe8b3818262ec61bff5e61eedd767118831b
Building wheel for cytoolz (pyproject.toml): started
Building wheel for cytoolz (pyproject.toml): finished with status 'done'
Created wheel for cytoolz: filename=cytoolz-1.1.0-cp314-cp314-android_24_arm64_v8a.whl size=2014357 sha256=ac4131f38af38ac5a55ff4b3153b2786b7783bf868347409f24403db56c220df
Stored in directory: /data/data/com.termux/files/home/.cache/pip/wheels/b0/f3/81/da0c46db35e802be926fe4888f7fb61d78bd7c713b88c8a0f4
Building wheel for regex (pyproject.toml): started
Building wheel for regex (pyproject.toml): finished with status 'done'
Created wheel for regex: filename=regex-2026.7.19-cp314-cp314-android_24_arm64_v8a.whl size=554942 sha256=8d88b88d4e6e6451f65494373b3541210fcb92b26ca00be999ffb716c66b9328
Stored in directory: /data/data/com.termux/files/home/.cache/pip/wheels/1b/7f/df/ddd8a2f642277e4547636b229cd3077ffe0d4c9a86c3c2b4fd
Successfully built pycryptodome bitarray ckzg cytoolz regex
Installing collected packages: ckzg, websockets, types-requests, toolz, regex, pyunormalize, pycryptodome, hexbytes, eth-typing, eth-hash, bitarray, parsimonious, cytoolz, eth-utils, rlp, eth-keys, eth-abi, eth-rlp, eth-keyfile, eth-account, web3
Successfully installed bitarray-3.9.2 ckzg-2.1.8 cytoolz-1.1.0 eth-abi-5.2.0 eth-account-0.13.7 eth-hash-0.8.0 eth-keyfile-0.8.1 eth-keys-0.7.0 eth-rlp-2.2.0 eth-typing-6.0.0 eth-utils-6.0.0 hexbytes-1.3.1 parsimonious-0.10.0 pycryptodome-3.23.0 pyunormalize-17.0.0 regex-2026.7.19 rlp-4.1.0 toolz-1.1.0 types-requests-2.33.0.20260712 web3-7.16.0 websockets-15.0.1
You'll also need a WebSocket endpoint. Services like Alchemy, Infura, or a self-hosted node work great.
Step 1: Connect to the Mempool
The mempool is the waiting room for unconfirmed transactions. We subscribe via WebSocket to see them as they arrive:
Step 2: Detect Sandwich Attacks
A sandwich attack has three legs: a front-run buy, the victim's trade, and a back-run sell. In the mempool we typically spot the front-run and victim close together:
Step 3: Track Arbitrage Patterns
Arbitrage transactions often call multiple DEXs in a single bundle. We detect them by looking for transactions that interact with multiple well-known router contracts in rapid succession:
Step 4: Structured Logging
Save findings to a JSONL file for later analysis or alerting:
Putting It All Together
The complete script continuously monitors the mempool and logs suspicious activity. Run it with and let it collect data over hours or days — you'll be surprised how much MEV activity you uncover.
Ethical Considerations
Monitoring MEV is a powerful educational tool. However, using this knowledge to front-run other users is unethical and in some jurisdictions illegal. Use this code responsibly — for research, security auditing, and building better DeFi protocols.
Resources
- Full source code and more blockchain tools: github.com/Byaigo
- Support my open-source work — ETH donations welcome:
Happy building, and stay curious! 🚀
Top comments (0)