본문으로 건너뛰기

NFT(CW721) 발행하기 (Javascript)

CW721을 이용하면 XPLA 블록체인에서 쉽게 NFT를 발행할 수 있습니다. 직접 Javascript로 NFT를 만들어보고, 게임에 적용해봅시다.

NFT란 무엇인가요?

NFT란 Non-Fungible Token의 약자로, 대체 불가능한 토큰이란 뜻입니다. 대체 불가능이라는 건 무슨 뜻일까요?

1달러짜리 지폐 두 장의 가치는 서로 같습니다. 둘 다 1달러의 가치를 지니므로, 하나가 다른 하나를 대체할 수 있습니다. 그러나 Alice가 그린 모나지라 그림과 루브르 박물관에 있는 모나리자의 가치는 어떨까요? Alice가 모나리자 그림의 모든 부분을 똑같이 묘사해도, 진짜 모나리자를 대체할 수는 없습니다. 이처럼 서로 대체할 수 없는 고유한 토큰을 두고 NFT라고 부릅니다.

NFT로 무엇을 할 수 있을까요? Alice는 그림을 하나 그려 프로필 사진으로 사용하려고 합니다. 그런데 누군가가 Alice의 그림을 복사하여 사용하고, 자신이 그린 그림이라고 주장하면 어떡할까요? Alice가 그림의 주요 정보를 NFT에 담는다면 그림의 소유권을 증명할 수 있을 것입니다.

CW721이란 무엇인가요?

XPLA 블록체인은 스마트 컨트랙트 플랫폼인 Cosmwasm을 이용합니다. 이더리움의 ERC721처럼, CW721Cosmwasm에서 대체 불가능한 토큰(Non-Fungible Tokens)에 대한 표준입니다. ERC721과 이름은 비슷할지 몰라도, 기능은 많이 다릅니다. 더 자세히 알고 싶다면 공식 문서를 참고해보세요.

Preview

먼저 Javascript로 CW721 컨트랙트를 생성하는 코드는 아래와 같습니다. 여러분의 니모닉 단어를 넣어 코드를 수정하고 실행해보세요. 조금만 기다리시면 우측에서 실행 결과를 볼 수 있습니다.

const { LCDClient, MnemonicKey, MsgInstantiateContract } = require("@xpla/xpla.js");

const lcd = new LCDClient({
  chainID: 'cube_47-5',
  URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
  const mk = new MnemonicKey({
    mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle' // Replace with your mnemonic words
  })

  const wallet = lcd.wallet(mk);
  const myWalletAddress = wallet.key.accAddress;

  const init_msg = {
    "name": "YourNFTName",
    "symbol": "YNN",
    "minter": myWalletAddress
  }

  const instantiate = new MsgInstantiateContract(
    myWalletAddress, // sender
    myWalletAddress, // admin
    3, // MAINNET, TESTNET CW721 code id
    init_msg,
    {}, // XPLA token amount to send to the contract (leave empty for now, not needed at the moment).
    'Input your label', // Enter the label you want to write.
  );

  const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
    msgs: [instantiate]
  });

  const txResult = await lcd.tx.broadcastSync(signedTx);
  console.log(txResult.txhash);
}
main()



컨트랙트를 잘 생성하셨나요? 이제 컨트랙트를 이용하여 NFT를 발행해봅시다. 아래 코드에서 여러분의 니모닉 단어를 넣어주세요. 이후 위에서 만든 컨트랙트 주소를 myContractAddress 변수에 대입해주세요. 코드를 실행하면 잠시 후 NFT 발행 결과를 확인할 수 있습니다.

생성한 컨트랙트의 주소는 어디서 확인하나요?

가장 쉬운 방법은 XPLA Explorer를 이용하는 것입니다. 실행 결과로 나온 트랜잭션 해시값을 XPLA Explorer에서 검색해봅시다. 이후 Transaction Details 화면에서 Event Logs 탭에 들어가시면, 생성된 컨트랙트의 주소를 확인할 수 있습니다. 예제 컨트랙트 주소는 이곳에서 확인할 수 있었습니다.

const { LCDClient, MnemonicKey, MsgExecuteContract } = require("@xpla/xpla.js");

const lcd = new LCDClient({
  chainID: 'cube_47-5',
  URL: 'https://cube-lcd.xpla.dev'
});

