Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"arbitrageEthMgs.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.19;\r
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";\r
import "@openzeppelin/contracts/security/Pausable.sol";\r
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";\r
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";\r
interface IUniswapV2Router02 {\r
function factory() external pure returns (address);\r
function WETH() external pure returns (address);\r
function swapExactTokensForTokens(uint amountIn,uint amountOutMin,address[] calldata path,address to,uint deadline) external returns (uint[] memory amounts);\r
function getAmountsOut(uint amountIn,address[] calldata path) external view returns (uint[] memory amounts);\r
}\r
interface IUniswapV3Router {\r
struct ExactInputSingleParams {address tokenIn;address tokenOut;uint24 fee;address recipient;uint256 deadline;uint256 amountIn;uint256 amountOutMinimum;uint160 sqrtPriceLimitX96;}\r
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);\r
}\r
interface IQuoterV2 {\r
function quoteExactInputSingle(address tokenIn,address tokenOut,uint24 fee,uint256 amountIn,uint160 sqrtPriceLimitX96) external view returns (uint256 amountOut,uint160 sqrtPriceX96After,uint32 initializedTicksCrossed,uint256 gasEstimate);\r
}\r
interface IAsset {}\r
interface IBalancerVault {\r
enum SwapKind { GIVEN_IN, GIVEN_OUT }\r
struct SingleSwap {bytes32 poolId;SwapKind kind;address assetIn;address assetOut;uint256 amount;bytes userData;}\r
struct FundManagement {address sender;bool fromInternalBalance;address payable recipient;bool toInternalBalance;}\r
struct BatchSwapStep {bytes32 poolId;uint256 assetInIndex;uint256 assetOutIndex;uint256 amount;bytes userData;}\r
function swap(SingleSwap memory singleSwap,FundManagement memory funds,uint256 limit,uint256 deadline) external payable returns (uint256);\r
function queryBatchSwap(SwapKind kind,BatchSwapStep[] memory swaps,IAsset[] memory assets,FundManagement memory funds) external view returns (int256[] memory);\r
}\r
interface IPool {\r
function flashLoanSimple(address receiverAddress,address asset,uint256 amount,bytes calldata params,uint16 referralCode) external;\r
}\r
interface IWETH {\r
function deposit() external payable;\r
function withdraw(uint256 amount) external;\r
}\r
contract arbitrageEthMgs is ReentrancyGuard, Pausable {\r
using SafeERC20 for IERC20;\r
error NotOperator();error InvalidTokens();error SameTokens();error UnsupportedTokens();error GasPriceTooHigh();error InvalidAmount();error InvalidGasLimit();error NoOpportunityFound();error OnlyWETHRoutes();error ProfitTooLow();error InvalidSlippageProtection();error NoETHSent();error InsufficientWETH();error ETHTransferFailed();error InvalidToken();error NoBalance();error InvalidDEX();error PremiumTooHigh();error InvalidGasPrice();error InvalidCaller();error InvalidInitiator();error InsufficientProfit();error NetProfitTooLow();error InvalidRecipient();error TradeSlippageExceeded();error InvalidBalancerPool();error ApproveResetFailed();error ApproveMaxFailed();error ETHOnlyFromWETH();error FunctionNotFound();error FlashLoanCooldownActive();error DailyFlashLoanLimitReached();\r
address public constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;\r
address public constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;\r
address public constant UNISWAP_V3_QUOTER = 0x61fFE014bA17989E743c5F6cB21bF9697530B21e;\r
address public constant SUSHISWAP_ROUTER = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;\r
address public constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;\r
address public constant AAVE_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;\r
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\r
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;\r
address public constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;\r
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;\r
address public constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;\r
address public immutable operator;\r
IPool public immutable aavePool;\r
IWETH public immutable weth;\r
IQuoterV2 public immutable quoterV2;\r
struct DEXInfo {address router;uint256 fee;uint8 dexType;}\r
struct ArbitrageParams {address tokenIn;address tokenOut;uint256 amountIn;uint256 minOutLeg1;uint256 minOutLeg2;uint8 dexIn;uint8 dexOut;uint24 uniV3FeeIn;uint24 uniV3FeeOut;bytes32 balancerPoolId;uint256 deadline;address recipient;uint256 minNetProfitWei;uint256 gasPriceWei;uint256 gasLimitEstimate;bool useOwnLiquidity;}\r
struct OpportunityData {uint8 dexIn;uint8 dexOut;uint24 uniV3FeeIn;uint24 uniV3FeeOut;uint256 expectedProfit;uint256 minOutLeg1;uint256 minOutLeg2;uint256 gasEstimate;bool isValid;}\r
DEXInfo[4] private dexConfigs;\r
mapping(bytes32 => bool) public validBalancerPools;\r
mapping(address => bool) public supportedTokens;\r
uint256 public constant MAX_SLIPPAGE = 300;\r
uint256 public constant MAX_TRADE_AMOUNT = 300 ether;\r
uint256 public constant MIN_PROFIT_THRESHOLD = 0.001 ether;\r
uint256 public constant MAX_GAS_PRICE_GWEI = 10; // Намаляваме от 30 на 10 Gwei\r
uint256 public constant MAX_GAS_PRICE_WEI = MAX_GAS_PRICE_GWEI * 1e9;\r
uint256 public constant GAS_OPTIMIZATION_THRESHOLD = 150000;\r
mapping(address => uint256) public lastFlashLoan;\r
mapping(address => uint256) public dailyFlashLoanCount;\r
mapping(address => uint256) public lastDailyReset;\r
uint256 public constant FLASH_LOAN_COOLDOWN = 30;\r
uint256 public constant DAILY_FLASH_LOAN_LIMIT = 100;\r
uint256 public totalTrades;\r
uint256 public totalVolume;\r
uint256 public totalProfit;\r
uint256 public gasPriceHintWei;\r
uint256 public flashPremiumBps = 5;\r
bool public gasOptimizationEnabled = true;\r
uint256 public maxGasPriceOverride = 0;\r
event ArbitrageExecuted(address indexed user,address indexed tokenIn,address indexed tokenOut,uint256 amountIn,uint256 profit,uint8 dexIn,uint8 dexOut,bool usedOwnLiquidity);\r
event OpportunityFound(address indexed tokenA,address indexed tokenB,uint256 amount,uint256 expectedProfit,uint8 dexIn,uint8 dexOut);\r
event CapitalDeposited(address indexed token, uint256 amount);\r
event CapitalWithdrawn(address indexed token, uint256 amount);\r
event TradeLegExecuted(uint8 indexed dexId,address indexed tokenIn,address tokenOut,uint256 amountIn,uint256 amountOut);\r
event GasPriceHintUpdated(uint256 newHintWei);\r
event FlashPremiumBpsUpdated(uint256 newBps);\r
event GasOptimizationToggled(bool enabled);\r
event MaxGasPriceUpdated(uint256 newMaxPrice);\r
constructor() {\r
operator = msg.sender;\r
aavePool = IPool(AAVE_POOL);\r
weth = IWETH(WETH);\r
quoterV2 = IQuoterV2(UNISWAP_V3_QUOTER);\r
gasPriceHintWei = 20000000000; // 20 Gwei - по-реалистично за текущите условия\r
dexConfigs[0] = DEXInfo({router: UNISWAP_V2_ROUTER,fee: 300,dexType: 0});\r
dexConfigs[1] = DEXInfo({router: SUSHISWAP_ROUTER,fee: 300,dexType: 2});\r
dexConfigs[2] = DEXInfo({router: UNISWAP_V3_ROUTER,fee: 500,dexType: 1});\r
dexConfigs[3] = DEXInfo({router: BALANCER_VAULT,fee: 100,dexType: 3});\r
validBalancerPools[0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014] = true;\r
validBalancerPools[0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019] = true;\r
validBalancerPools[0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a] = true;\r
supportedTokens[WETH] = true;supportedTokens[USDC] = true;supportedTokens[USDT] = true;supportedTokens[DAI] = true;supportedTokens[WBTC] = true;\r
}\r
modifier onlyOperator() {if (msg.sender != operator) revert NotOperator();_;}\r
modifier validTokens(address tokenA, address tokenB) {if (tokenA == address(0) || tokenB == address(0)) revert InvalidTokens();if (tokenA == tokenB) revert SameTokens();if (!supportedTokens[tokenA] || !supportedTokens[tokenB]) revert UnsupportedTokens();_;}\r
modifier rateLimited() {if (block.timestamp > lastDailyReset[msg.sender] + 1 days) {dailyFlashLoanCount[msg.sender] = 0;lastDailyReset[msg.sender] = block.timestamp;}_;}\r
modifier gasPriceCheck() {if (gasOptimizationEnabled) {uint256 maxAllowed = maxGasPriceOverride > 0 ? maxGasPriceOverride : MAX_GAS_PRICE_WEI;if (tx.gasprice > maxAllowed) revert GasPriceTooHigh();}_;}\r
function StartNative(uint256 maxAmount) external view returns (OpportunityData memory bestOpportunity,address tokenA,address tokenB,uint256 amount) {return _findBestNativeOpportunity(maxAmount);}\r
function _findSimpleOpportunity(address tokenA, address tokenB, uint256 amountIn, uint24 feeIn, uint24 feeOut) internal view returns (OpportunityData memory bestOpportunity) {\r
uint256 maxProfit = 0;\r
(uint256 profit1, uint256 minOut1_1, uint256 minOut2_1) = _calculateProfitForRoute(tokenA, tokenB, amountIn, 0, 2, feeIn, feeOut);\r
if (profit1 > maxProfit) {maxProfit = profit1;bestOpportunity = OpportunityData({dexIn: 0, dexOut: 2, uniV3FeeIn: feeIn, uniV3FeeOut: feeOut,expectedProfit: profit1, minOutLeg1: minOut1_1, minOutLeg2: minOut2_1,gasEstimate: _estimateGasCost(0, 2), isValid: profit1 > 0});}\r
(uint256 profit2, uint256 minOut1_2, uint256 minOut2_2) = _calculateProfitForRoute(tokenA, tokenB, amountIn, 2, 0, feeIn, feeOut);\r
if (profit2 > maxProfit) {bestOpportunity = OpportunityData({dexIn: 2, dexOut: 0, uniV3FeeIn: feeIn, uniV3FeeOut: feeOut,expectedProfit: profit2, minOutLeg1: minOut1_2, minOutLeg2: minOut2_2,gasEstimate: _estimateGasCost(2, 0), isValid: profit2 > 0});}\r
}\r
function StartNativeLite(uint256 maxAmount,address tokenB,uint24 feeIn,uint24 feeOut) external view returns (OpportunityData memory bestOpportunity,address tokenA,address tokenBOut,uint256 amount) {\r
if (maxAmount == 0 || maxAmount > MAX_TRADE_AMOUNT) revert InvalidAmount();\r
if (!supportedTokens[tokenB]) revert UnsupportedTokens();\r
tokenA = WETH;tokenBOut = tokenB;amount = maxAmount / 2;\r
if (amount == 0) return (bestOpportunity, tokenA, tokenBOut, 0);\r
bestOpportunity = _findSimpleOpportunity(tokenA, tokenBOut, amount, feeIn, feeOut);\r
}\r
function _findBestNativeOpportunity(uint256 maxAmount) internal view returns (OpportunityData memory bestOpportunity,address tokenA,address tokenB,uint256 amount) {\r
if (maxAmount == 0 || maxAmount > MAX_TRADE_AMOUNT) revert InvalidAmount();\r
address tokenIn = WETH;\r
address[] memory tokensOut = new address[](3);\r
tokensOut[0] = USDC;tokensOut[1] = USDT;tokensOut[2] = DAI;\r
uint24[1] memory v3Fees = [uint24(3000)];\r
uint256 maxProfit = 0;\r
for (uint j = 0; j < tokensOut.length; j++) {\r
address tokenOut = tokensOut[j];\r
uint256[] memory amounts = new uint256[](1);\r
amounts[0] = maxAmount / 2;\r
for (uint k = 0; k < amounts.length; k++) {\r
if (amounts[k] == 0) continue;\r
for (uint feeIn = 0; feeIn < v3Fees.length; feeIn++) {\r
for (uint feeOut = 0; feeOut < v3Fees.length; feeOut++) {\r
OpportunityData memory opp = _findOpportunity(tokenIn,tokenOut,amounts[k],v3Fees[feeIn],v3Fees[feeOut]);\r
if (opp.isValid && opp.expectedProfit > maxProfit) {maxProfit = opp.expectedProfit;bestOpportunity = opp;tokenA = tokenIn;tokenB = tokenOut;amount = amounts[k];}\r
}\r
}\r
}\r
}\r
}\r
function AutoArbitrage(uint256 maxAmount, uint256 estimatedGasLimit) external onlyOperator nonReentrant whenNotPaused {\r
if (maxAmount == 0 || maxAmount > MAX_TRADE_AMOUNT) revert InvalidAmount();\r
if (estimatedGasLimit == 0 || estimatedGasLimit >= 50000000) revert InvalidGasLimit();\r
(OpportunityData memory opportunity,address tokenA,address tokenB,uint256 bestAmount) = _findBestNativeOpportunity(maxAmount);\r
if (!opportunity.isValid) revert NoOpportunityFound();\r
if (tokenA != WETH) revert OnlyWETHRoutes();\r
if (opportunity.expectedProfit < MIN_PROFIT_THRESHOLD / 5) revert ProfitTooLow();\r
if (opportunity.minOutLeg1 == 0 || opportunity.minOutLeg2 == 0) revert InvalidSlippageProtection();\r
if (gasOptimizationEnabled && tx.gasprice > MAX_GAS_PRICE_WEI * 5) {revert GasPriceTooHigh();}\r
uint256 gasPrice = tx.gasprice;\r
uint256 finalGasLimit = estimatedGasLimit > opportunity.gasEstimate ? estimatedGasLimit : opportunity.gasEstimate;\r
ArbitrageParams memory params = ArbitrageParams({tokenIn: tokenA,tokenOut: tokenB,amountIn: bestAmount,minOutLeg1: opportunity.minOutLeg1,minOutLeg2: opportunity.minOutLeg2,dexIn: opportunity.dexIn,dexOut: opportunity.dexOut,uniV3FeeIn: opportunity.uniV3FeeIn,uniV3FeeOut: opportunity.uniV3FeeOut,balancerPoolId: _getBalancerPoolId(tokenA, tokenB),deadline: block.timestamp + 300,recipient: operator,minNetProfitWei: MIN_PROFIT_THRESHOLD / 5,gasPriceWei: gasPrice,gasLimitEstimate: finalGasLimit,useOwnLiquidity: IERC20(tokenA).balanceOf(address(this)) >= bestAmount});\r
_executeArbWithOptionalFlash(params);\r
}\r
function AutoArbitrageLite(uint256 maxAmount,address tokenB,uint24 feeIn,uint24 feeOut,uint256 estimatedGasLimit) external onlyOperator nonReentrant whenNotPaused {\r
if (maxAmount == 0 || maxAmount > MAX_TRADE_AMOUNT) revert InvalidAmount();\r
if (estimatedGasLimit == 0 || estimatedGasLimit >= 50000000) revert InvalidGasLimit();\r
if (!supportedTokens[tokenB]) revert UnsupportedTokens();\r
address tokenA = WETH;\r
uint256[2] memory samples = [maxAmount / 4, maxAmount / 2];\r
OpportunityData memory best;uint256 bestAmount = 0;uint256 maxProfit = 0;\r
for (uint256 i = 0; i < samples.length; i++) {\r
uint256 amt = samples[i];\r
if (amt == 0) continue;\r
OpportunityData memory opp = _findOpportunity(tokenA, tokenB, amt, feeIn, feeOut);\r
if (opp.isValid && opp.expectedProfit > maxProfit) {maxProfit = opp.expectedProfit;best = opp;bestAmount = amt;}\r
}\r
if (!best.isValid) revert NoOpportunityFound();\r
if (tokenA != WETH) revert OnlyWETHRoutes();\r
if (best.expectedProfit < MIN_PROFIT_THRESHOLD / 5) revert ProfitTooLow();\r
if (best.minOutLeg1 == 0 || best.minOutLeg2 == 0) revert InvalidSlippageProtection();\r
if (gasOptimizationEnabled && tx.gasprice > MAX_GAS_PRICE_WEI * 5) {revert GasPriceTooHigh();}\r
uint256 gasPrice = tx.gasprice;\r
uint256 finalGasLimit = estimatedGasLimit > best.gasEstimate ? estimatedGasLimit : best.gasEstimate;\r
ArbitrageParams memory params = ArbitrageParams({tokenIn: tokenA,tokenOut: tokenB,amountIn: bestAmount,minOutLeg1: best.minOutLeg1,minOutLeg2: best.minOutLeg2,dexIn: best.dexIn,dexOut: best.dexOut,uniV3FeeIn: best.uniV3FeeIn,uniV3FeeOut: best.uniV3FeeOut,balancerPoolId: _getBalancerPoolId(tokenA, tokenB),deadline: block.timestamp + 300,recipient: operator,minNetProfitWei: MIN_PROFIT_THRESHOLD / 5,gasPriceWei: gasPrice,gasLimitEstimate: finalGasLimit,useOwnLiquidity: IERC20(tokenA).balanceOf(address(this)) >= bestAmount});\r
_executeArbWithOptionalFlash(params);\r
}\r
function depositETH() external payable onlyOperator {if (msg.value == 0) revert NoETHSent();weth.deposit{value: msg.value}();emit CapitalDeposited(WETH, msg.value);}\r
function withdrawETH(uint256 amount) external onlyOperator {if (amount == 0) revert InvalidAmount();uint256 wethBalance = IERC20(WETH).balanceOf(address(this));if (wethBalance < amount) revert InsufficientWETH();weth.withdraw(amount);(bool success,) = msg.sender.call{value: amount}("");if (!success) revert ETHTransferFailed();emit CapitalWithdrawn(WETH, amount);}\r
function sweep(address token) external onlyOperator {if (token == address(0)) revert InvalidToken();uint256 balance = IERC20(token).balanceOf(address(this));if (balance == 0) revert NoBalance();IERC20(token).safeTransfer(msg.sender, balance);emit CapitalWithdrawn(token, balance);}\r
function pause() external onlyOperator {_pause();}\r
function unpause() external onlyOperator {_unpause();}\r
function setGasPriceHint(uint256 _gasPrice) external onlyOperator {gasPriceHintWei = _gasPrice;emit GasPriceHintUpdated(_gasPrice);}\r
function setFlashPremiumBps(uint256 _bps) external onlyOperator {if (_bps >= 100) revert PremiumTooHigh();flashPremiumBps = _bps;emit FlashPremiumBpsUpdated(_bps);}\r
function setGasOptimization(bool _enabled) external onlyOperator {gasOptimizationEnabled = _enabled;emit GasOptimizationToggled(_enabled);}\r
function setMaxGasPrice(uint256 _maxGasPriceGwei) external onlyOperator {if (_maxGasPriceGwei == 0 || _maxGasPriceGwei > 100) revert InvalidGasPrice();maxGasPriceOverride = _maxGasPriceGwei * 1e9;emit MaxGasPriceUpdated(maxGasPriceOverride);}\r
function getCurrentGasInfo() external view returns (uint256 currentGasPrice,uint256 maxAllowedGasPrice,bool gasOptimizationActive,bool canExecute) {\r
// Винаги използваме реалната gas цена от транзакцията\r
currentGasPrice = tx.gasprice > 0 ? tx.gasprice : 2000000000; // 2 Gwei по подразбиране\r
maxAllowedGasPrice = maxGasPriceOverride > 0 ? maxGasPriceOverride : MAX_GAS_PRICE_WEI;\r
gasOptimizationActive = gasOptimizationEnabled;\r
canExecute = !gasOptimizationEnabled || currentGasPrice <= maxAllowedGasPrice;\r
}\r
function checkGasPriceBeforeTrade() external view returns (bool canTrade) {if (!gasOptimizationEnabled) return true;uint256 maxAllowed = maxGasPriceOverride > 0 ? maxGasPriceOverride : MAX_GAS_PRICE_WEI;return tx.gasprice <= maxAllowed;}\r
function _findOpportunity(address tokenA,address tokenB,uint256 amountIn,uint24 v3FeeIn,uint24 v3FeeOut) internal view returns (OpportunityData memory bestOpportunity) {\r
(uint256 profit, uint256 minOut1, uint256 minOut2) = _calculateProfitForRoute(tokenA, tokenB, amountIn, 0, 2, v3FeeIn, v3FeeOut);\r
if (profit > 0) {bestOpportunity = OpportunityData({dexIn: 0,dexOut: 2,uniV3FeeIn: v3FeeIn,uniV3FeeOut: v3FeeOut,expectedProfit: profit,minOutLeg1: minOut1,minOutLeg2: minOut2,gasEstimate: _estimateGasCost(0, 2),isValid: true});}\r
}\r
function _calculateProfitForRoute(address tokenA,address tokenB,uint256 amountIn,uint8 dexIn,uint8 dexOut,uint24 v3FeeIn,uint24 v3FeeOut) internal view returns (uint256 profit, uint256 minOut1, uint256 minOut2) {\r
// Винаги използваме реалната gas цена от транзакцията\r
uint256 effectiveGasPrice = tx.gasprice > 0 ? tx.gasprice : 2000000000; // 2 Gwei по подразбиране\r
if (gasOptimizationEnabled && effectiveGasPrice > MAX_GAS_PRICE_WEI) {return (0, 0, 0);}\r
// Намаляваме минималния размер на търговията за по-ниски gas цени\r
if (amountIn < effectiveGasPrice * 200000) return (0, 0, 0);\r
uint256 intermediateAmount = _getExpectedAmountOut(tokenA, tokenB, amountIn, dexIn, v3FeeIn);\r
if (intermediateAmount == 0) return (0, 0, 0);\r
minOut1 = intermediateAmount * 9700 / 10000;\r
uint256 finalAmount = _getExpectedAmountOut(tokenB, tokenA, intermediateAmount, dexOut, v3FeeOut);\r
if (finalAmount <= amountIn) return (0, 0, 0);\r
minOut2 = finalAmount * 9700 / 10000;\r
uint256 grossProfit = finalAmount - amountIn;\r
// Намаляваме gas разходите за по-реалистични изчисления\r
uint256 gasCost = effectiveGasPrice * _estimateGasCost(dexIn, dexOut);\r
uint256 flashPremium = (amountIn * flashPremiumBps) / 10000;\r
uint256 minProfit = MIN_PROFIT_THRESHOLD / 10; // Намаляваме минималната печалба\r
uint256 totalCosts = gasCost + flashPremium + minProfit;\r
profit = grossProfit > totalCosts ? grossProfit - totalCosts : 0;\r
}\r
function _getExpectedAmountOut(address tokenIn,address tokenOut,uint256 amountIn,uint8 dexId,uint24 v3Fee) internal view returns (uint256) {\r
if (dexId >= 4) {return 0;}\r
DEXInfo memory dex = dexConfigs[dexId];\r
if (dex.dexType == 0 || dex.dexType == 2) {\r
address[] memory path = new address[](2);path[0] = tokenIn;path[1] = tokenOut;\r
try IUniswapV2Router02(dex.router).getAmountsOut(amountIn, path) returns (uint[] memory amounts) {return amounts[1];} catch {return 0;}\r
} else if (dex.dexType == 1) {\r
try quoterV2.quoteExactInputSingle(tokenIn,tokenOut,v3Fee,amountIn,0) returns (uint256 amountOut, uint160, uint32, uint256) {return amountOut;} catch {return 0;}\r
} else if (dex.dexType == 3) {\r
bytes32 poolId = _getBalancerPoolId(tokenIn, tokenOut);\r
if (poolId == bytes32(0)) {return 0;}\r
IBalancerVault.BatchSwapStep[] memory swaps = new IBalancerVault.BatchSwapStep[](1);\r
swaps[0] = IBalancerVault.BatchSwapStep({poolId: poolId,assetInIndex: 0,assetOutIndex: 1,amount: amountIn,userData: ""});\r
IAsset[] memory assets = new IAsset[](2);assets[0] = IAsset(tokenIn);assets[1] = IAsset(tokenOut);\r
IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({sender: address(this),fromInternalBalance: false,recipient: payable(address(this)),toInternalBalance: false});\r
try IBalancerVault(BALANCER_VAULT).queryBatchSwap(IBalancerVault.SwapKind.GIVEN_IN,swaps,assets,funds) returns (int256[] memory deltas) {return deltas[1] < 0 ? uint256(-deltas[1]) : 0;} catch {return 0;}\r
}\r
return 0;\r
}\r
function _estimateGasCost(uint8 dexIn, uint8 dexOut) internal view returns (uint256) {\r
// Намаляваме gas оценките за по-реалистични стойности\r
uint256 gasIn = 80000;uint256 gasOut = 80000;\r
if (dexConfigs[dexIn].dexType == 1) {gasIn = 100000;} // Uniswap V3\r
if (dexConfigs[dexOut].dexType == 1) {gasOut = 100000;}\r
if (dexConfigs[dexIn].dexType == 3) {gasIn = 120000;} // Balancer\r
if (dexConfigs[dexOut].dexType == 3) {gasOut = 120000;}\r
return gasIn + gasOut + 40000; // Намаляваме overhead-а\r
}\r
function _getBalancerPoolId(address tokenA, address tokenB) internal pure returns (bytes32) {\r
if ((tokenA == WETH && tokenB == USDC) || (tokenA == USDC && tokenB == WETH)) {return 0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019;}\r
if ((tokenA == WETH && tokenB == DAI) || (tokenA == DAI && tokenB == WETH)) {return 0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a;}\r
if ((tokenA == WETH && tokenB == WBTC) || (tokenA == WBTC && tokenB == WETH)) {return 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014;}\r
return bytes32(0);\r
}\r
function _executeArbWithOptionalFlash(ArbitrageParams memory params) internal {\r
bool hasOwnLiquidity = IERC20(params.tokenIn).balanceOf(address(this)) >= params.amountIn;\r
if (hasOwnLiquidity && params.useOwnLiquidity) {\r
uint256 balanceBefore = IERC20(params.tokenIn).balanceOf(address(this));\r
_executeArbitrageInternal(params);\r
uint256 balanceAfter = IERC20(params.tokenIn).balanceOf(address(this));\r
require(balanceAfter > balanceBefore, "No profit");\r
uint256 profit = balanceAfter - balanceBefore;\r
uint256 gasCost = params.gasPriceWei * params.gasLimitEstimate;\r
require(profit > gasCost + params.minNetProfitWei, "Net profit too low");\r
if (profit > 0) {IERC20(params.tokenIn).safeTransfer(params.recipient, profit);}\r
totalTrades++;totalVolume += params.amountIn;totalProfit += profit;\r
emit ArbitrageExecuted(params.recipient,params.tokenIn,params.tokenOut,params.amountIn,profit,params.dexIn,params.dexOut,true);\r
} else {_executeFlashLoanArbitrage(params);}\r
}\r
function _executeFlashLoanArbitrage(ArbitrageParams memory params) internal {\r
if (block.timestamp > lastDailyReset[msg.sender] + 1 days) {dailyFlashLoanCount[msg.sender] = 0;lastDailyReset[msg.sender] = block.timestamp;}\r
uint256 cooldownPeriod = msg.sender == operator ? 5 : FLASH_LOAN_COOLDOWN;\r
if (block.timestamp < lastFlashLoan[msg.sender] + cooldownPeriod) {revert FlashLoanCooldownActive();}\r
if (dailyFlashLoanCount[msg.sender] >= DAILY_FLASH_LOAN_LIMIT) {revert DailyFlashLoanLimitReached();}\r
lastFlashLoan[msg.sender] = block.timestamp;dailyFlashLoanCount[msg.sender]++;\r
bytes memory paramsData = abi.encode(params, msg.sender);\r
aavePool.flashLoanSimple(address(this),params.tokenIn,params.amountIn,paramsData,0);\r
}\r
function executeOperation(address asset,uint256 amount,uint256 premium,address initiator,bytes calldata params) external returns (bool) {\r
if (msg.sender != address(aavePool)) {revert InvalidCaller();}\r
if (initiator != address(this)) {revert InvalidInitiator();}\r
(ArbitrageParams memory arbParams, address originalUser) = abi.decode(params, (ArbitrageParams, address));\r
_executeArbitrageInternal(arbParams);\r
uint256 balanceAfter = IERC20(asset).balanceOf(address(this));\r
uint256 totalDebt = amount + premium;\r
if (balanceAfter < totalDebt) {revert InsufficientProfit();}\r
uint256 profit = balanceAfter - totalDebt;\r
uint256 gasCost = arbParams.gasPriceWei * arbParams.gasLimitEstimate;\r
if (profit < gasCost + arbParams.minNetProfitWei) {revert NetProfitTooLow();}\r
_approveMaxIfNeeded(asset, address(aavePool), totalDebt);\r
_finalizeArbitrage(asset, originalUser, profit, arbParams);\r
return true;\r
}\r
function _finalizeArbitrage(address asset,address user,uint256 profit,ArbitrageParams memory params) internal {\r
if (profit > 0) {IERC20(asset).safeTransfer(user, profit);}\r
totalTrades++;totalVolume += params.amountIn;totalProfit += profit;\r
emit ArbitrageExecuted(user,params.tokenIn,params.tokenOut,params.amountIn,profit,params.dexIn,params.dexOut,false);\r
}\r
function _executeArbitrageInternal(ArbitrageParams memory params) internal {\r
if (params.recipient == address(0)) {revert InvalidRecipient();}\r
uint256 intermediateAmount = _executeTrade(params.tokenIn,params.tokenOut,params.amountIn,params.minOutLeg1,params.dexIn,params.uniV3FeeIn,params.balancerPoolId,params.deadline);\r
_executeTrade(params.tokenOut,params.tokenIn,intermediateAmount,params.minOutLeg2,params.dexOut,params.uniV3FeeOut,params.balancerPoolId,params.deadline);\r
}\r
function _executeTrade(address tokenIn,address tokenOut,uint256 amountIn,uint256 minAmountOut,uint8 dexId,uint24 uniV3Fee,bytes32 balancerPoolId,uint256 deadline) internal returns (uint256 amountOut) {\r
DEXInfo memory dex = dexConfigs[dexId];\r
if (dex.dexType == 0 || dex.dexType == 2) {amountOut = _executeV2Trade(tokenIn, tokenOut, amountIn, minAmountOut, dex.router, deadline);} \r
else if (dex.dexType == 1) {amountOut = _executeV3Trade(tokenIn, tokenOut, amountIn, minAmountOut, uniV3Fee, deadline);} \r
else if (dex.dexType == 3) {amountOut = _executeBalancerTrade(tokenIn, tokenOut, amountIn, minAmountOut, balancerPoolId, deadline);}\r
emit TradeLegExecuted(dexId, tokenIn, tokenOut, amountIn, amountOut);\r
}\r
function _executeV2Trade(address tokenIn,address tokenOut,uint256 amountIn,uint256 minAmountOut,address router,uint256 deadline) internal returns (uint256) {\r
_approveMaxIfNeeded(tokenIn, router, amountIn);\r
address[] memory path = new address[](2);path[0] = tokenIn;path[1] = tokenOut;\r
uint256[] memory amounts = IUniswapV2Router02(router).swapExactTokensForTokens(amountIn,minAmountOut,path,address(this),deadline);\r
return amounts[1];\r
}\r
function _executeV3Trade(address tokenIn,address tokenOut,uint256 amountIn,uint256 minAmountOut,uint24 fee,uint256 deadline) internal returns (uint256) {\r
_approveMaxIfNeeded(tokenIn, UNISWAP_V3_ROUTER, amountIn);\r
IUniswapV3Router.ExactInputSingleParams memory params = IUniswapV3Router.ExactInputSingleParams({tokenIn: tokenIn,tokenOut: tokenOut,fee: fee,recipient: address(this),deadline: deadline,amountIn: amountIn,amountOutMinimum: minAmountOut,sqrtPriceLimitX96: 0});\r
return IUniswapV3Router(UNISWAP_V3_ROUTER).exactInputSingle(params);\r
}\r
function _executeBalancerTrade(address tokenIn,address tokenOut,uint256 amountIn,uint256 minAmountOut,bytes32 poolId,uint256 deadline) internal returns (uint256) {\r
if (!validBalancerPools[poolId]) {revert InvalidBalancerPool();}\r
_approveMaxIfNeeded(tokenIn, BALANCER_VAULT, amountIn);\r
IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap({poolId: poolId,kind: IBalancerVault.SwapKind.GIVEN_IN,assetIn: tokenIn,assetOut: tokenOut,amount: amountIn,userData: ""});\r
IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({sender: address(this),fromInternalBalance: false,recipient: payable(address(this)),toInternalBalance: false});\r
return IBalancerVault(BALANCER_VAULT).swap(singleSwap, funds, minAmountOut, deadline);\r
}\r
function _approveMaxIfNeeded(address token, address spender, uint256 amount) internal {\r
uint256 current = IERC20(token).allowance(address(this), spender);\r
if (current < amount) {IERC20(token).forceApprove(spender, 0);IERC20(token).forceApprove(spender, type(uint256).max);}\r
}\r
function GetStatus() external view returns (uint256 contractTotalTrades,uint256 contractTotalVolume,uint256 contractTotalProfit,bool canTrade,uint256 contractWETHBalance) {\r
contractTotalTrades = totalTrades;contractTotalVolume = totalVolume;contractTotalProfit = totalProfit;\r
uint256 timeSince = block.timestamp > lastFlashLoan[msg.sender] ? block.timestamp - lastFlashLoan[msg.sender] : 0;\r
uint256 cooldown = timeSince >= FLASH_LOAN_COOLDOWN ? 0 : FLASH_LOAN_COOLDOWN - timeSince;\r
canTrade = cooldown == 0 && dailyFlashLoanCount[msg.sender] < DAILY_FLASH_LOAN_LIMIT && !paused();\r
contractWETHBalance = IERC20(WETH).balanceOf(address(this));\r
}\r
function isTokenSupported(address token) external view returns (bool) {return supportedTokens[token];}\r
function getDEXInfo(uint8 dexId) external view returns (address router, uint256 fee, uint8 dexType) {if (dexId >= 4) {revert InvalidDEX();}DEXInfo memory dex = dexConfigs[dexId];return (dex.router, dex.fee, dex.dexType);}\r
function isValidBalancerPool(bytes32 poolId) external view returns (bool) {return validBalancerPools[poolId];}\r
function getUserCooldown(address user) external view returns (uint256) {uint256 timeSince = block.timestamp > lastFlashLoan[user] ? block.timestamp - lastFlashLoan[user] : 0;return timeSince >= FLASH_LOAN_COOLDOWN ? 0 : FLASH_LOAN_COOLDOWN - timeSince;}\r
function getOpportunityDetails(uint256 maxAmount) external view returns (bool opportunityFound,uint256 expectedProfit,uint256 currentThreshold,uint256 gasEstimate,bool gasOptimizationActive,uint256 currentGasPrice) {\r
(OpportunityData memory opportunity, , , ) = _findBestNativeOpportunity(maxAmount);\r
return (opportunity.isValid,opportunity.expectedProfit,MIN_PROFIT_THRESHOLD / 5,opportunity.gasEstimate,gasOptimizationEnabled,tx.gasprice);\r
}\r
function QuickCheck(uint256 maxAmount, address tokenB) external view returns (bool hasOpportunity, uint256 estimatedProfit) {\r
if (maxAmount == 0 || !supportedTokens[tokenB]) {return (false, 0);}\r
uint256 amt1 = _getExpectedAmountOut(WETH, tokenB, maxAmount/2, 0, 3000);\r
if (amt1 == 0) {return (false, 0);}\r
uint256 amt2 = _getExpectedAmountOut(tokenB, WETH, amt1, 2, 3000);\r
if (amt2 <= maxAmount/2) {return (false, 0);}\r
return (true, amt2 - maxAmount/2);\r
}\r
function StartNativeSimple(uint256 maxAmount, address tokenOut) external view returns (bool found, uint256 expectedProfit) {\r
if (maxAmount == 0 || maxAmount > MAX_TRADE_AMOUNT) {revert InvalidAmount();}\r
if (!supportedTokens[tokenOut]) {revert UnsupportedTokens();}\r
OpportunityData memory opp = _findOpportunity(WETH, tokenOut, maxAmount/2, 3000, 3000);\r
return (opp.isValid, opp.expectedProfit);\r
}\r
\r
// Нова функция за тестване на gas изчисленията\r
function testGasCalculation(uint256 amountIn, address tokenOut) external view returns (\r
uint256 currentGasPrice,\r
uint256 estimatedGasCost,\r
uint256 gasCostInWei,\r
uint256 minTradeSize,\r
bool canTrade\r
) {\r
currentGasPrice = tx.gasprice > 0 ? tx.gasprice : 2000000000;\r
estimatedGasCost = _estimateGasCost(0, 2);\r
gasCostInWei = currentGasPrice * estimatedGasCost;\r
minTradeSize = currentGasPrice * 200000;\r
canTrade = amountIn >= minTradeSize && currentGasPrice <= MAX_GAS_PRICE_WEI;\r
}\r
receive() external payable {if (msg.sender != WETH) {revert ETHOnlyFromWETH();}}\r
fallback() external payable {revert FunctionNotFound();}\r
}"
},
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"@openzeppelin/contracts/security/Pausable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
"
},
"@openzeppelin/contracts/security/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
"
},
"@openzeppelin/contracts/interfaces/IERC1363.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"@openzeppelin/contracts/interfaces/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";
"
},
"@openzeppelin/contracts/interfaces/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";
"
},
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 800
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-09-27 12:38:53
Comments
Log in to comment.
No comments yet.