KIP-17 is a contract standard for NFT (Non-Fungible Token) created by Klaytn. KIP-17 API provided by KAS lets you create and manage KIP-17 tokens with ease. Main features of KIP-17 API include deploying KIP-17 contracts, as well as issuing, burning and transferring of tokens.

In this tutorial we will walk you through how to not only deploy KIP-17 contracts but also generate and send KIP-17 tokens. For more details on KIP-17 API, please refer to KAS KIP-17 API Reference.

Deploying KIP-17 Contracts

KIP-17 API deploys and controls KIP-17 compliant NFT contracts .

info

For more information on KIP-17 contract functions, please refer to KIP-17.

API Request

You can deploy a KIP-17 contract by running the curl command.

cURLJavaScriptJava
Copy
Copied
curl --location --request POST "https://kip17-api.klaytnapi.com/v2/contract" \
  --header "x-chain-id: {chain-id}" \
  -u {access-key-id}:{secret-access-key} \
  --data-raw '{
  "alias": "my-first-kip17",
  "name": "My First KIP-17",
  "symbol": "MFK"
  }'
Copy
Copied
const result = await caver.kas.kip17.deploy(
  "My First KIP-17",
  "MFK",
  "my-first-kip17"
);
Copy
Copied
String name = "My First KIP-17";
String symbol = "MFK";
String alias = "my-first-kip17";
Kip17TransactionStatusResponse res = caver.kas.kip17.deploy(name, symbol, alias);

Let's break down curl command for a closer look. You can use Deploy New KIP-17 Contract with POST /v2/contract. KIP-17 API is provided at https://kip17-api.klaytnapi.com, so write down https://kip17-api.klaytnapi.com/v2/contract for the URL of the curl request, and POST (—-request POST) for request type.

The contract deployment API accepts the POST request and requires the following JSON data:

cURLJavaScriptJava
Copy
Copied
{
  "alias": "my-first-kip17",
  "name": "My First KIP-17",
  "symbol": "MFK"
}
Copy
Copied
// If a KIP-17 contract is being deployed using the caver-js-ext-kas, the data required for the API request body will be sent as function parameters
// caver.kas.kip17.deploy(name, symbol, alias [, callback])
const result = await caver.kas.kip17.deploy(
  "My First KIP-17",
  "MFK",
  "my-first-kip17"
);
Copy
Copied
String name = "My First KIP-17";
String symbol = "MFK";
String alias = "my-first-kip17";

Kip17TransactionStatusResponse res = caver.kas.kip17.deploy(name, symbol, alias);
System.out.println(res);

Here are some explanations for each field:

  • Alias ( alias ): A nickname for the contract. You can use it in place of the contract address in later APIs. The alias must contain only lowercase letters, numbers and hyphens. Note that the first letter of the alias must begin with a lowercase letter.
  • Name ( name ): Name of the contract. It is written as name in accordance with the KIP-17 standard. The name must only include alphabets, numbers and hyphens.
  • Symbol( symbol ): Symbol of the contract. It is written as symbol according to the KIP-17 standard. It normally consists of 3-4 uppercase alphabets, but there is no restriction.
  • Required Header

All KIP-17 APIs require an x-chain-id header. Valid values are 1001 on Baobab, and 8217 on Cypress.

  • Verification

All APIs provided by KAS must send authentication data, the access-key-id and the secret-access-key. For more details on the creation and obtainment of authentication data, please refer to Security.

API Response

Running the token deployment curl command will return the following results:

cURLJavaScriptJava
Copy
Copied
{
  "transactionHash": "0x...",
  "status": "Submitted"
}
Copy
Copied
{
 status: 'Submitted',
 transactionHash: '0x...',
}
Copy
Copied
class Kip17TransactionStatusResponse {
    status: Submitted
    transactionHash: 0x....
}

The transactionHash is available while executing RPC functions such as klay_getTransactionReceipt.

Getting KIP-17 Contract List

API Request

You can search the deployed contracts by implementing GET /v2/contract in the Get List of KIP-17 Contracts, KIP-17 API. Run the following command curl to search the contract list.

cURLJavaScriptJava
Copy
Copied
curl --location --request GET 'https://kip17-api.klaytnapi.com/v2/contract' \
  --header "x-chain-id: {chain-id}" \
  -u {access-key-id}:{secret-access-key}
