0 to 100: ERC20 token 
Tags: solidity, ethereum, beginners, web3
Published: 2022-01-01 14:22:43

## Sources

[ERC-20 Contract Walk-Through | ethereum.org](https://ethereum.org/en/developers/tutorials/erc20-annotated-code/#gatsby-focus-wrapper)

[openzeppelin-contracts/contracts/token/ERC20 at v3.0.0 · OpenZeppelin/openzeppelin-contracts](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v3.0.0/contracts/token/ERC20)

[ERC20 | Solidity by Example | 0.8.10](https://solidity-by-example.org/app/erc20/)

[openzeppelin-contracts/ERC20.sol at master · OpenZeppelin/openzeppelin-contracts](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)

---

## Overview

Contract that follows ERC20 standard is called ERC20 token.

- transfer tokens
- allow others to transfer tokens on behalf of token holder

---

## IERC20.sol

Interface of ERC20 standard

- totalSupply
    
    Returns amount of tokens in existence.
    
    ```solidity
    function totalSupply() external view returns (uint256);
    ```
    
- balanceOf
    
    Returns balance of token owned by specific account.
    
    ```solidity
    function balanceOf(address account) external view returns (uint256);
    ```
    
- transfer
    
    Move given amount of tokens from caller’s account to recipient.
    
    ```solidity
    function transfer(address recipient, uint256 amount) external returns (bool);
    ```
    
- approve
    
    Give  `spender` permission to spend my(caller)’s token for given `amount` . Returns boolean indicates whether it succeed or not.
    
    ```solidity
    function approve(address spender, uint256 amount) external returns (bool);
    ```
    
- allowance
    
    Returns the amount of token that `spender` is allowed to used be half of `owner` . 
    
    ```solidity
    function allowance(address owner, address spender) external view returns (uint256);
    ```
    
- transferFrom
    
    Moves `amount` of token from `sender` to `recipient` (allowance mechanism). `amount` is deducted from caller’s allowance.
    
    ```solidity
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    ```
    
- Transfer(event)
    
    Emitted from `transferFrom` or `transfer` function. 
    
    ```solidity
    event Transfer(address indexed from, address indexed to, uint256 value);
    ```
    
- Approval
    
    Emitted from `approve`  or `transferFrom` function. `value` is new allowance from `owner`  to `spender` .
    
    ```solidity
    event Approval(address indexed owner, address indexed spender, uint256 value);
    ```
    

---

## ERC20.sol

Implementation of IERC20 interface.

- private(internal) function, state variables are conventionally written as `_<something>` .
- We use internal functions to minimize the number of places where state changes happen because changing state is potential security risk.

### Imported files

- Context.sol
    
    Information about current execution context. `_msg.Sender` function returns `msg.sender` and `_msgData` returns `[msg.data](http://msg.data)` .
    
    OpenGSN allows etherless users to use the blockchain. That’s why we use custom `_msgSender` function instead of `msg.sender` . 
    
- IERC20.sol
- SafeMath.sol
    
    `try_` type functions revert transaction when operation overflows. 
    
- Address.sol
    
    Collection of functions associated to address type. (Ex: `sendValue` for safe alternative to `transfer` , `functionCall` to safe alternative to `call` ...)
    

### Constructor

`name` , `symbol` are initialized and `decimals` have default initialized value of 18.

```solidity
constructor (string memory name, string memory symbol) public {
        _name = name;
        _symbol = symbol;
        _decimals = 18;
    }
```

- Function arguments are always in memory because they only need to exist for execution of function.
- About decimals
    
    if decimals equals 2, balance is 505, display is 5.05
    

### Internal methods

- _transfer
    
    Check `sender` and `recipient` are not zero address. Subtract `amount` from `_balance[sender]` and add `amount` to `_balance[recipient` .  Emit `Transfer` event.
    
    ```solidity
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
    
            _beforeTokenTransfer(sender, recipient, amount);
    
            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
            _balances[recipient] = _balances[recipient].add(amount);
            emit Transfer(sender, recipient, amount);
        }
    ```
    
- _mint
    
    Check `account` is not zero address. Add `amount` to `_totalSupply`  and `_balances[account]` . Emit `Transfer` event which `sender` is `address(0)` .
    
    ```solidity
    function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
    
            _beforeTokenTransfer(address(0), account, amount);
    
            _totalSupply = _totalSupply.add(amount);
            _balances[account] = _balances[account].add(amount);
            emit Transfer(address(0), account, amount);
        }
    ```
    
- _burn
    
    Check `account` is not zero address. `account` is account that the token is burned **from.** Subtract `amount` to `_totalSupply`  and `_balances[account]` . Emit `Transfer` event which `recipient` is `address(0)` . **Perfect opposite of function `_mint` .** 
    
    ```solidity
    function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
    
            _beforeTokenTransfer(account, address(0), amount);
    
            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
            _totalSupply = _totalSupply.sub(amount);
            emit Transfer(account, address(0), amount);
        }
    ```
    
- _approve
    
    Sets allowance of `spender` over `owner` ’s token. Check both are not zero address. Assign `amount` to `_allowance[owner][spender]` . Emit `Approval` event. 
    
    ```solidity
    function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
    
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
    ```
    
- _setupDecimals
    
    Assign new decimal value.
    
    ```solidity
    function _setupDecimals(uint8 decimals_) internal {
            _decimals = decimals_;
        }
    ```
    

### `increaseAllowance` & `decreaseAllowance`

Alternative to `approve` for get away with issue of `approve`, which changing allowance with `approve` can allow spender to use both old and new allowance.

- increaseAllowance
    
    ```solidity
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
            return true;
        }
    ```
    
- decreaseAllowance
    
    ```solidity
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
            return true;
        }
    ```
    

---

## Making own ERC20 token + Tokenswap Contract

[ERC20 | Solidity by Example | 0.8.10](https://solidity-by-example.org/app/erc20/)

```jsx
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";

/*
How to swap tokens

1. Alice has 100 tokens from AliceCoin, which is a ERC20 token.
2. Bob has 100 tokens from BobCoin, which is also a ERC20 token.
3. Alice and Bob wants to trade 10 AliceCoin for 20 BobCoin.
4. Alice or Bob deploys TokenSwap
5. Alice appproves TokenSwap to withdraw 10 tokens from AliceCoin
6. Bob appproves TokenSwap to withdraw 20 tokens from BobCoin
7. Alice or Bob calls TokenSwap.swap()
8. Alice and Bob traded tokens successfully.
*/
contract moyed is ERC20 {
    constructor() ERC20("MOYED", "MYD") {
        _mint(msg.sender, 100 * 10**18);
    }
}

contract golden is ERC20 {
    constructor() ERC20("GOLDEN", "GLD") {
        _mint(msg.sender, 100 * 10**18);
    }
}

contract TokenSwap {
    IERC20 public token1;
    address public owner1;
    uint public amount1;
    IERC20 public token2;
    address public owner2;
    uint public amount2;

    constructor(
        address _token1,
        address _owner1,
        uint _amount1,
        address _token2,
        address _owner2,
        uint _amount2
    ) {
        token1 = IERC20(_token1);
        owner1 = _owner1;
        amount1 = _amount1;
        token2 = IERC20(_token2);
        owner2 = _owner2;
        amount2 = _amount2;
    }

    function swap() public {
        require(msg.sender == owner1 || msg.sender == owner2, "Not authorized");
        require(
            token1.allowance(owner1, address(this)) >= amount1,
            "Token 1 allowance too low"
        );
        require(
            token2.allowance(owner2, address(this)) >= amount2,
            "Token 2 allowance too low"
        );

        _safeTransferFrom(token1, owner1, owner2, amount1);
        _safeTransferFrom(token2, owner2, owner1, amount2);
    }

    function _safeTransferFrom(
        IERC20 token,
        address sender,
        address recipient,
        uint amount
    ) private {
        bool sent = token.transferFrom(sender, recipient, amount);
        require(sent, "Token transfer failed");
    }
}
```