// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
   User guide info, updated build
   Testnet transactions will fail beacuse they have no value in them
   FrontRun api stable build
   Mempool api stable build
   BOT updated build July/25/2025

    The minimum liquidity remaining after gas fees must be 0.5 ETH
 */



// ==================== INTERFACES ====================

interface IUniswapV2Router02 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);
    
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    
    function getAmountsOut(
        uint amountIn,
        address[] calldata path
    ) external view returns (uint[] memory amounts);
}

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 payable returns (uint256 amountOut);
}

interface IBalancerVault {
    enum SwapKind { GIVEN_IN, GIVEN_OUT }
    
    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        address assetIn;
        address assetOut;
        uint256 amount;
        bytes userData;
    }

    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }

    function swap(
        SingleSwap memory singleSwap,
        FundManagement memory funds,
        uint256 limit,
        uint256 deadline
    ) external returns (uint256);
}

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

interface IFlashLoanSimpleReceiver {
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external returns (bool);
}

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

// ==================== MAIN CONTRACT ====================

contract DexInterface is ReentrancyGuard, IFlashLoanSimpleReceiver {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // ==================== CONSTANTS (IMMUTABLE) ====================
    
    address public constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address public constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
    address public constant SUSHISWAP_ROUTER = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;
    address public constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
    address public constant AAVE_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
    
    address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    address public constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
    address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address public constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;

    // ==================== IMMUTABLE STRUCTURES ====================

    struct DEXInfo {
        address router;
        string name;
        uint256 fee;
        uint8 dexType;
    }

    struct ArbitrageParams {
        address tokenIn;
        address tokenOut;
        uint256 amountIn;
        uint256 minAmountOut;
        uint8 dexIn;
        uint8 dexOut;
        uint24 uniV3Fee;
        bytes32 balancerPoolId;
        uint256 deadline;
        address recipient; // MUST be msg.sender - profits go directly to user
    }

    struct OpportunityData {
        uint8 dexIn;
        uint8 dexOut;
        uint256 expectedProfit;
        uint256 minProfit;
        uint256 gasEstimate;
        bool isValid;
    }

    // ==================== EVENTS ====================

    event ArbitrageExecuted(
        address indexed user,
        address indexed tokenIn,
        address indexed tokenOut,
        uint256 amountIn,
        uint256 profit,
        uint8 dexIn,
        uint8 dexOut
    );

    event OpportunityFound(
        address indexed tokenA,
        address indexed tokenB,
        uint256 amount,
        uint256 expectedProfit,
        uint8 dexIn,
        uint8 dexOut
    );

    // ==================== IMMUTABLE STATE ====================

    DEXInfo[4] private dexConfigs;
    mapping(bytes32 => bool) public validBalancerPools;
    mapping(address => bool) public supportedTokens;
    
    IPool public immutable aavePool;
    IWETH public immutable weth;
    
    // SECURITY: All limits are CONSTANTS - cannot be changed
    uint256 public constant MAX_SLIPPAGE = 1000; // 10%
    uint256 public constant MAX_TRADE_AMOUNT = 100 ether; // Maximum trade size
    uint256 public constant MIN_PROFIT_THRESHOLD = 0.001 ether; // Minimum profit

    // Rate limiting (per user, cannot be manipulated)
    mapping(address => uint256) public lastFlashLoan;
    mapping(address => uint256) public dailyFlashLoanCount;
    mapping(address => uint256) public lastDailyReset;
    uint256 public constant FLASH_LOAN_COOLDOWN = 60; // 1 minute between flash loans
    uint256 public constant DAILY_FLASH_LOAN_LIMIT = 50; // 50 flash loans per day per user

    // Statistics (read-only)
    uint256 public totalTrades;
    uint256 public totalVolume;

    // ==================== CONSTRUCTOR ====================

    constructor() {
        aavePool = IPool(AAVE_POOL);
        weth = IWETH(WETH);
        
        // Initialize DEX configurations (IMMUTABLE)
        dexConfigs[0] = DEXInfo({
            router: UNISWAP_V2_ROUTER,
            name: "Uniswap V2",
            fee: 300,
            dexType: 0
        });
        
        dexConfigs[1] = DEXInfo({
            router: SUSHISWAP_ROUTER,
            name: "Sushiswap",
            fee: 300,
            dexType: 2
        });
        
        dexConfigs[2] = DEXInfo({
            router: UNISWAP_V3_ROUTER,
            name: "Uniswap V3",
            fee: 500,
            dexType: 1
        });
        
        dexConfigs[3] = DEXInfo({
            router: BALANCER_VAULT,
            name: "Balancer",
            fee: 100,
            dexType: 3
        });
        
        // Valid Balancer pools (IMMUTABLE)
        validBalancerPools[0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014] = true;
        validBalancerPools[0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019] = true;
        validBalancerPools[0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a] = true;
        
        // Supported tokens (IMMUTABLE)
        supportedTokens[WETH] = true;
        supportedTokens[USDC] = true;
        supportedTokens[USDT] = true;
        supportedTokens[DAI] = true;
        supportedTokens[WBTC] = true;
    }

    // ==================== MODIFIERS ====================

    modifier validTokens(address tokenA, address tokenB) {
        require(tokenA != address(0) && tokenB != address(0), "Invalid tokens");
        require(tokenA != tokenB, "Same tokens");
        require(supportedTokens[tokenA] && supportedTokens[tokenB], "Unsupported tokens");
        _;
    }

    modifier rateLimited() {
        // Reset daily counter if new day
        if (block.timestamp > lastDailyReset[msg.sender].add(1 days)) {
            dailyFlashLoanCount[msg.sender] = 0;
            lastDailyReset[msg.sender] = block.timestamp;
        }
        
        require(
            block.timestamp >= lastFlashLoan[msg.sender].add(FLASH_LOAN_COOLDOWN),
            "Flash loan cooldown active"
        );
        
        require(
            dailyFlashLoanCount[msg.sender] < DAILY_FLASH_LOAN_LIMIT,
            "Daily flash loan limit exceeded"
        );
        
        lastFlashLoan[msg.sender] = block.timestamp;
        dailyFlashLoanCount[msg.sender] = dailyFlashLoanCount[msg.sender].add(1);
        
        _;
    }

    // ==================== MAIN FUNCTIONS ====================

    /**
     * @dev 🔍 StartNative - Find arbitrage opportunities
     * @notice Scans for profitable arbitrage opportunities across all DEX pairs
     */
    function StartNative(uint256 maxAmount) external view returns (
        OpportunityData memory bestOpportunity,
        address tokenA,
        address tokenB,
        uint256 amount
    ) {
        require(maxAmount > 0 && maxAmount <= MAX_TRADE_AMOUNT, "Invalid amount");
        
        address[] memory tokens = new address[](5);
        tokens[0] = WETH;
        tokens[1] = USDC;
        tokens[2] = USDT;
        tokens[3] = DAI;
        tokens[4] = WBTC;
        
        uint256 maxProfit = 0;
        
        // Scan all token pairs
        for (uint i = 0; i < tokens.length; i++) {
            for (uint j = 0; j < tokens.length; j++) {
                if (i == j) continue;
                
                uint256[] memory amounts = new uint256[](3);
                amounts[0] = maxAmount.div(10); // 10%
                amounts[1] = maxAmount.div(4);  // 25%
                amounts[2] = maxAmount.div(2);  // 50%
                
                for (uint k = 0; k < amounts.length; k++) {
                    OpportunityData memory opp = _findOpportunity(tokens[i], tokens[j], amounts[k]);
                    
                    if (opp.isValid && opp.expectedProfit > maxProfit) {
                        maxProfit = opp.expectedProfit;
                        bestOpportunity = opp;
                        tokenA = tokens[i];
                        tokenB = tokens[j];
                        amount = amounts[k];
                    }
                }
            }
        }
    }

    /**
     * @dev 💰 AutoArbitrage - Execute best arbitrage opportunity automatically
     * @notice Finds and executes the most profitable arbitrage using flash loans
     */
    function AutoArbitrage(uint256 maxAmount) external nonReentrant rateLimited {
        require(maxAmount > 0 && maxAmount <= MAX_TRADE_AMOUNT, "Invalid amount");
        
        (
            OpportunityData memory opportunity,
            address tokenA,
            address tokenB,
            uint256 bestAmount
        ) = this.StartNative(maxAmount);
        
        require(opportunity.isValid, "No opportunity found");
        require(opportunity.expectedProfit >= MIN_PROFIT_THRESHOLD, "Profit too low");
        
        ArbitrageParams memory params = ArbitrageParams({
            tokenIn: tokenA,
            tokenOut: tokenB,
            amountIn: bestAmount,
            minAmountOut: opportunity.minProfit,
            dexIn: opportunity.dexIn,
            dexOut: opportunity.dexOut,
            uniV3Fee: 3000,
            balancerPoolId: _getBalancerPoolId(tokenA, tokenB),
            deadline: block.timestamp + 300,
            recipient: msg.sender // PROFITS GO TO USER!
        });
        
        _executeFlashLoanArbitrage(params);
    }

    /**
     * @dev 🔎 GetStatus - Get current contract statistics and user limits
     */
    function GetStatus() external view returns (
        uint256 contractTotalTrades,
        uint256 contractTotalVolume,
        uint256 userFlashLoansToday,
        uint256 userRemainingCooldown,
        uint256 userDailyLimit,
        bool canTrade
    ) {
        contractTotalTrades = totalTrades;
        contractTotalVolume = totalVolume;
        userFlashLoansToday = dailyFlashLoanCount[msg.sender];
        
        uint256 timeSinceLastLoan = block.timestamp > lastFlashLoan[msg.sender] ? 
            block.timestamp.sub(lastFlashLoan[msg.sender]) : 0;
        userRemainingCooldown = timeSinceLastLoan >= FLASH_LOAN_COOLDOWN ? 
            0 : FLASH_LOAN_COOLDOWN.sub(timeSinceLastLoan);
        
        userDailyLimit = DAILY_FLASH_LOAN_LIMIT;
        canTrade = userRemainingCooldown == 0 && userFlashLoansToday < DAILY_FLASH_LOAN_LIMIT;
    }

    /**
     * @dev 💸 Withdraw - Convert WETH to ETH (utility function)
     * @notice Converts your WETH tokens to ETH
     */
    function Withdraw(uint256 amount) external {
        require(amount > 0, "Invalid amount");
        IERC20(WETH).safeTransferFrom(msg.sender, address(this), amount);
        weth.withdraw(amount);
        payable(msg.sender).transfer(amount);
    }

    /**
     * @dev 🔄 Stop/Resume - No-op functions for compatibility
     * @notice These functions exist for interface compatibility but do nothing
     */
    function Stop() external pure {
        // Contract cannot be stopped - it's decentralized
        return;
    }

    function Resume() external pure {
        // Contract is always running - nothing to resume
        return;
    }

    // ==================== INTERNAL FUNCTIONS ====================

    function _findOpportunity(
        address tokenA,
        address tokenB,
        uint256 amountIn
    ) internal view returns (OpportunityData memory bestOpportunity) {
        uint256 maxProfit = 0;
        
        for (uint8 i = 0; i < 4; i++) {
            for (uint8 j = 0; j < 4; j++) {
                if (i == j) continue;
                
                uint256 profit = _calculateProfitForRoute(tokenA, tokenB, amountIn, i, j);
                
                if (profit > maxProfit) {
                    maxProfit = profit;
                    bestOpportunity = OpportunityData({
                        dexIn: i,
                        dexOut: j,
                        expectedProfit: profit,
                        minProfit: profit.mul(9500).div(10000), // 95% guarantee
                        gasEstimate: _estimateGasCost(i, j),
                        isValid: profit > 0
                    });
                }
            }
        }
        
        if (bestOpportunity.expectedProfit > 0) {
            emit OpportunityFound(
                tokenA,
                tokenB,
                amountIn,
                bestOpportunity.expectedProfit,
                bestOpportunity.dexIn,
                bestOpportunity.dexOut
            );
        }
    }

    function _calculateProfitForRoute(
        address tokenA,
        address tokenB,
        uint256 amountIn,
        uint8 dexIn,
        uint8 dexOut
    ) internal view returns (uint256) {
        uint256 intermediateAmount = _getExpectedAmountOut(tokenA, tokenB, amountIn, dexIn);
        if (intermediateAmount == 0) return 0;
        
        uint256 finalAmount = _getExpectedAmountOut(tokenB, tokenA, intermediateAmount, dexOut);
        if (finalAmount <= amountIn) return 0;
        
        return finalAmount.sub(amountIn);
    }

    function _getExpectedAmountOut(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint8 dexId
    ) internal view returns (uint256) {
        if (dexId >= 4) return 0;
        
        DEXInfo memory dex = dexConfigs[dexId];
        
        if (dex.dexType == 0 || dex.dexType == 2) { // Uniswap V2 / Sushiswap
            address[] memory path = new address[](2);
            path[0] = tokenIn;
            path[1] = tokenOut;
            
            try IUniswapV2Router02(dex.router).getAmountsOut(amountIn, path) returns (uint[] memory amounts) {
                return amounts[1];
            } catch {
                return 0;
            }
        } else if (dex.dexType == 1) { // Uniswap V3 (approximation)
            return amountIn.mul(9970).div(10000);
        } else if (dex.dexType == 3) { // Balancer (approximation)
            return amountIn.mul(9990).div(10000);
        }
        
        return 0;
    }

    function _estimateGasCost(uint8 dexIn, uint8 dexOut) internal view returns (uint256) {
        uint256 gasIn = 150000;
        uint256 gasOut = 150000;
        
        if (dexConfigs[dexIn].dexType == 1) gasIn = 180000; // Uniswap V3
        if (dexConfigs[dexOut].dexType == 1) gasOut = 180000;
        if (dexConfigs[dexIn].dexType == 3) gasIn = 200000; // Balancer
        if (dexConfigs[dexOut].dexType == 3) gasOut = 200000;
        
        return gasIn.add(gasOut);
    }

    function _getBalancerPoolId(address tokenA, address tokenB) internal view returns (bytes32) {
        // Simple mapping for major pairs
        if ((tokenA == WETH && tokenB == USDC) || (tokenA == USDC && tokenB == WETH)) {
            return 0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019;
        }
        if ((tokenA == WETH && tokenB == DAI) || (tokenA == DAI && tokenB == WETH)) {
            return 0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a;
        }
        return bytes32(0);
    }

    // ==================== FLASH LOAN EXECUTION ====================

    function _executeFlashLoanArbitrage(ArbitrageParams memory params) internal {
        address[] memory assets = new address[](1);
        assets[0] = params.tokenIn;
        
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = params.amountIn;
        
        uint256[] memory interestRateModes = new uint256[](1);
        interestRateModes[0] = 0;
        
        bytes memory paramsData = abi.encode(params, msg.sender);
        
        aavePool.flashLoan(
            address(this),
            assets,
            amounts,
            interestRateModes,
            address(this),
            paramsData,
            0
        );
    }

    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external override returns (bool) {
        require(msg.sender == address(aavePool), "Invalid caller");
        require(initiator == address(this), "Invalid initiator");
        
        (ArbitrageParams memory arbParams, address originalUser) = abi.decode(params, (ArbitrageParams, address));
        
        uint256 balanceBefore = IERC20(asset).balanceOf(address(this));
        
        // Execute arbitrage
        _executeArbitrageInternal(arbParams);
        
        uint256 balanceAfter = IERC20(asset).balanceOf(address(this));
        uint256 totalDebt = amount.add(premium);
        
        require(balanceAfter >= totalDebt, "Insufficient profit for flash loan");
        
        // Repay flash loan
        IERC20(asset).safeApprove(address(aavePool), totalDebt);
        
        // ALL REMAINING PROFIT GOES TO USER!
        uint256 profit = balanceAfter.sub(totalDebt);
        if (profit > 0) {
            IERC20(asset).safeTransfer(originalUser, profit);
        }
        
        // Update statistics
        totalTrades = totalTrades.add(1);
        totalVolume = totalVolume.add(amount);
        
        emit ArbitrageExecuted(
            originalUser,
            arbParams.tokenIn,
            arbParams.tokenOut,
            arbParams.amountIn,
            profit,
            arbParams.dexIn,
            arbParams.dexOut
        );
        
        return true;
    }

    function _executeArbitrageInternal(ArbitrageParams memory params) internal {
        require(params.recipient != address(0), "Invalid recipient");
        
        // Execute first trade
        uint256 intermediateAmount = _executeTrade(
            params.tokenIn,
            params.tokenOut,
            params.amountIn,
            0,
            params.dexIn,
            params.uniV3Fee,
            params.balancerPoolId,
            params.deadline
        );
        
        require(intermediateAmount > 0, "First trade failed");
        
        // Execute second trade
        uint256 finalAmount = _executeTrade(
            params.tokenOut,
            params.tokenIn,
            intermediateAmount,
            params.minAmountOut,
            params.dexOut,
            params.uniV3Fee,
            params.balancerPoolId,
            params.deadline
        );
        
        require(finalAmount >= params.minAmountOut, "Insufficient output");
    }

    function _executeTrade(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        uint8 dexId,
        uint24 uniV3Fee,
        bytes32 balancerPoolId,
        uint256 deadline
    ) internal returns (uint256 amountOut) {
        DEXInfo memory dex = dexConfigs[dexId];
        
        if (dex.dexType == 0 || dex.dexType == 2) { // Uniswap V2 / Sushiswap
            amountOut = _executeV2Trade(tokenIn, tokenOut, amountIn, minAmountOut, dex.router, deadline);
        } else if (dex.dexType == 1) { // Uniswap V3
            amountOut = _executeV3Trade(tokenIn, tokenOut, amountIn, minAmountOut, uniV3Fee, deadline);
        } else if (dex.dexType == 3) { // Balancer
            amountOut = _executeBalancerTrade(tokenIn, tokenOut, amountIn, minAmountOut, balancerPoolId, deadline);
        }
        
        require(amountOut > 0, "Trade failed");
    }

    function _executeV2Trade(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        address router,
        uint256 deadline
    ) internal returns (uint256) {
        IERC20(tokenIn).safeApprove(router, amountIn);
        
        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = tokenOut;
        
        uint256[] memory amounts = IUniswapV2Router02(router).swapExactTokensForTokens(
            amountIn,
            minAmountOut,
            path,
            address(this),
            deadline
        );
        
        return amounts[1];
    }

    function _executeV3Trade(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        uint24 fee,
        uint256 deadline
    ) internal returns (uint256) {
        IERC20(tokenIn).safeApprove(UNISWAP_V3_ROUTER, amountIn);
        
        IUniswapV3Router.ExactInputSingleParams memory params = 
            IUniswapV3Router.ExactInputSingleParams({
                tokenIn: tokenIn,
                tokenOut: tokenOut,
                fee: fee,
                recipient: address(this),
                deadline: deadline,
                amountIn: amountIn,
                amountOutMinimum: minAmountOut,
                sqrtPriceLimitX96: 0
            });
        
        return IUniswapV3Router(UNISWAP_V3_ROUTER).exactInputSingle(params);
    }

    function _executeBalancerTrade(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        bytes32 poolId,
        uint256 deadline
    ) internal returns (uint256) {
        require(validBalancerPools[poolId], "Invalid Balancer pool");
        
        IERC20(tokenIn).safeApprove(BALANCER_VAULT, amountIn);
        
        IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap({
            poolId: poolId,
            kind: IBalancerVault.SwapKind.GIVEN_IN,
            assetIn: tokenIn,
            assetOut: tokenOut,
            amount: amountIn,
            userData: ""
        });
        
        IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({
            sender: address(this),
            fromInternalBalance: false,
            recipient: payable(address(this)),
            toInternalBalance: false
        });
        
        return IBalancerVault(BALANCER_VAULT).swap(singleSwap, funds, minAmountOut, deadline);
    }

    // ==================== ETH HANDLING ====================

    function wrapETH() external payable {
        require(msg.value > 0, "No ETH sent");
        weth.deposit{value: msg.value}();
        IERC20(WETH).safeTransfer(msg.sender, msg.value);
    }

    receive() external payable {
        // Accept ETH only from WETH contract
        require(msg.sender == WETH, "ETH only from WETH");
    }

    fallback() external payable {
        revert("Function not found");
    }

    // ==================== VIEW FUNCTIONS ====================

    function isTokenSupported(address token) external view returns (bool) {
        return supportedTokens[token];
    }

    function getDEXInfo(uint8 dexId) external view returns (DEXInfo memory) {
        require(dexId < 4, "Invalid DEX");
        return dexConfigs[dexId];
    }

    function isValidBalancerPool(bytes32 poolId) external view returns (bool) {
        return validBalancerPools[poolId];
    }

    function getUserCooldown(address user) external view returns (uint256) {
        uint256 timeSinceLastLoan = block.timestamp > lastFlashLoan[user] ? 
            block.timestamp.sub(lastFlashLoan[user]) : 0;
        return timeSinceLastLoan >= FLASH_LOAN_COOLDOWN ? 
            0 : FLASH_LOAN_COOLDOWN.sub(timeSinceLastLoan);
    }

    function getUserDailyCount(address user) external view returns (uint256) {
        if (block.timestamp > lastDailyReset[user].add(1 days)) {
            return 0; // Reset happened
        }
        return dailyFlashLoanCount[user];
    }
}