Index

ICO

  1. Chapter 1
  2. Chapter 2
  3. Chapter 3
  4. Chapter 4
  5. Chapter 5
  6. Chapter 6

Chapter 6

function buyTokens
pragma solidity >=0.4.22 <0.7.0;

import "./DappToken.sol";

// 9. Import SafeMath
import "./SafeMath.sol";

contract DappTokenSale {

    // 10. using the SafeMath Library
    using SafeMath for uint256;

    address admin;
    DappToken public tokenContract;
    uint256 public tokenPrice;

    // 4. Token Sold
    uint256 public tokensSold;

    // 6. Sell Event
    event Sell(address _buyer, uint256 _amount);

    constructor(DappToken _tokenContract, uint256 _tokenPrice) public {

        // 1. Assign an Admin
        admin = msg.sender;

        // 2. Token Contract
        tokenContract = _tokenContract;

        //3. Token Price
        tokenPrice = _tokenPrice;

    }
    function buyTokens(uint256 _numberOfTokens) public payable {

        // 8. Require that value is equal to tokens
        require(msg.value == _numberOfTokens.mul(tokenPrice), "msg.value is not equal to required value");

        // 9. Require that the contract has enough tokens
        require(tokenContract.balanceOf(address(this)) >= _numberOfTokens, "contract don't have enough tokens");

        // 10. Require that a transfer is successful
        // Why this functionality is required?
        // It does the token tranfer from contract address to sender, otherwise buyer i.e. msg.sender balance will not increase,
        // neither balance of contract address will descrease
        require(tokenContract.transfer(msg.sender, _numberOfTokens), "transfer the _numberofTokens from contract address to msg.sender");


        // 5. Keep track of tokenSold
        tokensSold += _numberOfTokens;

        // 7. Trigger Sell Event
        emit Sell(msg.sender, _numberOfTokens);

    }
}


Reset migrations
truffle migrate --reset



Notes on msg.value:
msg.value is a member of the msg (message) object when sending (state transitioning) transactions on the Ethereum network.

msg.value contains the amount of wei (ether / 1e18) sent in the transaction.
msg.data (bytes): complete calldata
msg.gas (uint): remaining gas - deprecated in version 0.4.21 and to be replaced by gasleft()
msg.sender (address): sender of the message (current call)
msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)
msg.value (uint): number of wei sent with the message



Buy tokens tests:
const DappTokenSale = artifacts.require("DappTokenSale");
// 1. Decalare DappToken
const DappToken = artifacts.require("DappToken");

contract("DappTokenSale"function(accounts) {
  let tokenSaleInstance;
  var tokenInstance;
  // 2. Set the admin, who has all money.
  var admin = accounts[0];
  var buyer = accounts[1];
  var tokenPrice = 1000000000000000//wei (0.001 ether)
  // 3. Set the token available for sale
  var tokensAvailable = 750000;
  var numberOfTokens;

  it("initializes the contract with the correct values"function() {
    return DappTokenSale.deployed()
      .then(function(instance) {
        tokenSaleInstance = instance;
        return tokenSaleInstance.address;
      })
      .then(function(address) {
        assert.notEqual(address, 0x0"has contract address");
        return tokenSaleInstance.tokenContract();
      })
      .then(function(address) {
        assert.notEqual(address, 0x0"has contract address");
        return tokenSaleInstance.tokenPrice();
      })
      .then(function(price) {
        assert.equal(price, tokenPrice, "token price is correct");
      });
  });

  it("facilitates token buying"function() {
    return DappToken.deployed()
      .then(function(instance) {
        // 4. Grab token instance first
        tokenInstance = instance;
        return DappTokenSale.deployed();
      })
      .then(function(instance) {
        // 5. Then grap token sale instance
        tokenSaleInstance = instance;

        // 6. Provision 75% of all tokens to the token sale
        return tokenInstance.transfer(tokenSaleInstance.address, tokensAvailable, { from: admin });
      })
      .then(function(receipt) {
        numberOfTokens = 10;
        var value = numberOfTokens * tokenPrice;
        // 7. Memo: tokenPrice is 0.001 ether in wei
        // Memo: msg.value contains the amount of wei (ether / 1e18) sent in the transaction.
        return tokenSaleInstance.buyTokens(numberOfTokens, { from: buyer, value: value });
      })
      .then(function(receipt) {
        assert.equal(receipt.logs.length1"triggers one event");
        assert.equal(receipt.logs[0].event, "Sell"'should be "Sell" event');
        assert.equal(receipt.logs[0].args._buyer, buyer, "logs the account that purchased the tokens");
        assert.equal(receipt.logs[0].args._amount, numberOfTokens, "logs the number of tokens purchaged");
        return tokenSaleInstance.tokensSold();
      })
      .then(function(amount) {
        assert.equal(amount.toNumber(), numberOfTokens, "increments the number of tokens in tokenSold variable");
        return tokenInstance.balanceOf(buyer);
      })
      .then(function(balance) {
        // console.log(balance.toNumber(), "balance of buyer");
        assert.equal(balance.toNumber(), numberOfTokens, "balance of buyer is not same as number of tokens given to buyToken function");
        return tokenInstance.balanceOf(tokenSaleInstance.address);
      })
      .then(function(balance) {
        // console.log(balance.toNumber(), "balance of contract address");
        assert.equal(balance.toNumber(), tokensAvailable - numberOfTokens, "contract balance didn't decreased by numberOfTokens send to buyToken function");
        //8. Try to buy tokens different with the ether value, the value i.e. wei sent in the transcation is much less than numberOfTokens * tokenPrice
        return tokenSaleInstance.buyTokens(numberOfTokens, { from: buyer, value: 1 });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"msg.value must be more than numberOfTokens * tokenPrice");
        //9. Ask to buy with numberOfTokens more than the smart contract has
        return tokenSaleInstance.buyTokens(800000, { from: buyer, value: 800000 * tokenPrice });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.reason == "contract don't have enough tokens""cannot purchase more tokens than available");
      });
  });
});