Index

ICO

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

Chapter 3

ERC-20 Token Standard
https://eips.ethereum.org/EIPS/eip-20

This standard provides basic functionality to transfer tokens, as well as allow tokens to be approved so they can be spent by another on-chain third party.


Delegate Transfer

Its a two step process

One function is going to allow our account to approve the transfer

Another function is going to handle delegated transfer



approve


Allows _spender to withdraw from your account multiple times, up to the _value amount. If this function is called again it overwrites the current allowance with _value.


NOTE: To prevent attack vectors like the one described here and discussed here, clients SHOULD make sure to create user interfaces in such a way that they set the allowance first to 0 before setting it to another value for the same spender. THOUGH The contract itself shouldn’t enforce it, to allow backwards compatibility with contracts deployed before

function approve(address _spender, uint256 _value) public returns (bool success)


transferFrom

Transfers _value amount of tokens from address _from to address _to, and MUST fire the Transfer event.
The transferFrom method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD throw unless the _from account has deliberately authorized the sender of the message via some mechanism.
Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event.

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)




allowance



Returns the amount which _spender is still allowed to withdraw from _owner.

function allowance(address _owner, address _spender) public view returns (uint256 remaining)




Approval



MUST trigger on any successful call to approve(address _spender, uint256 _value).

event Approval(address indexed _owner, address indexed _spender, uint256 _value)



Now lets code...


DappToken.sol
 function approve(address _spender, uint256 _value) public returns (bool success) {

        return true;
    }



Remember from last chapter:
approve.call(accounts[1],100)
does not create a transaction and don't write to blockchain.

DappToken.js test
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')
      });
    })



Now call approve without call to trigger event:
 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)
      }).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");
      });
    })



DappToken.sol
 // 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
    );



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

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

        return true;
    }



Now lets set the allowance, (hay this account is allowed to spend this much from my account)

mapping(address => mapping(address => uint256)) public allowance;


allownance variable, it contains nested mapping. We have mapping within a mapping

First address is owner account, second addresses are of other users who are allowed to spend on behalf of owner, and uint256 is the value.


Add from from address

return tokenInstance.approve(accounts[1], 100, {from: accounts[0]})


Then test for allowance
 assert.equal(allowance, 100'stores the allowance for delegated transfer');


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");
      }).then(function(allowance) {
        assert.equal(allowance, 100'stores the allowance for delegated transfer');
      });
    })




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