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
spender
permission 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
spender
is allowed to used be half ofowner
.
function allowance(address owner, address spender) external view returns (uint256);
-
transferFrom
Moves
amount
of token fromsender
torecipient
(allowance mechanism).amount
is deducted from caller’s allowance.
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
-
Transfer(event)
Emitted from
transferFrom
ortransfer
function.
event Transfer(address indexed from, address indexed to, uint256 value);
-
Approval
Emitted from
approve
ortransferFrom
function.value
is new allowance fromowner
tospender
.
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 returnsmsg.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 ofmsg.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 totransfer
,functionCall
to 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
sender
andrecipient
are not zero address. Subtractamount
from_balance[sender]
and addamount
to_balance[recipient
. EmitTransfer
event.
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. Addamount
to_totalSupply
and_balances[account]
. EmitTransfer
event whichsender
isaddress(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
account
is not zero address.account
is account that the token is burned from. Subtractamount
to_totalSupply
and_balances[account]
. EmitTransfer
event whichrecipient
isaddress(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
spender
overowner
’s token. Check both are not zero address. Assignamount
to_allowance[owner][spender]
. EmitApproval
event.
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)