Index

ICO

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

Chapter 4

transferFrom

After approval, transfer tokens on behalf.

pragma solidity >=0.4.22 <0.7.0;

contract DappToken {
    string public name = "DApp Token";
    string public symbol = "DAPP";
    string public standard = "DApp Token v1.0";

    uint256 public totalSupply;


    event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 _value

    );

    // 2. Approval even, see the documentation
    // I the owner, approved _spender to spend _value number of dapp tokens
    event Approval(
        address indexed _owner,
        address indexed _spender,
        uint256 _value
    );

    mapping(address => uint256) public balanceOf;

    // 4. allownance variable, it contains nested mapping. We have mapping within a mapping
    mapping(address => mapping(address => uint256)) public allowance;

    constructor (uint256 _initialSupply) public {
        balanceOf[msg.sender] = _initialSupply;
        totalSupply = _initialSupply;
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {

    require(balanceOf[msg.sender] >= _value, "Don't have enough money");

    balanceOf[msg.sender] -= _value;
    balanceOf[_to] += _value;

    emit Transfer(msg.sender, _to, _value);

    return true;
    }

    // 1. Approve function (Allows _spender to withdraw from your account multiple times)
    function approve(address _spender, uint256 _value) public returns (bool success) {

    // 5. Allowance
    allowance[msg.sender][_spender] = _value;

    // 3. Call the Approval event
        emit Approval(msg.sender, _spender, _value);

        return true;
    }
    // 6. Transfer money, similar to transfer function, but its done by me (msg.sender) on behalf of _from address
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        
        // 7. Check if _from account has enough balance
        require(_value <= balanceOf[_from], "You are trying to spend more than user has");

        // 8. Check if msg.sender tranfers amount of money that is allowed
        require(_value <= allowance[_from][msg.sender], "You are trying to spend more than allowed limit");

        // 9. Tranfer event
        emit Transfer(_from, _to, _value);

        return true;

    }
}



Tests:

it("handles delegated token transfers"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        fromAccount = accounts[2];
        toAccount = accounts[3];
        spendingAccount = accounts[4]; // This is msg.sender

        // Tranfer some tokens to fromAccount, so that it has some tokens to transfer
        return tokenInstance.transfer(fromAccount, 100, { from: accounts[0] });
      })
      .then(function(receipt) {
        // Approve spendingAccount to spend 10 tokens from fromAccount
        return tokenInstance.approve(spendingAccount, 10, { from: fromAccount });
      })
      .then(function(receipt) {
        // Try transferring something larger than the sender's balance
        return tokenInstance.transferFrom(fromAccount, toAccount, 9999, { from: spendingAccount });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"cannot transfer value larger than balance");
        // Try transferring something larger than the approved amount

        return tokenInstance.transferFrom(fromAccount, toAccount, 20, { from: spendingAccount });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"cannot transfer value larger than approved amount");
        return tokenInstance.transferFrom.call(fromAccount, toAccount, 10, { from: spendingAccount });

      }).then(function(success){
        assert.equal(success, true);
        return tokenInstance.transferFrom(fromAccount, toAccount, 10, { from: spendingAccount });

      }).then(function(receipt){
        assert.equal(receipt.logs.length1"triggers one event");
        assert.equal(receipt.logs[0].event, "Transfer"'should be "Transfer" event');
        assert.equal(receipt.logs[0].args._from, fromAccount, "logs the account the tokens are transferred from");
        assert.equal(receipt.logs[0].args._to, toAccount, "logs the account the tokens are transferred to");
        assert.equal(receipt.logs[0].args._value, 10"logs the transfer amount");

      });
  });



Deduct balance and deduct allowance amount:

pragma solidity >=0.4.22 <0.7.0;

