I'm creating an ERC20 token that will have 9 separate funding rounds. When I call the startNextRound function in the JavaScript VM, I get the following error:

transact to browser/SatanCoin.sol:SatanCoin.startNextRound errored: VM error: invalid opcode.
    The constructor should be payable if you send value.
    The execution might have thrown.
    Debug the transaction to get more information. 
creation of browser/SatanCoin.sol:SatanCoin pending... 

When I deployed the same contract on Rinkeby through Remix and called the same function, the transaction ran out of gas:

transact to browser/SatanCoin.sol:SatanCoin.startNextRound errored: Gas required exceeds limit: 3000000.  An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)

When I use the Remix debugger, it tells me the function is stopping at require(msg.sender == owner);. What exactly is going wrong here?

Here's the code. I'm inheriting StandardToken from openZepplin.

contract TestCoin is StandardToken {


  uint256 public rate = 0.0666 ether; //Each Testcoin will be worth .0666 ETH, Must be bought in exact increments
  address public owner = msg.sender;

  bool public roundActive = false; //only allows buying during a round
  uint public roundNum = 0;//current round number out of 9
  uint public roundMax = 74;//max number of tokens to be issued every round
  uint public roundIssued;//number of tokens issued during current or previous round
  address[] roundBuyers; //buyer address is recorded for every token issued

  modifier onlyOwner {
      require(msg.sender == owner);
      _;
  }

  event Raffled(uint roundNumber, address winner, uint amount);
  event RoundStart(uint roundNumber);


  function name() constant returns (string) { return "TestCoin"; }
  function symbol() constant returns (string) { return "TEST"; }
  function decimals() constant returns (uint8) { return 0; }


  function startNextRound()
    public
  {
      require(msg.sender == owner);
      assert(endPreviousRound()); //end the previous round before starting the next
      require(roundNum<9); //only 9 rounds may occur

      roundActive = true;
      roundBuyers = new address[](74);
      roundIssued = 0;
      roundNum++;

      RoundStart(roundNum);
  }

  function endPreviousRound()
    private
    returns (bool)
  {
      //raffles off remaining tokens if any are left
      if(roundIssued < roundMax) assert(raffle(amountRemaining()));
      roundActive = false;
      return true;
  }

  //raffles off remainig tokens to a random buyer from the previous round
  //the more tokens a buyer has, the greater their chance to win
  function raffle(uint raffleAmount)
    private
    returns (bool)
  {
    uint randomIndex = uint(block.blockhash(block.number))%(roundMax-raffleAmount)+1;
    mint(roundBuyers[randomIndex], raffleAmount);

    Raffled(roundNum, roundBuyers[randomIndex], raffleAmount);
  }

  function mint(address receiver, uint amount) 
    private
  {

    totalSupply = safeAdd(totalSupply, amount);
    balances[receiver] = safeAdd(balances[receiver], amount);

    for(uint i = 0; i < amount; i++) {
      roundBuyers[roundIssued+i] = receiver;
    }

    roundIssued = safeAdd(roundIssued, amount);

    // This will make the mint transaction apper in EtherScan.io
    Minted(receiver, amount);
   }

  //This function is called when Ether is sent to the contract address
  //Even if 0 ether is sent.
  function () payable {
    //If value is zero or not multiple of the rate, refund user. 
    if (msg.value <= 0 || (msg.value % rate) != 0) revert(); 

    uint tokenAmount = safeDiv(msg.value, rate);

    //Make sure there is an active round
    if(roundActive == false) revert();
    //Make sure a buyer can't buy more than round amount availible.
    if (tokenAmount > amountRemaining()) revert();
    //Make sure that no more than 666 TestCoins can be issued.
    if ((tokenAmount+totalSupply) > 666) revert();
    //Extra precaution to contract attack
    if (tokenAmount < 1) revert();  

    mint(msg.sender, tokenAmount);
    owner.transfer(msg.value);          //Send the ETH
  }  

  //returns number of tokens left to be issued in current round
  function amountRemaining()
    public
    constant
    returns (uint)
  {
    return (roundMax-roundIssued);
  }

}

If the function fails due to the requires/assert/throw calls you'll get the error you are getting about running out of gas or vm invalid opcode.

So, if the debugging shows execution is stopping at require(msg.sender == owner); then the only possible explanation is that you are calling this function with an account that is not the owner.

  • That's what I thought. However, I've compared the owner variable with the account address I'm calling the function from and they're exactly the same. – CylonCat Oct 20 '17 at 23:26

As fas as I know the reason for an "Invalid Opcode" message is often a variable being empty. I think in your case the following line is not correct:

address public owner = msg.sender;

When saying that

contract TestCoin is StandardToken

the 'owner' variable gets already set by the constructor of the StandardToken-Contract.

So perhapts you should change the line above into:

address public owner;

Hope it helps.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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