0

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 {}
}
5
  • Can we see the entire contract? I am afraid your ETH might be locked in the contract forever.
    – Ougi
    Commented Aug 10 at 9:56
  • I edited the post to include the entire code :) Commented Aug 10 at 13:31
  • 1
    I don't think there's a way for you to save those funds. Why did you send ETH to a contract that doesn't need ETH in the first place?
    – Ougi
    Commented Aug 10 at 15:54
  • Becouse i was stupid and did not know the diffrence xD. welp if those funds are lost then it is what it is Commented Aug 10 at 16:30
  • I'm sorry to hear about your loss. $400+ is a significant amount, but it could've been worse. I hope this experience helps you stay more vigilant in the future. Take care and don't be too hard on yourself as mistakes happen to all of us!
    – Ougi
    Commented Aug 10 at 16:41

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.