contract DappToken {
    string public name = "DApp Token";
    string public symbol = "DAPP";
    string public standard = "DApp Token v1.0";

    uint256 public totalSupply;


    event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 _value

    );

    // 2. Approval even, see the documentation
    // I the owner, approved _spender to spend _value number of dapp tokens
    event Approval(
        address indexed _owner,
        address indexed _spender,
        uint256 _value
    );

    mapping(address => uint256) public balanceOf;

    // 4. allownance variable, it contains nested mapping. We have mapping within a mapping
    mapping(address => mapping(address => uint256)) public allowance;

    constructor (uint256 _initialSupply) public {
        balanceOf[msg.sender] = _initialSupply;
        totalSupply = _initialSupply;
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {

    require(balanceOf[msg.sender] >= _value, "Don't have enough money");

    balanceOf[msg.sender] -= _value;
    balanceOf[_to] += _value;

    emit Transfer(msg.sender, _to, _value);

    return true;
    }

    // 1. Approve function (Allows _spender to withdraw from your account multiple times)
    function approve(address _spender, uint256 _value) public returns (bool success) {

    // 5. Allowance
    allowance[msg.sender][_spender] = _value;

    // 3. Call the Approval event
        emit Approval(msg.sender, _spender, _value);

        return true;
    }
    // 6. Transfer money, similar to transfer function, but its done by me (msg.sender) on behalf of _from address
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        
        // 7. Check if _from account has enough balance
        require(_value <= balanceOf[_from], "You are trying to spend more than user has");

        // 8. Check if msg.sender tranfers amount of money that is allowed
        require(_value <= allowance[_from][msg.sender], "You are trying to spend more than allowed limit");

        // 10. Deduct balance _from account, and adding it to _to account
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;

        // 11. Deduct the allowance
        allowance[_from][msg.sender] -= _value;


        // 9. Tranfer event
        emit Transfer(_from, _to, _value);

        return true;

    }
}



Tests:
it("handles delegated token transfers"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        fromAccount = accounts[2];
        toAccount = accounts[3];
        spendingAccount = accounts[4]; // This is msg.sender

        // Tranfer some tokens to fromAccount, so that it has some tokens to transfer
        return tokenInstance.transfer(fromAccount, 100, { from: accounts[0] });
      })
      .then(function(receipt) {
        // Approve spendingAccount to spend 10 tokens from fromAccount
        return tokenInstance.approve(spendingAccount, 10, { from: fromAccount });
      })
      .then(function(receipt) {
        // Try transferring something larger than the sender's balance
        return tokenInstance.transferFrom(fromAccount, toAccount, 9999, { from: spendingAccount });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"cannot transfer value larger than balance");
        // Try transferring something larger than the approved amount

        return tokenInstance.transferFrom(fromAccount, toAccount, 20, { from: spendingAccount });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"cannot transfer value larger than approved amount");
        return tokenInstance.transferFrom.call(fromAccount, toAccount, 10, { from: spendingAccount });

      }).then(function(success){
        assert.equal(success, true);
        return tokenInstance.transferFrom(fromAccount, toAccount, 10, { from: spendingAccount });

      }).then(function(receipt){
        assert.equal(receipt.logs.length1"triggers one event");
        assert.equal(receipt.logs[0].event, "Transfer"'should be "Transfer" event');
        assert.equal(receipt.logs[0].args._from, fromAccount, "logs the account the tokens are transferred from");
        assert.equal(receipt.logs[0].args._to, toAccount, "logs the account the tokens are transferred to");
        assert.equal(receipt.logs[0].args._value, 10"logs the transfer amount");
        return tokenInstance.balanceOf(fromAccount);
      }).then(function(balance){
        assert.equal(balance.toNumber(), 90'left amount after deduction');
        return tokenInstance.balanceOf(toAccount);
      }).then(function(balance){
        assert.equal(balance.toNumber(), 10'adds the amount from the recieving account');
        return tokenInstance.allowance(fromAccount, spendingAccount);
      }).then(function(allowance) {
        assert.equal(allowance.toNumber(), 0'deducts the amount from the allowance');
      })
  });




Full test code (DappToken.js):
const DappToken = artifacts.require("DappToken");

