[Solidity] 클레이튼 체인 KIP-7 기반 토큰(코인) 만들기 (Baobab Testnet)
Solidity / KIP-7 기반 토큰(코인) 만들기 / 카이카스 / Baobab 테스트넷
KIP-7 기반 토큰(코인) 만들기
Baobab 테스트넷에 KIP-7 토큰 만들고 배포하기
-1. 카이카스에 접속하고, Baobab 테스트넷 을 선택한다
-2. Baobab 테스트넷 에서 사용할 테스트용 KLAY 를 받는다
https://baobab.wallet.klaytn.foundation/faucet
(Baobab 테스트넷 클레이튼 Faucet 👆)
-3. Kaikas 지갑에서 개인키를 발급받는 방법
-4. Klaytn IDE에서 SimpleToken.sol
파일을 생성 후 코드를 입력한다
-5. EVM VERSION 을 👉 byzantium 으로 선택 후 컴파일 한다
-6. DEPLOY & RUN TRANSACTIONS 항목으로 이동한다
6-1. ENVIRONMENT 부분을 Baobab로 변경하여 ‘카이카스’와 연동한다
6-2. Account 옆에있는 + 버튼을 눌러서 발급받은 개인키를 입력한다
6-3. DEPLOY에 들어갈 인자값을 입력한다
- GETNAME 👉 코인이름
- GETSYMBOL 👉 코인이름약자
6-4. 설정이 완료되면 transact 버튼을 누른다
-7. klaytn finder 에 주소를 입력후 컨트랙트 주소를 확인해본다
https://baobab.klaytnfinder.io/
(이더스캔과 같은 klaytn finder 👆)
-8. 토큰 추가를 클릭하여 컨트랙트 주소를 입력하고 추가한다
-9. KIP-7 코드의 설정한 ‘총 발행량 (totalSupply)’ 개수만큼 잔액이 적용된 토큰을 확인 할 수 있다
토큰 주고 받기 테스트까지 성공
KIP-7 기반 토큰을 만들기 위한 코드
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.5.6;
// KIP-7은 ERC-20을 기반으로 만들어졌다
// 솔리디티에서 'Interface'는 사용할 함수의 형태를 선언한다
// 실제 함수의 내용은 Contract에서 사용한다
// 기본적인 KIP-7의 인터페이스 목록이다 👇
contract IKIP {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount)
external
returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function transferFrom(
address spender,
address recipient,
uint256 amount
) external returns (bool);
// 함수(function)는 이더리움에서 제공하는 함수이며, event는 이더리움에서 제공하는 로그이다
// 'function'과 'event'를 선언할 때는 입력값과 반환값은 선택할 수 있으나,
// 'function'의 자료형 / 이름 / 순서를 주의해야 한다
event Transfer(address indexed from, address indexed to, uint256 amount);
event Transfer(
address indexed spender,
address indexed from,
address indexed to,
uint256 amount
);
event Approval(
address indexed owner,
address indexed spender,
uint256 oldAmount,
uint256 amount
);
}
// 'IKIP'의 ⭐ 'Transfer' 이벤트는 토큰이 이동할 때마다 로그를 남기고,
// ⭐ 'Approval' 이벤트는 'approve' 함수가 실행 될 때 로그를 남긴다
//----------------------------------------------------------
// 👉 'IKIP'는 'SimpleKlaytnToken' 컨트랙트에서 사용할 함수의 형태를 선언하며,
// 'KIP-7' 에서 사용하는 함수들의 형태를 선언한 것을 확인할 수 있다
contract SimpleKlaytnToken is IKIP {
// contract SimpleKlaytnToken 뒤에 'is IKIP'를 붙여서
// SimpleKlaytnToken 컨트랙트가 'IKIP' 함수를 사용할 수 있다고 선언한다
// 이렇게 사용하면 'SimpleKlaytnToken' 안에서 'IKIP'에 선언된 함수와 이벤트를 사용할 수 있다
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) public _allowances;
// 이중으로 매핑된 approvals를 확인할 수 있다
uint256 public _totalSupply; // 토큰 총발행량
string public _name; // 토큰 이름
string public _symbol; // 토큰 약자
uint8 public _decimals; // 18진수
constructor(string memory getName, string memory getSymbol) public {
_name = getName;
_symbol = getSymbol;
_decimals = 18;
_totalSupply = 100000000e18; // 토큰 총발행량 설정
_balances[msg.sender] = _totalSupply; // 추가
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function decimals() public view returns (uint8) {
return _decimals;
}
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
// 👉 totalSupply는 토큰의 총 발행량을 반환한다
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
// 👉 balanceOf는 매핑된 값인 '_balanceOf'에서 입력한
// address인 account가 가지고있는 토큰의 수를 리턴한다
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
emit Transfer(msg.sender, recipient, amount);
return true;
}
// 👉 transfer는 내부 함수인 '_transfer'를 호출한다
// 호출이 정상적으로 완료되었을 경우 Transfer event를 발생시킴
function allowance(address owner, address spender)
public
view
returns (uint256)
{
return _allowances[owner][spender];
}
// allowance는 입력한 두개의 주소값에 대한 '_allowances'값,
// 다시말해 '내가(owner)'가 '토큰을 양도할 상대방(spender)'에게 토큰을 등록한 양을 반환한다
function approve(address spender, uint256 amount) public returns (bool) {
uint256 currentAllownace = _allowances[msg.sender][spender];
require(
currentAllownace >= amount,
"ERC20: Transfer amount exceeds allowance"
);
_approve(msg.sender, spender, currentAllownace, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public returns (bool) {
_transfer(sender, recipient, amount);
emit Transfer(msg.sender, sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][msg.sender];
require(
currentAllowance >= amount,
"ERC20: transfer amount exceeds allowance"
);
_approve(
sender,
msg.sender,
currentAllowance,
currentAllowance - amount
);
return true;
}
// 👉 transferFrom은 양도를 수행하는 거래 대행자(msg.sender)가 sender가 허락해준 값만큼 상대방(recipient)에게 토큰을 이동한다
// 이동을 위해 _transfer 함수를 실행시킨다
// '_transfer' 에서는 양도를 하는 sender의 잔고를 amount만큼 줄이고, recipient의 잔고를 amount만큼 늘린다
// '_transfer' 함수 실행이 완료되고, require를 모두 통과한다면 currentAllowance를 체크하여 _approve 함수를 실행한다
function _transfer(
address sender,
address recipient,
uint256 amount
) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
uint256 senderBalance = _balances[sender];
require(
senderBalance >= amount,
"ERC20: transfer amount exceeds balance"
);
_balances[sender] = senderBalance - amount;
_balances[recipient] += amount;
}
// _transfer는 require를 통해 세가지 조건을 검사한다
// 1. 보내는 사람의 주소가 잘못되었는지 체크한다
// 2. 받는사람의 주소가 잘못되었는지 체크한다
// 3. transfer 함수를 실행한 사람(sender)이 가진 토큰(senderBalance)이 신청한 값(amount)보다 많은 토큰을 가지고 있는지 체크한다
// 👉 위의 세 조건을 충족하는 경우,
// 실행한 사람(sender)이 가진 토큰의 지갑에서 토큰을 개수만큼 빼고,
// 받을 사람(recipient)의 토큰 지갑에 개수만큼 더해준다
function _approve(
address owner,
address spender,
uint256 currentAmount,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
require(
currentAmount == _allowances[owner][spender],
"ERC20: invalid currentAmount"
);
_allowances[owner][spender] = amount;
emit Approval(owner, spender, currentAmount, amount);
}
// 내부 함수인 _approve를 호출한다
//_approve에서는 내가 토큰을 양도할 상대방(spender)에게 양도할 값(amount)를 allowances에 기록한다
// 그리고 Approval event를 호출하여 기록한다
// 이 상태에서는 양도가 실제로 이루어진 것이 아니라, 양도를 할 주소와 양을 정한 것이다
// 👉 approve는 단순 변경을 위한 함수이기 때문에 내부적으로 값을 올리고,
// 내리는 'increaseApproval'과 / 'decreaseApproval' 함수를 사용하기도 한다
// approve 는 spender 가 당신의 계정으로부터 amount 한도 하에서 여러 번 출금하는 것을 허용합니다.
// 이 함수를 여러번 호출하면, 단순히 허용량을 amount 으로 재설정한다
}