Untitled

mail@pastecode.io avatar
unknown
typescript
8 months ago
6.0 kB
8
Indexable
Never
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@aave/protocol-v2/contracts/interfaces/ILendingPool.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract MEVFlashbotArbitrage is Ownable, ReentrancyGuard {
    using SafeMath for uint256;

    ILendingPool public lendingPool;
    IUniswapV2Router02 public uniswapRouter;
    AggregatorV3Interface public priceOracle;

    uint256 public slippageTolerance = 1;
    uint256 public tradeDeadline = 600;
    uint256 public minTradeSize = 100;

    event ArbitrageExecuted(address indexed executor, uint256 profit);

    constructor(
        address _lendingPoolAddress,
        address _uniswapRouterAddress,
        address _priceOracle
    ) {
        lendingPool = ILendingPool(_lendingPoolAddress);
        uniswapRouter = IUniswapV2Router02(_uniswapRouterAddress);
        priceOracle = AggregatorV3Interface(_priceOracle);
    }

    modifier validTradeSize(uint256 amount) {
        require(amount >= minTradeSize, "Trade size below minimum");
        _;
    }

    function executeArbitrage(
        address tokenA,
        address tokenB,
        uint256 amountA,
        uint256 amountB
    ) external onlyOwner nonReentrant {
        require(IERC20(tokenA).balanceOf(address(this)) >= amountA, "Insufficient balance for flash loan");
        lendingPool.flashLoan(address(this), tokenA, amountA, abi.encode(tokenA, tokenB, amountB));
    }

    function executeOperation(
        address sender,
        address underlying,
        uint256 amount,
        uint256,
        bytes calldata params
    ) external nonReentrant {
        (address tokenA, address tokenB, uint256 originalAmountA, uint256 originalAmountB) =
            abi.decode(params, (address, address, uint256, uint256));
        require(msg.sender == address(lendingPool), "Invalid sender");

        _executeArbitrageTrade(tokenA, tokenB, originalAmountA, originalAmountB);
        IERC20(underlying).approve(address(lendingPool), amount.add(2));
        lendingPool.repay(underlying, amount.add(2), 2, address(this));

        uint256 profit = calculateProfit(tokenA, tokenB);
        IERC20(tokenA).transfer(owner(), profit);

        emit ArbitrageExecuted(owner(), profit);
    }

    function _executeArbitrageTrade(
        address tokenA,
        address tokenB,
        uint256 amountA,
        uint256 amountB
    ) internal validTradeSize(amountA) validTradeSize(amountB) {
        uint256 tokenBPrice = _getLatestTokenBPrice();
        address[] memory path = new address[](2);
        path[0] = tokenA;
        path[1] = tokenB;

        uint256[] memory amounts = uniswapRouter.swapExactTokensForTokens(
            amountA,
            amountB,
            path,
            address(this),
            block.timestamp + tradeDeadline
        );

        require(amounts[1] > 0, "Uniswap trade failed");
    }

    function calculateProfit(address tokenA, address tokenB) internal view returns (uint256) {
        uint256 tokenABalance = IERC20(tokenA).balanceOf(address(this));
        uint256 tokenBPrice = _getLatestTokenBPrice();
        return tokenABalance.mul(tokenBPrice).div(1e18);
    }

    function _getLatestTokenBPrice() internal view returns (uint256) {
        (, int256 price, , , ) = priceOracle.latestRoundData();
        require(price > 0, "Invalid Chainlink price");
        return uint256(price);
    }

    // Function to recover ETH from the contract
    function recoverEth() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }

    // Function to recover ERC20 tokens from the contract
    function recoverTokens(address tokenAddress) external onlyOwner {
        IERC20 token = IERC20(tokenAddress);
        token.transfer(owner(), token.balanceOf(address(this)));
    }

    // Function to change slippage tolerance
    function setSlippage(uint256 _slippagePercent) external onlyOwner {
        require(_slippagePercent <= 40, "Slippage cannot exceed 40%");
        slippageTolerance = _slippagePercent;
    }

    // Function to set trade deadline
    function setTradeDeadline(uint256 _tradeDeadline) external onlyOwner {
        tradeDeadline = _tradeDeadline;
    }

    // Function to set minimum trade size
    function setMinTradeSize(uint256 _minTradeSize) external onlyOwner {
        minTradeSize = _minTradeSize;
    }

    // Fallback function to accept any incoming ETH
    receive() external payable {}

    // Function to get the balance of a provided token contract address for this contract
    function getBalance(address _tokenContractAddress) external view returns (uint256) {
        return IERC20(_tokenContractAddress).balanceOf(address(this));
    }

    // Function to withdraw tokens to the owner
    function withdrawTokensToOwner(address _token, uint256 _amount) external onlyOwner {
        IERC20(_token).transfer(owner(), _amount);
    }

    // Function to withdraw ETH to the owner
    function withdrawEthToOwner(uint256 _amount) external onlyOwner {
        payable(owner()).transfer(_amount);
    }

    // Function to change the Aave lending pool address
    function setLendingPoolAddress(address _lendingPoolAddress) external onlyOwner {
        lendingPool = ILendingPool(_lendingPoolAddress);
    }

    // Function to change the Uniswap router address
    function setUniswapRouterAddress(address _uniswapRouterAddress) external onlyOwner {
        uniswapRouter = IUniswapV2Router02(_uniswapRouterAddress);
    }

    // Function to change the Chainlink price oracle address
    function setPriceOracleAddress(address _priceOracle) external onlyOwner {
        priceOracle = AggregatorV3Interface(_priceOracle);
    }
}
Leave a Comment