Revert SafeERC20: low-level call failed when trying to run buyTokens function in a Crowdsale

I’m getting error VM Exception while processing transaction: revert SafeERC20: low-level call failed
when trying to run buyTokens function

My code:

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/token/ERC20/SafeERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/GSN/Context.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/math/SafeMath.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/utils/ReentrancyGuard.sol";

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/ownership/Ownable.sol";
import "./MyToken.sol";
import "./TokenWhitelist.sol";

contract MyCrowdsale is Context, ReentrancyGuard, Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;

address payable private _startupAddr;

uint256 private _tokenPrice; 
uint256 private _tokensSold;

// Amount of wei raised
uint256 private _weiRaised;


IERC20 private _tokenContract;
TokenWhitelist private _whitelist;


/**
 * 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 TokensPurchased(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);


constructor(
    uint256 rate,                  
    address payable startupAddr,   
    MyToken token,          
    TokenWhitelist whitelist_       
)
public
{
    require(rate > 0, "Crowdsale: rate is 0");
    require(address(whitelist_) == 0x80C92cc0b675018ef95b4c000e22C675c3A5a41a, "Crowdsale: wrong whitelist address"); // endereço hardcoded
    require(whitelist_.checkWhitelisted(startupAddr), "Crowdsale: wallet is not whitelisted");
    require(address(token) != address(0), "Crowdsale: token is the zero address");
    _tokenPrice = rate;
    _tokenContract = token;
    _whitelist = whitelist_;
    _startupAddr = startupAddr;
}

function tokenPrice() public view returns (uint256) {
    return _tokenPrice;
}

function tokensSold() public view returns (uint256) {
    return _tokensSold;
}

function totalTokenSupply() public view returns (uint256){
    return _tokenContract.totalSupply();
}

/**
 * @return the amount of wei raised.
 */
function weiRaised() public view returns (uint256) {
    return _weiRaised;
}

/**
 * @dev fallback function ***DO NOT OVERRIDE***
 * Note that other contracts will transfer funds with a base gas stipend
 * of 2300, which is not enough to call buyTokens. Consider calling
 * buyTokens directly when purchasing tokens from a contract.
 */
function () external payable {
    buyTokens(_msgSender());
}

 /**
 * @dev low level token purchase ***DO NOT OVERRIDE***
 * This function has a non-reentrancy guard, so it shouldn't be called by
 * another `nonReentrant` function.
 * @param beneficiary Recipient of the token purchase
 */
