[Ethereum] token transfer fails in crowdsale contract

contract-developmenttruffleweb3js

I have a token contract and a crowdsale contract. When I try to send ether to the crowdsale contract from accounts[3] (or any other really), I get an opcode error:

Error: VM Exception while processing transaction: invalid opcode.

I'm testing with Truffle. I've sent Testtokens to the crowdsalecontract address. I've sent Ether to the token contract address. Everything on the token contract works, when I interface with it directly. Tried a lot of things but I remain stuck at this point. Any help much appreciated. Running these contracts on testrpc

truffle migration code:

module.exports = function(deployer,accounts) {
  var sendaccounts = web3.eth.accounts;
  var sendTo = sendaccounts[0];

  deployer.deploy(TestToken).then(function() {
    return deployer.deploy(TestCrowdSale, sendTo, TestToken.address);
  });

web3 code that generates the error:

var sender = accounts[3];
var receiver = crowdsaleAddress;

var amount = web3.toWei(0.5, 'ether');

web3.eth.sendTransaction({from:sender, to:receiver, value: amount});

in crowdsale solidity code, if this line is commented out, the error does not occur:

tokenReward.transfer(msg.sender, amount);

token solidity code:

pragma solidity ^0.4.13;

contract TestToken {
  /* Public variables of the token */
  string public constant name = 'TestToken';
  string public constant symbol = 'TEST';
  uint256 public constant decimals = 6;
  address owner;
  address contractaddress;
  //Swap value in % of ETH
  uint256 public swapValue = 50;
  // One hundred million coins, each divided to up to 10^decimals units.
  uint256 private constant totalSupply = 100000000 * (10 ** decimals);
  uint256 public constant TestWei = 100000000000000;
  bool crowdsaleClosed = false;

  /* This creates an array with all balances */
  mapping (address => uint256) public balanceOf;
  mapping (address => mapping (address => uint256)) public allowance;

  /* This generates a public event on the blockchain that will notify clients */
  event Transfer(address indexed from, address indexed to, uint256 value);
  //event LogWithdrawal(address receiver, uint amount);

  modifier onlyOwner() {
    // Only owner is allowed to do this action.
    if (msg.sender != owner) {
      revert();
    }
    _;
  }

  /* Initializes contract with initial supply tokens to the creator of the contract */
  function TestToken() {
    contractaddress = address(this);
    balanceOf[msg.sender] = totalSupply / 2;              // Give the creator half of all tokens
    balanceOf[contractaddress] = totalSupply / 2;              // Give the contract half of all tokens
    owner = msg.sender;
  }

  /*ERC20*/

  /* Internal transfer, only can be called by this contract */
  function _transfer(address _from, address _to, uint256 _value) internal {
  //function _transfer(address _from, address _to, uint _value) public {
    require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
    require (balanceOf[_from] > _value);                // Check if the sender has enough
    require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
    balanceOf[_from] -= _value;                         // Subtract from the sender
    balanceOf[_to] += _value;                            // Add the same to the recipient
    Transfer(_from, _to, _value);
  }

  /// @notice Send `_value` tokens to `_to` from your account
  /// @param _to The address of the recipient
  /// @param _value the amount to send
  function transfer(address _to, uint256 _value) public returns (bool success) {
    if(_to == contractaddress ){
      swap(msg.sender, _value);
    }
    else{
      _transfer(msg.sender, _to, _value);
      return true;
    }
  }

  //deposit collateral ETH
  function depositFunds() public payable onlyOwner
  {
  }

  /*fallback function*/
  function () public payable onlyOwner{}

  //raise the minimum eth payout, minimum will always be 50% ETH
  function raiseSwap(uint _newSwap)
  onlyOwner
  {
    //swap can only be more than 50%
    //to guarantee there will always be at least a 50% payout
    if(_newSwap > 50){
      swapValue = _newSwap;
    }
  }

  //swap for swapvalue % ether (minimum 50)
  function swap(address _sender,uint256 _value) internal returns(bool success){
    //must be even integer
    uint even = _value / 2;
    require((2 * even) == _value);

    uint TestRefund = (_value / 100) * swapValue;
    uint ethRefund = TestRefund * TestWei;
    // Test is tranferred to contract balance
    uint256 originalBalance = balanceOf[_sender];
    // all necessary checks happen inside _transfer function
    _transfer(_sender, owner, _value);
    // check if tokens are returned succesfully
    if(originalBalance == balanceOf[_sender] + _value){
      //transfer swapvalue % of eth per Test back to sender
      _sender.transfer(ethRefund);
      return true;
    }
  }

  function getBalance(address addr) public returns(uint256) {
    return balanceOf[addr];
  }

  function getEtherBalance() public returns(uint256) {
    //return contract ether balance;
    return this.balance;
  }

  function getOwner() public returns(address) {
    return owner;
  }

  /* exchange ETH for Test with the contract address */
  function buyTest() public payable returns (bool){
    require(msg.value > 0);
    uint even = msg.value / 2;
    require((2 * even) == msg.value);
    uint _value;
    if(swapValue > 100){
      uint amount = (msg.value / swapValue) * 100;
      _value = amount / TestWei;
    }
    else{
      //Receive 10000 Test per 1 ETH
      _value = msg.value / TestWei;
    }

    _transfer(contractaddress, msg.sender, _value);
    return true;
  }

  function closeCrowdSale() public
  onlyOwner{
    crowdsaleClosed = true;
  }
}

crowdsale code:

pragma solidity ^0.4.13;
import './TestToken.sol';

contract TestCrowdSale {
  address public beneficiary;
  address public crowdsaleAddress;
  //debugging
  address public tokenAddress;
  uint public fundingGoal;
  uint public amountRaised;
  uint public deadline;
  uint public initiation;
  uint public price;
  uint256 public constant TestWei = 100000000000000;

  TestToken public tokenReward;
  mapping(address => uint256) public balanceOf;
  bool fundingGoalReached = false;
  bool crowdsaleClosed = false;

  event GoalReached(address beneficiary, uint amountRaised);
  event FundTransfer(address backer, uint amount, bool isContribution);

  /**
   * Constructor function
   *
   * Setup the owner
   */
  function TestCrowdSale(
    address ifSuccessfulSendTo,
    address addressOfTokenUsedAsReward
  ) {
    beneficiary = ifSuccessfulSendTo;
    fundingGoal = 10 * 1 ether;
    deadline = now + 100 * 1 minutes;
    initiation = now;
    price = TestWei;
    crowdsaleAddress = address(this);
    tokenAddress = addressOfTokenUsedAsReward;
    tokenReward = TestToken(addressOfTokenUsedAsReward);
  }

  /**
   * Fallback function
   *
   * The function without name is the default function that is called whenever anyone sends funds to a contract
   */
  function () public payable {
    //require(!crowdsaleClosed);
    uint256 amount = msg.value / price;
    balanceOf[msg.sender] += amount;
    amountRaised += amount;
    tokenReward.transfer(msg.sender, amount);
    FundTransfer(msg.sender, amount, true);
  }

  modifier afterDeadline() { if (now >= deadline) _; }

  /**
   * Check if goal was reached
   *
   * Checks if the goal or time limit has been reached and ends the campaign
   */
  function checkGoalReached() afterDeadline {
    if (amountRaised >= fundingGoal){
      fundingGoalReached = true;
      GoalReached(beneficiary, amountRaised);
    }
    crowdsaleClosed = true;
  }

  /**
   * Withdraw the funds
   *
   * Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached,
   * sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw
   * the amount they contributed.
   */
  function safeWithdrawal() afterDeadline {
    if (!fundingGoalReached) {
      uint amount = balanceOf[msg.sender];
      balanceOf[msg.sender] = 0;
      if (amount > 0) {
        if (msg.sender.send(amount)) {
          FundTransfer(msg.sender, amount, false);
        } else {
          balanceOf[msg.sender] = amount;
        }
      }
    }

    if (fundingGoalReached && beneficiary == msg.sender) {
      if (beneficiary.send(amountRaised)) {
        FundTransfer(beneficiary, amountRaised, false);
      } else {
        //If we fail to send the funds to beneficiary, unlock funders balance
        fundingGoalReached = false;
      }
    }
  }
}

Best Answer

I'm about 90% sure that it's failing on this line:

require (balanceOf[_from] > _value); // Check if the sender has enough

TestCrowdSale's fallback function is calling

tokenReward.transfer(msg.sender, amount);

Then TestToken.transfer(address _to, uint256 _value) is calling

_transfer(msg.sender, _to, _value);

This means that balanceOf[_from] is going always going to be balanceOf[TestCrowdSale]. If TestCrowdSale doesn't have a balance greater than msg.value / price, your transaction is going to fail.

I'd recommend creating a new function in TestToken, eg subscribe. transfer is meant for just that, transferring, so this is not appropriate for a new subscriber to your crowdsale.

Related Topic