1

I have build a token and a crowdsale contract based on ethereum tutorials. Whenever I send 1 Ether to the Crowdsale contract, which I linked with the Token contract, it sends back 0 tokens. enter image description here enter image description here Crowdsale.sol:

pragma solidity ^0.4.24;

import 'browser/SafeMath.sol';
import 'browser/ERC20.sol';

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

contract Crowdsale {
    using SafeMath for uint;
    address public beneficiary;
    uint public fundingGoal;
    uint public amountRaised;
    uint public deadline;
    uint public price;
    token public tokenReward;
    mapping(address => uint256) public balanceOf;
    bool fundingGoalReached = false;
    bool crowdsaleClosed = false;

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

    /**
     * Constructor function
     *
     * Setup the owner
     */
    constructor (
        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);
    }

    /**
     * Fallback function
     *
     * The function without name is the default function that is called whenever anyone sends funds to a contract
     */
    function () payable public {
        require(!crowdsaleClosed);
        uint amount = msg.value;
        balanceOf[msg.sender] += amount;
        amountRaised += amount;
        tokenReward.transfer(msg.sender, amount / price);
       emit 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() public afterDeadline {
        if (amountRaised >= fundingGoal){
            fundingGoalReached = true;
            emit 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() public afterDeadline {
        if (!fundingGoalReached) {
            uint amount = balanceOf[msg.sender];
            balanceOf[msg.sender] = 0;
            if (amount > 0) {
                if (msg.sender.send(amount)) {
                   emit FundTransfer(msg.sender, amount, false);
                } else {
                    balanceOf[msg.sender] = amount;
                }
            }
        }

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

Token.sol:

pragma solidity ^0.4.24;

import 'browser/SafeMath.sol';
import 'browser/ERC20.sol';
import 'browser/Approve.sol';
import 'browser/Owner.sol';

contract FixedSupplyToken is ERC20Interface, Owned {
    using SafeMath for uint;

    string public symbol;
    string public  name;
    uint8 public decimals;
    uint _totalSupply;

    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowed;

    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
    constructor(string _symbol, string _name, uint8 _decimals, uint256 __totalSupply) public {
        symbol = _symbol;
        name = _name;
        decimals = _decimals;
        _totalSupply = __totalSupply;
        _totalSupply = _totalSupply * 10**uint(decimals);
        balances[owner] = _totalSupply;
        emit Transfer(address(0), owner, _totalSupply);
    }


    // ------------------------------------------------------------------------
    // Total supply
    // ------------------------------------------------------------------------
    function totalSupply() public view returns (uint) {
        return _totalSupply.sub(balances[address(0)]);
    }


    // ------------------------------------------------------------------------
    // Get the token balance for account `tokenOwner`
    // ------------------------------------------------------------------------
    function balanceOf(address tokenOwner) public view returns (uint balance) {
        return balances[tokenOwner];
    }


    // ------------------------------------------------------------------------
    // Transfer the balance from token owner's account to `to` account
    // - Owner's account must have sufficient balance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transfer(address to, uint tokens) public returns (bool success) {
        balances[msg.sender] = balances[msg.sender].sub(tokens);
        balances[to] = balances[to].add(tokens);
        emit Transfer(msg.sender, to, tokens);
        return true;
    }


    // ------------------------------------------------------------------------
    // Token owner can approve for `spender` to transferFrom(...) `tokens`
    // from the token owner's account
    //
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
    // recommends that there are no checks for the approval double-spend attack
    // as this should be implemented in user interfaces 
    // ------------------------------------------------------------------------
    function approve(address spender, uint tokens) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }


    // ------------------------------------------------------------------------
    // Transfer `tokens` from the `from` account to the `to` account
    // 
    // The calling account must already have sufficient tokens approve(...)-d
    // for spending from the `from` account and
    // - From account must have sufficient balance to transfer
    // - Spender must have sufficient allowance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transferFrom(address from, address to, uint tokens) public returns (bool success) {
        balances[from] = balances[from].sub(tokens);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
        balances[to] = balances[to].add(tokens);
        emit Transfer(from, to, tokens);
        return true;
    }


    // ------------------------------------------------------------------------
    // Returns the amount of tokens approved by the owner that can be
    // transferred to the spender's account
    // ------------------------------------------------------------------------
    function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }


    // ------------------------------------------------------------------------
    // Token owner can approve for `spender` to transferFrom(...) `tokens`
    // from the token owner's account. The `spender` contract function
    // `receiveApproval(...)` is then executed
    // ------------------------------------------------------------------------
    function approveAndCall(address spender, uint tokens, bytes data) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data);
        return true;
    }


    // ------------------------------------------------------------------------
    // Don't accept ETH
    // ------------------------------------------------------------------------
    function () public payable {
        revert();
    }


    // ------------------------------------------------------------------------
    // Owner can transfer out any accidentally sent ERC20 tokens
    // ------------------------------------------------------------------------
    function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
        return ERC20Interface(tokenAddress).transfer(owner, tokens);
    }
}
  • 1
    You don't seem to be actually using your FixedSupplyToken from your second code block. In the payable function of Crowdsale, you're merely calling an empty method of an interface, transfer, that is. Try to import your token contract. – Paul Razvan Berg Aug 25 '18 at 23:54
  • @PaulRBerg Could you explain what you mean? It looks to me like the fallback function in Crowdsale has the line tokenReward.transfer(msg.sender, amount / price);. tokenReward is a state variable which is assigned an address in the constructor. – user19510 Aug 26 '18 at 5:59
  • Most likely the issue is that amount / price < 1. For example, it looks from the screenshot (though counting 0s in a screenshot is error prone) that you sent 0.5 ether. If etherCostOfEachToken is 1, then price is 1 ether, and 0.5 ether / 1 ether is 0. (Solidity uses only integer math.) If you share a link to the transaction on Etherscan, it would be possible to quickly determine the actual values. – user19510 Aug 26 '18 at 6:03
  • @smarx you're right, I didn't catch that part in the constructor. – Paul Razvan Berg Aug 26 '18 at 9:57
