Caver-js로 Klaytn NFT 발행하기

지난 번엔 트러플(Truffle)을 이용해서 이더리움 네트워크에 NFT를 발행하는 방법을 소개했는데요. 이번엔 이더리움 대신 클레이튼(Klaytn) 네트워크에 NFT를 발행하는 방법을 다뤄 보겠습니다.

이더리움도 그렇지만, 클레이튼에서도 다양한 방법으로 스마트계약을 배포할 수 있습니다. 이더리움처럼 트러플을 사용할 수도 있고, Klaytn IDE나 클레이튼에서 제공하는 API 서비스인 KAS를 사용해도 되죠.

NFT 발행은 트러플로 하면 간단하지만, 트러플은 지난 번에 한번 사용했기에 이 글에서는 Klaytn SDK 속에 포함된 caver-js를 사용해 NFT 스마트계약을 한번 처리해 보기로 하겠습니다. 실제로 dApp을 개발하거나 Web3 관련 프로젝트를 해야할 경우라면 caver-js 같은 자바스크립트 라이브러리는 꼭 필요합니다.

Caver-js 설치하기

Caver-js는 HTTP 또는 웹소켓 연결을 통해 클레이튼 EN과 상호작용할 수 있도록 해주는 자바스크립트 라이브러리죠. 이더리움의 web3.js와 같은 역할이라고 보면 되는데요. 예전엔 web3.js 같은 인터페이스를 사용하다가 최근(v1.4.1이후) web3.js와는 다른 인터페이스로 변경되었죠.

caver-js는 다음과 같은 패키지들로 구성됩니다.

caver-js의 패키지
caver-js의 패키지

Caver-js를 사용하기 위해 프로젝트 디렉터리에 caver-js 패키지를 설치합니다. NFT 스마트계약을 발행하기 위해 @klaytn/contracts 패키지도 함께 설치합니다.

$ npm i caver-js @klaytn/contracts

NFT 계약 만들기

Caver-js 설치가 완료되었다면, 이제 NFT 발행을 위한 스마트계약을 하나 만들어 보겠습니다. 클레이튼에서 NFT는 KIP17 규약에 따른 스마트계약이며 이더리움의 ERC-721과 호환됩니다.

우선 contracts 디렉터리를 만들고 계약 파일을 하나 추가합니다.

$ mkdir -p contracts
proglang-nft$ touch contracts/ProgLangNFT.sol

여기서는 클레이튼에서 미리 만들어서 제공하는 KIP17Token 계약을 상속받아 사용하기로 하겠습니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.1;

import "@klaytn/contracts/token/KIP17/KIP17Token.sol";

contract ProgLangNFT is KIP17Token {
  constructor() public KIP17Token("ProgLang NFT", "PROGLANG") {
  }
}

계약은 프로그래밍 언어의 아이콘 파일들을 NFT로 만들어 발행하려고 합니다. NFT 이름은 “ProgLang NFT”라고 주었습니다.

계약을 작성했다면 이제 Solidity 컴파일러로 컴파일하면 됩니다. Solidity 컴파일러는 편한 것으로 사용하면 되지만, 여기서는 solcjs를 래핑한 @0x/sol-compiler를 사용했습니다.

$ npx sol-compiler
Pre-fetching solidity versions: 0.5.17...
Downloading soljson-v0.5.17+commit.d19bba13.js...
Compiling 24 contracts (ProgLangNFT.sol,KIP17Token.sol,KIP17Full.sol,KIP17.sol,IKIP17.sol,IKIP13.sol,IERC721Receiver.sol,IKIP17Receiver.sol,SafeMath.sol,Address.sol,Counters.sol,KIP13.sol,KIP17Enumerable.sol,IKIP17Enumerable.sol,KIP17Metadata.sol,IKIP17Metadata.sol,KIP17MetadataMintable.sol,MinterRole.sol,Roles.sol,KIP17Mintable.sol,KIP17Burnable.sol,KIP17Pausable.sol,Pausable.sol,PauserRole.sol) with Solidity 0.5.17+commit.d19bba13...
ProgLangNFT artifact saved!