const main = async () => {
  const mk = new MnemonicKey({
    mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle' // Replace with your mnemonic words
  })

  const wallet = lcd.wallet(mk);
  const myWalletAddress = wallet.key.accAddress;
  const myContractAddress = "xpla10r58td65tgvpaqngg7802rtpg5l6as8my2unnlnpa8en894qqstshxq0u0"; // Input your Contract Address

  const time = new Date();
  const mintJSON = {
    mint: {
      owner: myWalletAddress,
      token_id: `token_id_${time.getTime()}`,
      // token_uri: "https://your.token.uri.address", // input your token_uri
      extension: {
        name : "My Test XPLA NFT",
        // image: "https://your.image.url", // input your nft image url
        attributes: [
          {
            value: 'Alice',
            trait_type: 'CharacterName',
          },
          {
            value: '100',
            trait_type: 'CP',
          },
          {
            value: '10',
            trait_type: 'HP',
          },
        ],
        description : "This is my First NFT in XPLA Blockchain!",
      },
    },
  };
  const mint = new MsgExecuteContract(
    myWalletAddress,
    myContractAddress,
    mintJSON
  );

  const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
    msgs: [mint]
  });

  const txResult = await lcd.tx.broadcastSync(signedTx);
  console.log(txResult.txhash);
}
main()


Preview Code를 이해하셨다면, 아래 내용을 읽지 않고 바로 다음 단계로 넘어가셔도 좋습니다.


Javascript로 CW721 컨트랙트 생성하기

CW721 컨트랙트를 생성해봅시다. CW721 컨트랙트의 Code ID는 메인넷과 테스트넷 모두 3입니다.

Code ID란 무엇인가요?

EVM과 달리, Cosmwasm에서는 코드 배포(Code Deploy)와 컨트랙트 생성이 한번에 이뤄지지 않습니다. 먼저 코드를 블록체인에 배포(StoreCode)하면 해당 코드의 Code ID를 발급 받습니다. 이후 Code ID를 기반으로 컨트랙트를 생성(InstantiateContract)할 수 있습니다.

똑같은 Code ID로 생성된 컨트랙트들은 모두 같은 코드 기반입니다. 다만 컨트랙트를 생성할 때 입력하는 초깃값(init_msg)이 다르면, 컨트랙트의 세부 사항도 달라집니다. 더 자세한 사항은 XPLA Docs를 참고해보세요.


코드 실행

  1. example-6.js 파일을 생성해주세요.

  2. 아래 코드를 복사하여 example-6.js 파일에 붙여넣고 저장합니다.

    const { LCDClient, MnemonicKey, MsgInstantiateContract } = require("@xpla/xpla.js");

    const lcd = new LCDClient({
    chainID: 'cube_47-5',
    URL: 'https://cube-lcd.xpla.dev'
    });

    const main = async () => {
    const mk = new MnemonicKey({
    mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle' // Replace with your mnemonic words
    })

    const wallet = lcd.wallet(mk);
    const myWalletAddress = wallet.key.accAddress;

    const init_msg = {
    "name": "YourNFTName",
    "symbol": "YNN",
    "minter": myWalletAddress
    }

    const instantiate = new MsgInstantiateContract(
    myWalletAddress, // sender
    myWalletAddress, // admin
    3, // MAINNET, TESTNET CW721 code id
    init_msg,
    {}, // XPLA token amount to send to the contract (leave empty for now, not needed at the moment).
    'Input your label', // Enter the label you want to write.
    );

    const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
    msgs: [instantiate]
    });

    const txResult = await lcd.tx.broadcastSync(signedTx);
    console.log(txResult.txhash);
    }
    main()
  3. 10번째 줄의 니모닉 단어들을 여러분의 니모닉 단어로 수정해주세요.

  4. VSCode Terminal에서 아래 명령어를 입력합니다.

    node example-6.js
  5. Terminal에서 결과를 확인합니다. CW721 컨트랙트가 생성되고, 생성 트랜잭션의 해시값을 확인할 수 있을 것입니다.


설명

우리는 CW721에 관한 컨트랙트 코드를 작성하지 않았습니다. 코드는 이미 XPLA 블록체인에 배포되었고, 단지 배포된 코드로 컨트랙트를 생성했을 뿐입니다.

먼저 니모닉 단어로 지갑을 불러왔습니다. 여러분은 다른 니모닉 단어를 이용하여 여러분의 지갑을 불러오셨을 것입니다.

