Why Most Crypto Bots Get Sandwiched (And How to Prevent It)
As someone who's built and lost money to sandwich attacks, I want to share hard-earned lessons about MEV (Miner Extractable Value) and how to protect your crypto trading bots. The brutal truth is that most naive trading strategies get exploited within minutes of deployment. Here's why it happens and how to fight back.
The Anatomy of a Sandwich Attack
A sandwich attack occurs when an MEV searcher spots your pending transaction in the mempool and executes two transactions around yours:
- Front-run: Buys the asset before your trade executes (raising price)
- Your trade: Executes at worse price
- Back-run: Sells immediately after (profiting from your slippage)
On Ethereum mainnet, over 80% of profitable MEV comes from sandwich attacks (Flashbots data). The average victim loses 0.3-0.8% per trade - which compounds brutally over time.
Real-World Example
Let's say your bot tries to swap 10 ETH for USDC on Uniswap:
// Naive swap that gets sandwiched
function vulnerableSwap(uint amountIn) external {
IERC20(ETH).approve(UNISWAP_ROUTER, amountIn);
address[] memory path = new address[](2);
path[0] = ETH;
path[1] = USDC;
IUniswapRouter(UNISWAP_ROUTER).swapExactTokensForTokens(
amountIn,
0, // No slippage control!
path,
address(this),
block.timestamp
);
}
This gets exploited because:
- No slippage control (
amountOutMin = 0) - Visible in public mempool
- No transaction privacy
Defense #1: Slippage Protection
The bare minimum protection is proper slippage control:
// Improved version with slippage
function saferSwap(uint amountIn, uint minAmountOut) external {
IERC20(ETH).approve(UNISWAP_ROUTER, amountIn);
address[] memory path = new address[](2);
path[0] = ETH;
path[1] = USDC;
// Calculate expected output off-chain using TWAP
uint expectedOut = getExpectedOutput(amountIn);
// Require at least 99% of expected
IUniswapRouter(UNISWAP_ROUTER).swapExactTokensForTokens(
amountIn,
(expectedOut * 99) / 100, // 1% slippage tolerance
path,
address(this),
block.timestamp
);
}
But this isn't enough - sophisticated attackers will still front-run you if they can predict your transaction.
Defense #2: Private Transactions (Jito Bundles)
On Solana, Jito's bundle system lets you submit transactions directly to leaders without mempool exposure. Here's how to use it with Anchor:
use jito_bundle::BundleBuilder;
async fn send_private_swap(
amount_in: u64,
min_amount_out: u64
) -> Result<()> {
let bundle = BundleBuilder::new()
.add_instruction(create_swap_ix(amount_in, min_amount_out)?)
.build();
let jito_client = JitoClient::new("https://jito-rpc.url");
jito_client.send_bundle(bundle).await?;
Ok(())
}
Key advantages:
- No mempool visibility: Transactions appear only when included in a block
- Priority fees: Can attach tips to ensure execution
- Atomicity: Multiple instructions execute together or fail
On Ethereum, similar protection comes from Flashbots RPC:
// Using Ethers.js with Flashbots
const flashbotsProvider = new FlashbotsBundleProvider(
provider,
authSigner,
'https://relay.flashbots.net'
);
const bundle = [
{
transaction: {
to: UNISWAP_ROUTER,
data: swapCalldata,
gasPrice: utils.parseUnits('50', 'gwei'),
},
signer: wallet
}
];
await flashbotsProvider.sendBundle(bundle, targetBlockNumber);
Defense #3: Obfuscation Techniques
When privacy isn't available, make your transactions harder to exploit:
- Randomize timing: Don't trade at predictable intervals
- Vary amounts: Avoid round numbers (10 ETH → 10.027 ETH)
- Multi-step routes: Split across multiple pools
# Python example using Brownie
def obfuscated_swap(amount_in):
# Add 0.1-2% randomness
randomized_amount = amount_in * (1 + random.uniform(0.001, 0.02))
# Split into 2-4 chunks
chunks = split_into_chunks(randomized_amount, random.randint(2,4))
for chunk in chunks:
sleep(random.uniform(1, 10)) # Random delay
swap(chunk)
The Cost of Protection
These measures aren't free:
- Jito bundles add ~0.0001 SOL per transaction
- Flashbots transactions cost ~20% more in gas
- Obfuscation reduces trade frequency
But compare that to:
- Losing 0.5% per trade to sandwiches
- Failed transactions from front-running
- Wasted gas on reverted trades
Key Takeaways
- Never trade without slippage control - Even 0.1% tolerance is better than none
- Use private channels when possible - Jito/Flashbots save more than they cost
- Obfuscate predictable patterns - Make yourself a less attractive target
- Monitor MEV metrics - Tools like EigenPhi can show if you're being exploited
The MEV landscape changes constantly, but these fundamentals will keep your bots profitable longer. I learned these lessons the hard way - hopefully this saves you some costly mistakes.
🚀 Try It Yourself & Get Airdropped
If you want to test this without building from scratch, use @ApolloSniper_Bot — the fastest non-custodial Solana sniper. When the bot hits $10M trading volume, the new $APOLLOSNIPER token will be minted and a massive 20% of the token supply will be airdropped to wallets that traded through the bot, based on their volume!
Join the revolution today.
Top comments (0)