2021년 9월에 Klaytn Token에 대해 공부하면서 정리한 글입니다. 이 때문에, 현재는 아래의 내용과는 다른 부분이 있을 수 있습니다. 아래 문서는 Klaytn Token에 대한 참고 용도로만 사용하는 것을 권장하고, 더 정확한 건 공식 문서를 확인하시기 바랍니다.
참고 자료 : Klaytn Docs (Klaytn 자체 코인 - KLAY, Klaytn 호환 토큰)
KLAY = Klaytn에서 전송 가능한 주요 내부 암호화폐
peb
은 가장 작은 화폐단위.ston
= Gpeb
KLAY
= 10^18 peb
단위 | peb 환산 가치 |
---|---|
peb | 1 peb |
kpeb | 10^3 peb |
Mpeb | 10^6 peb |
ston (Gpeb) | 10^9 peb |
KLAY | 10^18 peb |
KIP-7 = ERC-20 기반 Klaytn 대체 가능 토큰 표준이자, Klaytn 호환 토큰(KCT, Klaytn Compatible Token)의 일종. 특정 기술 스펙을 구현한 특별한 타입의 스마트 컨트랙트로, Klaytn에서 토큰을 발행하려면 이 스펙을 따라야 한다.
대체 가능한 토큰 = 균등성과 가분(可分)성을 가진 토큰.
(각 토큰 단위는 동일한 가치를 가지므로 모든 가용 토큰은 서로 호환된다.)
아래 인터페이스를 기반으로, 새로운 기능과 논리를 추가하여 토큰을 커스터마이징하고, Klaytn 네트워크에 배포할 수 있다.
(KIP-17을 구현 시 KIP-13도 반드시 함께 구현해야 함)
// IKIP7
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function safeTransfer(address recipient, uint256 amount, bytes data) external;
function safeTransfer(address recipient, uint256 amount) external;
function safeTransferFrom(address sender, address recipient, uint256 amount, bytes data) external;
function safeTransferFrom(address sender, address recipient, uint256 amount) external;
// IKIP7Metadata (optional)
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
// IKIP7Mintable (optional)
function mint(address _to, uint256 _amount) external returns (bool);
function isMinter(address _account) external view returns (bool);
function addMinter(address _account) external;
function renounceMinter() external;
// IKIP7Burnable (optional)
function burn(uint256 _amount) external;
function burnFrom(address _account, uint256 _amount) external;
// IKIP7Pausable (optional)
event Paused(address _account);
event Unpaused(address _account);
function paused() external view returns (bool);
function pause() external;
function unpause() external;
function isPauser(address _account) external view returns (bool);
function addPauser(address _account) external;
function renouncePauser() external;
/// @title KIP-7 Fungible Token Standard, optional wallet interface
/// @dev Note: the KIP-13 identifier for this interface is 0x9d188c22.
interface IKIP7TokenReceiver {
/// @notice Handle the receipt of KIP-7 token
/// @dev The KIP-7 smart contract calls this function on the recipient
/// after a `safeTransfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _amount The token amount which is being transferred.
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onKIP7Received(address,address,uint256,bytes)"))`
/// unless throwing
function onKIP7Received(address _operator, address _from, uint256 _amount, bytes _data) external returns(bytes4);
}
interface IKIP7 {
/// value만큼의 token이 from에서 to로 이동한 후,
/// token 생성(from==0), 또는 token 삭제(to==0)시 발생하는 이벤트.
/// (value는 0이 될 수 있다.)
event Transfer(address indexed from, address indexed to, uint256 value);
/// @dev Emitted when the allowance of a `spender` for an `owner` is set by
/// a call to {approve}. `value` is the new allowance.
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice 존재하는 모든 토큰 양을 반환.
function totalSupply() external view returns (uint256);
/// @notice 특정 계정이 소유한 토큰 양을 반환.
/// @param account: 특정 계정 주소, address type.
/// @return 토큰의 양, uint256 type.
function balanceOf(address account) external view returns (uint256);
/// @notice 함수를 호출한 사람의 계정에서 'recipient'의 계정으로 'amount'만큼의 토큰을 보냄.
/// @dev 함수를 호출한 사람의 계정 잔액에 충분한 토큰이 없을 경우 에러 발생.
/// 마찬가지로, Contract가 pausable하거나 paused된 경우 에러 발생.
/// {Transfer} 이벤트를 발생시킴.
/// @param recipient: 토큰을 받을 주소, address type.
/// @param amount: 보낼 토큰 양, uint256 type.
/// @return 성공 여부, bool type.
function transfer(address recipient, uint256 amount) external returns (bool);
/// @notice {transferFrom}을 통해 'owner' 대신 'spender'가 사용할 수 있는 남은 토큰 수를 반환.
/// 기본값은 0.
/// @dev 마찬가지로, Contract가 pausable하거나 paused된 경우 에러 발생.
/// 이 값은 {approve} 또는 {transferFrom}이(가) 호출되면 변경됨.
/// @param owner: 'spender'가 계정에서 토큰을 인출하도록 허용한 계정, address type.
/// @param spender: 토큰을 인출하도록 승인된 주소, address type.
/// @return 'owner'가 승인한 'spender' 토큰 금액, uint256 type.
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
/// @dev Throws if the contract is pausable and paused.
///
/// IMPORTANT: Beware that changing an allowance with this method brings the risk
/// that someone may use both the old and the new allowance by unfortunate
/// transaction ordering. One possible solution to mitigate this race
/// condition is to first reduce the spender's allowance to 0 and set the
/// desired value afterwards:
/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
///
/// Emits an {Approval} event.
/// @param spender The address is approved to withdraw the tokens.
/// @param amount The token amount will be approved.
/// @return a boolean value indicating whether the operation succeeded.
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Moves `amount` tokens from `sender` to `recipient` using the
/// allowance mechanism. `amount` is then deducted from the caller's
/// allowance.
/// @dev Throw unless the `sender` account has deliberately authorized the sender of the message via some mechanism.
/// Throw if `sender` or `recipient` is the zero address.
/// Throws if the contract is pausable and paused.
///
/// Emits a {Transfer} event.
/// Emits an `Approval` event indicating the updated allowance.
/// @param sender The current owner of the tokens.
/// @param recipient The owner will receive the tokens.
/// @param amount The token amount will be transferred.
/// @return A boolean value indicating whether the operation succeeded.
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/// @notice Moves `amount` tokens from the caller's account to `recipient`.
/// @dev Throws if the message caller's balance does not have enough tokens to spend.
/// Throws if the contract is pausable and paused.
/// Throws if `_to` is the zero address.
/// When transfer is complete, this function checks if `_to` is a smart
/// contract (code size > 0). If so, it calls
/// `onKIP7Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onKIP7Received(address,address,uint256,bytes)"))`.
/// @param recipient The owner will receive the tokens.
/// @param amount The token amount will be transferred.
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransfer(address recipient, uint256 amount, bytes data) external;
/// @notice Moves `amount` tokens from the caller's account to `recipient`.
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param recipient The owner will receive the tokens.
/// @param amount The token amount will be transferred.
function safeTransfer(address recipient, uint256 amount) external;
/// @notice Moves `amount` tokens from `sender` to `recipient` using the
/// allowance mechanism. `amount` is then deducted from the caller's
/// allowance.
/// @dev Throw unless the `sender` account has deliberately authorized the sender of the message via some mechanism.
/// Throw if `sender` or `recipient` is the zero address.
/// Throws if the contract is pausable and paused.
/// When transfer is complete, this function checks if `_to` is a smart
/// contract (code size > 0). If so, it calls
/// `onKIP7Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onKIP7Received(address,address,uint256,bytes)"))`.
/// Emits a {Transfer} event.
/// Emits an `Approval` event indicating the updated allowance.
/// @param sender The current owner of the tokens.
/// @param recipient The owner will receive the tokens.
/// @param amount The token amount will be transferred.
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address sender, address recipient, uint256 amount, bytes data) external;
/// @notice Moves `amount` tokens from `sender` to `recipient` using the
/// allowance mechanism. `amount` is then deducted from the caller's
/// allowance.
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param sender The current owner of the tokens.
/// @param recipient The owner will receive the tokens.
/// @param amount The token amount will be transferred.
function safeTransferFrom(address sender, address recipient, uint256 amount) external;
}
KIP-17 = ERC-721 기반 Klaytn 대체 불가능 토큰 표준이자, Klaytn 호환 토큰(KCT, Klaytn Compatible Token)의 일종. 특정 기술 스펙을 구현한 특별한 타입의 스마트 컨트랙트로, Klaytn에서 토큰을 발행하려면 이 스펙을 따라야 한다.
(이 때문에, Klaytn에선 ERC-721 기반의 NFT 또한 발급 가능하다. 단, 일부 호환되지 않는 부분이 존재할 수 있으므로, 특별한 이유가 없다면 KIP-17 기반으로 개발할 것을 권고하고 있다.)
NFT = 고유한 자산을 나타내는 특수한 토큰 유형.
아래 인터페이스를 기반으로, 새로운 기능과 논리를 추가하여 토큰을 커스터마이징하고, Klaytn 네트워크에 배포할 수 있다.
(KIP-7을 구현 시 KIP-13도 반드시 함께 구현해야 함.)
// IKIP17
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
// IKIP17Metadata (optional)
function name() external view returns (string _name);
function symbol() external view returns (string _symbol);
function tokenURI(uint256 _tokenId) external view returns (string);
// IKIP17Enumerable (optional)
function totalSupply() external view returns (uint256);
function tokenByIndex(uint256 _index) external view returns (uint256);
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
// IKIP17Mintable (optional)
function mint(address _to, uint256 _tokenId) public returns (bool);
function isMinter(address _account) public view returns (bool);
function addMinter(address _account) public;
function renounceMinter() public;
// IKIP17MetadataMintable (optional)
function mintWithTokenURI(address _to, uint256 _tokenId, string memory _tokenURI) public returns (bool);
function isMinter(address _account) public view returns (bool);
function addMinter(address _account) public;
function renounceMinter() public;
// IKIP17Burnable (optional)
function burn(uint256 _tokenId) public;
// IKIP17Pausable (optional)
event Paused(address _account);
event Unpaused(address _account);
function paused() public view returns (bool);
function pause() public;
function unpause() public;
function isPauser(address _account) public view returns (bool);
function addPauser(address _account) public;
function renouncePauser() public;
pragma solidity 0.4.24;
/// @title KIP-17 Non-Fungible Token Standard, optional wallet interface
/// @dev Note: the KIP-13 identifier for this interface is 0x6745782b.
interface IKIP17TokenReceiver {
/// @notice Handle the receipt of an NFT
/// @dev The KIP-17 smart contract calls this function on the recipient
/// after a `safeTransfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onKIP17Received(address,address,uint256,bytes)"))`
/// unless throwing
function onKIP17Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
}
pragma solidity 0.4.24;
interface IKIP17Enumerable {
/// @notice 이 Contract에 의해 tracking된 NFT의 갯수를 센다.
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply() external view returns (uint256);
/// @notice Enumerate valid NFTs
/// @dev Throws if `_index` >= `totalSupply()`.
/// @param _index A counter less than `totalSupply()`
/// @return The token identifier for the `_index`th NFT,
/// (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);
/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
/// `_owner` is the zero address, representing invalid NFTs.
/// @param _owner An address where we are interested in NFTs owned by them
/// @param _index A counter less than `balanceOf(_owner)`
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
/// (sort order not specified)
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}