2

I am trying to execute the method "withdrawICOFunds" but I am getting an error in Mainnet. In Goerli works ok

Any idea what's wrong?

Transaction failed: https://etherscan.io/tx/0xff5e31d1035cc2a206eb266d311d2653907866c31bf43856957cd1ecbb790c1b

The internal transactions that failed are this: enter image description here

Smart contract: https://etherscan.io/address/0x7488451db91df618759b8af15e36f70c0fdd529e and I created the smart contract with the following args: enter image description here

The function I am calling:

    function withdrawICOFunds() external {
        require(_isICOActive() == false, "ICOStillActive");
        
        uint256 usdtBalance = IERC20(usdtToken).balanceOf(address(this));
        require(IERC20(usdtToken).transfer(xWallet, usdtBalance));

        uint256 usdcBalance = IERC20(usdcToken).balanceOf(address(this));
        require(IERC20(usdcToken).transfer(xWallet, usdcBalance));

        uint256 ethbalance = address(this).balance;
        payable(xWallet).transfer(ethbalance);

        emit onWithdrawICOFunds(usdtBalance, usdcBalance, ethbalance);
    }

Smart contract code:

    // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./AggregatorV3Interface.sol";
import "./IERC20.sol";

contract xICO2 {

    address immutable private xWallet;                      // x wallet
    address immutable private xToken;                       // x token address
    address immutable private usdtToken;                        // USDT token address
    address immutable private usdcToken;                        // USDC token address
    uint256 immutable private minTokensBuyAllowed;              // Minimum tokens allowed
    uint256 immutable private maxICOTokens;                     // Max ICO tokens to sell
    uint256 immutable private icoStartDate;                     // ICO start date
    uint256 immutable private icoEndDate;                       // ICO end date
    AggregatorV3Interface immutable internal priceFeed;         // Chainlink price feeder ETH/USD

    uint256 public icoTokensBought;                             // Tokens sold
    uint256 public tokenListingDate;                            // Token listing date
    mapping(address => uint256) private userBoughtTokens;       // Mapping to store all the buys
    mapping(address => uint256) private userWithdrawTokens;     // Mapping to store the user tokens withdraw

    bool private icoFinished;
    uint256 internal constant _MIN_COINS_FOR_VESTING = 23530 * 10 ** 18;    // Min for vesting: 10000$

    event onTokensBought(address _buyer, uint256 _tokens, uint256 _paymentAmount, address _tokenPayment);
    event onWithdrawICOFunds(uint256 _usdtBalance, uint256 _usdcBalance, uint256 _ethbalance);
    event onWithdrawBoughtTokens(address _user, uint256 _maxTokensAllowed);
    event onICOFinished(uint256 _date);

    /**
     * @notice Constructor
     * @param _wallet               --> x master wallet
     * @param _token                --> x token address
     * @param _icoStartDate         --> ICO start date
     * @param _icoEndDate           --> ICO end date
     * @param _usdtToken            --> USDT token address
     * @param _usdcToken            --> USDC token address
     * @param _minTokensBuyAllowed  --> Minimal amount of tokens allowed to buy
     * @param _maxICOTokens         --> Number of tokens selling in this ICO
     * @param _tokenListingDate     --> Token listing date for the ICO vesting
     */
     //_wallet 0x1ef7f06bb20a8ebf23da0e8d6e5589c1e726eb18
     //_token 0xc987b821048edcb9f5aa126125b95e44e2532a6a
    constructor(address _wallet, address _token, uint256 _icoStartDate, uint256 _icoEndDate, address _usdtToken, address _usdcToken, uint256 _minTokensBuyAllowed, uint256 _maxICOTokens, uint256 _tokenListingDate) {
        xWallet = _wallet;
        xToken = _token;
        icoStartDate = _icoStartDate;
        icoEndDate = _icoEndDate;
        usdtToken = _usdtToken;
        usdcToken = _usdcToken;
        minTokensBuyAllowed = _minTokensBuyAllowed;
        maxICOTokens = _maxICOTokens;
        tokenListingDate = _tokenListingDate;
        priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
    }

    /**
     * @notice Buy function. Used to buy tokens using ETH, USDT or USDC
     * @param _paymentAmount    --> Result of multiply number of tokens to buy per price per token. Must be always multiplied per 1000 to avoid decimals 
     * @param _tokenPayment     --> Address of the payment token (or 0x0 if payment is ETH)
     */
    function buy(uint256 _paymentAmount, address _tokenPayment) external payable {
        require(_isICOActive() == true, "ICONotActive");
               
        uint256 paidTokens = 0;

        if (msg.value == 0) {
            // Stable coin payment
            require(_paymentAmount > 0, "BadPayment");
            require(_tokenPayment == usdtToken || _tokenPayment == usdcToken, "TokenNotSupported");
            require(IERC20(_tokenPayment).transferFrom(msg.sender, address(this), _paymentAmount));
            paidTokens = _paymentAmount * 1000 / 425;   // 0.425$ per token in the ICO
        } else {
            // ETH Payment
            uint256 usdETH = _getUSDETHPrice();
            uint256 paidUSD = msg.value * usdETH / 10**18;
            paidTokens = paidUSD * 1000 / 425;   // 0.425$ per token in the ICO
        }

        require((paidTokens + 1*10**18) >= minTokensBuyAllowed, "BadTokensQuantity");    // One extra token as threshold rounding decimals
        require(maxICOTokens - icoTokensBought >= paidTokens, "NoEnoughTokensInICO");
        userBoughtTokens[msg.sender] += paidTokens;
        icoTokensBought += paidTokens;
        if (maxICOTokens - icoTokensBought < minTokensBuyAllowed) {
            // We finish the ICO
            icoFinished = true;
            emit onICOFinished(block.timestamp);
        }

        emit onTokensBought(msg.sender, paidTokens, _paymentAmount, _tokenPayment);
    }

    /**
     * @notice Withdraw user tokens when the vesting rules allow it
     */
    function withdrawBoughtTokens() external {
        require(_isICOActive() == false, "ICONotActive");
        require(userBoughtTokens[msg.sender] > 0, "NoBalance");
        require(block.timestamp >= tokenListingDate, "TokenNoListedYet");

        uint256 boughtBalance = userBoughtTokens[msg.sender];
        uint256 maxTokensAllowed = 0;
        if ((block.timestamp >= tokenListingDate) && (block.timestamp < tokenListingDate + 90 days)) {
            if (boughtBalance <= _MIN_COINS_FOR_VESTING) {
                maxTokensAllowed = boughtBalance - userWithdrawTokens[msg.sender];
            } else {
                uint maxTokens = boughtBalance * 25 / 100;
                if (userWithdrawTokens[msg.sender] < maxTokens) {
                    maxTokensAllowed = maxTokens;
                }
            }
        } else if ((block.timestamp >= tokenListingDate + 90 days) && (block.timestamp < tokenListingDate + 180 days)) {
            uint256 maxTokens = boughtBalance * 50 / 100;
            if (userWithdrawTokens[msg.sender] < maxTokens) {
                maxTokensAllowed = maxTokens - userWithdrawTokens[msg.sender];
            }
        } else if ((block.timestamp >= tokenListingDate + 180 days) && (block.timestamp < tokenListingDate + 270 days)) {
            uint256 maxTokens = boughtBalance * 75 / 100;
            if (userWithdrawTokens[msg.sender] < maxTokens) {
                maxTokensAllowed = maxTokens - userWithdrawTokens[msg.sender];
            }
        } else {
            uint256 maxTokens = boughtBalance;
            if (userWithdrawTokens[msg.sender] < maxTokens) {
                maxTokensAllowed = maxTokens - userWithdrawTokens[msg.sender];
            }
        }

        require(maxTokensAllowed > 0, "NoTokensToWithdraw");

        userWithdrawTokens[msg.sender] += maxTokensAllowed;
        require(IERC20(xToken).transfer(msg.sender, maxTokensAllowed));

        emit onWithdrawBoughtTokens(msg.sender, maxTokensAllowed);
    }

    /**
     * @notice Returns the crypto numbers and balance in the ICO contract
     */
    function withdrawICOFunds() external {
        require(_isICOActive() == false, "ICOStillActive");
        
        uint256 usdtBalance = IERC20(usdtToken).balanceOf(address(this));
        require(IERC20(usdtToken).transfer(xWallet, usdtBalance));

        uint256 usdcBalance = IERC20(usdcToken).balanceOf(address(this));
        require(IERC20(usdcToken).transfer(xWallet, usdcBalance));

        uint256 ethbalance = address(this).balance;
        payable(xWallet).transfer(ethbalance);

        emit onWithdrawICOFunds(usdtBalance, usdcBalance, ethbalance);
    }

    /**
     * @notice Withdraw the unsold x tokens to the x wallet when the ICO is finished
     */
    function withdrawICOTokens() external {
        require(_isICOActive() == false, "ICONotActive");
        require(msg.sender == xWallet, "Onlyx");

        uint256 balance = maxICOTokens - icoTokensBought;
        require(IERC20(xToken).transfer(xWallet, balance));
    }

    /**
     * @notice OnlyOwner function. Change the listing date to start the vesting
     * @param _tokenListDate --> New listing date in UnixDateTime UTC format
     */
    function setTokenListDate(uint256 _tokenListDate) external {
        require(msg.sender == xWallet, "BadOwner");
        require(block.timestamp <= tokenListingDate, "TokenListedYet");

        tokenListingDate = _tokenListDate;
    }

    /**
     * @notice Returns the number of tokens and user has bought
     * @param _user --> User account
     * @return Returns the user token balance in wei units
     */
    function getUserBoughtTokens(address _user) external view returns(uint256) {
        return userBoughtTokens[_user];
    }

    /**
     * @notice Returns the number of tokens and user has withdrawn
     * @param _user --> User account
     * @return Returns the user token withdrawns in wei units
     */
    function getUserWithdrawnTokens(address _user) external view returns(uint256) {
        return userWithdrawTokens[_user];
    }

    /**
     * @notice Returns the crypto numbers in the ICO
     * @return x Returns the x tokens balance in the contract
     * @return eth Returns the ETHs balance in the contract
     * @return usdt Returns the USDTs balance in the contract
     * @return usdc Returns the USDCs balance in the contract
     */
    function getICOData() external view returns(uint256 x, uint256 eth, uint256 usdt, uint256 usdc) {
        x = IERC20(xToken).balanceOf(address(this));
        usdt = IERC20(usdtToken).balanceOf(address(this));
        usdc = IERC20(usdcToken).balanceOf(address(this));
        eth = address(this).balance;
    }

    /**
     * @notice Traslate a payment in USD to ETHs
     * @param _paymentAmount --> Payment amount in USD
     * @return Returns the ETH amount in weis
     */
    function calculateETHPayment(uint256 _paymentAmount) external view returns(uint256) {
        uint256 usdETH = _getUSDETHPrice();
        return (_paymentAmount * 10 ** 18) / usdETH;
    }

    /**
     * @notice Get the vesting unlock dates
     * @param _period --> There are 4 periods (0,1,2,3)
     * @return _date Returns the date in UnixDateTime UTC format
     */
    function getVestingDate(uint256 _period) external view returns(uint256 _date) {
        if (_period == 0) {
            _date = tokenListingDate;
        } else if (_period == 1) {
            _date = tokenListingDate + 90 days;
        } else if (_period == 2) {
            _date = tokenListingDate + 180 days;
        } else if (_period == 3) {
            _date = tokenListingDate + 270 days;
        }
    }

    /**
     * @notice Public function that returns ETHUSD par
     * @return Returns the how much USDs are in 1 ETH in weis
     */
    function getUSDETHPrice() external view returns(uint256) {
        return _getUSDETHPrice();
    }

    /**
     * @notice Uses Chainlink to query the USDETH price
     * @return Returns the ETH amount in weis (Fixed value of 3932.4 USDs in localhost development environments)
     */
    function _getUSDETHPrice() internal view returns(uint256) {
        (, int price, , , ) = priceFeed.latestRoundData();

        return uint256(price * 10**10);
    }

    /**
     * @notice Internal - Is ICO active?
     * @return Returns true or false
     */
    function _isICOActive() internal view returns(bool) {
        if ((block.timestamp < icoStartDate) || (block.timestamp > icoEndDate) || (icoFinished == true)) return false;
        else return true;
    }

    /**
     * @notice External - Is ICO active?
     * @return Returns true or false
     */
    function isICOActive() external view returns(bool) {
        return _isICOActive();
    }
}

