본문으로 건너뛰기
버전: 3.3.0

IRC21 표준

자체 배포 토큰을 위한 표준 인터페이스입니다.

개요

IOST에 배포되는 표준 토큰은 반드시 시스템 컨트랙트 token.iost 기반으로 구현되어야 합니다. 대부분의 경우 token.iost 컨트랙트를 통해 직접 토큰을 만들 수 있지만, 커스터마이즈된 토큰을 만들려면 자체 토큰 컨트랙트를 구현하여 배포해야 합니다.

커스텀 토큰 컨트랙트는 지갑이나 거래소 같은 애플리케이션을 지원하기 위해 다음 인터페이스를 구현해야 합니다.

ABI

{
"lang": "javascript",
"version": "1.0.0", // 또는 다른 버전
"abi": [
// optional
{
"name": "issue",
"args": [
"string", // token_symbol
"string", // to
"string" // amount
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
// required
{
"name": "transfer",
"args": [
"string", // token_symbol
"string", // from
"string", // to
"string", // amount
"string" // memo
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
// optional
{
"name": "transferFreeze",
"args": [
"string", // token_symbol
"string", // from
"string", // to
"string", // amount
"number", // 나노초 단위 타임스탬프
"string" // memo
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
// optional
{
"name": "destroy",
"args": [
"string", // token_symbol
"string", // from
"string" // amount
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
// optional
{
"name": "supply",
"args": [
"string" // token_symbol
]
},
// optional
{
"name": "totalSupply",
"args": [
"string" // token_symbol
]
},
// optional
{
"name": "balanceOf",
"args": [
"string", // token_symbol
"string" // owner
]
}
]
}

명세

토큰 정보

토큰 정보는 token.iost에 저장됩니다. 지갑 등 애플리케이션은 정보의 신뢰성을 확보하기 위해 token.iost 컨트랙트에 저장된 정보를 직접 사용해야 합니다.

issue(tokenSymbol, acc, amountStr)

Optional: 애플리케이션은 이 메서드가 존재한다고 가정해서는 안 됩니다.

필요 권한: tokenSymbol의 issuer

acc 계정에 tokenSymbol을 발행합니다. amountStr은 발행할 수량을 나타내는 문자열로, "100", "100.999"와 같은 양의 고정소수점 표현이어야 합니다.

transfer(tokenSymbol, accFrom, accTo, amountStr, memo)

필요 권한: accFrom

tokenSymbol을 accFrom에서 accTo로 amountStr 만큼 memo와 함께 전송합니다. amount는 양의 고정소수점 표현이어야 하며, memo는 512 바이트를 초과하지 않는 부가 문자열입니다.

전송 성공 여부 판단 기준은 token.iost 컨트랙트의 표준과 동일합니다. 트랜잭션이 성공한 상태에서 tx_receiptreceipts 필드 중 func_nametoken.iost/transfer인 항목이 있으면 전송이 성공한 것입니다. 통화, 계정, 금액은 해당 항목의 content 필드에서 추가 파싱해야 합니다. 자세한 내용은 송금 성공 판단 방법을 참고하세요.

transferFreeze(tokenSymbol, accFrom, accTo, amountStr, unfreezeTime, memo)

Optional: 애플리케이션은 이 메서드가 존재한다고 가정해서는 안 됩니다.

필요 권한: accFrom

tokenSymbol을 accFrom에서 accTo로 amountStr 만큼 memo와 함께 전송하되, unfreezeTime까지 이 분량의 토큰을 동결합니다. unfreezeTime은 토큰 동결 해제 시점의 unix 시간을 나노초 단위로 표현한 값입니다.

전송 성공 여부 판단 기준은 token.iost 컨트랙트의 표준과 동일합니다. 트랜잭션이 성공한 상태에서 tx_receiptreceipts 필드 중 func_nametoken.iost/transferFreeze인 항목이 있으면 전송이 성공한 것입니다. 통화, 계정, 금액, unfreezeTime은 해당 항목의 content 필드에서 추가 파싱해야 합니다. 자세한 내용은 송금 성공 판단 방법을 참고하세요.

destroy(tokenSymbol, accFrom, amountStr)

Optional: 애플리케이션은 이 메서드가 존재한다고 가정해서는 안 됩니다.

필요 권한: accFrom

accFrom 계정의 토큰 중 amountStr 만큼 소각합니다. 소각 이후에는 토큰의 supply도 같은 만큼 줄어듭니다. 즉, 일부 토큰을 소각하면 totalSupply 범위 내에서 추가로 발행이 가능해집니다.

balanceOf(tokenSymbol, acc)

Optional: 애플리케이션은 이 메서드가 존재한다고 가정해서는 안 됩니다.

필요 권한: 없음

특정 토큰의 계정 잔액을 조회합니다.

supply(tokenSymbol)

Optional: 애플리케이션은 이 메서드가 존재한다고 가정해서는 안 됩니다.

필요 권한: 없음

특정 토큰의 현재 공급량을 조회합니다.

totalSupply(tokenSymbol)

Optional: 애플리케이션은 이 메서드가 존재한다고 가정해서는 안 됩니다.

필요 권한: 없음

특정 토큰의 totalSupply를 조회합니다.

구현

아래는 기본 구현입니다. 코드를 수정하여 커스터마이즈할 수 있습니다.

// ABI:
{
"lang": "javascript",
"version": "1.0.0",
"abi": [
{
"name": "can_update",
"args": [
"string"
]
},
{
"name": "issue",
"args": [
"string",
"string",
"string"
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
{
"name": "transfer",
"args": [
"string",
"string",
"string",
"string",
"string"
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
{
"name": "transferFreeze",
"args": [
"string",
"string",
"string",
"string",
"number",
"string"
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
{
"name": "destroy",
"args": [
"string",
"string",
"string"
],
"amountLimit": [{
"token": "*",
"val": "unlimited"
}]
},
{
"name": "supply",
"args": [
"string"
]
},
{
"name": "totalSupply",
"args": [
"string"
]
},
{
"name": "balanceOf",
"args": [
"string",
"string"
]
}
]
}

// code:
const name = "ytk";
const fullName = "YTK Stable Coin"; // 지갑과 브라우저에서는 토큰 이름을 "name(fullName)" 형식으로 표시하는 것을 권장합니다. 예: ytk(YTK stable coin)
const decimal = 8;
const totalSupply = 90000000000;
const admin = "your_admin";

class Token {
init() {
blockchain.callWithAuth("token.iost", "create", [
name,
blockchain.contractName(),
totalSupply,
{
fullName,
decimal,
canTransfer: true,
onlyIssuerCanTransfer: true,
}
]);
}

can_update(data) {
return blockchain.requireAuth(blockchain.contractOwner(), "active");
}

_amount(amount) {
return new BigNumber(new BigNumber(amount).toFixed(decimal));
}

_checkToken(token_name) {
if (token_name !== name) {
throw "token not exist";
}
}

issue(token_name, to, amount) {
if (!blockchain.requireAuth(admin, "active")) {
throw "permission denied";
}
this._checkToken(token_name);
amount = this._amount(amount);
blockchain.callWithAuth("token.iost", "issue", [token_name, to, amount]);
}

transfer(token_name, from, to, amount, memo) {
this._checkToken(token_name);
amount = this._amount(amount);
blockchain.callWithAuth("token.iost", "transfer", [token_name, from, to, amount, memo])
}

transferFreeze(token_name, from, to, amount, timestamp, memo) {
this._checkToken(token_name);
amount = this._amount(amount);
blockchain.callWithAuth("token.iost", "transferFreeze", [token_name, from, to, amount, timestamp, memo]);
}

destroy(token_name, from, amount) {
this._checkToken(token_name);
amount = this._amount(amount);
blockchain.callWithAuth("token.iost", "destroy", [token_name, from, amount]);
}

// ABI를 호출하고 결과를 JSON 문자열로 파싱
_call(contract, api, args) {
const ret = blockchain.callWithAuth(contract, api, args);
if (ret && Array.isArray(ret) && ret.length >= 1) {
return ret[0];
}
return null;
}

balanceOf(token_name, owner) {
this._checkToken(token_name);
return this._call("token.iost", "balanceOf", [token_name, owner]);
}

supply(token_name) {
this._checkToken(token_name);
return this._call("token.iost", "supply", [token_name]);
}

totalSupply(token_name) {
this._checkToken(token_name);
return this._call("token.iost", "totalSupply", [token_name]);
}
}

module.exports = Token;