Copy
Copied
const result = await caver.kas.kip17.getContractList();
Copy
Copied
Kip17ContractListResponse response = caver.kas.kip17.getContractList();
System.out.println(response);

API Response

If the contract has been deployed correctly, you may receive a response as follows:

cURLJavaScriptJava
Copy
Copied
{
  "items": [
    {
      "address": "0x...",
      "alias": "my-first-kip17",
      "name": "My First KIP-17",
      "symbol": "MFK"
    }
  ],
  "cursor": ""
}
Copy
Copied
{
 cursor: '',
 items: [
  {
   address: '0x...',
   alias: 'my-first-kip17',
   name: 'My First KIP-17',
   symbol: 'MFK',
  },
 ],
}
Copy
Copied
class Kip17ContractListResponse {
    cursor: "..."
    items: [class Kip17ContractListResponseItem {
        address: 0x...
        alias: my-first-kip17
        name: My First KIP-17
        symbol: MFK
    },
 {...}
 ]
}

Minting KIP-17 Tokens

Now that you have successfully deployed the contract, you can actually start creating tokens with the API, POST /v2/contract/{alias-or-address}/token. The value of {alias-or-address} is available with two options: the alias previously used for deploying tokens with the contract's alias or address, or the address checked on the Get List of KIP-17 Contracts API after deploying the tokens.

API Request

Following is the curl command for calling Mint a New KIP-17 Token using the alias my-first-kip17 from the example above.

cURLJavaScriptJava
Copy
Copied
curl --location --request POST 'https://kip17-api.klaytnapi.com/v2/contract/my-first-kip17/token' \
  --header "x-chain-id: {chain-id}" \
  -u {access-key-id}:{secret-access-key} \
 --data-raw '{
   "to": "0x...",
   "id": "0x1",
   "uri": "https://link.to.your/token/metadata-0x1.json"
 }'
Copy
Copied
const result = await caver.kas.kip17.mint(
  "my-first-kip17",
  "0x...",
  "0x1",
  "https://link.to.your/token/metadata-0x1.json"
);
Copy
Copied
String contractAlias = "my-first-kip17";
String to = "0x{toAddress}";
String id = "0x1";
String uri = "https://link.to.your/token/metadata-0x1.json";

Kip17TransactionStatusResponse response = caver.kas.kip17.mint(contractAlias, to, id, uri);
System.out.println(response);

Required header and authentication data are the same as those for contract deployment. Data such as location and request can be entered with reference to the mint token API (POST /v2/contract/{alias-or-address}/token).

The mint token API requires the following JSON data:

cURLJavaScriptJava
Copy
Copied
{
  "to": "0x...",
  "id": "0x1",
  "uri": "https://link.to.your/token/metadata-0x1.json"
}
Copy
Copied
// If a KIP-17 contract is being deployed using the caver-js-ext-kas, the data required for the API request body will be sent as function parameters
// caver.kas.kip17.mint(addressOrAlias, to, tokenId, tokenURI [, callback])
const result = await caver.kas.kip17.mint(
  "my-first-kip17",
  "0x...",
  "0x1",
  "https://link.to.your/token/metadata-0x1.json"
);
Copy
Copied
// If a KIP-17 contract is being deployed using the caver-js-ext-kas, the data required for the API request body will be sent as function parameters
// caver.kas.kip17.mint((addressOrAlias, to, tokenId, tokenURI);
String contractAlias = "my-first-kip17";
String to = "0x{toAddress}";
String id = "0x1"
String uri = "https://link.to.your/token/metadata-0x1.json";

Kip17TransactionStatusResponse response = caver.kas.kip17.mint(contractAlias, to, id, uri);
System.out.println(response);
  • Recipient ( to ): Klaytn account address of the token’s recipient. The token generation API will create a new token at the designated address.
  • Token ID ( id ): Unique ID of the token. It is a hexadecimal number. An ID of an already existing token cannot be reused, unless it is burnt.
  • Token URI ( uri ): Location of the JSON file containing the token’s data expressed as URI . The token’s data and its attributes are recorded and deployed in advance on a location which can be represented as URI, and the URI is included when the token is generated. Since the validity of the URI link is not checked for you, make sure to double-check before entering it.
info