const mk = new MnemonicKey({
mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle',
})

const wallet = lcd.wallet(mk);
const myWalletAddress = wallet.key.accAddress;

CW721 컨트랙트 생성을 위해 초깃값을 미리 지정해줬습니다. 아래 값들은 여러분이 원하는대로 수정하셔서, 여러분만의 NFT를 만들어 보시는 것도 좋습니다.

const init_msg = {
"name": "YourNFTName",
"symbol": "YNN",
"minter": myWalletAddress
}

누가 컨트랙트를 생성하는지(sender), 컨트랙트의 주인은 누구인지(admin), Code ID, 초깃값, 라벨 등을 입력하여 컨트랙트 생성을 준비합니다.

const instantiate = new MsgInstantiateContract(
myWalletAddress, // sender
myWalletAddress, // admin
3, // MAINNET, TESTNET CW721 code id
init_msg,
{}, // XPLA token amount to send to the contract (leave empty for now, not needed at the moment).
'Input your label', // Enter the label you want to write.
);

createAndSignTx 함수를 이용하여 트랜잭션을 생성하고, 서명까지 진행합니다.

const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
msgs: [instantiate]
});

broadcastSync 함수를 이용하여 생성한 트랜잭션을 네트워크에 송신합니다. 트랜잭션이 XPLA 블록체인에 잘 생성되었다면, 트랜잭션 해시값을 결과로 확인할 수 있을 것입니다.

const txResult = await lcd.tx.broadcastSync(signedTx);
console.log(txResult.txhash);

생성한 컨트랙트 주소를 XPLA Explorer에서 확인해봅시다. 실행 결과로 나온 트랜잭션 해시값을 XPLA Explorer에서 검색합니다. 이후 Transaction Details 화면에서 Event Logs 탭에 들어가시면, 생성된 컨트랙트의 주소를 확인할 수 있습니다. 예제 컨트랙트 주소는 이곳에서 xpla10r58td65tgvpaqngg7802rtpg5l6as8my2unnlnpa8en894qqstshxq0u0인 것을 확인할 수 있습니다.


생성한 CW721 컨트랙트로 NFT 발행하기

지금까지 우리는 CW721 컨트랙트를 생성했습니다. 이제부터 생성한 컨트랙트로 NFT를 발행(Mint)해봅시다.


코드 실행

  1. example-7.js 파일을 생성해주세요.

  2. 아래 코드를 복사하여 example-7.js 파일에 붙여넣고 저장합니다.

    const { LCDClient, MnemonicKey, MsgExecuteContract } = require("@xpla/xpla.js");

    const lcd = new LCDClient({
    chainID: 'cube_47-5',
    URL: 'https://cube-lcd.xpla.dev'
    });

    const main = async () => {
    const mk = new MnemonicKey({
    mnemonic: 'myth snow ski simple century dad gun dolphin sail lawsuit fringe image toast betray frown keep harbor flash table prevent isolate panic tag vehicle' // Replace with your mnemonic words
    })

    const wallet = lcd.wallet(mk);
    const myWalletAddress = wallet.key.accAddress;
    const myContractAddress = "xpla10r58td65tgvpaqngg7802rtpg5l6as8my2unnlnpa8en894qqstshxq0u0"; // Input your Contract Address

    const time = new Date();
    const mintJSON = {
    mint: {
    owner: myWalletAddress,
    token_id: `token_id_${time.getTime()}`,
    // token_uri: "https://your.token.uri.address", // input your token_uri
    extension: {
    name : "My Test XPLA NFT",
    // image: "https://your.image.url", // input your nft image url
    attributes: [
    {
    value: 'Alice',
    trait_type: 'CharacterName',
    },
    {
    value: '100',
    trait_type: 'CP',
    },
    {
    value: '10',
    trait_type: 'HP',
    },
    ],
    description : "This is my First NFT in XPLA Blockchain!",
    },
    },
    };
    const mint = new MsgExecuteContract(
    myWalletAddress,
    myContractAddress,
    mintJSON
    );

    const signedTx = await lcd.wallet(mk).createAndSignTx({ // Creating and signing the transaction
    msgs: [mint]
    });

    const txResult = await lcd.tx.broadcastSync(signedTx);
    console.log(txResult.txhash);
    }
    main()
  3. 10번째 줄의 니모닉 단어들을 여러분의 니모닉 단어로 수정해주세요.

  4. 앞서 만든 컨트랙트 주소를 15번째 줄의 myContractAddress 변수에 대입해주세요.

  5. VSCode Terminal에서 아래 명령어를 입력합니다.

    node example-7.js
  6. Terminal에서 결과를 확인합니다. 여러분의 NFT가 발행되었을 것입니다.


