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:
TestCrowdSale's fallback function is calling
Then TestToken.transfer(address _to, uint256 _value) is calling
This means that
balanceOf[_from]
is going always going to bebalanceOf[TestCrowdSale]
. IfTestCrowdSale
doesn't have a balance greater thanmsg.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.