Assignment - ERC20

pragma solidity 0.8.0;

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
      require(balances[msg.sender] >= _value, 'not enough funds');
      balances[msg.sender] -= _value;
      balances[_to] += _value;
      emit Transfer(msg.sender, _to, _value);
      _success = true;
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
      allowed[msg.sender][_spender] = _value;
      emit Approval(msg.sender, _spender, _value);
      _success = true;
  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
      require(_value <= balances[_from], 'not enough funds');
      require(_value <= allowed[_from][msg.sender], 'not allowed for this amount');
      
      balances[_from] -= _value;
      balances[_to] += _value;
      allowed[_from][msg.sender] -= _value;
      
      emit Transfer(_from, _to, _value);
      _success = true;
  }

}
1 Like

Here is my solution, there was not much to add from Filip’s code, The only thing that i found trouble with is that I tried to add some require's to check the the updated balances after the transaction occurred using the function function balanceOf() in the provided code, but had no success, so I commented those lines. Any help?

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2;

// ERC-20 stadard Assignment

contract CUSTOM_TOKEN{
    
    address public owner = msg.sender;
    
    // modifier
    modifier OnlyOwner{
        require(owner == msg.sender);
        _;
    }
    
    //-----------------------------------------------------------------------------------------
    // events
    /*
    * @dev Trigger when tokens are transferred, including zero value transfers.
    */
    event Transfer(address indexed _from,address indexed _to,uint256 _value);
    
    /*
    * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
    */
    event Approval(address indexed _owner,address indexed _spender,uint256 _value);
    
    
    //------------------------------------------------------------------------------------------
    // struct to keep record of the transactions
    mapping(address => mapping(address => uint256)) internal allowed;
    
    // data for storing users
    mapping(address => uint256) internal balances;
    
    
    //-----------------------------------------------------------------------------------------
    // function to get the information of the balance of a users  /*
    /*
    * @dev Returns the account balance of another account with address _owner.
    * @param _owner The address from which the balance will be retrieved.
    */
    function balanceOf(address _owner) external view returns (uint256 _balance){
        _balance = balances[_owner];
    }
    
    //------------------------------------------------------------------------------------------
    //deposit balance to owner to test code
    function deposit2Owner(uint256 _amount) public OnlyOwner payable{
        balances[msg.sender] += _amount;
    }
    
    
    //------------------------------------------------------------------------------------------
    //  Transfer
    /*
    * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
    * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
    * @param _to The address of the recipient.
    * @param _value The amount of token to be transferred.
    */
    function transfer(address payable _to, uint256 _value) public returns (bool _success){
        require(balances[msg.sender] >= _value);
        
        
        balances[msg.sender] -= _value;
        balances[_to] += _value;
        
        emit Transfer(msg.sender, _to, _value);
        
        _success = true;
    }
    
    /*
    * @dev Allows _spender to withdraw/transfer from your account multiple times, up to the _value amount. If
    * this function is called again it overwrites the current allowance with _value. SHOULD emit the Approval event.
    * @param _spender The address of the account able to transfer the tokens.
    * @param _value The amount of tokens to be approved for transfer.
    */
    function approve(address _spender, uint256 _value) public OnlyOwner returns (bool _success) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        _success = true;
    }
    
    /*
    * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
    * @param _owner The address of the account owning tokens.
    * @param _spender The address of the account able to transfer the tokens.
    */
    function allowance(address _owner, address _spender) external view returns (uint256 _remaining){
        _remaining = allowed[_owner][_spender];
    }
    
    /*
    * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
    * Transfer event.
    * @param _from The address of the sender.
    * @param _to The address of the recipient.
    * @param _value The amount of token to be transferred.
    */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool _success){
        require(balances[_from] >= _value, "Not enough balance");
        require(_from != _to, "Can not auto transfer funds");
                
        balances[_from] -= _value;
        balances[_to] += _value;
        
        /*
        uint256 balance_after_sending = balanceOf(_from);
        uint256 balance_after_receiving = balanceOf(_to);
        
        require(balances[_from] == balance_after_sending);
        require(balances[_to] == balance_after_receiving);
        */
        emit Transfer(_from, _to, _value);
        
        _success = true;
        
  }
    
}

Hi @Ignacio

Your function transferFrom misses an important require statement.

require(balances[_from] >= _value, "Not enough balance");
require(_from != _to, "Can not auto transfer funds");