1

A small amount of sleuthing uncovered the actual transaction on Ropsten: https://ropsten.etherscan.io/tx/0x2821e222ed6983c8df0ab338016f81ec539b84f76314890dc8737e8d0add0b70.

That confirms my hypothesis in the comments above. 0.5 ether was transferred to contract 0xb979e6585e521bc7928e061bf6176a447f378f4b, and assuming the above code is the right code for that contract, the price should be found at storage slot 4:

$ http post https://ropsten.infura.io jsonrpc=1.0 method=eth_getStorageAt params:='["0xb979e6585e521bc7928e061bf6176a447f378f4b", "0x04", "latest"]' id:=1 | jq -r .result
0x0000000000000000000000000000000000000000000000000de0b6b3a7640000

0xde0b6b3a7640000 == 1000000000000000000, which is 1 ether. The fallback function for the crowdsale contract divides the incoming amount by the price, and 0.5 ether / 1 ether == 0. So 0 token units are transferred.

  • Yes I used 0.5 ether. Shouldn't it give me a fraction of the token since I used decimals? And how can I fix it? I want for 0.5 ether to reward a buyer with more tokens. Oh, btw I used etherPriceOfEachToken=1ether. If I buy for 1 ether or more metamask sends error. I also used etherpriceofeachtoken=5 and bought for 1 ether and the result was still 0 – Mihai Andrei Checiu Aug 26 '18 at 7:48
  • You're the one setting the price, so you can make this do whatever you want. Assuming the token has 18 decimal places (just like ether), you probably just want to drop the * 1 ether and just do price = etherCostOfEachToken. – user19510 Aug 26 '18 at 7:59
  • For the error you get when you try to use more ether (probably something about an always failing transaction?), my guess is that you never transferred any tokens to the Crowdsale contract, so it has no tokens to transfer out. – user19510 Aug 26 '18 at 8:00
  • I'm trying again keeping in mind your advices. But whenever I change the * 1 ether either with static variable like a number or divide it by 2 for example, metamask throws error – Mihai Andrei Checiu Aug 26 '18 at 8:01
  • When does MetaMask give you an error, and what error is it? Again, I'm assuming the problem is that you never transferred any tokens to the Crowdsale contract. So any time you try to get more than 0 tokens out of it, it should revert the transaction. – user19510 Aug 26 '18 at 8:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.