목차
1. ERC 표준 라이브러리
2. ERC20 표준 메서드 분석하기
1. ERC 표준 라이브러리
오픈제플린 프레임워크에서 제공하는 라이브러리로 ERC-20 토큰, ERC-721 토큰, 컨트랙트 소유권 관리, 액세스 제어, 타이밍 제어 등의 기능을 포함한 컨트랙트에 대한 표준을 라이브러리로 만들어 제공하므로 스마트 컨트랙트 개발자가 ERC 토큰 표준을 따르면서 토큰을 발행할 수 있도록 도와주는 라이브러리이다.
$ npm init -y
$ npm install @openzeppelin/contracts
ERC20 표준으로 토큰을 만들기 위해서 스마트 컨트랙트 라이브러리를 활용하여 표준의 가이드라인을 지키면서 토큰을 발행하는 스마트 컨트랙트를 작성할 수 있다.
2. ERC20 표준 메서드 분석하기
token/ERC20.sol
openZeppelin에서 제공하는 ERC20 라이브러리의 함수를 확인하고, 각각의 하는 역할을 확인해보려 한다.
파일 내에 있는 주석을 다 지운 상태로 컨트렉트 코드만 보면 아래와 같다.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
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);
}
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
1. name() : 토큰의 이름을 반환한다.
2. symbol() : 토큰의 심볼(단위)를 반환하는 함수이다. (BTC, ETH,...)
3. decimals() : 토큰의 소수점 자릿수를 반환한다. 기본적으로 18로 지정되어 있다. (1 ETH = 1 * 10 ** 18)
4. totalSupply() : 토큰의 총발행량을 반환한다. _mint 함수를 사용하여 자신이 원하는 주소에 초기 발행량을 지정할 수 있다.
5. balanceOf(address account) : 매개변수로 전달되는 account의 토큰 잔액을 반환한다.
6. transfer(address to, uint256 amount) : 다른 주소로 토큰을 전송할 때 사용하는 메서드이다. 첫 번째 인자값에 토큰을 받는 사람, 두 번째 인자값으로 보낼 값을 적는다.
7. allowance(address owner, address spender) : owner가 spender에게 사용할 수 있는 권한을 부여한 토큰의 양을 반환하는 메서드이다. owner가 실제 토큰의 소유자이고, spender가 대행업체라고 생각하면 이해하기 쉽다.
(ex : 1000원을 가진 owner의 돈 중에서 spender가 쓸 수 있는 돈은 500원이라 했을 때, 이 함수의 반환값은 500원이다.)
8. approve(address spender, uint256 amount) : 함수를 사용하는 owner는 spender에게 특정 amount 만큼만 사용할 권한을 부여할 수 있다. (ex : 1000월 가진 owner가 spender에게 500원을 쓸 수 있다고 지정할 때 사용된다. allowance() 함수를 호출하기 전에 사용된다.)
9. transferFrom(adress from, address to, uint256 amount) : 대행업체가 권한을 얻은 돈(500원)을 제삼자에게 보낼 때 쓰는 메서드로 amount 값은 allowance()의 값과 같거나 작아야 한다.
10. increaseAllowance(address spender, uint256 addedValue) : 사용할 수 있는 토큰의 양을 더 늘리고 싶을 때 사용하는 메서드이다. (ex : 500원의 권한이 있는 spender에게 300원(addedValue)의 권한을 더 줄 때 사용한다.)
11. decreaseAllowance(address spender, uint256 subtractedValue) : 사용할 수 있는 토큰의 양을 줄일 때 사용하는 메서드이다. (ex : 800원의 권한이 있는 spender에게 200원의 권한을 감소(600원만 권한을 주고 싶다)시키고 싶을 때 사용한다. subtractedValue(200원)의 값은 allowance(800원)의 반환값보다 작아야 한다.)
12. _transfer(address from, address to, uint256 amount) : from의 계좌에서 to의 계좌로 amount만큼의 토큰을 전송할 때 쓰는 메서드이다. 해당 컨트랙트 내에서만 쓸 수 있는 내부함수로 외부에서 쓸 때는 위에서 설명했던 trensfer을 이용한다. transfer 함수에서 _transfer를 사용하여 실질적으로 전송하는 메서드이다.
13. _mint(address account, uint256 amount) : account의 계좌로 지정된 수량만큼의 토큰을 발행해서 추가하는 내부함수이다. 이 메서드를 사용하면 총 발행량 자체가 증가하고 생성된 amount를 account에 전송해 준다.
14. _burn(address account, uint256 amount) : account에서 지정된 수량의 토큰을 소각하는 내부함수이다. 해당 account에서 amount만큼을 차감되고, 총공급량이 감소한다. 너무 많은 수량의 토큰이 발행되면 가치가 떨어지기 때문에 소각하여 토큰의 가치를 올릴 수 있다.
15. _approve(address owner, address spender, uint256 amount) : owner가 spender에게 토큰을 사용할 수 있는 권한을 주기 위한 메서드이며, 내부함수이다. 위에서 사용한 approve의 내부에서 사용되며, 외부에서 approve함수를 이용하면 실제로 _approve의 함수가 동작을 하여 처리하는 것이다.
16. _spendAllowance(address owner, address spender, uint256 amount) : transferFrom 함수가 실행될 때 실행되는 내부함수로 허용량을 소비하는 함수이다.
(ex : 500원을 사용할 수 있는 권한이 있을 때, 200원을 transferFrom으로 전송을 하고 나면 다음에 가지고 있는 spender가 소비할 수 있는 권한이 있는 amount는 300원이다.) 이 함수는 직접 전송을 하는 것이 아닌 권한이 있는 토큰의 양을 관리하는 함수이다.
17. _beforeTokenTransfer(address from, address to, uint256 amount): 토큰 전송 전에 실행되는 함수이다. 필요할 경우 재정의하여 추가 로직을 실행할 수 있다. 기본상태는 로직이 없는 상태이다.
18. _afterTokenTransfer(address from, address to, uint256 amount): 토큰 전송 후에 실행되는 함수이다. 필요할 경우 재정의하여 추가 로직을 실행할 수 있다. 기본상태는 로직이 없는 상태이다.
'BlockChain' 카테고리의 다른 글
| ERC721 표준 메서드 종류 및 역할 (0) | 2023.06.03 |
|---|---|
| NFT(Non-Fungible Token) 기초 (0) | 2023.06.02 |
| NFT(Non-Fungible Token)의 개념 (0) | 2023.05.31 |
| Ethereum - 사과가게 만들기 (0) | 2023.05.30 |
| Blockchain - Layer2 (레이어2) (0) | 2023.05.30 |