You need another one there, think about it and let me know if you need help :slight_smile:

This code is not useful and should not be there as it will just waste gas and make your tx more expansive:

uint256 balance_after_sending = balanceOf(_from);
uint256 balance_after_receiving = balanceOf(_to);  
require(balances[_from] == balance_after_sending);
require(balances[_to] == balance_after_receiving);

Also you have declared balanceOf as external therefore you cannot call it from a function that is inside your contract.

Regards,
Dani

Thanks @dan-i , I figured out that changing balanceOf() from external to internal/public works. However you are correct in pointing out that it is a waste of gas.

Regarding the missing parts in the function transferFrom are

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool _success){
        require(balances[_from] >= _value, "Not enough balance");
        require(_from != _to, "Can not auto transfer funds");
        require(allowed[_from][msg.sender] >= _value);

                
        balances[_from] -= _value;
        balances[_to] += _value;
        allowed[_from][msg.sender] -= _value;
        
        /* // unnecesary waste of gas
        uint256 balance_after_sending = balanceOf(_from);
        uint256 balance_after_receiving = balanceOf(_to);
        
        require(balances[_from] == balance_after_sending);
        require(balances[_to] == balance_after_receiving);
        */
        
        emit Transfer(_from, _to, _value);
        
        _success = true;

One final question, I am wondering if we are missing the implementation of security against re-entrency attacks? (like the one shown in Security Considerations block, “Call, Send & transfer EIP1884” lecture), e.g. we have used msg.sender.call{value: amount}

pragma solidity 0.8.0;

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
   require(_value<=balances[msg.sender]);
   balances[msg.sender] -= _value;
   balances[_to]+=_value;
   emit Transfer (msg.sender, _to, _value);
   return true;
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
   allowed[msg.sender][_spender] = _value;
   emit Approval(msg.sender, _spender,_value);
   return true;
  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
    require(balances[_from]>=_value);
    require(allowed[_from][msg.sender]>=_value);
    balances[_from]-=_value;
    balances[_to] += _value;
    allowed[_from][msg.sender]-=_value;
    emit Transfer(_from,_to,_value);
    return true;
  }

}
1 Like
pragma solidity 0.8.0;

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
 import "safemath.sol";
 
contract Token {
    
   using SafeMath for uint; 
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
      require(balances[msg.sender] >= _value);
      
      uint256 prevSenderBalance = balances[msg.sender];
      uint256 prevReceiverBalance = balances[_to];
      
      balances[msg.sender] = balances[msg.sender].sub(_value);
      balances[_to] = balances[_to].add(_value);
      
      emit Transfer(msg.sender, _to, _value);
      
      assert(balances[msg.sender] == prevSenderBalance.sub(_value));
      assert(balances[_to] == prevReceiverBalance.add(_value));
      _success = true;

  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
          allowed[msg.sender][_spender] = _value;
          emit Approval(msg.sender, _spender, _value);
          _success = true;

  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
      require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
      
      uint256 prevSenderBalance = balances[_from];
      uint256 prevReceiverBalance = balances[_to];
      
      allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
      
      
      balances[_from] = balances[msg.sender].sub(_value);
      balances[_to] = balances[_to].add(_value);
      
      emit Transfer(_from, _to, _value);
      
      assert(balances[_from] == prevSenderBalance.sub(_value));
      assert(balances[_to] == prevReceiverBalance.add(_value));
          
          
      _success = true;

  }

}
1 Like

Hey @Ignacio

No risk of re-entrancy here as you are not sending ether to an external contract, therefore no fallback function will be triggered.

Also, as long as you follow the check/effect/interaction you will be ok.

Cheers,
Dani

I got nervous doing this one o, i feel like im handling peoples money already xD
The functions work and stuff but i feel like im doing stuff wrong somewhere, like im leaving holes here and there, but imma submit the homework and receive your guys’s input and fix the code accordingly