contract("DappToken"function(accounts) {
  var tokenInstance;
  it("initialize the contract with correct values"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        return tokenInstance.name();
      })
      .then(function(name) {
        assert.equal(name"DApp Token""has the correct name");
        return tokenInstance.symbol();
      })
      .then(function(symbol) {
        assert.equal(symbol, "DAPP""has the correct symbol");
        return tokenInstance.standard();
      })
      .then(function(standard) {
        assert.equal(standard, "DApp Token v1.0""has the correct standard");
      });
  });
  it("allocates the initial supply supply upon deployment"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        return tokenInstance.totalSupply();
      })
      .then(function(totalSupply) {
        assert.equal(totalSupply.toNumber(), 1000000"sets the total supply to 1,000,000");
        return tokenInstance.balanceOf(accounts[0]);
      })
      .then(function(adminBalance) {
        assert.equal(adminBalance.toNumber(), 1000000"it allocates the intial supply to the admin account");
      });
  });

  it("transfer token ownership"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        // console.log(accounts[1])
        return tokenInstance.transfer.call(accounts[1], 999999999999);
      })
      .then(assert.fail)
      .catch(function(error) {
        // console.log(error.message)
        assert(error.message.indexOf("revert") >= 0"error message must contain revert");
        return tokenInstance.transfer.call(accounts[1], 250000, { from: accounts[0] });
      })
      .then(function(success) {
        assert.equal(success, true"it return true");
        return tokenInstance.transfer(accounts[1], 250000, { from: accounts[0] });
      })
      .then(function(receipt) {
        assert.equal(receipt.logs.length1"triggers one event");
        assert.equal(receipt.logs[0].event, "Transfer"'should be "Transfer event');
        assert.equal(receipt.logs[0].args._from, accounts[0], "logs the account the tokens are transferred from");
        assert.equal(receipt.logs[0].args._to, accounts[1], "logs the account the tokens are transferred to");
        assert.equal(receipt.logs[0].args._value, 250000"logs the transfer amount");
        return tokenInstance.balanceOf(accounts[1]);
      })
      .then(function(balance) {
        assert.equal(balance.toNumber(), 250000"adds the amount to the receiving account");
        return tokenInstance.balanceOf(accounts[0]);
      })
      .then(function(balance) {
        assert.equal(balance.toNumber(), 750000"deducts the amount from the sending account");
      });
  });
  it("approves tokens for delegated transfer"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        return tokenInstance.approve.call(accounts[1], 100);
      })
      .then(function(success) {
        assert.equal(success, true"it returns true");
        return tokenInstance.approve(accounts[1], 100, { from: accounts[0] });
      })
      .then(function(receipt) {
        assert.equal(receipt.logs.length1"triggers one event");
        assert.equal(receipt.logs[0].event, "Approval"'should be "Approval" event');
        assert.equal(receipt.logs[0].args._owner, accounts[0], "logs the account the tokens are authorized by");
        assert.equal(receipt.logs[0].args._spender, accounts[1], "logs the account the tokens are authorized to");
        assert.equal(receipt.logs[0].args._value, 100"logs the transfer amount");
        return tokenInstance.allowance(accounts[0], accounts[1]);
      })
      .then(function(allowance) {
        assert.equal(allowance.toNumber(), 100"stores the allowance for delegated transfer");
      });
  });

  it("handles delegated token transfers"function() {
    return DappToken.deployed()
      .then(function(instance) {
        tokenInstance = instance;
        fromAccount = accounts[2];
        toAccount = accounts[3];
        spendingAccount = accounts[4]; // This is msg.sender

        // Tranfer some tokens to fromAccount, so that it has some tokens to transfer
        return tokenInstance.transfer(fromAccount, 100, { from: accounts[0] });
      })
      .then(function(receipt) {
        // Approve spendingAccount to spend 10 tokens from fromAccount
        return tokenInstance.approve(spendingAccount, 10, { from: fromAccount });
      })
      .then(function(receipt) {
        // Try transferring something larger than the sender's balance
        return tokenInstance.transferFrom(fromAccount, toAccount, 9999, { from: spendingAccount });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"cannot transfer value larger than balance");
        // Try transferring something larger than the approved amount

        return tokenInstance.transferFrom(fromAccount, toAccount, 20, { from: spendingAccount });
      })
      .then(assert.fail)
      .catch(function(error) {
        assert(error.message.indexOf("revert") >= 0"cannot transfer value larger than approved amount");
        return tokenInstance.transferFrom.call(fromAccount, toAccount, 10, { from: spendingAccount });

      }).then(function(success){
        assert.equal(success, true);
        return tokenInstance.transferFrom(fromAccount, toAccount, 10, { from: spendingAccount });

      }).then(function(receipt){
        assert.equal(receipt.logs.length1"triggers one event");
        assert.equal(receipt.logs[0].event, "Transfer"'should be "Transfer" event');
        assert.equal(receipt.logs[0].args._from, fromAccount, "logs the account the tokens are transferred from");
        assert.equal(receipt.logs[0].args._to, toAccount, "logs the account the tokens are transferred to");
        assert.equal(receipt.logs[0].args._value, 10"logs the transfer amount");
        return tokenInstance.balanceOf(fromAccount);
      }).then(function(balance){
        assert.equal(balance.toNumber(), 90'left amount after deduction');
        return tokenInstance.balanceOf(toAccount);
      }).then(function(balance){
        assert.equal(balance.toNumber(), 10'adds the amount from the recieving account');
        return tokenInstance.allowance(fromAccount, spendingAccount);
      }).then(function(allowance) {
        assert.equal(allowance.toNumber(), 0'deducts the amount from the allowance');
      })
  });
});