1

My goal is to create a token and sell it via the crowdsale contract. I used the examples:

The code below is the exact same as on the ethereum homepage besides the constructor naming.

pragma solidity ^0.4.16;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    uint256 public totalSupply;

    // 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);

    // This generates a public event on the blockchain that will notify clients
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // This notifies clients about the amount burnt
    event Burn(address indexed from, uint256 value);

    /**
     * Constructor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    constructor (
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        emit Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * 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) {
        _transfer(msg.sender, _to, _value);
        return true;
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` on behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        emit Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other account
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        emit Burn(_from, _value);
        return true;
    }
}

Which created a new token contract (address 1). All created tokens belong to message.sender.

Then I used the crowdsale contract to sell these ERC20 tokens. I deployed the crowdsale contract (locally with Ganache) and set the tokenAddress in the constructor to address 1. By sending ETH from a second account (<acc2>) to the contract address (address 2), the default function gets called and balanceOf[<acc2>] shows me the ETH balance <acc2> sent to the contract.

The code below is the exact same as on the ethereum homepage besides the constructor naming.

pragma solidity ^0.4.18;

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;
    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;
            }
        }
    }
}

I'm not understanding three things with the default function:

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);
}

tokenReward.transfer(msg.sender, amount / price); should transfer the amount of tokens bought immediately as far as I understand. Checking on MEW (locally) still shows me that <acc1> owns all the tokens of the token creation. <acc2> owns less ETH and the amount it bought but not of the tokens address instead of the crowdsales address.

Question 1) How has the crowdsale contract access to the tokens of acc1, is this in the ERC20 standard and set by tokenReward = token(addressOfTokenUsedAsReward);

Question 2) Why is acc2 not owning any tokens from the original tokens address (address 1) and instead owning tokens from the crowdsale address (address 2)?

Question 3) The _transfer function in the ERC20 example checks if the sender has enough tokens:

 // Check if the sender has enough
require(balanceOf[_from] >= _value);

Does this mean the last person has to buy the exact remaining amount of tokens otherwise the crowdsale throws an error?

3
  • Please post your full smart contracts source code then we can answer more correctly
    – Tony Dang
    Aug 29, 2018 at 13:49
  • It's the example on the ethereum home page (the link) no changes besides the constructor naming. But I will add it here too.
    – Andi Giga
    Aug 30, 2018 at 5:23
  • I add my answer, PTAL
    – Tony Dang
    Aug 30, 2018 at 6:59

1 Answer 1

1

Question 1) How has the crowdsale contract access to the tokens of acc1, is this in the ERC20 standard and set by tokenReward = token(addressOfTokenUsedAsReward);

A1: It can if acc1 approve the crowdsale contract to delegate it to transfer some amount of token via approve() function in the ERC20 token.

this code tokenReward = token(addressOfTokenUsedAsReward); is basic as other programming language, you can cast ERC20 token to token interface in the crowdsale contract because ERC20 token implement the necessary function transfer of token interface.

Question 2) Why is acc2 not owning any tokens from the original tokens address (address 1) and instead owning tokens from the crowdsale address (address 2)?

A2:: When you call tokenReward.transfer(msg.sender, amount / price); then the msg.sender inside the tokenReward.transfer() will be the smart contract address, not the <address1>. It means that we're transferring tokens from crowdsale contract to the msg.sender that calling fallback/default function.

Question 3) The _transfer function in the ERC20 example checks if the sender has enough tokens:

A3: This is basic validation we need to do in every coding/business logic, not only in the ERC20. If not pass validation then tnx will be reverted and ETH amount will be refunded to the caller.

8
  • Hi thx, for the reply. Regarding 2) Are you sure, I think msg.sender is the caller of the default function, in this case <acc2> who sends funds to the contracts' address. I can also see in balanceOf[msg.sender] that the msg.sender is indeed the caller of the function. (see solidity.readthedocs.io/en/develop/…). What I still don't get why it is not transferring the ERC20 contract's token.
    – Andi Giga
    Aug 30, 2018 at 7:32
  • Regarding 3) Yes, but if it exceeds the amount there is no deduction, right? Which means if I create e.g. 1000 tokens and 999 are sold, the last person has exactly to buy 1 token? If he tries to buy two tokens the contract throws an error, right?
    – Andi Giga
    Aug 30, 2018 at 7:34
  • Regarding 3) yes, the tnx will be reverted and refund to the last person
    – Tony Dang
    Aug 30, 2018 at 7:54
  • Regarding 2) Sorry, I dont write clearly. I mean the msg.sender inside the tokenReward.transfer() function will be the crowd sale smart contract address. It also mean that we're transferring token from crowdsale smart contract to msg.sender of the fallback/default function.
    – Tony Dang
    Aug 30, 2018 at 7:58
  • 2) Ok but I checked MEW and added custom tokens A: The address of the token creation contract (address 1) B: The address of the crowd sale (address 2) Then 100% of the created tokens still remain by the creator. The buyer owns the tokens with the address of the crowd sale. If the crowd sale contract calls the ERC20 contract and transfers the tokens to msg.sender shouldn't have the msg.sender the tokens which have been created with the token creation contract?
    – Andi Giga
    Aug 30, 2018 at 8:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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