function buyTokens(address beneficiary) public nonReentrant 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 TokensPurchased(_msgSender(), 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` in contracts that inherit from Crowdsale to extend their validations.
 * Example from CappedCrowdsale.sol's _preValidatePurchase method:
 *     super._preValidatePurchase(beneficiary, weiAmount);
 *     require(weiRaised().add(weiAmount) <= cap);
 * @param beneficiary Address performing the token purchase
 * @param weiAmount Value in wei involved in the purchase
 */
function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
    require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address");
    require(weiAmount != 0, "Crowdsale: weiAmount is 0");
    require(_whitelist.checkWhitelisted(beneficiary), "Crowdsale: beneficiary is not whitelisted");
    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
}

/**
 * @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 view {
    // solhint-disable-previous-line no-empty-blocks
}

/**
 * @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 {
    _tokenContract.safeTransfer(beneficiary, tokenAmount);
}

/**
 * @dev Executed when a purchase has been validated and is ready to be executed. Doesn't necessarily emit/send
 * 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 {
    // solhint-disable-previous-line no-empty-blocks
}

/**
 * @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.div(_tokenPrice);
}

/**
 * @dev Determines how ETH is stored/forwarded on purchases.
 */
function _forwardFunds() internal {
    _startupAddr.transfer(msg.value);
}
}
1 Like

Can you provide the contract MyToken.sol and TokenWhitelist.sol, or can you verify the contract on a testnet?

1 Like

Hi @pedromtelho,

You appear to have cloned a crowdsale. I would suggest extending the Crowdsales from OpenZeppelin Contracts: https://docs.openzeppelin.com/contracts/2.x/crowdsales

Have you transferred an amount of your token to the crowdsale so it has tokens to sell?

Exactly! I thought that some functions could be called by other people and I do not want it so I decided to override them using onlyOwner function. I’ll post my contracts here.

MyToken.sol:

pragma solidity ^0.5.2;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/token/ERC20/ERC20Mintable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/token/ERC20/ERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/token/ERC20/ERC20Detailed.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/token/ERC20/ERC20Pausable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/ownership/Ownable.sol";
import "./TokenWhitelist.sol";

contract MyToken is ERC20, ERC20Pausable, ERC20Mintable, Ownable {
    string private _name;
    string private _symbol;
    uint8 private _decimals = 18;
    uint256 private _exp = 10**18;
    uint256 private _totalSupply;

TokenWhitelist public whitelist;

mapping (address => uint256) private _balances;

mapping (address => mapping (address => uint256)) private _allowances;



// ... see "Tokens" for more info
constructor(string memory name, string memory symbol, address startupAddr, TokenWhitelist _whitelist, uint256 totalSupply) public
    ERC20Mintable()
    ERC20Pausable()
    {
        _name = name;
        _symbol = symbol;
        whitelist = _whitelist;
        mint(startupAddr, totalSupply);
    }


function name() public view returns (string memory) {
    return _name;
}


function symbol() public view returns (string memory) {
    return _symbol;
}

function decimals() public view returns (uint8) {
    return _decimals;
}

function totalSupply() public view returns (uint256) {
    return _totalSupply;
}

function balanceOf(address account) public view returns (uint256) {
    return _balances[account];
}
    
function mint(address toAccount, uint256 amount) public onlyMinter whenNotPaused returns (bool) {
    require(whitelist.checkWhitelisted(toAccount), "Account is not whitelisted");
    _mint(toAccount, amount);
    return true;
}

function transfer(address recipient, uint256 amount) public whenNotPaused returns (bool) {
    require(whitelist.checkWhitelisted(recipient), "Recipient is not whitelisted");
    _transfer(_msgSender(), recipient, amount);
    return true;
}

function allowance(address owner, address spender) public view returns (uint256) {
    return _allowances[owner][spender];
}


function approve(address spender, uint256 amount) public onlyOwner whenNotPaused returns (bool) {
    require(whitelist.checkWhitelisted(spender), "Spender is not whitelisted");
    _approve(_msgSender(), spender, amount);
    return true;
}

function transferFrom(address sender, address recipient, uint256 amount) public whenNotPaused returns (bool) {
    require(whitelist.checkWhitelisted(sender), "Sender is not whitelisted");
    require(whitelist.checkWhitelisted(recipient), "Recipient is not whitelisted");
    _transfer(sender, recipient, amount);
    _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
    return true;
}

function increaseAllowance(address spender, uint256 addedValue) public onlyOwner whenNotPaused returns (bool) {
    require(whitelist.checkWhitelisted(spender), "Spender is not whitelisted");
    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
    return true;
}


function decreaseAllowance(address spender, uint256 subtractedValue) public onlyOwner whenNotPaused returns (bool) {
    require(whitelist.checkWhitelisted(spender), "Spender is not whitelisted");
    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
    return true;
}

function _mint(address account, uint256 amount) internal {
    require(account != address(0), "ERC20: mint to the zero address");
    uint256 convertedAmount = amount*_exp;
    _totalSupply = _totalSupply.add(convertedAmount);
    _balances[account] = _balances[account].add(convertedAmount);
    emit Transfer(address(0), account, convertedAmount);
}


function _transfer(address sender, address recipient, uint256 amount) internal {
    require(sender != address(0), "ERC20: transfer from the zero address");
    require(recipient != address(0), "ERC20: transfer to the zero address");
    require(whitelist.checkWhitelisted(sender), "Sender is not checked in whitelist");
    require(whitelist.checkWhitelisted(recipient), "Sender is not checked in whitelist");

    _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
    _balances[recipient] = _balances[recipient].add(amount);
    emit Transfer(sender, recipient, amount);
}


function _approve(address owner, address spender, uint256 amount) internal {
    require(owner != address(0), "ERC20: approve from the zero address");
    require(spender != address(0), "ERC20: approve to the zero address");

    _allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);}}

MyCrowdsale.sol:

pragma solidity ^0.5.2;

    import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/GSN/Context.sol";
    import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/math/SafeMath.sol";
    import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/utils/ReentrancyGuard.sol";

    import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/ownership/Ownable.sol";
    import "./MyToken.sol";
    import "./TokenWhitelist.sol";

    contract MyCrowdsale is Context, ReentrancyGuard, Ownable {
        using SafeMath for uint256;

        address payable private _startupAddr;
        
        uint256 private _tokenPrice; // preço do token
        uint256 private _tokensSold; // tokens vendidos

        // Amount of wei raised
        uint256 private _weiRaised;


    MyToken private _tokenContract; // contrato do token
    TokenWhitelist private _whitelist;
    
    mapping(address => uint256) private _tokenRequests;
    
    

    /**
     * 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 TokensPurchased(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
    
    
    constructor(
        uint256 rate,                    // preço
        address payable startupAddr,    // endereço da startup
        MyToken token,                  // endereço do token da startup
        TokenWhitelist whitelist_       // whitelist da nossa empresa
    )
    public
    {
        require(rate > 0, "Crowdsale: rate is 0");
        require(address(whitelist_) == 0x80C92cc0b675018ef95b4c000e22C675c3A5a41a, "Crowdsale: wrong whitelist address"); // endereço hardcoded
        require(whitelist_.checkWhitelisted(startupAddr), "Crowdsale: wallet is not whitelisted");
        require(address(token) != address(0), "Crowdsale: token is the zero address");
        _tokenPrice = rate;
        _tokenContract = token;
        _whitelist = whitelist_;
        _startupAddr = startupAddr;
    }
    
    function tokenPrice() public view returns (uint256) {
        return _tokenPrice;
    }
    
    function tokensSold() public view returns (uint256) {
        return _tokensSold;
    }
    
    function totalTokenSupply() public view returns (uint256){
        return _tokenContract.totalSupply();
    }
    
    /**
     * @return the amount of wei raised.
     */
    function weiRaised() public view returns (uint256) {
        return _weiRaised;
    }
    
    /**
     * @dev fallback function ***DO NOT OVERRIDE***
     * Note that other contracts will transfer funds with a base gas stipend
     * of 2300, which is not enough to call buyTokens. Consider calling
     * buyTokens directly when purchasing tokens from a contract.
     */
    function () external payable {
        buyTokens(_msgSender());
    }
    
     /**
     * @dev low level token purchase ***DO NOT OVERRIDE***
     * This function has a non-reentrancy guard, so it shouldn't be called by
     * another `nonReentrant` function.
     * @param beneficiary Recipient of the token purchase
     */
    function buyTokens(address beneficiary) public nonReentrant 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 TokensPurchased(_msgSender(), beneficiary, weiAmount, tokens);

        // _updatePurchasingState(beneficiary, weiAmount);

        _forwardFunds();
        // _postValidatePurchase(beneficiary, weiAmount);
    }
    
    function withdrawTokens(address beneficiary) public {
        require(_whitelist.checkWhitelisted(beneficiary), "Crowdsale: beneficiary is not whitelisted");
        require(_tokenRequests[beneficiary] != 0, "Crowdsale: beneficiary has no orders");
        uint256 amount = _tokenRequests[beneficiary];
        require(amount > 0, "PostDeliveryCrowdsale: beneficiary is not due any tokens");

        _tokenRequests[beneficiary] = 0;
        _tokenContract.transferFrom(_startupAddr, beneficiary, amount);
    }
    
    function tokenRequests(address beneficiary) public view returns (uint256) {
        return _tokenRequests[beneficiary];
    }
    
    /**
     * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met.
     * Use `super` in contracts that inherit from Crowdsale to extend their validations.
     * Example from CappedCrowdsale.sol's _preValidatePurchase method:
     *     super._preValidatePurchase(beneficiary, weiAmount);
     *     require(weiRaised().add(weiAmount) <= cap);
     * @param beneficiary Address performing the token purchase
     * @param weiAmount Value in wei involved in the purchase
     */
    function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
        require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address");
        require(weiAmount != 0, "Crowdsale: weiAmount is 0");
        require(_whitelist.checkWhitelisted(beneficiary), "Crowdsale: beneficiary is not whitelisted");
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    }

    /**
     * @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 view {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @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 {
        _tokenContract.transferFrom(_startupAddr, beneficiary, tokenAmount);
    }
    
    function _buyOrder(address beneficiary, uint256 tokenAmount) internal {
        _tokenRequests[beneficiary] += tokenAmount;
        _tokensSold += tokenAmount;
    }

    /**
     * @dev Executed when a purchase has been validated and is ready to be executed. Doesn't necessarily emit/send
     * tokens.
     * @param beneficiary Address receiving the tokens
     * @param tokenAmount Number of tokens to be purchased
     */
    function _processPurchase(address beneficiary, uint256 tokenAmount) internal {
        _buyOrder(beneficiary, tokenAmount);
        // _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 {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @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) {
        uint256 _exp = 10**18;
        uint256 _mult = weiAmount.mul(_exp);
        return _mult.div(_tokenPrice);
    }

    /**
     * @dev Determines how ETH is stored/forwarded on purchases.
     */
    function _forwardFunds() internal {
        _startupAddr.transfer(msg.value);
    }
}

TokenWhitelist.sol:
pragma solidity ^0.5.2;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/ownership/Ownable.sol";


// Contrato da nossa whitelist deve ser o primeiro deploy 


contract TokenWhitelist is Ownable {
    mapping(address => bool) private whitelist;

    event Whitelisted(address indexed wallet);
    event Dewhitelisted(address indexed wallet);

    function enableWallet(address _wallet) public onlyOwner {
        require(_wallet != address(0), "Invalid wallet");
        whitelist[_wallet] = true;
        emit Whitelisted(_wallet);
    }

    function disableWallet(address _wallet) public onlyOwner {
        whitelist[_wallet] = false;
        emit Dewhitelisted(_wallet);
    }

    function checkWhitelisted(address _wallet) public view returns (bool) {
        return whitelist[_wallet];
    }
}
1 Like

Sorry, I tried to deploy it on a testnet, but always get an error when compile the contracts, so can you just verify it on the testnet?

1 Like

Hi @pedromtelho,

You may want to take a step back and start with a simple example: Simple ERC20 Crowdsale.

I would suggest extending OpenZeppelin Contracts Crowdsales rather than creating your own. This would also make the code much easier to read (and for your audits, audit). https://docs.openzeppelin.com/contracts/2.x/crowdsales