컴파일이 완료되면 프로젝트 디렉터리 내 artifacts 하위 디렉터리 내에 컴파일된 결과물이 JSON 파일로 저장됩니다.

NFT 계약 배포

계약 컴파일이 완료되었으면 이제 배포할 차례입니다. 이제 caver-js를 쓸 때가 되었네요.

우선 임의의 디렉터리를 하나 만들고 그 속에 deploy.js 라는 파일을 하나 추가합니다. 이름은 뭐 아무거나 줘도 상관 없습니다. 그런 다음처럼 코드를 추가합니다.

// scripts/deploy.js
const fs = require('fs')
const Caver = require('caver-js')
const Artifact = require('../artifacts/ProgLangNFT.json')

async function deploy() {
  const caver = new Caver('https://your.en.url:8651/')

  // Add a keyring to caver.wallet
  const privateKey = fs.readFileSync(".secret").toString().trim()
  const deployer = caver.wallet.keyring.createFromPrivateKey(privateKey)
  caver.wallet.add(deployer)

  const gas = 150000000
  const abi = Artifact.compilerOutput.abi
  const data = Artifact.compilerOutput.evm.bytecode.object

  const contract = caver.contract.create(abi)
  const deployed = await contract.deploy({from: deployer.address, gas}, data)
  console.log(deployed.options.address)
}

deploy()

이 코드를 잠깐 설명하자면, 우선 Klaytn EN의 URL을 입력하여 Caver 객체를 생성합니다. 그리고 개인키(private key)를 사용하여 키링(keyring)을 만들고 그 키링을 caver.wallet에 추가합니다. 이렇게 추가한 키링은 나중에 계약을 발행할 때 사용됩니다.

// Add a keyring to caver.wallet
const privateKey = fs.readFileSync(".secret").toString().trim()
const deployer = caver.wallet.keyring.createFromPrivateKey(privateKey)
caver.wallet.add(deployer)

그런 다음 앞서 컴파일한 JSON 파일로부터 ABI 데이터를 불러와서 caver.contract.create 메서드로 계약 객체를 만듭니다.

const abi = Artifact.compilerOutput.abi
const data = Artifact.compilerOutput.evm.bytecode.object
const contract = caver.contract.create(abi)

마지막으로 생성된 계약 객체의 deploy 메서드를 호출하여 계약을 발행합니다. 이 때 키링 속에 들은 발행자 주소와 발행에 필요한 가스(gas) 값, 그리고 컴파일된 계약의 바이트코드(bytecode)를 data 값으로 전달합니다.

const deployed = await contract.deploy({from: deployer.address, gas}, data)

스크립트 코드를 실행해 보면 다음과 같이 계약이 발행되고, 발행된 계약의 주소(address) 값이 출력되는 것을 확인할 수 있습니다.

$ node scripts/deploy.js
0xB3F5d9713aE31D3b45bEA0e2b4519207f82A311e

제대로 발행되었는지 Klaytnscope에서 확인해 봅니다. 여기서는 테스트를 위해 바오밥(Baobab) 테스트넷에 배포했습니다.

https://baobab.scope.klaytn.com/account/0xb3f5d9713ae31d3b45bea0e2b4519207f82a311e?tabId=txList

ProgLang NFT 계약 배포
ProgLang NFT 계약 배포

NFT 발행하기

계약 배포가 정상적으로 완료되었으면, 이제 계약에 의해 NFT 토큰을 발행해 봅니다. 토큰 발행 역시 앞서 소개한 deploy.js 스크립트와 같은 방법으로 caver-js를 사용해서 처리하면 됩니다. 차이점은, 앞서는 계약의 deploy 메서드를 호출했다면, 이번엔 send 메서드를 호출한다는 점만 다를 뿐입니다.

const deployed = caver.contract.create(abi, contractAddress)
const receipt = await deployed.send({from: deployer.address, gas},
      'mintWithTokenURI', toAddress, 1, `${baseURI}/1`)

