I have ETH stuck in a smart contract that I deployed. The contract can receive ETH, but its withdrawal function doesn't work with ETH, only with ERC20 tokens. This leaves the ETH inaccessible
Details and research:
The contract is called SlippageBot, deployed on the Ethereum mainnet. It has functions for trading ERC20 tokens and withdrawing ERC20 tokens. It can receive ETH (has a receive() function) but no way to send ETH out. The contract doesn't have any upgrade mechanisms. I am the owner of the contract and have full access to owner functions.
What I've tried:
Considered creating a rescue contract to interact with SlippageBot, but realized it wouldn't work because SlippageBot can't trade or withdraw its ETH.
here is the relevant part of the code:
function withdraw(address token) external onlyOwner {
uint256 balance = IERC20(token).balanceOf(address(this));
require(balance > 0, "No funds to withdraw");
IERC20(token).transfer(owner, balance);
}
As you can see, this function only works with ERC20 tokens and not with ETH.
Here is the adress to the bot: 0xc346292e0797304B20898512CBFAF95b6D239c23
I'm looking for any possible solutions to recover the ETH from this contract. Has anyone encountered a similar issue or know of any advanced techniques to rescue ETH from a contract where the withdrawal function doesn't support ETH?
Any help or direction would be greatly appreciated. If more details about the contract are needed, I can provide them.
Entire code for those that want to see it:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract SlippageBot {
address public owner;
IUniswapV2Factory public uniswapFactory;
IUniswapV2Router02 public uniswapRouter;
uint256 public maxGasPrice = 100 * 1e9;// Maximum gas price for transactions
uint256 public stopLossThreshold = 5; // 5% stop loss
uint256 public profitThreshold = 1 ether; // Automatically withdraw profits above 1 ETH
uint256 public slippageTolerance = 3; // 3% default slippage tolerance
uint256 public cooldownPeriod = 5 minutes; // Cooldown period between trades
uint256 public lastTradeTime = 0; // Timestamp of the last trade
event NewPairDetected(address pair, address token0, address token1);
event TradeExecuted(address pair, uint256 amountIn, uint256 amountOut);
event ProfitTaken(uint256 amount);
event StopLossTriggered(uint256 amount);
event TradePaused(string reason);
modifier onlyOwner() {
require(msg.sender == owner, "Not authorized");
_;
}
modifier ensureCooldown() {
require(block.timestamp >= lastTradeTime + cooldownPeriod, "Cooldown period not yet elapsed");
_;
}
constructor(address _factory, address _router) public {
owner = msg.sender;
uniswapFactory = IUniswapV2Factory(_factory);
uniswapRouter = IUniswapV2Router02(_router);
}
// Monitor Uniswap Factory for newly created pairs
function monitorNewPairs() external onlyOwner {
uint256 allPairsLength = uniswapFactory.allPairsLength();
for (uint256 i = 0; i < allPairsLength; i++) {
address pair = uniswapFactory.allPairs(i);
IUniswapV2Pair liquidityPair = IUniswapV2Pair(pair);
address token0 = liquidityPair.token0();
address token1 = liquidityPair.token1();
emit NewPairDetected(pair, token0, token1);
}
}
// Check the liquidity of a pair
function checkLiquidity(address pair) public view returns (uint256) {
IUniswapV2Pair liquidityPair = IUniswapV2Pair(pair);
(uint112 reserve0, uint112 reserve1, ) = liquidityPair.getReserves();
return reserve0 + reserve1;
}
// Automatically adjust slippage tolerance based on recent outcomes
function adjustSlippage(uint256 lastTradeAmountIn, uint256 lastTradeAmountOut) internal {
if (lastTradeAmountOut < lastTradeAmountIn * (100 - stopLossThreshold) / 100) {
// Increase slippage tolerance if we hit a stop-loss
slippageTolerance += 1;
} else if (lastTradeAmountOut > lastTradeAmountIn * 110 / 100) {
// Decrease slippage tolerance after a highly profitable trade
if (slippageTolerance > 1) {
slippageTolerance -= 1;
}
}
}
// Automatically adjust max gas price based on recent transaction costs
function adjustGasPrice(uint256 lastGasPrice) internal {
if (lastGasPrice > maxGasPrice) {
maxGasPrice = lastGasPrice + 10 * 1e9; // Increase max gas price slightly above the last used price (10 gwei in wei)
} else if (lastGasPrice < maxGasPrice / 2) {
maxGasPrice = lastGasPrice + 10 * 1e9; // Increase max gas price slightly above the last used price (10 gwei in wei)
}
}
// Execute a trade on the Uniswap pair if slippage or other conditions are met
function executeTrade(
address pair,
address tokenIn,
uint256 amountIn,
uint256 minAmountOut
) external onlyOwner ensureCooldown {
require(tx.gasprice <= maxGasPrice, "Gas price too high");
IUniswapV2Pair liquidityPair = IUniswapV2Pair(pair);
require(checkLiquidity(pair) > 0, "Insufficient liquidity");
// Approve the router to spend tokens
IERC20(tokenIn).approve(address(uniswapRouter), amountIn);
address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = liquidityPair.token0() == tokenIn ? liquidityPair.token1() : liquidityPair.token0();
uint256[] memory amounts;
try uniswapRouter.swapExactTokensForTokens(
amountIn,
(minAmountOut * (100 - slippageTolerance)) / 100,
path,
address(this),
block.timestamp + 300 // 5 minute deadline
) returns (uint256[] memory swapAmounts) {
amounts = swapAmounts;
adjustSlippage(amountIn, amounts[1]); // Adjust slippage based on trade result
lastTradeTime = block.timestamp; // Update the last trade time
// Check for stop-loss
if (amounts[1] < (amountIn * (100 - stopLossThreshold)) / 100) {
emit StopLossTriggered(amounts[1]);
} else {
emit TradeExecuted(pair, amountIn, amounts[1]);
}
// Automatically take profit if above threshold
uint256 profit = IERC20(path[1]).balanceOf(address(this));
if (profit > profitThreshold) {
IERC20(path[1]).transfer(owner, profit);
emit ProfitTaken(profit);
}
} catch {
// Pause trading if a critical error occurs
emit TradePaused("Critical error in trade execution");
}
}
// Withdraw funds from the contract
function withdraw(address token) external onlyOwner {
uint256 balance = IERC20(token).balanceOf(address(this));
require(balance > 0, "No funds to withdraw");
IERC20(token).transfer(owner, balance);
}
// Set maximum gas price for trades
function setMaxGasPrice(uint256 _maxGasPrice) external onlyOwner {
maxGasPrice = _maxGasPrice;
}
// Set stop-loss threshold
function setStopLossThreshold(uint256 _stopLossThreshold) external onlyOwner {
stopLossThreshold = _stopLossThreshold;
}
// Set profit threshold for automatic withdrawal
function setProfitThreshold(uint256 _profitThreshold) external onlyOwner {
profitThreshold = _profitThreshold;
}
// Set slippage tolerance
function setSlippageTolerance(uint256 _slippageTolerance) external onlyOwner {
slippageTolerance = _slippageTolerance;
}
// Set cooldown period between trades
function setCooldownPeriod(uint256 _cooldownPeriod) external onlyOwner {
cooldownPeriod = _cooldownPeriod;
}
// Allow the contract to receive ETH
receive() external payable {}
}