Token-Transfer schlägt im Crowdsale-Vertrag fehl

Ich habe einen Token-Vertrag und einen Crowdsale-Vertrag. Wenn ich versuche, Ether an den Crowdsale-Vertrag accounts[3](oder wirklich an einen anderen) zu senden, erhalte ich einen Opcode-Fehler:

Fehler: VM-Ausnahme beim Verarbeiten der Transaktion: ungültiger Opcode.

Ich teste mit Trüffel. Ich habe Testtokens an die Adresse des Crowdsale-Vertrags gesendet. Ich habe Ether an die Token-Vertragsadresse gesendet. Alles auf dem Token-Vertrag funktioniert, wenn ich direkt damit interagiere. Habe schon einiges ausprobiert, aber an diesem Punkt bleibe ich hängen. Jede Hilfe sehr geschätzt. Ausführen dieser Verträge auf testrpc

Trüffel-Migrationscode:

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, der den Fehler generiert:

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

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

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

Wenn diese Zeile im Crowdsale Solidity-Code auskommentiert ist, tritt der Fehler nicht auf:

tokenReward.transfer(msg.sender, amount);

Token-Soliditätscode:

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;
      }
    }
  }
}
Der Fehler wurde durch eine unzureichende Gasmenge verursacht, um die Transaktion abzuschließen. Siehe Kommentare unten.

Antworten (1)

Ich bin mir zu 90% sicher, dass es in dieser Zeile fehlschlägt:

erfordern (balanceOf[_from] > _value); // Prüfen, ob der Absender genug hat

Die Fallback-Funktion von TestCrowdSale ruft auf

tokenReward.transfer (msg.sender, Betrag);

Dann ruft TestToken .transfer(address _to, uint256 _value) auf

_transfer(msg.sender, _to, _value);

Das bedeutet, dass balanceOf[_from]es immer so sein wird balanceOf[TestCrowdSale]. Wenn TestCrowdSaleder Kontostand nicht größer als msg.value / priceist, wird Ihre Transaktion fehlschlagen.

Ich würde empfehlen, eine neue Funktion in TestToken zu erstellen , zB subscribe. transferist nur für das Übertragen gedacht, daher ist dies nicht für einen neuen Abonnenten Ihres Crowdsale geeignet.

Nein, der Fehler bleibt bestehen, auch wenn ich diesen Haken in einen Kommentar setze. balanceOf[TestCrowdSale] verfügt über eine ausreichende Menge an Token. Der Ether-Saldo des Crowdsale-Vertrags ist jedoch 0. Könnte dies das Problem sein? when tokenReward.transfer(msg.sender, amount); aufgerufen wird, welche Adresse muss für diese Transaktion bezahlt werden?
15 Ether an die Crowdsale-Adresse gesendet. Fehler besteht weiterhin.
Auch wenn alle require-Anweisungen unkommentiert waren, blieb der Fehler bestehen, aber ich bekam einen weiteren Fehler, „das Gas ging aus“, also testete ich die benötigte Gasmenge, und in meinem Web3-Javascript-Aufruf fügte ich hinzu: gas: 1000000 Works now ... will now add die require-Anweisungen zurück und sehen, ob es noch funktioniert.
Problem gelöst, nicht genug Benzin, um die Transaktion abzuschließen, war das Problem. Ich kann nicht glauben, dass ich nicht früher selbst daran gedacht habe. Danke für die Hilfe! @nyusternie