앞선 코드와는 달리 이번엔 caver.contract.create 메서드의 두번 째 인자 값으로 앞서 발행한 계약의 주소값을 입력하였음에 유의하세요. 그리고 여기서는 NFT를 발행하기 위해 KIP17Token 계약 속의 mintWithTokenURI메서드를 호출하고 있음에도 유의하세요. 물론 실제 프로젝트에서라면 이 발행(mint) 기능을 프로젝트 니즈에 맞춰 커스터마이징해서 사용하면 될 듯 하구요. mintWithTokenURI 메서드의 세 번째 인자는 토큰의 URI, 즉 토큰의 메타데이터에 대한 URI를 지정하면 됩니다.

그럼 스크립트를 한번 실행해 보겠습니다.

$ node scripts/mint.js
{
  blockHash: '0xc9f9bbd69f64df82ac500079312422aea53da83d8a158c94edb1f42c0f8da89a',
  blockNumber: 83587526,
  contractAddress: null,
  from: '0x4899970a72fd38c5e391d8013f98acb14afb93de',
  gas: '0x8f0d180',
  gasPrice: '0x5d21dba00',
  gasUsed: 258674,
  input: '0x50bb4e7f0000000000000000000000007eb6a920e40039ffb6ea45613347a5fb202e9d2d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000005168747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f736a6f6f6e6b2f6d792d6e66742f6d61737465722f6d657461646174612f70726f676c616e672f746f6b656e732f31000000000000000000000000000000',
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000200000000000000000000000040000000000000000000000000008000000000000000000040000000000000000000000000000020000000000000000000800000000000000000000000010000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000004000000002000000000000000000000000000000000000000000000000000060000000000000000000000000000000000002000000000000000000000000000000',
  nonce: '0x1a',
  senderTxHash: '0x072b0a83204a5aafdd9298cfe7cdbdae375072cde9c7a3e98dc0227c592a8fc2',
  signatures: [
    {
      V: '0x7f6',
      R: '0x29972e0b1961afa5a1017a8e2389abb0f9123e32ff0a6d3afc9eb1e16321843a',
      S: '0x3d97887a51d8d5176f660730bf93ca7452206353241f537319749c7d0491f1f'
    }
  ],
  status: true,
  to: '0xb3f5d9713ae31d3b45bea0e2b4519207f82a311e',
  transactionHash: '0x072b0a83204a5aafdd9298cfe7cdbdae375072cde9c7a3e98dc0227c592a8fc2',
  transactionIndex: 0,
  type: 'TxTypeSmartContractExecution',
  typeInt: 48,
  value: '0x0',
  events: {
    Transfer: {
      address: '0xB3F5d9713aE31D3b45bEA0e2b4519207f82A311e',
      blockNumber: 83587526,
      transactionHash: '0x072b0a83204a5aafdd9298cfe7cdbdae375072cde9c7a3e98dc0227c592a8fc2',
      transactionIndex: 0,
      blockHash: '0xc9f9bbd69f64df82ac500079312422aea53da83d8a158c94edb1f42c0f8da89a',
      logIndex: 0,
      id: 'log_9d104069',
      returnValues: [Result],
      event: 'Transfer',
      signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
      raw: [Object]
    }
  }
}

짜잔!! 첫 번째 토큰이 발행되었네요!

역시 이번에도 Klaytnscope에서 한번 확인해 볼게요.

https://baobab.scope.klaytn.com/nft/0xb3f5d9713ae31d3b45bea0e2b4519207f82a311e?tabId=nftTransfer

Klayscope에서 ProgLang NFT 확인
Klayscope에서 ProgLang NFT 확인

NFT 계약 주소로 검색하면 오픈시에서도 확인할 수 있겠죠.

https://testnets.opensea.io/assets/baobab/0xb3f5d9713ae31d3b45bea0e2b4519207f82a311e/1

오픈시에서 NFT 배포 확인
오픈시에서 NFT 배포 확인

발행한 ProgLang NFT 컬렉션을 오픈시에서 확인해 보세요!

ProgLang NFT 컬렉션
ProgLang NFT 컬렉션

Contact

유스풀패러다임
03159 서울특별시 종로구 종로 33
그랑서울타워1, 7층

+82 02 720 5059
Contact Us

Connect

Links

유스풀패러다임의 다른 사이트들도 만나 보세요.

Usefulparadigm blog
WordPress 가이드
Landing Jekyll
Hello Gatsby