Search code examples
solidityweb3jsethers.jsremixico

ICO, transfering funds


I'm creating a simple ICO contract, it sends the amount of the token bought to the buyer as soon as the contract receives ether and then sent the received ether to a different wallet, I've been testing it on remix all day but, the contracts receives the ether but don't send out the ether to the wallet neither does the buyer received hers. Here's the buying function.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "./SafeMath.sol";


interface IERC20 {
    function totalSupply() external view returns (uint);

    function balanceOf(address account) external view returns (uint);

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

    function allowance(
        address owner,
        address spender
    ) external view returns (uint);

    function approve(address spender, uint amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);

    function burn(uint256 _value) external returns (bool success);
}

contract Crowdsale {
    using SafeMath for uint256;

    // The token being sold
    IERC20 public token;

    // Address where funds are collected
    IERC20 public wallet;

    // How many token units a buyer gets per wei
    uint256 public rate;

    // Amount of wei raised
    uint256 public weiRaised;

    /**
     * Event for token purchase logging
     * @param purchaser who paid for the tokens
     * @param beneficiary who got the tokens
     * @param value weis paid for purchase
     * @param amount amount of tokens purchased
     */
    event TokenPurchase(
        address indexed purchaser,
        address indexed beneficiary,
        uint256 value,
        uint256 amount
    );

    /**
     * @param _rate Number of token units a buyer gets per wei
     * @param _wallet Address where collected funds will be forwarded to
     * @param _token Address of the token being sold
     */

    constructor(uint256 _rate, address _wallet, address _token) {
        require(_rate > 0);
        require(_wallet != address(0));
        require(_token != address(0));

        rate = _rate;
        wallet = IERC20(_wallet);
        token = IERC20(_token);
    }

    /**
   * @dev fallback function ***DO NOT OVERRIDE***
  //  */
    fallback() external payable {
        buyTokens(msg.sender);
    }

    // receive() external payable{}

    /**
     * @dev low level token purchase ***DO NOT OVERRIDE***
     * @param _beneficiary Address performing the token purchase
     */
    function buyTokens(address _beneficiary) public payable {
        uint256 weiAmount = msg.value;
        _preValidatePurchase(_beneficiary, weiAmount);

        // calculate token amount to be created
        uint256 tokens = _getTokenAmount(weiAmount);

        // update state
        weiRaised = weiRaised.add(weiAmount);
        _processPurchase(_beneficiary, tokens);

        emit TokenPurchase(msg.sender, _beneficiary, weiAmount, tokens);

        _updatePurchasingState(_beneficiary, weiAmount);
        _forwardFunds();
        _postValidatePurchase(_beneficiary, weiAmount);
    }

    /**
     * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met. Use super to concatenate validations.
     * @param _beneficiary Address performing the token purchase
     * @param _weiAmount Value in wei involved in the purchase
     */
    function _preValidatePurchase(
        address _beneficiary,
        uint256 _weiAmount
    ) internal pure {
        require(_beneficiary != address(0));
        require(_weiAmount != 0);
    }

    /**
     * @dev Validation of an executed purchase. Observe state and use revert statements to undo rollback when valid conditions are not met.
     * @param _beneficiary Address performing the token purchase
     * @param _weiAmount Value in wei involved in the purchase
     */
    function _postValidatePurchase(
        address _beneficiary,
        uint256 _weiAmount
    ) internal {
        // optional override
    }

    // 0x4815A8Ba613a3eB21A920739dE4cA7C439c7e1b1
    // 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
    /**
     * @dev Source of tokens. Override this method to modify the way in which the crowdsale ultimately gets and sends its tokens.
     * @param _beneficiary Address performing the token purchase
     * @param _tokenAmount Number of tokens to be emitted
     */
    function _deliverTokens(
        address _beneficiary,
        uint256 _tokenAmount
    ) internal {
        token.transfer(_beneficiary, _tokenAmount);
    }

    /**
     * @dev Executed when a purchase has been validated and is ready to be executed. Not necessarily emits/sends tokens.
     * @param _beneficiary Address receiving the tokens
     * @param _tokenAmount Number of tokens to be purchased
     */
    function _processPurchase(
        address _beneficiary,
        uint256 _tokenAmount
    ) internal {
        _deliverTokens(_beneficiary, _tokenAmount);
    }

    /**
     * @dev Override for extensions that require an internal state to check for validity (current user contributions, etc.)
     * @param _beneficiary Address receiving the tokens
     * @param _weiAmount Value in wei involved in the purchase
     */
    function _updatePurchasingState(
        address _beneficiary,
        uint256 _weiAmount
    ) internal {
        // optional override
    }

    /**
     * @dev Override to extend the way in which ether is converted to tokens.
     * @param _weiAmount Value in wei to be converted into tokens
     * @return Number of tokens that can be purchased with the specified _weiAmount
     */
    function _getTokenAmount(
        uint256 _weiAmount
    ) internal view returns (uint256) {
        return _weiAmount.mul(rate);
    }

    /**
     * @dev Determines how ETH is stored/forwarded on purchases.
     */
    function _forwardFunds() internal {
        // bool sent =  wallet.transfer(address(wallet),msg.value);
        wallet.transfer(address(wallet), msg.value);
        // require(sent, "Transaction failed");
    }

    function getWallet() public view returns (uint) {
        return address(wallet).balance;
    }

    function getTokenBalance() public view returns (uint) {
        return address(token).balance;
    }
}