Also i do not know if its good or bad, but i didnt use the entire ERC20 code i just put the function together in a separated contract.

  pragma solidity 0.8.2;
  
  contract erc20{
      
      mapping(address => uint) balances;
      
      mapping (address => mapping (address => uint256)) internal allowed;
      
     function deposit () public payable returns (uint){
        balances[msg.sender] += msg.value;
         return balances[msg.sender];
     }
         
         event Transfer(address indexed _from,address indexed _to,uint256 _value);
         
         event Approval(address indexed _owner,address indexed _spender,uint256 _value);


  
      function transfer(address payable _to, uint256 _value) public returns (bool _success){
      require(balances[msg.sender] >= _value, "Funds not sufficient");
      require(_to != msg.sender, "Do not send money to yourself");
      balances[msg.sender] = balances[msg.sender] - _value;
      balances[_to] = balances[_to] + _value;
      emit Transfer(msg.sender, _to, _value);
      return true;
    }
    
    function approve(address _spender,uint256 _value) public returns (bool _success) {
     require(balances[msg.sender] > _value, "funds not suffcient");
     allowed[msg.sender][_spender] = _value;
     emit Approval(msg.sender, _spender, _value);
     return true;
  }
  
   function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
      require(allowed[_from][msg.sender] >= _value, "Funds not sufficient");
      require(balances[_from] >= _value, "Funds not sufficient");
      require(_to != msg.sender, "Do not send money to yourself");
      require(_to != _from, "Do not send money to your boss");
      allowed[_from][msg.sender] = allowed[_from][msg.sender] - _value;
      balances[_from] = balances[_from] - _value;
      balances[_to] = balances[_to] + _value;
      emit Transfer (_from, _to, _value);
      return true;
  }
  
   function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
    
  }

  }
  

i did not put the entire ERC20 code because i couldnt deploy the thing xD, it gives me this error :
creation of Token errored: Error encoding arguments: Error: invalid BigNumber string (argument="value", value="", code=INVALID_ARGUMENT, version=bignumber/5.0.8)

So i used my second remix document where i test my functions, also i dont know how to use that abi enooder v2 thing, and i was trying to import SafeMath, but it would not let me compile the code or deploy it -.-, and it was saying something about my code being 0.8.2 or SafeMath being 0.8.2 or i dont even remember i got realle mad so i just stopped trying to import SafeMath :frowning:

i fixed the function transferFrom i forgot to update the balance of the owner xD i put this, omg!! one last thing ia added another require :

balances[_from] = balances[_from] - _value;

 require(balances[_from] >= _value, "Funds not sufficient");

AH i jsut watched Filiips video now i know why i wasnt being able to deploy the ERC20 contract, but im still having issues with SafeMath :frowning: but i bet its soemthing that is easy to figure our on my own, so imma give it another try here

1 Like

Here is my code. Please let me know if there are errors or ways to improve it. Thank you.

pragma solidity 0.8.0;
import "Safemath.sol";

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
    
    using SafeMath for uint;
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
    require(balances[msg.sender] >= _value, "This balance does not have enough tokens!");
    require(allowed[msg.sender][_to] <= _value,"You must have prior approval before transferring this amount!");
    if(_success) {
        transferFrom(msg.sender, _to, _value);
    }
        
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
      require(balances[msg.sender] >= _value, "This balance does not have enough tokens!");
      require(_spender != msg.sender, "As a spender, you cannot approve your own transfer!");
      if(_success) {
          allowed[msg.sender][_spender] = _value;
          emit Approval(msg.sender, _spender, _value);
      }


  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
      if(_success) {
          balances[_from] = balances[_from].sub(_value);
          balances[_to] = balances[_to].add(_value);
          emit Transfer(_from, _to, _value);
      }

  }

}

I’m not sure how correct is this; I built it the way I felt it made sense:

pragma solidity 0.8.0;

import "./SafeMath.sol";
/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
  using SafeMath for uint256;
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
        require(balances[msg.sender]>=_value, "no enough balance");
        require(msg.sender!=_to, "can't send to youself");
        uint256 balanceBefore=balances[msg.sender];
        balances[msg.sender]=balances[msg.sender].sub(_value);
        
        (_success, )=msg.sender.call {value:_value} ("");
        
        if (_success) {
            emit Transfer(msg.sender,_to,_value);
        }else {
            balances[msg.sender]=balanceBefore;
        }
        
        return _success;
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
     require(allowed[msg.sender][_spender]>=_value, "no allowance");
     require(msg.sender!=_spender,"dont send to yourself");
        
        emit Approval(msg.sender, _spender, _value);
     
  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address payable _to,uint256 _value) public returns (bool _success){
    require(approve(_from, _value));
    uint256 previouslyAllowed=allowed[_from][_to];
    allowed[_from][_to]=allowed[_from][_to].sub(_value);
    _success=transfer(_to, _value);
     if (!_success) {
         allowed[_from][_to]=previouslyAllowed;
        }   

    return _success;
   
  }

}