A Klaytn account address is given in the hexadecimal format. 20 bytes long, it consists of 42 hexadecimal characters including the prefix "0x".

info

What is a URI? Wikipedia Link

API Response

Running the token generation curl command will return the following response:

cURLJavaScriptJava
Copy
Copied
{
  "transactionHash": "0x...",
  "status": "Submitted"
}
Copy
Copied
{
 status: 'Submitted',
 transactionHash: '0x...',
}
Copy
Copied
class Kip17TransactionStatusResponse {
    status: Submitted
    transactionHash: 0x....
}

You will notice that the response is in the same form as that of contract deployment.

Getting Token List

API Request

To verify that tokens have been generated correctly, you can use the Get Generated Token List API GET /v2/contract/{alias-or-address}/token and check if the tokens have been added to the contract. You can retrieve the my-first-kip17 contract token list by using the following curl command.

cURLJavaScriptJava
Copy
Copied
curl --location --request GET 'https://kip17-api.klaytnapi.com/v2/contract/my-first-kip17/token' \
  --header "x-chain-id: {chain-id}" \
  -u {access-key-id}:{secret-access-key}
Copy
Copied
const result = await caver.kas.kip17.getTokenList("my-first-kip17");
Copy
Copied
Kip17TokenListResponse response = caver.kas.kip17.getTokenList("my-first-kip17");
System.out.println(response);

API Response

If the tokens have been successfully generated, you will receive a response as the following:

cURLJavaScriptJava
Copy
Copied
{
 "items": [{
   "createdAt": <UNIX timestamp>,
   "owner": "0x...",
   "previousOwner": "0x0000000000000000000000000000000000000000",
   "tokenId": "0x1",
   "tokenUri": "https://link.to.your/token/metadata-0x1.json",
   "transactionHash": "0x...",
   "updatedAt": <UNIX timestamp>
  }],
 "cursor": ""
}
Copy
Copied
{
 cursor: '',
 items: [
  {
   createdAt: 1610524249,
   owner: '0x...',
   previousOwner: '0x0000000000000000000000000000000000000000',
   tokenId: '0x1',
   tokenUri: 'https://link.to.your/token/metadata-0x1.json',
   transactionHash: '0x...',
   updatedAt: 1610524249,
  },
 ],
}
Copy
Copied
class Kip17TokenListResponse {
    cursor:
    items: [class Kip17TokenListResponseItem {
        createdAt: 1610524249
        owner: 0x...
        previousOwner: 0x0000000000000000000000000000000000000000
        tokenId: 0x1
        tokenUri: https://link.to.your/token/metadata-0x1.json
        transactionHash: 0x...
        updatedAt: 1610524249
    }, class Kip17TokenListResponseItem {
        ...
    }]
}
info

Take a close look at the status of the response, you will notice the word "Submitted" instead of "Success" or "Completed". Every blockchain including Klaytn operates asynchronously, which means that the response of a request doesn’t return immediately. Therefore, there is no way of knowing right away if the request has succeeded or not. Especially with token generation, where a request can fail depending on the request values (e.g. using an existing token ID), make sure to confirm the status by implementing Get Token List.

Transferring KIP-17 Token

API Request

KIP-17 Token transfer API is POST /v2/contract/{alias-or-address}/token/{id}. {alias-or-address}, {id} use data of the contract and the token to be transferred.

This tutorial assumes that the KAS user has an account on KAS Wallet Account Pool. Let's say the address is, for convenience sake, 0xOWNER.

info

For creation and management of accounts, click here.

info

In order to send a token with KIP-17 API, the sender account’s cryptographic key must be registered in the KAS Wallet Account Pool. When the Key is not included in the Default Account Pool, enter the pool’s KRN on the x-krn header manually.

info

In the tutorial we used 0xOWNER just for convenience sake. You have to enter your Klaytn account address correctly.

The following curl command sends 0x321 tokens to 0xRECIPIENT via my-first-kip17 contract owned by 0xOWNER.

cURLJavaScriptJava
Copy
Copied
curl --location --request POST 'https://kip17-api.klaytnapi.com/v2/contract/my-first-kip17/token/0x321' \
  --header "x-chain-id: {chain-id}" \
  -u {access-key-id}:{secret-access-key} \
 --data-raw '{
   "sender": "0xOWNER",
   "owner": "0xOWNER",
   "to": "0xRECIPIENT"
 }'
