Well, I'm creating a contract to be my ICO, but I have a "simple" problem using the SafeMath lib.

What is the problem? I need to limit the purchase of my token to a maximum of "5 ether(bnb)", so I created a variable called "investorBalances" where I save the amount that was purchased.

That done, I put a check on my code where I confirm that the amount purchased by the investor is not higher than the limit. But then the error happens, it returns my "revert" message saying that it has already reached the limit, however, this is the first purchase...

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SALES is Ownable {
  using SafeMath for uint256;
  IERC20 token;

  uint256 private rate = 3000; // Number of tokens per BNB  
  uint256 private start = 1636914760; // 16/11/2021
  uint256 private dayMax = 45; // 45 Day
  uint256 private buyMax = 5; //5 bnb
  uint256 public initialTokens; // Initial number of tokens available
  bool public initialized = false;
  uint256 public raisedAmount = 0;

  mapping(address => uint256) investorBalances;

   * BoughtTokens
   * @dev Log tokens bought onto the blockchain
  event BoughtTokens(address indexed to, uint256 value);

  constructor(address _token){

   * @dev Fallback function if ether is sent to address insted of buyTokens function
  receive() external payable {

   * buyTokens
   * @dev function that sells available tokens
  function buyTokens() public payable whenSaleIsActive {
    uint256 weiAmount = msg.value; // Calculate tokens to sell
    uint256 tokens = weiAmount.mul(rate);
    uint256 amountMax = investorBalances[msg.sender].add(weiAmount);

    if(amountMax >= buyMax){
      revert("You have reached the purchase limit");

    emit BoughtTokens(msg.sender, tokens); // log event onto the blockchain
    raisedAmount = raisedAmount.add(msg.value); // Increment raised amount
    token.transfer(msg.sender, tokens); // Send tokens to buyer
    investorBalances[msg.sender] = amountMax;

    payable(owner()).transfer(msg.value);// Send money to owner

   * whenSaleIsActive
   * @dev ensures that the contract is still active
  modifier whenSaleIsActive() {
    // Check if sale is active

  function isActive() public view returns (bool) {
    return (
        initialized == true &&
        block.timestamp >= start && // Must be after the START date
        block.timestamp <= start.add(dayMax * 1 days)// Must be before the end date        

   * terminate
   * @notice Terminate contract and refund to owner
  function terminateSales() public onlyOwner  {
    // Transfer tokens back to owner    
    uint256 balance = token.balanceOf(address(this));
    assert(balance > 0);
    token.transfer(owner(), balance);    

   * tokensAvailable
   * @dev returns the number of tokens allocated to this contract
  function tokensAvailable() public view returns (uint256) {
    return token.balanceOf(address(this));

   * investorBalanceAvailable
   * @dev returns the number of tokens the investor bought
  function investorBalanceAvailable(address _investor) public view returns (uint256) {
    return investorBalances[_investor];

   * initialize
   * @dev Initialize the contract
  function initialize() public onlyOwner {
      require(initialized == false); // Can only be initialized once      
      initialized = true;

  function setRate(uint _rate) public onlyOwner{
    rate = _rate;

  function setBuyMax(uint _buyMax) public onlyOwner{
    buyMax = _buyMax;

  function setStart(uint _start) public onlyOwner{
    start = _start;

  function setDays(uint _days) public onlyOwner{
    dayMax = _days;

  function setAddrERC20(address _tokenAddr) public onlyOwner{
    require(_tokenAddr != address(0));
    token = IERC20(_tokenAddr);

My function in test.js

it(`Buy with account Investor1 4 ether = 12000 tokens`, async () => {
            await contractInstance.initialize();
            await contractInstance.buyTokens({
                from: investor1,
                value: web3.utils.toWei("4", "ether"),
                gas: 200000
            let total = await coinInstance.balanceOf(investor1);


Validate pre-sales from account privateSales
         Buy with account Investor1 4 ether = 12000 tokens:
     Error: Returned error: VM Exception while processing transaction: revert You have reached the purchase limit -- Reason given: You have reached the purchase limit.  ```

1 Answer 1


You compare wei (msg.value) with something which doesnt look like wei (but rather ethers/bnb), uint256 buyMax = 5;

To compare and have the behavior you're describing in comment, you need to multiply buyMax by 10^18 (or use the "ether" unit, uint256 buyMax = 5 ether;, which is a shortcut for 10^18)

edit for clarity: if you replace your variables by their values, your test is failing on : if(amountMax >= buyMax) because you're testing if(5000000000000000000 >= 5)

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.