// SPDX-License-Identifier: MIT /** pragma solidity ^0.8.28; import "../base/GluedLoanReceiver.sol"; /** * @title Example Arbitrage Bot * @notice Demonstrates how to use the GluedLoanReceiver base contract for arbitrage operations * @dev This contract shows practical usage of flash loans for arbitrage strategies */ contract ExampleArbitrageBot is GluedLoanReceiver { /// @notice Owner of the contract address public owner; /// @notice Minimum profit threshold for arbitrage opportunities uint256 public minimumProfit; /** * @notice Constructor sets the owner and minimum profit * @param _owner Address of the contract owner * @param _minimumProfit Minimum profit required for arbitrage */ constructor(address _owner, uint256 _minimumProfit) { owner = _owner; minimumProfit = _minimumProfit; } /** * @notice Modifier to restrict access to owner only */ modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } /** * @notice Request a cross-glue loan for arbitrage opportunity * @param useERC721 True to use ERC721 glues, false for ERC20 glues * @param glues Array of glue addresses to borrow from * @param collateral Token to borrow for arbitrage * @param amount Total amount to borrow * @param arbitrageData Encoded data for arbitrage execution * @return success True if loan was successfully requested */ function requestArbitrageLoan( bool useERC721, address[] memory glues, address collateral, uint256 amount, bytes memory arbitrageData ) external onlyOwner returns (bool success) { // Emit loan request event emit LoanRequested("gluedLoan", collateral, amount); // Request the glued loan using our helper function return _requestGluedLoan(useERC721, glues, collateral, amount, arbitrageData); } /** * @notice Request a simple flash loan from a single glue * @param glue Address of the glue to borrow from * @param collateral Token to borrow * @param amount Amount to borrow * @param strategyData Encoded data for the strategy * @return success True if loan was successfully requested */ function requestSimpleLoan( address glue, address collateral, uint256 amount, bytes memory strategyData ) external onlyOwner returns (bool success) { // Emit loan request event emit LoanRequested("flashLoan", collateral, amount); // Request the flash loan using our helper function return _requestFlashLoan(glue, collateral, amount, strategyData); } /** * @notice Implementation of flash loan logic * @param params Encoded arbitrage parameters * @return success True if arbitrage was successful */ function _executeFlashLoanLogic(bytes memory params) internal override returns (bool success) { // Decode parameters (string memory strategy, address targetDex, uint256 expectedProfit) = abi.decode(params, (string, address, uint256)); // Get current loan information using external interface ( address[] memory glues, address collateral, uint256[] memory expectedAmounts, uint256 totalBorrowed, uint256 totalRepay, uint256 totalFees ) = this.getCurrentLoanInfo(); // Emit loan execution start event emit FlashLoanLogicStarted(collateral, totalBorrowed); // Calculate required profit (fees + minimum profit) uint256 requiredProfit = totalFees + minimumProfit; // Ensure expected profit exceeds our requirements require(expectedProfit >= requiredProfit, "Insufficient profit opportunity"); // Example arbitrage logic (simplified) if (keccak256(abi.encodePacked(strategy)) == keccak256(abi.encodePacked("dex_arbitrage"))) { success = _executeDexArbitrage(targetDex, collateral, totalBorrowed, expectedProfit); } else if (keccak256(abi.encodePacked(strategy)) == keccak256(abi.encodePacked("liquidation"))) { success = _executeLiquidation(targetDex, collateral, totalBorrowed); } else { // Unknown strategy return false; } // Emit successful execution event if (success) { emit FlashLoanExecuted(collateral, totalBorrowed, totalRepay, totalFees); } return success; } /** * @notice Execute DEX arbitrage strategy * @param dex Address of the DEX to arbitrage against * @param token Token to arbitrage * @param amount Amount available for arbitrage * @param expectedProfit Expected profit from the arbitrage * @return success True if arbitrage was profitable */ function _executeDexArbitrage( address dex, address token, uint256 amount, uint256 expectedProfit ) internal returns (bool success) { // Simplified DEX arbitrage logic // In a real implementation, this would: // 1. Swap tokens on DEX A // 2. Swap back on DEX B at better rate // 3. Keep the profit // Mock arbitrage execution // This would be replaced with actual DEX interaction code // For this example, we'll simulate a successful arbitrage // that generates the expected profit // Check if we have enough profit to cover fees and minimum uint256 totalFees = this.getCurrentTotalFees(); if (expectedProfit >= totalFees + minimumProfit) { // Simulate profit generation // In reality, this would be actual token swaps return true; } return false; } /** * @notice Execute liquidation strategy * @param protocol Address of the lending protocol * @param collateral Collateral token * @param amount Amount to use for liquidation * @return success True if liquidation was successful */ function _executeLiquidation( address protocol, address collateral, uint256 amount ) internal returns (bool success) { // Simplified liquidation logic // In a real implementation, this would: // 1. Identify underwater positions // 2. Execute liquidation // 3. Sell seized collateral // 4. Keep the liquidation bonus // Mock liquidation execution protocol; // Silence unused variable warning collateral; // Silence unused variable warning amount; // Silence unused variable warning // For this example, assume liquidation was successful return true; } /** * @notice Update minimum profit threshold * @param newMinimumProfit New minimum profit threshold */ function setMinimumProfit(uint256 newMinimumProfit) external onlyOwner { minimumProfit = newMinimumProfit; } /** * @notice Plan and validate an arbitrage opportunity before execution * @param glue Address of the glue to borrow from * @param collateral Collateral token for the loan * @param amount Amount needed for arbitrage * @return feasible True if the arbitrage is feasible * @return available Maximum amount available for loan * @return totalCost Total cost including fees * @return minimumProfitNeeded Minimum profit needed to be profitable */ function planArbitrage( address glue, address collateral, uint256 amount ) external view returns ( bool feasible, uint256 available, uint256 totalCost, uint256 minimumProfitNeeded ) { // Get comprehensive loan information ( available, , uint256 calculatedFee, totalCost, bool canAfford ) = this.getLoanPlan(glue, collateral, amount); // Calculate minimum profit needed minimumProfitNeeded = calculatedFee + minimumProfit; // Arbitrage is feasible if glue has enough liquidity feasible = canAfford; return (feasible, available, totalCost, minimumProfitNeeded); } /** * @notice Find the best glue for a loan based on available liquidity * @param glues Array of potential glue addresses * @param collateral Collateral token to check * @param amount Desired loan amount * @return bestGlue Address of the glue with most liquidity * @return available Amount available from the best glue */ function findBestGlue( address[] memory glues, address collateral, uint256 amount ) external view returns (address bestGlue, uint256 available) { uint256 maxAvailable = 0; for (uint256 i = 0; i < glues.length; i++) { uint256 glueAvailable = this.maxLoan(glues[i], collateral); if (glueAvailable >= amount && glueAvailable > maxAvailable) { maxAvailable = glueAvailable; bestGlue = glues[i]; } } return (bestGlue, maxAvailable); } /** * @notice Execute arbitrage with automatic loan planning * @param glue Address of the glue to borrow from * @param collateral Collateral token for arbitrage * @param amount Amount to borrow * @param targetDex DEX to arbitrage against * @param expectedProfit Expected profit from the arbitrage */ function executeSmartArbitrage( address glue, address collateral, uint256 amount, address targetDex, uint256 expectedProfit ) external onlyOwner { // Plan the arbitrage first (bool feasible, , , uint256 minimumProfitNeeded) = this.planArbitrage(glue, collateral, amount); require(feasible, "Arbitrage not feasible - insufficient liquidity"); require(expectedProfit >= minimumProfitNeeded, "Expected profit too low"); // Execute the arbitrage bytes memory strategyData = abi.encode("dex_arbitrage", targetDex, expectedProfit); require(_requestFlashLoan(glue, collateral, amount, strategyData), "Flash loan request failed"); } /** * @notice Emergency function to withdraw stuck tokens * @param token Token to withdraw * @param amount Amount to withdraw */ function emergencyWithdraw(address token, uint256 amount) external onlyOwner { require(!this.isFlashLoanActive(), "Cannot withdraw during flash loan"); if (token == ETH_ADDRESS) { payable(owner).transfer(amount); } else { // Transfer ERC20 token (bool success, bytes memory data) = token.call( abi.encodeWithSelector(0xa9059cbb, owner, amount) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "Transfer failed"); } } /** * @notice Allow contract to receive ETH */ receive() external payable {} /** * @notice Transfer ownership to new owner * @param newOwner Address of the new owner */ function transferOwnership(address newOwner) external onlyOwner { require(newOwner != address(0), "Invalid address"); owner = newOwner; } }