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;
}
}
}
}
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 TestCrowdSale
der Kontostand nicht größer als msg.value / price
ist, wird Ihre Transaktion fehlschlagen.
Ich würde empfehlen, eine neue Funktion in TestToken zu erstellen , zB subscribe
. transfer
ist nur für das Übertragen gedacht, daher ist dies nicht für einen neuen Abonnenten Ihres Crowdsale geeignet.
hries