[React] 클레이튼 체인에서 NFT 발행하고 조회하기 (KIP-17)
NFT 블록체인 마켓 앱 만들기 3기 / Klip / NFT / caver-js / KIP-17
🦁 [PROJECT LION] NFT 블록체인 마켓 앱 만들기 with 그라운드X 3기 - 챕터 5-4 회고
KIP-17
-
KIP-17은 ERC-721 토큰에서 파생되었다
-
‘KIP-17’ 기반으로 발행된 토큰은 대체가 불가능하다
👉 대체 불가능 토큰 (NFT, Non-Fungible Token)
NFT 발행하고 조회하기
-1. Klaytn IDE 에서 ‘KIP-17’ 스마트 컨트랙트 코드 배포(Deploy)
⭐ Klip 은 테스트넷(Baobab) 에서 실행을 못하기때문에
메인넷에서 사용할 실제 KLAY 가 필요하다 ❗
‘KIP17Token’ 을 선택해서 컴파일한 후 ABI 복사
‘KIP17Token’ 을 배포하고 나온 컨트랙트 주소 를 복사한다
-2. ‘KIP17Token’ 을 배포하고 나온 함수를 실행시켜 NFT 발행 (민팅)
mintWithTokenURI
함수 실행to 👉 NFT 발행 지갑주소 (소유주)
tokenId 👉 발행하는 NFT 임의의 번호
tokenURI 👉 NFT의 정보 (이미지, 제목, 세부내용 등등)
저는 일단 이미지 2개를 준비해서
tokenId 100번, 101번 으로 발행시켜 보겠습니다
( https:// ~~ .png 로 준비했습니다 )
-3. NFT를 발행 했으면 조회해본다
balanceOf 👉 해당 주소에 몇개의 NFT를 보유중인지 개수 확인
ownerOf 👉 NFT의 TokenId 를 입력하면 누가 소유중인지 주소를 확인
tokenOfOwnerByIndex 👉 주소와, 배열의 몇번째 를 입력하면 TokenId 를 확인
(총 2개를 발행했으니 배열에는[100, 101]
이렇게 2개가 담겨있다
👉 0번째 -> 100 / 1번째 -> 101tokenURI 👉 NFT의 TokenId 를 입력하면 URI를 확인
-4. ‘ABI / 컨트랙트 주소’ 로 caver-js 를 통해 리액트와 연결한다
⭐ Klaytn IDE에서 조회를 했던 함수들을 리액트에서도 호출할 수 있다
조회를 할때는 지갑에 가지고있는 NFT의 이미지를 다 client 에 출력할 것이므로 여러과정을 거쳐야한다
balanceOf
를 이용해 몇개의 NFT를 보유중인지 개수를 변수에 담는다- 개수를 반복문을 돌려서 순차적으로 대입
tokenOfOwnerByIndex
를 이용해 TokonId 추출tokenURI
를 이용해 tokenURI 추출- tokenId 와 tokenURI 를 짝지어서 출력한다
-5. App.js 👉 client 에 출력하기 위한 코드입력
호출한 함수들을 useState 빈배열에 담아준다
-6. npm run start
로 실행시킨뒤 Klip 지갑 연결
QR코드로 Klip 지갑을 연결하고
버튼을 누르면 지갑에 있는 모든 NFT를 조회 할 수 있다
KIP-17 스마트컨트랙트 코드
caver-js 를 통해 리액트와 연결하는 코드
UseCaver.js
import Caver from "caver-js";
import KIP17ABI from "../abi/KIP17TokenABI.json";
import {
ACCESS_KEY_ID,
SECRET_ACCESS_KEY,
CHAIN_ID,
COUNT_CONTRACT,
KIP17,
} from "../constants/index";
const option = {
// Klaytn Node API를 사용하기 위한 코드 (Docs에 나와있음)
headers: [
{
name: "Authorization",
value:
"Basic " +
Buffer.from(ACCESS_KEY_ID + ":" + SECRET_ACCESS_KEY).toString("base64"),
},
{ name: "x-chain-id", value: CHAIN_ID },
],
};
const caver = new Caver(
new Caver.providers.HttpProvider(
"https://node-api.klaytnapi.com/v1/klaytn",
option
)
); // Klaytn Node API를 사용하기 위한 코드 (Docs에 나와있음)
const NFTContract = new caver.contract(KIP17ABI, KIP17);
// // ⭐ 개발자는 '스마트 컨트랙트 주소'와 'ABI'를 알면 'caver' 를 통해
// // ⭐ 스마트 컨트랙트를 생성하고 / 특정 함수를 실행할 수 있다
export const fetchCardsOf = async (address) => {
const nftOf = await NFTContract.methods.balanceOf(address).call();
// 스마트컨트랙트 배포후에 나오는 함수
// 해당 주소에 몇개의 NFT를 보유중인지 개수를 표시하는 함수 balanceOf
console.log(nftOf);
const tokenIds = [];
for (let i = 0; i < nftOf; i++) {
const id = await NFTContract.methods.tokenOfOwnerByIndex(address, i).call();
// 해당주소와 배열의 몇번째인지 숫자를 입력하면 tokenId 가 나온다
tokenIds.push(id);
// 빈배열에 반복문을 돌려서 tokenId를 push 한다
}
const tokenUris = [];
for (let i = 0; i < nftOf; i++) {
const uris = await NFTContract.methods.tokenURI(tokenIds[i]).call();
// 해당 tokenId를 입력하면 tokenURI(이미지) 가 나온다
tokenUris.push(uris);
// 빈배열에 반복문을 돌려서 tokenURI를 push 한다
}
console.log(`${tokenIds}`);
console.log(tokenUris);
const nfts = [];
for (let i = 0; i < nftOf; i++) {
nfts.push({ id: tokenIds[i], uri: tokenUris[i] });
// 빈배열에 반복문을 돌려서 객체타입으로 담는다
}
console.log(nfts);
return nfts;
};
// 블록체인 노드에 직접적으로 콜하기 어려워서 KAS 를 사용
// KAS 는 우리가 블록체인에 접근하는걸 도와준다
// 스마트컨트랙트에 있는 데이터를 읽고 실행하는걸 도와준다
// 이 과정에서 Caver-js 를 쓰면 인간이 사용하는 코드를
// 스마트컨트랙트 블록체인이 이해할수 있는 코드로 변형해줘서
// Klaytn 노드와 상호작용할 수 있도록 도와준다
export const getBalance = (address) => {
return caver.rpc.klay.getBalance(address).then((res) => {
// 클레이 잔고를 확인 (다른코인이 있다면 klay말고 적으면 된다)
const balance = caver.utils.convertFromPeb(
// convertFromPeb -> 10 ** 18 로 클레이단위를 변경
caver.utils.hexToNumberString(res)
// 16진수문자열로 변경
);
console.log(balance);
return balance;
});
};
App.js 👉 client 에 출력하기 위한 코드
App.js
import "./App.css";
import "./market.css";
import { useState } from "react";
import QRCode from "qrcode.react";
import * as KlipAPI from "./api/UseKlip";
import { fetchCardsOf, getBalance } from "./api/UseCaver";
import { Alert, Container, Card } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
const DEFAULT_QR_CODE = "DEFAULT"; // QR코드는 기본 DEFAULT 값
const DEFAULT_ADDRESS = "0x00000000000000000"; // 주소 기본값
function App() {
const [nfts, setNfts] = useState([]);
// 인자로 들어가는값은 tokenId, tokenUri
const [myBalance, setMyBalance] = useState("0");
const [myAddress, setMyAddress] = useState(DEFAULT_ADDRESS);
const [qrvalue, setQrvalue] = useState(DEFAULT_QR_CODE);
const fetchMyNFT = async () => {
const nftList = await fetchCardsOf(
"0x319229707F620F673a1261DCcCe4E239A71f3Bc0"
); // 해당 주소에 있는 nft 리스트
setNfts(nftList);
// 빈배열에 담아준다
};
const getUserData = () => {
KlipAPI.getAddress(setQrvalue, async (address) => {
setMyAddress(address);
const balance = await getBalance(address);
// UseCaver.js 에서 가져온 getBalance
setMyBalance(balance);
});
};
return (
<div className="App">
<div>
<div>내 지갑</div>
{myAddress}
<br />
<Alert onClick={getUserData} variant={"balance"}>
{myBalance}
</Alert>
<div className="container">
{/* map을 이용하여 nfts 배열에서 index 순서대로 이미지 출력 */}
{nfts.map((list, index) => (
<Card.Img className="img-responsive" src={nfts[index].uri} />
))}
</div>
</div>
<Container>
<QRCode value={qrvalue} size={256} />
</Container>
<button onClick={fetchMyNFT}>NFT 가져오기</button>
</div>
);
}
export default App;