import * as PendingTransactionFilter from './primitives/PendingTransactionFilter/index.js';
import * as FilterId from './primitives/FilterId/index.js';
const filterId = FilterId.from(await rpc.eth_newPendingTransactionFilter());
const filter = PendingTransactionFilter.from(filterId);
// Track pending swaps
const pendingSwaps = new Map(); // methodId -> txs
// Uniswap swapExactTokensForTokens selector
const swapSelector = "0x38ed1739";
setInterval(async () => {
const hashes = await rpc.eth_getFilterChanges(filter.filterId);
for (const hash of hashes) {
const tx = await rpc.eth_getTransactionByHash(hash);
// Check if swap transaction
if (tx.input.startsWith(swapSelector)) {
const gasPrice = tx.maxFeePerGas || tx.gasPrice;
// Check for frontrunning (same method, higher gas)
const existing = pendingSwaps.get(swapSelector) || [];
const frontruns = existing.filter(ptx => ptx.gasPrice < gasPrice);
if (frontruns.length > 0) {
console.log(`POTENTIAL FRONTRUN DETECTED`);
console.log(`Original: ${frontruns[0].hash}`);
console.log(`Frontrun: ${hash}`);
}
pendingSwaps.set(swapSelector, [...existing, { hash, gasPrice }]);
}
}
// Clean up old txs (keep last 100)
for (const [key, txs] of pendingSwaps) {
if (txs.length > 100) {
pendingSwaps.set(key, txs.slice(-100));
}
}
}, 5000);