Sources
ERC-20 Contract Walk-Through | ethereum.org
openzeppelin-contracts/contracts/token/ERC20 at v3.0.0 · OpenZeppelin/openzeppelin-contracts
ERC20 | Solidity by Example | 0.8.10
openzeppelin-contracts/ERC20.sol at master · OpenZeppelin/openzeppelin-contracts
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.
function totalSupply() external view returns (uint256); -
balanceOf
Returns balance of token owned by specific account.
function balanceOf(address account) external view returns (uint256); -
transfer
Move given amount of tokens from caller’s account to recipient.
function transfer(address recipient, uint256 amount) external returns (bool); -
approve
Give
spenderpermission to spend my(caller)’s token for givenamount. Returns boolean indicates whether it succeed or not.
function approve(address spender, uint256 amount) external returns (bool); -
allowance
Returns the amount of token that
spenderis allowed to used be half ofowner.
function allowance(address owner, address spender) external view returns (uint256); -
transferFrom
Moves
amountof token fromsendertorecipient(allowance mechanism).amountis deducted from caller’s allowance.
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); -
Transfer(event)
Emitted from
transferFromortransferfunction.
event Transfer(address indexed from, address indexed to, uint256 value); -
Approval
Emitted from
approveortransferFromfunction.valueis new allowance fromownertospender.
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.Senderfunction returnsmsg.senderand_msgDatareturns[msg.data](http://msg.data).OpenGSN allows etherless users to use the blockchain. That’s why we use custom
_msgSenderfunction instead ofmsg.sender. IERC20.sol
-
SafeMath.sol
try_type functions revert transaction when operation overflows. -
Address.sol
Collection of functions associated to address type. (Ex:
sendValuefor safe alternative totransfer,functionCallto safe alternative tocall...)
Constructor
name , symbol are initialized and decimals have default initialized value of 18.
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
senderandrecipientare not zero address. Subtractamountfrom_balance[sender]and addamountto_balance[recipient. EmitTransferevent.
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
accountis not zero address. Addamountto_totalSupplyand_balances[account]. EmitTransferevent whichsenderisaddress(0).
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
accountis not zero address.accountis account that the token is burned from. Subtractamountto_totalSupplyand_balances[account]. EmitTransferevent whichrecipientisaddress(0). Perfect opposite of function_mint.
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
spenderoverowner’s token. Check both are not zero address. Assignamountto_allowance[owner][spender]. EmitApprovalevent.
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.
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
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } -
decreaseAllowance
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
// 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");
}
}
Top comments (0)