설명

지갑을 불러오는 부분까지는 이전과 동일하므로 생략하겠습니다.

먼저 어떤 주요 정보(metadata)를 가진 NFT를 발행할 것인지 정해야 합니다. 예제에서는 Alice라는 이름의 캐릭터 NFT를 만드는 것으로 가정해보았습니다. 이 NFT 캐릭터는 CP가 100이고, HP가 10이겠군요.

아래 값들을 변경하여 자신만의 NFT를 만들어보세요.

const time = new Date();
const mintJSON = {
mint: {
owner: myWalletAddress,
token_id: `token_id_${time.getTime()}`,
// token_uri: "https://your.token.uri.address", // input your token_uri
extension: {
name : "My Test XPLA NFT",
// image: "https://your.image.url", // input your nft image url
attributes: [
{
value: 'Alice',
trait_type: 'CharacterName',
},
{
value: '100',
trait_type: 'CP',
},
{
value: '10',
trait_type: 'HP',
},
],
description : "This is my First NFT in XPLA Blockchain!",
},
},
};
주요 정보(metadata)에는 어떤 값들이 들어가야 하나요?

주요 정보(metadata)에 들어갈 수 있는 항목들은 아래와 같습니다.

{
mint: {
owner: "NFT Owner의 주소를 입력합니다.",
token_id: "개별 NFT 고유 식별자로 사용되는 Token ID입니다.",
token_uri: "아래 extension 정보를 JSON 형식으로 제공하는 URI입니다. OpenSea에서 사용하는 형식과 동일합니다.",
extension: {
name : "NFT 이름입니다.",
description : "NFT에 대한 설명입니다.",
image : "NFT 이미지를 제공하는 URL입니다. 용량은 40MB를 권장하며, 최대 100MB까지 지원합니다.",
animation_url : "NFT 영상을 제공하는 URL 입니다. 용량은 40MB를 권장하며, 최대 100MB까지 지원합니다.",
youtube_url : "Youtube 영상의 URL 로, 현재는 지원하지 않습니다."
attributes: [ // NFT의 특징을 나타냅니다.
{
category : "NFT 아이템 카테고리를 나타냅니다.",
collection : "NFT Collection을 나타냅니다.",
creator : "NFT 창작자 혹은 저작권자를 나타냅니다.",
provider : "NFT 판매 혹은 배포 권리를 가진 사람을 나타냅니다.",
thumbnail_url : "NFT의 썸네일 이미지를 제공하는 URL 입니다.",
extension_url : "NFT extension 정보를 JSON 형식으로 제공하는 URL 입니다.",

// 아래와 같이 NFT의 특징을 추가할 수도 있습니다.
trait_type: "CharacterName",
value: "Alice",
},
],
},
},
};

더 자세히 알고 싶으시다면 CW721 Github을 살펴보시거나, XPLA Explorer에서 다른 NFT를 참고해보시기 바랍니다. OpenSea Metadata 기준을 읽어보시는 것도 좋습니다.


작성한 metadata를 이용하여 앞서 생성한 컨트랙트를 Execute합니다.

const mint = new MsgExecuteContract(
myWalletAddress,
myContractAddress,
mintJSON
);

이후 트랜잭션 생성, 서명, 네트워크 송신 등 작업을 거칩니다. 트랜잭션이 블록체인에 잘 생성되었다면, NFT 발행(Mint)이 성공적으로 진행된 것입니다.

마무리

이렇게 CW721 컨트랙트로 NFT를 발행해보았습니다. NFT도 토큰이기 때문에, 컨트랙트로 발행하는 것임을 기억해주세요. 단지 토큰마다 고유한 메타데이터가 있기 때문에, 같은 컨트랙트에서 만들어진 다른 토큰들과 대체할 수 없다는 것이 큰 특징입니다.

다음 단계에서는 직접 만든 NFT 데이터를 블록체인에서 가져와보겠습니다.