Hi @Carlosvh96

Good job with your code, just few suggestions:

function transfer(address payable _to, uint256 _value)

The address _to does not need to be payable.

function approve(address _spender,uint256 _value) public returns (bool _success) {
     require(balances[msg.sender] > _value, "funds not suffcient");

The balance could be >=.

The issue with SafeMath that you are facing seems to be related to the Solidity version you are using to compile.
Can you post here the version of Solidity you are using in SafeMath?

Happy learning,
Dani

1 Like

Hi @MrSeven

Double check your transferFrom function.
You are missing all the requirements that should be there.
Also the function will never run any code because:

 if(_success) {

will always return false.

Happy coding,
Dani

Hey @samartrade

Few suggestions:

1 - The function approve has a return declaration returns (bool _success) but is not returning anything.

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

2 - The function transfer does not send ether but only modifies the mapping balances because you are moving ERC tokens.
Also you are decreasing the ERC balance of msg.sender but you are not increasing the ERC balance of _to.
Take some time to watch this video once again: https://academy.ivanontech.com/products/ethereum-smart-contract-programming-201/categories/4829865/posts/16251712

3 - The function transferFrom is missing the requirements and is not moving tokens.

Let me know if you have questions and watch the videos once again, surely they will clarify your doubts :slight_smile:

Happy learning,
Dani

Thanks @dan-i for the feedback, I got it all mixed up :slight_smile:

1 Like

Resubmitting my code as there were errors in my first attempt.

pragma solidity 0.8.0;
import "Safemath.sol";

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
    
    using SafeMath for uint;
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
    require(balances[msg.sender] >= _value, "This balance does not have enough tokens!");
    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    _success = true;
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
    require(balances[msg.sender] >= _value, "This balance does not have enough tokens!");
    require(_spender != msg.sender, "As a spender, you cannot approve your own transfer!");
    allowed[msg.sender][_spender] = _value;
    emit Approval(msg.sender, _spender, _value);
    _success = true;
  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
    require(balances[_from] >= _value, "You have enough funds to make this transfer!");
    require(allowed[_from][msg.sender] <= _value,"You must have prior approval before transferring this amount!");
    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    emit Transfer(_from, _to, _value);
    _success = true;
  }
}

Here is my solution:

pragma solidity 0.8.0;

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
    
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
      require(balances[msg.sender] >= _value, "Balance not sufficient");
      balances[msg.sender] -= _value;
      balances[_to] += _value;
      emit Transfer(msg.sender, _to, _value);
      return true;
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
      allowed[msg.sender][_spender] = _value;
      emit Approval(msg.sender, _spender, _value);
      return true;

  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
      require(balances[_from] >= _value, "Insufficient  Balance.");
      balances[_from] -= _value;
      balances[_to] += _value;
      allowed[_from][msg.sender] -= _value;
      emit Transfer (_from, _to, _value);
      return true;

  }
    
}

Much better! Well done @MrSeven :clap:

Hey @cecilia

A requirement is missing in your function transferFrom.
You are correctly checking that balances[_from] >= _value but you also need another one.
Check it out and let me know if you need info.

Cheers,
Dani

Loving 201 so far, I learn best with these hands-on excercises.

My solution:

pragma solidity 0.8.0;
import "./SafeMath.sol";



/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
    using SafeMath for uint;

  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply()external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
    require(balances[msg.sender] >= _value, "You dont have enough LukeTokens to send that much.");
    uint senderBeforeBalance = balances[msg.sender];
    uint receiverBeforeBalance = balances[_to];

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    
    emit Transfer(msg.sender, _to, _value);
    assert(balances[msg.sender] == senderBeforeBalance.sub(_value));
    assert(balances[msg.sender] == receiverBeforeBalance.add(_value));

    _success = true;
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
      allowed[msg.sender][_spender] = _value;
      emit Approval(msg.sender, _spender, _value);
      _success = true;
  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
    require(allowed[_from][msg.sender] >= _value, "You are over your allowed limit of sender's LukeTokens.");
    require(balances[_from] >= _value, "Sender doesnt have enough LukeTokens to send that much.");
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    uint senderBeforeBalance = balances[_from];
    uint receiverBeforeBalance = balances[_to];

    balances[msg.sender] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    
    emit Transfer(msg.sender, _to, _value);
    assert(balances[msg.sender] == senderBeforeBalance.sub(_value));
    assert(balances[msg.sender] == receiverBeforeBalance.add(_value));

    _success = true;
  }

}
1 Like
pragma solidity 0.8.0;

