Can't send transaction even if I use an example from official Ethereum webpage
Asked Answered
A

2

9

I write Crowdsale using this example. But I can't send a transaction, my test fails with an error:

 Contract: Crowdsale should accept payments after start:
     AssertionError: expected promise to be fulfilled but it was rejected with 'Error: VM Exception while processing the transaction: revert'

I tried to set gas price to the transaction like this crowdsale.sendTransaction({value, from: buyer, gas: 4712388}) but it doesn't help me.

My Crowdsale:

pragma solidity ^0.4.16;

interface token {
  function transfer(address receiver, uint amount) external;
}

contract Crowdsale {
  address public beneficiary;
  uint public fundingGoal;
  uint public amountRaised;
  uint public deadline;
  uint public price;
  token public tokenReward;
  mapping(address => uint256) public balanceOf;

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

  function Crowdsale(
    address ifSuccessfulSendTo,
    uint fundingGoalInEthers,
    uint durationInMinutes,
    uint etherCostOfEachToken,
    address addressOfTokenUsedAsReward
  ) public {
    beneficiary = ifSuccessfulSendTo;
    fundingGoal = fundingGoalInEthers * 1 ether;
    deadline = now + durationInMinutes * 1 minutes;
    price = etherCostOfEachToken * 1 ether;
    tokenReward = token(addressOfTokenUsedAsReward);
  }

  function () public payable {
    uint amount = msg.value;
    balanceOf[msg.sender] += amount;
    amountRaised += amount;
    tokenReward.transfer(msg.sender, amount / price);
    FundTransfer(msg.sender, amount, true);
  }
}

My tests (I use these tests as an example):

// You can find all those helpers here: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers
import ether from '../helpers/ether';
import { advanceBlock } from '../helpers/advanceToBlock';
import { increaseTimeTo, duration } from '../helpers/increaseTime';
import latestTime from '../helpers/latestTime';

const BigNumber = web3.BigNumber;

const should = require('chai')
  .use(require('chai-as-promised'))
  .use(require('chai-bignumber')(BigNumber))
  .should();

const Crowdsale = artifacts.require('Crowdsale');
const Coin = artifacts.require('Coin');

contract('Crowdsale', accounts => {
  let startTime;

  let crowdsale;
  let token;

  const value = ether(8);
  const buyer = accounts[1];
  const tokenReward = accounts[2];
  const beneficiary = accounts[2];

  before(async () => {
    // Advance to the next block to correctly read time in the solidity "now" function interpreted by testrpc
    await advanceBlock();
  });

  beforeEach(async () => {
    token = await Coin.new();

    // Transfer an amount to address of token used as reward
    const tokenRewardAmount = 1000000;
    token.transfer(tokenReward, tokenRewardAmount);

    startTime = latestTime() + duration.minutes(1);

    crowdsale = await Crowdsale.new(beneficiary, 200, 30, 1, tokenReward);
  });

  it('should accept payments after start', async () => {
    await increaseTimeTo(startTime);
    await crowdsale.sendTransaction({value, from: buyer, gas: 4712388}).should.be.fulfilled;
  });
});

My Coin:

pragma solidity ^0.4.19;

import 'zeppelin-solidity/contracts/token/ERC20/MintableToken.sol';

contract Coin is MintableToken {
  uint8 public decimals = 0;
  uint public initialSupply = 1000000 * 10 ** uint(decimals); // 1,000,000;
  string public name = "My Coin";
  string public symbol = "XMY";

  function Coin() public {
    totalSupply_ = initialSupply;
    balances[msg.sender] = totalSupply_;
  }
}

I am developing a “bookmaker” where a crowdsale represents an event. It means I will create dozens of crowdsales and I can't use Zeppelin's crowdsale since it mint a token. So, is there any other ways to create a crowdsale without minting tokens? I think there should be other ways since the example from Ethereum's webpage works without minting but because of some reason, it doesn't work for me. Where is my mistake? How can I solve the problem?

Asleep answered 17/2, 2018 at 15:48 Comment(0)
S
2

As I understand your question you are trying to send some funds to your crowdsale contract.

Change this line:

await crowdsale.sendTransaction({value, from: buyer, gas: 4712388}).should.be.fulfilled;

To this:

await web3.eth.sendTransaction({
  from: buyer, 
  to: crowdsale,
  value: value,  
  gas: 4712388
}).should.be.fulfilled;
Submariner answered 17/2, 2018 at 17:58 Comment(1)
Hey Roman, thanks for the answer! I tried, but it fails with an error: AssertionError: expected promise to be fulfilled but it was rejected with 'Error: VM Exception while processing transaction: revert'. Then I changed to: crowdsale to to: crowdsale.address and it also fails.Asleep
M
2

First off, your test case has a bug. You're sending in the address of the beneficiary account instead of the address of your token contract.

crowdsale = await Crowdsale.new(beneficiary, 200, 30, 1, tokenReward);

should be

crowdsale = await Crowdsale.new(beneficiary, 200, 30, 1, token.address);

Second, as written, you are going to fail in the transaction itself for a couple reasons. Your fallback function is trying to transfer tokens with tokenReward.transfer(msg.sender, amount / price); and then the coin contract's transfer method checks the balance with require(_value <= balances[msg.sender]);. The two values of msg.sender are different. In the first usage, msg.sender will be the value of buyer. However, on the second usage, msg.sender is the address of the token contract. msg.sender is the sender of the message, not the original sender of the transaction.

From the Solidity docs:

The values of all members of msg, including msg.sender and msg.value can change for every external function call. This includes calls to library functions.

For example, if address A calls contract B, which then calls contract C. The value of msg.sender will be A inside contract B, but it is the address of contract B while in contract C.

transfer should only be called by the token owner directly. For your use case, you can either call approve first and then call transferFrom, or since you're using a MintableToken, you can mint the tokens instead. You're already using the Zeppelin library, so take a look at the buyTokens functions in their CrowdSale contract.

One final note, it's never a good idea to put too much logic inside a fallback function. They are limited to 2300 gas and frequently fail when you try to do much more than log events.

Mercator answered 17/2, 2018 at 18:25 Comment(1)
Hi Adam! Thank you, it’s helpful! The only problem is that I am developing a “bookmaker” where customized Crowdsales represents events. So I will create dozens of Crowdsales. And in Zeppelin's crowdsale it creates a token contract which means I will able to create only one "event". But on Ethereum webpage I found an example that should work for me. It uses tokens from tokenReward address.Asleep

© 2022 - 2024 — McMap. All rights reserved.