Solution

  • We need to agree on your goal first:

    Anyone can purchase your token using the native coin of the chain at a rate you've predefined. Once someone successfully purchases the token then the amount of native coin accessible in the msg.value will be forwarded to a wallet you've also predefined.

    Also, regarding how your contract is constructed, it seems you plan on deploying the ERC20 token separately from the Crowdsale contract. If not comment below that you want the Crowdsale to also deploy the ERC20 token.

    After agreeing on the goal, there are multiple fixes needed to achieve it with the current code.

    First, the deployment order and supply of ERC20 tokens:

    1. Deploy an ERC20 contract - need the contract address to be able to deploy the Crowdsale contract (address _token)
    2. Deploy this Crowdsale contract
    3. Transfer the number of tokens you want to sell on this ICO to the Crowdsale.

    Second, we need to fix parts of your contract to enable forwarding the native coin the buyer used in order to buy your token:

    1. Currently, you set the type of the target wallet as IERC20 and this is incorrect. Because you want to transfer the native coin of the chain you need to set the type of the variable to Address because you simply are saving the target wallet address and not a contract.
        // Address where funds are collected
        address public wallet;
    
    1. In the _forwardFunds function you want to transfer the ETH not an ERC20 token so the function should look something like this:
        /**
         * @dev Determines how ETH is stored/forwarded on purchases.
         */
        function _forwardFunds(uint256 _value) internal {
            // bool sent =  wallet.transfer(address(wallet),msg.value);
       address payable receiver = payable(wallet);
       receiver.transfer(_value);
            // require(sent, "Transaction failed");
        }
    
    
    1. You should also add the receive function to be able to accept the native currency of the chain and for it to also appear correctly on the block explorers. The differences between receive and fallback :
        //Required to be able to accept native currency of the network(ETH)
        event Received(address, uint256);
    
        receive() external payable {
            emit Received(msg.sender, msg.value);
        }
    
    

    The full code:

    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.19;
    import "./SafeMath.sol";
    
    
    interface IERC20 {
        function totalSupply() external view returns (uint);
    
        function balanceOf(address account) external view returns (uint);
    
        function transfer(address recipient, uint amount) external returns (bool);
    
        function allowance(
            address owner,
            address spender
        ) external view returns (uint);
    
        function approve(address spender, uint amount) external returns (bool);
    
        function transferFrom(
            address sender,
            address recipient,
            uint amount
        ) external returns (bool);
    
        event Transfer(address indexed from, address indexed to, uint value);
        event Approval(address indexed owner, address indexed spender, uint value);
    
        function burn(uint256 _value) external returns (bool success);
    }
    
    contract Crowdsale {
        using SafeMath for uint256;
    
        // The token being sold
        IERC20 public token;
    
        // Address where funds are collected
        address public wallet;
    
        // How many token units a buyer gets per wei
        uint256 public rate;
    
        // Amount of wei raised
        uint256 public weiRaised;
    
        /**
         * Event for token purchase logging
         * @param purchaser who paid for the tokens
         * @param beneficiary who got the tokens
         * @param value weis paid for purchase
         * @param amount amount of tokens purchased
         */
        event TokenPurchase(
            address indexed purchaser,
            address indexed beneficiary,
            uint256 value,
            uint256 amount
        );
    
        /**
         * @param _rate Number of token units a buyer gets per wei
         * @param _wallet Address where collected funds will be forwarded to
         * @param _token Address of the token being sold
         */
    
        constructor(uint256 _rate, address _wallet, address _token) {
            require(_rate > 0);
            require(_wallet != address(0));
            require(_token != address(0));
    
            rate = _rate;
            wallet = _wallet;
            token = IERC20(_token);
        }
    
        /**
       * @dev fallback function ***DO NOT OVERRIDE***
      //  */
        fallback() external payable {
            buyTokens(msg.sender);
        }
    
        //Required to be able to accept native currency of the network(ETH)
        event Received(address, uint256);
    
        receive() external payable {
            emit Received(msg.sender, msg.value);
            buyTokens(msg.sender);
        }
    
        /**
         * @dev low level token purchase ***DO NOT OVERRIDE***
         * @param _beneficiary Address performing the token purchase
         */
        function buyTokens(address _beneficiary) public payable {
            uint256 weiAmount = msg.value;
            _preValidatePurchase(_beneficiary, weiAmount);
    
            // calculate token amount to be created
            uint256 tokens = _getTokenAmount(weiAmount);
    
            // update state
            weiRaised = weiRaised.add(weiAmount);
            _processPurchase(_beneficiary, tokens);
    
            emit TokenPurchase(msg.sender, _beneficiary, weiAmount, tokens);
    
            _updatePurchasingState(_beneficiary, weiAmount);
            _forwardFunds();
            _postValidatePurchase(_beneficiary, weiAmount);
        }
    
        /**
         * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met. Use super to concatenate validations.
         * @param _beneficiary Address performing the token purchase
         * @param _weiAmount Value in wei involved in the purchase
         */
        function _preValidatePurchase(
            address _beneficiary,
            uint256 _weiAmount
        ) internal pure {
            require(_beneficiary != address(0));
            require(_weiAmount != 0);
        }
    
        /**
         * @dev Validation of an executed purchase. Observe state and use revert statements to undo rollback when valid conditions are not met.
         * @param _beneficiary Address performing the token purchase
         * @param _weiAmount Value in wei involved in the purchase
         */
        function _postValidatePurchase(
            address _beneficiary,
            uint256 _weiAmount
        ) internal {
            // optional override
        }
    
        // 0x4815A8Ba613a3eB21A920739dE4cA7C439c7e1b1
        // 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
        /**
         * @dev Source of tokens. Override this method to modify the way in which the crowdsale ultimately gets and sends its tokens.
         * @param _beneficiary Address performing the token purchase
         * @param _tokenAmount Number of tokens to be emitted
         */
        function _deliverTokens(
            address _beneficiary,
            uint256 _tokenAmount
        ) internal {
            token.transfer(_beneficiary, _tokenAmount);
        }
    
        /**
         * @dev Executed when a purchase has been validated and is ready to be executed. Not necessarily emits/sends tokens.
         * @param _beneficiary Address receiving the tokens
         * @param _tokenAmount Number of tokens to be purchased
         */
        function _processPurchase(
            address _beneficiary,
            uint256 _tokenAmount
        ) internal {
            _deliverTokens(_beneficiary, _tokenAmount);
        }
    
        /**
         * @dev Override for extensions that require an internal state to check for validity (current user contributions, etc.)
         * @param _beneficiary Address receiving the tokens
         * @param _weiAmount Value in wei involved in the purchase
         */
        function _updatePurchasingState(
            address _beneficiary,
            uint256 _weiAmount
        ) internal {
            // optional override
        }
    
        /**
         * @dev Override to extend the way in which ether is converted to tokens.
         * @param _weiAmount Value in wei to be converted into tokens
         * @return Number of tokens that can be purchased with the specified _weiAmount
         */
        function _getTokenAmount(
            uint256 _weiAmount
        ) internal view returns (uint256) {
            return _weiAmount.mul(rate);
        }
    
        /**
         * @dev Determines how ETH is stored/forwarded on purchases.
         */
        function _forwardFunds() internal {
            // bool sent =  wallet.transfer(address(wallet),msg.value);
       address payable receiver = payable(wallet);
       receiver.transfer(msg.value);
            // require(sent, "Transaction failed");
        }
    
        function getWalletBalance() public view returns (uint) {
            return address(wallet).balance;
        }
    
        function getTokenBalance() public view returns (uint) {
            return address(token).balance;
        }
    
    }
    
    
    
    

    P.S: Saw that your getWallet public function purpose is to fetch the wallet balance so I renamed it to getWalletBalance.

    Edit 1:

    I have successfully tested it end to end. I used Remix for deployment and contract interaction and deployed it to the Mumbai network (Polygon testnet).Link to the transaction

    Maybe you accidentally executed the buyTokens function without actually passing the native coin with a request? you need to change the value from default 0 to the requested amount, attaching an image:

    Set transaction native coin value

    Edit 2

    You don't need to pass the msg.value, they are persistent through the life cycle of the transaction (my mistake, I've removed the section about that)

    You need to trigger the buyTokens function on the receive & fallback functions if you want to trigger the purchase by only transferring native coins (ETH/MATIC and etc').