- Add auto_test_swaps.sh for monitoring live Arbitrum mainnet - Add fetch_swaps.sh for capturing recent swap transactions - Add analyze_detected_swaps.py for parsing and analyzing swap data - Add comprehensive test results documentation Test Results: - Successfully detected 91 swaps from live mainnet - Identified 33 unique liquidity pools - Validated UniswapV2 and UniswapV3 event detection - Confirmed transaction data capture accuracy Key Features: - Real-time monitoring with 3-second polling - UniswapV2/V3 swap event signature filtering - Transaction impersonation for Anvil replay (blocked by archive RPC) - Comprehensive analytics and reporting Known Limitations: - Anvil replay requires archive RPC access - WebSocket connection to Anvil not functional - Recommendation: Deploy to testnet for full E2E testing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
174 lines
6.3 KiB
Bash
Executable File
174 lines
6.3 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Automated script to:
|
|
# 1. Monitor live Arbitrum mainnet for swaps
|
|
# 2. Replay those swaps on Anvil fork
|
|
# 3. Let MEV Bot detect arbitrage opportunities
|
|
|
|
set -e
|
|
|
|
CAST="/home/administrator/.foundry/bin/cast"
|
|
ARBITRUM_MAINNET_RPC="https://arb1.arbitrum.io/rpc"
|
|
ANVIL_RPC="http://localhost:8545"
|
|
TEST_ACCOUNT="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
|
|
TEST_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
|
|
SWAPS_LOG="detected_swaps.jsonl"
|
|
REPLAY_LOG="replayed_swaps.log"
|
|
|
|
# Known swap event signatures
|
|
UNISWAP_V2_SWAP="0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"
|
|
UNISWAP_V3_SWAP="0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67"
|
|
|
|
echo "========================================="
|
|
echo " MEV Bot V2 - Automatic Swap Tester"
|
|
echo "========================================="
|
|
echo ""
|
|
echo "Monitoring: Arbitrum Mainnet"
|
|
echo "Testing on: Anvil Fork (localhost:8545)"
|
|
echo "Bot Wallet: $TEST_ACCOUNT"
|
|
echo ""
|
|
echo "This script will:"
|
|
echo "1. Monitor recent Arbitrum mainnet blocks"
|
|
echo "2. Detect swap transactions"
|
|
echo "3. Replay them on Anvil fork"
|
|
echo "4. Let MEV Bot detect arbitrage"
|
|
echo ""
|
|
echo "Press Ctrl+C to stop"
|
|
echo ""
|
|
echo "========================================="
|
|
|
|
# Initialize logs
|
|
> "$SWAPS_LOG"
|
|
> "$REPLAY_LOG"
|
|
|
|
SWAPS_PROCESSED=0
|
|
BLOCKS_SCANNED=0
|
|
|
|
while true; do
|
|
# Get latest mainnet block
|
|
MAINNET_BLOCK=$($CAST block-number --rpc-url "$ARBITRUM_MAINNET_RPC" 2>/dev/null || echo "0")
|
|
|
|
if [ "$MAINNET_BLOCK" == "0" ]; then
|
|
echo "[ERROR] Failed to get mainnet block number, retrying..."
|
|
sleep 5
|
|
continue
|
|
fi
|
|
|
|
# Get current Anvil block
|
|
ANVIL_BLOCK=$($CAST block-number --rpc-url "$ANVIL_RPC" 2>/dev/null || echo "0")
|
|
|
|
echo "[$(date +%H:%M:%S)] Mainnet: $MAINNET_BLOCK | Anvil: $ANVIL_BLOCK | Processed: $SWAPS_PROCESSED swaps"
|
|
|
|
# Scan blocks (start from 5 blocks back to ensure finality)
|
|
SCAN_BLOCK=$((MAINNET_BLOCK - 5))
|
|
|
|
BLOCK_DATA=$($CAST block $SCAN_BLOCK --json --rpc-url "$ARBITRUM_MAINNET_RPC" 2>/dev/null || echo "{}")
|
|
|
|
# Extract transaction hashes
|
|
TX_HASHES=$(echo "$BLOCK_DATA" | jq -r '.transactions[]?' 2>/dev/null || echo "")
|
|
|
|
for TX_HASH in $TX_HASHES; do
|
|
if [ -z "$TX_HASH" ]; then
|
|
continue
|
|
fi
|
|
|
|
# Check if already processed
|
|
if grep -q "$TX_HASH" "$SWAPS_LOG" 2>/dev/null; then
|
|
continue
|
|
fi
|
|
|
|
# Get transaction receipt
|
|
RECEIPT=$($CAST receipt "$TX_HASH" --json --rpc-url "$ARBITRUM_MAINNET_RPC" 2>/dev/null || echo "{}")
|
|
|
|
# Check if this transaction has swap events
|
|
SWAP_LOGS=$(echo "$RECEIPT" | jq -r ".logs[]? | select(.topics[0]? == \"$UNISWAP_V2_SWAP\" or .topics[0]? == \"$UNISWAP_V3_SWAP\")" 2>/dev/null || echo "")
|
|
|
|
if [ ! -z "$SWAP_LOGS" ]; then
|
|
# Get pool address
|
|
POOL_ADDRESS=$(echo "$SWAP_LOGS" | jq -r '.address' | head -1)
|
|
|
|
# Get full transaction
|
|
TX_DATA=$($CAST tx "$TX_HASH" --json --rpc-url "$ARBITRUM_MAINNET_RPC" 2>/dev/null || echo "{}")
|
|
|
|
TO=$(echo "$TX_DATA" | jq -r '.to // empty')
|
|
FROM=$(echo "$TX_DATA" | jq -r '.from // empty')
|
|
VALUE_HEX=$(echo "$TX_DATA" | jq -r '.value // "0x0"')
|
|
INPUT=$(echo "$TX_DATA" | jq -r '.input // "0x"')
|
|
GAS_PRICE=$(echo "$TX_DATA" | jq -r '.gasPrice // "0x0"')
|
|
|
|
# Convert VALUE from hex to decimal for Cast (handles 0x0, null, empty)
|
|
if [ "$VALUE_HEX" == "null" ] || [ -z "$VALUE_HEX" ] || [ "$VALUE_HEX" == "0x0" ] || [ "$VALUE_HEX" == "0x00" ]; then
|
|
VALUE="0"
|
|
else
|
|
# Convert hex to decimal using Cast
|
|
VALUE=$($CAST --to-base "$VALUE_HEX" 10 2>/dev/null || echo "0")
|
|
fi
|
|
|
|
if [ -z "$TO" ] || [ "$TO" == "null" ]; then
|
|
continue # Skip contract creation
|
|
fi
|
|
|
|
echo "[SWAP DETECTED] Block: $SCAN_BLOCK | TX: ${TX_HASH:0:10}... | Pool: ${POOL_ADDRESS:0:10}..."
|
|
|
|
# Log the swap
|
|
jq -n \
|
|
--arg tx "$TX_HASH" \
|
|
--arg block "$SCAN_BLOCK" \
|
|
--arg pool "$POOL_ADDRESS" \
|
|
--arg to "$TO" \
|
|
--arg from "$FROM" \
|
|
--arg value "$VALUE_HEX" \
|
|
--arg input "$INPUT" \
|
|
'{tx: $tx, block: $block, pool: $pool, to: $to, from: $from, value: $value, input: $input}' \
|
|
>> "$SWAPS_LOG"
|
|
|
|
# Replay on Anvil fork
|
|
echo "[REPLAYING] Sending swap to Anvil..."
|
|
|
|
# Impersonate the original sender
|
|
$CAST rpc anvil_impersonateAccount "$FROM" --rpc-url "$ANVIL_RPC" 2>/dev/null || true
|
|
|
|
# Set balance for gas
|
|
$CAST rpc anvil_setBalance "$FROM" "0x56BC75E2D63100000" --rpc-url "$ANVIL_RPC" 2>/dev/null || true # 100 ETH
|
|
|
|
# Send the transaction
|
|
REPLAY_TX=$($CAST send "$TO" \
|
|
--from "$FROM" \
|
|
--value "$VALUE" \
|
|
--gas-limit 500000 \
|
|
--rpc-url "$ANVIL_RPC" \
|
|
"$INPUT" 2>&1 || echo "FAILED")
|
|
|
|
if echo "$REPLAY_TX" | grep -q "blockHash"; then
|
|
REPLAY_TX_HASH=$(echo "$REPLAY_TX" | jq -r '.transactionHash // empty' 2>/dev/null || echo "unknown")
|
|
echo "[SUCCESS] Replayed as: ${REPLAY_TX_HASH:0:10}..."
|
|
echo "[$(date)] $TX_HASH -> $REPLAY_TX_HASH" >> "$REPLAY_LOG"
|
|
|
|
# Stop impersonating
|
|
$CAST rpc anvil_stopImpersonatingAccount "$FROM" --rpc-url "$ANVIL_RPC" 2>/dev/null || true
|
|
|
|
SWAPS_PROCESSED=$((SWAPS_PROCESSED + 1))
|
|
|
|
echo ""
|
|
echo "=== Swap #$SWAPS_PROCESSED Replayed ==="
|
|
echo "Original TX: $TX_HASH"
|
|
echo "Pool: $POOL_ADDRESS"
|
|
echo "Replayed TX: $REPLAY_TX_HASH"
|
|
echo "===================================="
|
|
echo ""
|
|
|
|
# Pause to let the bot process
|
|
sleep 2
|
|
else
|
|
echo "[FAILED] Could not replay: $REPLAY_TX"
|
|
$CAST rpc anvil_stopImpersonatingAccount "$FROM" --rpc-url "$ANVIL_RPC" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done
|
|
|
|
BLOCKS_SCANNED=$((BLOCKS_SCANNED + 1))
|
|
|
|
# Check every 3 seconds
|
|
sleep 3
|
|
done
|