1 Answer 1

0

Considering every function call to withdrawICOFunds() has failed,

enter image description here

and lack of provided gas is definitely not the issue:

enter image description here

My next assumption would be that there is an error in the actual function code

 function withdrawICOFunds() external {
        require(_isICOActive() == false, "ICOStillActive");
        
        uint256 usdtBalance = IERC20(usdtToken).balanceOf(address(this));
        require(IERC20(usdtToken).transfer(xifraWallet, usdtBalance));

        uint256 usdcBalance = IERC20(usdcToken).balanceOf(address(this));
        require(IERC20(usdcToken).transfer(xifraWallet, usdcBalance));

        uint256 ethbalance = address(this).balance;
        payable(xifraWallet).transfer(ethbalance);

        emit onWithdrawICOFunds(usdtBalance, usdcBalance, ethbalance);
    }

After reading through the code, it's clear that the dev has used the same ERC20 interface for USDC and USDT, which is known for causing issues since USDT doesn't actually follow the EIP-20 guidelines by not returning a boolean in the core functions.

Here is the transfer() function in the interface the devs created:

function transfer(address recipient, uint256 amount) external returns (bool);

Here is the transfer() function in the USDT contract:

function transfer(address to, uint value) public;

Since the contract uses the incorrect function signature, the function call reverts without an error message. (The USDT function call is also inside of a require statement that is checking for a return value of true.)

2
  • Thank u for the answer. There is another way to recover the smart contract funds? Commented Sep 9, 2023 at 16:15
  • @Estebanrestrepo Considering there is 190 ETH stuck on the contract still, I would say its probably impossible to recover the funds.
    – Rohan Nero
    Commented Sep 9, 2023 at 20:22

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.