Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To estimate a DEX pool’s recent sandwich attack rate, query hourly bars for the pool, then calculate a transaction-weighted average of the bars’ sandwichRate values. Treat the result as an indexer-derived historical estimate—not a prediction that a particular future swap will or will not be attacked.
What a pool’s sandwich attack rate measures
A sandwich attack typically places an attacker’s swap immediately before and after a victim’s swap. The first trade changes the pool’s reserves; the victim then receives a worse exchange rate, and the attacker’s second trade can capture value from the price movement. Slippage limits can cause a transaction to fail if the execution price moves beyond the allowed bound, but they do not guarantee a trade is safe. A 2022 study of Uniswap and Sushiswap on Ethereum examined activity from May 4, 2020, through April 30, 2021, and reported 480,276 sandwich attacks across 5,728 pools during that historical period.
Codex documents sandwichRate as sandwiched events divided by transactions, and says it is null when transaction data is unavailable. A null is missing data, not a zero rate. When combining hourly bars, weight each valid hourly rate by that bar’s transaction count; a plain average gives a low-volume hour the same influence as a high-volume hour.
Recommended Free Tools
Query hourly pool data with Python
The example below calls Codex’s GraphQL endpoint at https://graph.codex.io/graphql. It uses the getBars query with a pool address and network ID in the symbol and requests 60-minute bars for a Unix-time interval. Set the API key, pool address, network ID, and dates for the pool and period you intend to inspect.
#1 Best Overall
import os
import requests
from datetime import datetime, timezone
from decimal import Decimal
ENDPOINT = "https://graph.codex.io/graphql"
API_KEY = os.environ["CODEX_API_KEY"]
# Replace these with the intended pool and network, and the desired UTC window.
POOL_ADDRESS = "0xYourPoolAddress"
NETWORK_ID = 1
START = int(datetime(2026, 10, 1, tzinfo=timezone.utc).timestamp())
END = int(datetime(2026, 10, 8, tzinfo=timezone.utc).timestamp())
query = """
query PoolBars($symbol: String!, $from: Int!, $to: Int!, $resolution: String!) {
getBars(symbol: $symbol, from: $from, to: $to, resolution: $resolution) {
pair {
address
token0 { symbol }
token1 { symbol }
protocol { name }
}
bars {
timestamp
transactions
sandwichRate
fee
mevRiskLevel
}
}
}
"""
variables = {
"symbol": f"{NETWORK_ID}:{POOL_ADDRESS}",
"from": START,
"to": END,
"resolution": "60",
}
response = requests.post(
ENDPOINT,
headers={"Authorization": API_KEY},
json={"query": query, "variables": variables},
timeout=30,
)
response.raise_for_status()
payload = response.json()
if payload.get("errors"):
raise RuntimeError(payload["errors"])
result = payload["data"]["getBars"]
pair = result["pair"]
bars = result["bars"]
# Do not accept a syntactically successful response until the returned pool matches.
if pair["address"].lower() != POOL_ADDRESS.lower():
raise ValueError(f"Unexpected pool returned: {pair['address']}")
def decimal_or_none(value):
return None if value is None else Decimal(str(value))
valid = []
for bar in bars:
rate = decimal_or_none(bar.get("sandwichRate"))
transactions = bar.get("transactions")
if rate is not None and transactions is not None:
valid.append((rate, int(transactions)))
denominator = sum(transactions for _, transactions in valid)
weighted_rate = (
sum(rate * transactions for rate, transactions in valid) / denominator
if denominator else None
)
estimated_sandwiched_transactions = (
sum(rate * transactions for rate, transactions in valid)
if denominator else None
)
print({
"pool": pair["address"],
"tokens": [pair["token0"]["symbol"], pair["token1"]["symbol"]],
"protocol": pair["protocol"]["name"],
"bars_returned": len(bars),
"transactions_with_rate": denominator,
"weighted_sandwich_rate": (
str(weighted_rate) if weighted_rate is not None else None
),
"estimated_sandwiched_transactions": (
str(estimated_sandwiched_transactions)
if estimated_sandwiched_transactions is not None else None
),
"hourly_mev_risk_levels": [bar.get("mevRiskLevel") for bar in bars],
"fee_total": sum(
(decimal_or_none(bar.get("fee")) or Decimal(0) for bar in bars),
Decimal(0),
),
})
Install the HTTP client with python -m pip install requests, then provide the key in the environment rather than placing it in a script. For example, in a Unix-like shell, run export CODEX_API_KEY='your-key' before starting Python. The request uses the key directly in the Authorization header, without a Bearer prefix, as described for this endpoint.
Check the response before trusting the result
- Check the HTTP status and GraphQL
errorsbefore readingdata.getBars. - Confirm both the intended network ID and the returned
pair.address. A successful response alone does not prove that the intended pool was queried; the how-to author reports that a token address can resolve to a pool. - EVM addresses are case-insensitive, so comparing them in lowercase is appropriate. Solana base58 addresses are case-sensitive; do not lowercase them.
- Codex decimal-valued fields are returned as strings in the described response. Convert them explicitly and preserve null as missing.
- The code reports the weighted denominator—the transaction count from bars with a non-null rate—rather than all transactions across all bars. This matches the data actually used to calculate the rate.
Understand what the calculation returns
The calculation is sum(rate × transactions) / sum(transactions), using only bars with non-null rates and transaction counts. The weighted numerator is an estimate of represented sandwiched transactions, not necessarily a count of individually inspected attack traces. If no valid transaction denominator exists, the aggregate is unavailable; reporting zero would incorrectly imply observed transactions with no sandwich activity.
Rank #2
The sample computes fee totals only from non-null fee fields and treats missing fee values as unavailable for that bar, not as evidence of zero fees. Fee fields can be null because of indexing availability or chain-specific fee structure, so interpret that total cautiously. Confirm current field semantics and supported-network coverage in Codex documentation before relying on fee or MEV fields.
Interpret and compare rates carefully
There is no official “good” sandwich-rate benchmark in the consulted how-to. Its author recommends comparing pools for the same token pair over several days; that is practical guidance, not an industry standard. Make a comparison meaningful by holding constant the observation window, rate definition, chain, and—where possible—data coverage. Include each pool’s weighted rate, valid transaction denominator, and missing-bar coverage. A small denominator or substantial missing data makes the estimate less informative.
Do not substitute mevRiskLevel for sandwichRate. The how-to describes the former as based on builder-tip share, which can reflect arbitrage, back-runs, liquidations, and other MEV activity as well as circumstances unrelated to sandwich incidence. In an author-reported example dated September 29, 2026, an Ethereum USDC/WETH pool had a zero sandwich rate while most hourly bars had medium MEV risk. That single example illustrates why the fields can diverge; it is not a general benchmark.
Why a low historical rate does not make a swap safe
A pool-level rate summarizes indexed past activity over a selected window. It cannot establish how an individual future trade will execute. Trade size, slippage tolerance, timing, and transaction submission path can all matter. Slippage limits may cause a swap to revert when execution moves beyond the allowed bound; they do not prevent every adverse outcome or guarantee inclusion at a particular price. Assess the proposed trade separately rather than treating a quiet historical pool as protection.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Use Dune for EVM attack-trade forensics
For trade-level investigation on EVM networks, Dune documents dex.sandwiches as a table of the outer front-run and back-run trades in sandwich attacks. Its purpose differs from Codex’s ready-made hourly pool rate: a rate derived from trade data requires an explicit pool filter, date range, and denominator that defines which transactions count. See Dune’s dex.sandwiches documentation for the table description and coverage. The companion victim table mentioned by the how-to author is not established by that documentation page, so verify its current schema before building a query around it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

