diff --git a/.gitmodules b/.gitmodules index bd8a6ae..24209c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/v3-core"] path = lib/v3-core url = https://github.com/uniswap/v3-core +[submodule "lib/chainlink-brownie-contracts"] + path = lib/chainlink-brownie-contracts + url = https://github.com/smartcontractkit/chainlink-brownie-contracts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1797f5f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "solidity.compileUsingRemoteVersion": "v0.8.18+commit.87f61d96" +} \ No newline at end of file diff --git a/foundry.lock b/foundry.lock index 214da2c..d1fee9e 100644 --- a/foundry.lock +++ b/foundry.lock @@ -2,6 +2,12 @@ "lib/balancer-v2-monorepo": { "rev": "36d282374b457dddea828be7884ee0d185db06ba" }, + "lib/chainlink-brownie-contracts": { + "tag": { + "name": "1.3.0", + "rev": "5cb41fbc9b525338b6098da5ea7dd0b7e92f89e4" + } + }, "lib/forge-std": { "rev": "b93cf4bc34ff214c099dc970b153f85ade8c9f66" }, diff --git a/foundry.toml b/foundry.toml index d923a7e..9209be5 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,11 +2,15 @@ src = "src" out = "out" libs = ["lib"] +solc_version = "0.8.18" +via_ir = false + remappings = [ "forge-std/=lib/forge-std/src/", - "@balancer/balancer-v2-monorepo/pkg/=lib/balancer-v2-monorepo/pkg/", - "@openzeppelin/openzeppelin-contracts/contracts/=lib/openzeppelin-contracts/contracts/", - "@uniswap/v3-periphery/contracts/interfaces=lib/v3-periphery/contracts/interfaces", + "@balancer-labs/v2-interfaces/=lib/balancer-v2-monorepo/pkg/interfaces/", + "@balancer-labs/v2-vault/=lib/balancer-v2-monorepo/pkg/vault/", + "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", + "@uniswap/v3-periphery/contracts/interfaces/=lib/v3-periphery/contracts/interfaces/", "@uniswap/v3-core/contracts/interfaces/=lib/v3-core/contracts/interfaces/", -] -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + "@chainlink/contracts/=lib/chainlink-brownie-contracts/contracts/", +] \ No newline at end of file diff --git a/lib/chainlink-brownie-contracts b/lib/chainlink-brownie-contracts new file mode 160000 index 0000000..5cb41fb --- /dev/null +++ b/lib/chainlink-brownie-contracts @@ -0,0 +1 @@ +Subproject commit 5cb41fbc9b525338b6098da5ea7dd0b7e92f89e4 diff --git a/script/DeployArbitrage.s.sol b/script/DeployArbitrage.s.sol index 6180e20..0b35e7f 100644 --- a/script/DeployArbitrage.s.sol +++ b/script/DeployArbitrage.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import {Script, console2} from "forge-std/Script.sol"; -import {FlashArbitrage} from "../src/FlashArbitrage.sol"; +import {Arbitrage} from "../src/FlashArbitrage.sol"; import {HelperConfig} from "../script/HelperConfig.s.sol"; /** @@ -32,7 +32,7 @@ contract DeployArbitrage is Script { /// @notice The deployed FlashArbitrage contract instance /// @dev Main arbitrage contract that will be deployed and configured - FlashArbitrage public flashArbitrage; + Arbitrage public flashArbitrage; ////////////////////////////////////////////////////////////// // FUNCTIONS // @@ -46,7 +46,7 @@ contract DeployArbitrage is Script { */ function setUp() public { helperConfig = new HelperConfig(); - modeConfig = helperConfig.getSepoliaETHConfig(); + SepoliaConfig = helperConfig.getSepoliaETHConfig(); } /** @@ -58,12 +58,12 @@ contract DeployArbitrage is Script { * @custom:gas Consider gas optimization and limit settings for mainnet deployment * @custom:network Verify network configuration before deployment to avoid wrong network deployment */ - function run() public returns (FlashArbitrage) { + function run() public returns (Arbitrage) { // Start broadcasting transactions - required for actual deployment vm.startBroadcast(); // Deploy the FlashArbitrage contract with default constructor parameters - flashArbitrage = new FlashArbitrage(); + flashArbitrage = new Arbitrage(); // Stop broadcasting transactions vm.stopBroadcast(); diff --git a/script/PriceManipulation.s.sol b/script/PriceManipulation.s.sol index 015b777..4007b4b 100644 --- a/script/PriceManipulation.s.sol +++ b/script/PriceManipulation.s.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.18; import {Script, console} from "forge-std/Script.sol"; import {HelperConfig} from "./HelperConfig.s.sol"; -import {IERC20} from "@openzeppelin/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol"; diff --git a/script/Swap.s.sol b/script/Swap.s.sol index e4daeda..75aa78d 100644 --- a/script/Swap.s.sol +++ b/script/Swap.s.sol @@ -2,15 +2,15 @@ pragma solidity ^0.8.13; import {Script, console} from "forge-std/Script.sol"; -import {FlashArbitrage} from "../src/FlashArbitrage.sol"; +import {Arbitrage} from "../src/FlashArbitrage.sol"; import {DeployArbitrage} from "../script/DeployArbitrage.s.sol"; import {HelperConfig} from "../script/HelperConfig.s.sol"; import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol"; -import {IERC20} from "@openzeppelin/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Swap is Script { - FlashArbitrage public arbitrage; + Arbitrage public arbitrage; HelperConfig public helperConfig; HelperConfig.NetworkConfig currentConfig; diff --git a/src/FlashArbitrageV2.sol b/src/FlashArbitrageV2.sol index 77c6f36..daf34b0 100644 --- a/src/FlashArbitrageV2.sol +++ b/src/FlashArbitrageV2.sol @@ -8,8 +8,6 @@ import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.so import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title ImprovedFlashArbitrage @@ -32,8 +30,6 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; * @custom:optimization Gas-optimized storage layout and execution paths */ contract ImprovedFlashArbitrage is IFlashLoanRecipient, ReentrancyGuard, Ownable, Pausable { - using SafeERC20 for IERC20; - ////////////////////////////////////////////////////////////// // CONSTANTS // ////////////////////////////////////////////////////////////// @@ -148,6 +144,12 @@ contract ImprovedFlashArbitrage is IFlashLoanRecipient, ReentrancyGuard, Ownable /// @param unlockTime Timestamp when withdrawal becomes available event EmergencyWithdrawalInitiated(uint256 unlockTime); + /// @notice Emitted when emergency withdrawal is executed + /// @param token Token address withdrawn + /// @param amount Amount withdrawn + /// @param recipient Recipient address + event EmergencyWithdrawal(address indexed token, uint256 amount, address indexed recipient); + ////////////////////////////////////////////////////////////// // MODIFIERS // ////////////////////////////////////////////////////////////// @@ -267,12 +269,12 @@ contract ImprovedFlashArbitrage is IFlashLoanRecipient, ReentrancyGuard, Ownable require(finalBalance >= initialBalance + expectedMinOut, "Insufficient profit realized"); // Repay flash loan - tokens[0].safeTransfer(address(VAULT), flashAmount); + tokens[0].transfer(address(VAULT), flashAmount); // Calculate and transfer profit uint256 profit = finalBalance - initialBalance - flashAmount; if (profit > 0) { - tokens[0].safeTransfer(profitRecipient, profit); + tokens[0].transfer(profitRecipient, profit); } // Emit success event @@ -336,7 +338,7 @@ contract ImprovedFlashArbitrage is IFlashLoanRecipient, ReentrancyGuard, Ownable require(withdrawAmount <= balance, "Insufficient balance"); - tokenContract.safeTransfer(profitRecipient, withdrawAmount); + tokenContract.transfer(profitRecipient, withdrawAmount); emit EmergencyWithdrawal(token, withdrawAmount, profitRecipient); } @@ -401,7 +403,7 @@ contract ImprovedFlashArbitrage is IFlashLoanRecipient, ReentrancyGuard, Ownable IERC20 tokenInContract = IERC20(tokenIn); // Approve tokens for swap - tokenInContract.safeApprove(router, amountIn); + tokenInContract.approve(router, amountIn); // Calculate minimum output with slippage protection uint256 minAmountOut = amountIn - ((amountIn * slippageBps) / MAX_BPS); @@ -422,7 +424,7 @@ contract ImprovedFlashArbitrage is IFlashLoanRecipient, ReentrancyGuard, Ownable amountOut = ISwapRouter(router).exactInputSingle(swapParams); // Reset approval for security - tokenInContract.safeApprove(router, 0); + tokenInContract.approve(router, 0); } /// @notice Updates contract statistics after successful arbitrage diff --git a/src/FlashArbitrageV3.sol b/src/FlashArbitrageV3.sol index 9554af5..edf3150 100644 --- a/src/FlashArbitrageV3.sol +++ b/src/FlashArbitrageV3.sol @@ -8,9 +8,7 @@ import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.so import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol"; /** * @title ImprovedFlashArbitrage V3 @@ -34,8 +32,6 @@ import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; * @custom:optimization Advanced routing and gas optimization strategies */ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownable, Pausable { - using SafeERC20 for IERC20; - ////////////////////////////////////////////////////////////// // CONSTANTS // ////////////////////////////////////////////////////////////// @@ -111,6 +107,15 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab bytes[] extraData; // For protocol-specific parameters } + /// @notice Parameters for advanced arbitrage execution + struct ArbitrageParamsV3 { + MultiFlashParams flashParams; + ArbitrageRoute buyRoute; + ArbitrageRoute sellRoute; + uint256 expectedProfit; + uint256 maxExecutionTime; + } + /// @notice Enhanced statistics with volatility tracking struct StatisticsV3 { uint256 totalTrades; @@ -155,21 +160,36 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab /// @notice Enhanced contract statistics StatisticsV3 public stats; + /// @notice Circuit breaker state + CircuitBreaker public circuitBreaker; + /// @notice Mapping of authorized addresses mapping(address => bool) public authorizedCallers; + /// @notice Tracks last execution block for MEV protection + mapping(address => uint256) public lastExecutionBlock; + /// @notice Price feed configurations by token mapping(address => PriceFeedConfig) public priceFeeds; /// @notice Supported DEX routers by protocol mapping(DexProtocol => address[]) public dexRouters; + /// @notice Quoter addresses by protocol + mapping(DexProtocol => address) public quoters; + /// @notice Failed route tracking for optimization mapping(bytes32 => uint256) public failedRoutes; /// @notice Maximum gas price for execution uint256 public maxGasPrice = 100 gwei; + /// @notice Dynamic profit multiplier for minimum profit calculation + uint256 public dynamicProfitMultiplier = BASE_MIN_PROFIT_BPS; + + /// @notice Profit sharing configuration array + ProfitSharing[] public profitSharings; + ////////////////////////////////////////////////////////////// // EVENTS // ////////////////////////////////////////////////////////////// @@ -337,7 +357,7 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab // Repay flash loans and calculate profits uint256 totalProfit = 0; for (uint256 i = 0; i < tokens.length; i++) { - tokens[i].safeTransfer(address(VAULT), amounts[i] + feeAmounts[i]); + tokens[i].transfer(address(VAULT), amounts[i] + feeAmounts[i]); uint256 finalBalance = tokens[i].balanceOf(address(this)); if (finalBalance > initialBalances[i]) { @@ -523,7 +543,7 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab uint24 fee, uint256 minAmountOut ) internal returns (uint256 amountOut) { - IERC20(tokenIn).safeApprove(router, amountIn); + IERC20(tokenIn).approve(router, amountIn); ISwapRouter.ExactInputSingleParams memory swapParams = ISwapRouter.ExactInputSingleParams({ tokenIn: tokenIn, @@ -537,7 +557,7 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab }); amountOut = ISwapRouter(router).exactInputSingle(swapParams); - IERC20(tokenIn).safeApprove(router, 0); + IERC20(tokenIn).approve(router, 0); } /// @notice Distribute profits according to sharing configuration @@ -547,7 +567,7 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab for (uint256 i = 0; i < profitSharings.length; i++) { uint256 share = (totalProfit * profitSharings[i].basisPoints) / MAX_BPS; if (share > 0) { - IERC20(token).safeTransfer(profitSharings[i].recipient, share); + IERC20(token).transfer(profitSharings[i].recipient, share); distributed += share; emit ProfitShared(profitSharings[i].recipient, share, profitSharings[i].basisPoints); @@ -557,7 +577,7 @@ contract ImprovedFlashArbitrageV3 is IFlashLoanRecipient, ReentrancyGuard, Ownab // Send remainder to owner uint256 remainder = totalProfit - distributed; if (remainder > 0) { - IERC20(token).safeTransfer(owner(), remainder); + IERC20(token).transfer(owner(), remainder); } } diff --git a/test/fork/PriceManipulationTest.t.sol b/test/fork/PriceManipulationTest.t.sol index 8574de6..0acaa2e 100644 --- a/test/fork/PriceManipulationTest.t.sol +++ b/test/fork/PriceManipulationTest.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.18; import {Test, console} from "forge-std/Test.sol"; import {HelperConfig} from "../../script/HelperConfig.s.sol"; -import {IERC20} from "@openzeppelin/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol"; diff --git a/test/unit/ArbitrageTest.t.sol b/test/unit/ArbitrageTest.t.sol index d85ba48..4653085 100644 --- a/test/unit/ArbitrageTest.t.sol +++ b/test/unit/ArbitrageTest.t.sol @@ -3,13 +3,13 @@ pragma solidity 0.8.18; import {Test, console} from "forge-std/Test.sol"; import {StdUtils} from "forge-std/StdUtils.sol"; -import {FlashArbitrage} from "../../src/FlashArbitrage.sol"; +import {Arbitrage} from "../../src/FlashArbitrage.sol"; import {HelperConfig} from "../../script/HelperConfig.s.sol"; -import {IERC20} from "@openzeppelin/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; contract ArbitrageTest is Test { - FlashArbitrage public arbitrage; + Arbitrage public arbitrage; HelperConfig public helperConfig; HelperConfig.ForkNetworkConfig public networkConfig; @@ -28,7 +28,7 @@ contract ArbitrageTest is Test { baseSepoliaFork = vm.createSelectFork(ETH_SEPOLIA_RPC_URL); vm.startPrank(owner); - arbitrage = new FlashArbitrage(); + arbitrage = new Arbitrage(); vm.stopPrank(); deal(networkConfig.usdc, owner, 69);