Copy
Copied
// caver.kas.kip17.transfer(addressOrAlias, sender, owner, to, tokenId [, callback])
const result = await caver.kas.kip17.transfer(
  "my-first-kip17",
  "0xOWNER",
  "0xOWNER",
  "0xRECIPIENT",
  "0x321"
);
Copy
Copied
String contractAlias = "my-first-kip17";
String ownerAddress = "0x{OwnerAddress}";
String recipientAddress = "0x{RecipientAddress}";
String tokenId = "0x321";
Kip17TransactionStatusResponse response = caver.kas.kip17.transfer(contractAlias, ownerAddress, ownerAddress, recipientAddress, tokenId);
System.out.println(response);

The JSON data required for transferring token are as follows:

  • Sender ( sender ): The address of the person using token transfer API.
  • Owner ( owner ): The address of the person that owns the token to be transferred. When the sender is different from the owner, the address must be verified for transfer in advance. For more detailed APIs on token transfer verification, please refer to token transfer verification .
  • Recipient ( to ): The address of the person receiving the token. Keep in mind that owing to the nature of blockchain technologies, you cannot revert once the ownership has been changed.

When the information of the sender or the owner is incorrect, the transfer request will fail. When the sender’s address has not been verified for transfer, it must be identical with the owner’s, and the owner’s address must be that of the actual owner of the token being transferred.

info

Even when the information of the sender or the owner is incorrect, the transfer request API will still return a response. The KIP-17 API only validates the request method and syntax, and not the semantics, such as whether the sender has been verified, or the owner actually owns the coin.

To check if the transfer request has been applied correctly on the blockchain, use KIP-17 API's Get Token List or Get Token Information, or check the results after running Node API klay_getTransactionReceipt.

API Response

You will receive the response as shown below when you run token transfer API.

cURLJavaScriptJava
Copy
Copied
{
  "transactionHash": "0x...",
  "status": "Submitted"
}
Copy
Copied
{
 status: 'Submitted',
 transactionHash: '0x...',
}
Copy
Copied
class Kip17TransactionStatusResponse {
    status: Submitted
    transactionHash: 0x....
}

To verify that token has been successfully transferred, implement GET /v2/contract/{alias-or-address}/token from Get List of Tokens API to check whether the token's transfer data is updated.

cURLJavaScriptJava
Copy
Copied
curl --location --request GET 'https://kip17-api.klaytnapi.com/v2/contract/my-first-kip17/token'
 --header "x-chain-id: {chain-id}" \
  -u {access-key-id}:{secret-access-key}
Copy
Copied
const result = await caver.kas.kip17.getTokenList("my-first-kip17");
Copy
Copied
Kip17TokenListResponse response = caver.kas.kip17.getTokenList("my-first-kip17");
System.out.println(response);

When the token has been correctly sent, you will see the changed data of the transferred token. See values: owner and previousOwner.

cURLJavaScriptJava
Copy
Copied
{
 "items": [
  ...,
  {
   "createdAt": <UNIX timestamp>,
   "owner": "0xRECIPIENT",
   "previousOwner": "0xOWNER",
   "tokenId": "0x321",
   "tokenUri": "https://link.to.your/token/metadata-0x1.json",
   "transactionHash": "0x...",
   "updatedAt": <UNIX timestamp>
  },
  ...],
 "cursor": ""
}
Copy
Copied
{
 cursor: '',
 items: [
  ...
  {
   createdAt: 1610524249,
   owner: '0xRECIPIENT',
   previousOwner: '0xOWNER',
   tokenId: '0x321',
   tokenUri: 'https://link.to.your/token/metadata-0x1.json',
   transactionHash: '0x...',
   updatedAt: 1610524249,
  },
  ...
 ],
}
Copy
Copied
class Kip17TokenListResponse {
    cursor:
    items: [class Kip17TokenListResponseItem {
        createdAt: 1610524249
        owner: 0x...
        previousOwner: 0x0000000000000000000000000000000000000000
        tokenId: 0x1
        tokenUri: https://link.to.your/token/metadata-0x1.json
        transactionHash: 0x...
        updatedAt: 1610524249
    }, class Kip17TokenListResponseItem {
        ...
    }]
}