/**
 * @title ERC20 standard token implementation.
 * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ.
 */
contract Token {
  /*
   * Token name.
   */
  string internal tokenName;

  /*
   * Token symbol.
   */
  string internal tokenSymbol;

  /*
   * Number of decimals.
   */
  uint8 internal tokenDecimals;

  /*
   * Total supply of tokens.
   */
  uint256 internal tokenTotalSupply;

  /*
   * Balance information map.
   */
  mapping (address => uint256) internal balances;

  /*
   * Token allowance mapping.
   */
  mapping (address => mapping (address => uint256)) internal allowed;

  /*
   * @dev Trigger when tokens are transferred, including zero value transfers.
   */
  event Transfer(address indexed _from,address indexed _to,uint256 _value);

  /*
   * @dev Trigger on any successful call to approve(address _spender, uint256 _value).
   */
  event Approval(address indexed _owner,address indexed _spender,uint256 _value);
  
  constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _initialOwnerBalance) {
      tokenName = _name;
      tokenSymbol = _symbol;
      tokenDecimals = _decimals;
      tokenTotalSupply = _initialOwnerBalance;
      balances[msg.sender] = _initialOwnerBalance;
  }

  /*
   * @dev Returns the name of the token.
   */
  function name() external view returns (string memory _name){
    _name = tokenName;
  }

  /*
   * @dev Returns the symbol of the token.
   */
  function symbol() external view returns (string memory _symbol){
    _symbol = tokenSymbol;
  }

  /*
   * @dev Returns the number of decimals the token uses.
   */
  function decimals() external view returns (uint8 _decimals){
    _decimals = tokenDecimals;
  }

  /*
   * @dev Returns the total token supply.
   */
  function totalSupply() external view returns (uint256 _totalSupply){
    _totalSupply = tokenTotalSupply;
  }

  /*
   * @dev Returns the account balance of another account with address _owner.
   * @param _owner The address from which the balance will be retrieved.
   */
  function balanceOf(address _owner) external view returns (uint256 _balance){
    _balance = balances[_owner];
  }

  /*
   * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The
   * function SHOULD throw if the "from" account balance does not have enough tokens to spend.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transfer(address payable _to, uint256 _value) public returns (bool _success){
      require(balances[msg.sender] >= _value, "msg.sender does not have enough balance to send these tokens");
      balances[msg.sender]-=(_value);  
      balances[_to]+=(_value); 
      emit Transfer(msg.sender, _to, _value); 
      _success = true; 
  }

  /*
   * @dev 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. SHOULD emit the Approval event.
   * @param _spender The address of the account able to transfer the tokens.
   * @param _value The amount of tokens to be approved for transfer.
   */
  function approve(address _spender,uint256 _value) public returns (bool _success) {
      require(balances[msg.sender] >= _value, "msg.sender does not have enough balance to approve these tokens"); 
      allowed[msg.sender][_spender] = _value; 
      emit Approval(msg.sender, _spender, _value); 
      _success = true; 
  }

  /*
   * @dev Returns the amount which _spender is still allowed to withdraw from _owner.
   * @param _owner The address of the account owning tokens.
   * @param _spender The address of the account able to transfer the tokens.
   */
  function allowance(address _owner,address _spender) external view returns (uint256 _remaining){
    _remaining = allowed[_owner][_spender];
  }

  /*
   * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the
   * Transfer event.
   * @param _from The address of the sender.
   * @param _to The address of the recipient.
   * @param _value The amount of token to be transferred.
   */
  function transferFrom(address _from,address _to,uint256 _value) public returns (bool _success){
      require(balances[_from] >= _value);
      require(allowed[_from][msg.sender] >= _value); 
      
      balances[_from]-=(_value); 
      balances[_to]+=(_value); 
      allowed[_from][msg.sender]-=(_value); 

      emit Transfer(_from, _to, _value);
      _success = true;
  }

}

This one is fun! Also wrote some pretend ICO (presale, public) contracts to pair with this dummy contract

1 Like