박주니 개발 정리

공연티켓 nft 발행 및 signature 본문

블록체인

공연티켓 nft 발행 및 signature

박주니 2023. 3. 17. 13:58
728x90
반응형

설명 전)  nft 발행을 하고나서 signature 값을 받는 이유는 signatrue 값이 해당 nft 발행한 것을 증명하는 것이기 때문에 구매자가 해당 nft를 구매할 때 발행한 nft을 구매할려면 해당 siganture이 필요합니다. 그래서 저장해서 해당 값을 가져오는 목적으로 이용한다면 그 과정이 필요가 없지만 거래의 목적으로 하신다면 발행과 signature은 세트라고 보시는 것이 좋을 거 같습니다. 

 

주의사항) 현재 abi 구성에 해당 method가 있는다고해서 무조건 돌아가는 것이 아닙니다. 배포할 때 스마트 컨트렉트에 해당 클레스가 존재해야하고 그것을 abi로 구성을 맞춘 것이기 때문에 그부분을 주의하시면서 진행하시는 것을 추천합니다.

 

react 및 polygon 기준으로 설명하겠습니다. 

nft 발행 

1. 먼저 스마트컨트렉트에서 구성한 mintTickets, getSeatNumber, getQrCode, purchaseNFT 셋팅한 abi를 셋팅합니다.

{
  "abi": [
    {
      "inputs": [
        {
          "internalType": "string[]",
          "name": "seatNumbers",
          "type": "string[]"
        },
        {
          "internalType": "string[]",
          "name": "qrCodes",
          "type": "string[]"
        },
        {
          "internalType": "uint256[]",
          "name": "price",
          "type": "uint256[]"
        }
      ],
      "name": "mintTickets",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "tokenId",
          "type": "uint256"
        }
      ],
      "name": "getSeatNumber",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "tokenId",
          "type": "uint256"
        }
      ],
      "name": "getQrCode",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "buyer",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "tokenId",
          "type": "uint256"
        },
        {
          "internalType": "bytes",
          "name": "signature",
          "type": "bytes"
        }
      ],
      "name": "purchaseNFT",
      "outputs": [],
      "payable": true,
      "stateMutability": "payable",
      "type": "function"
    }
  ]
}

tip) 현재 abi 구성에 대해서 어떻게 만들어지는 지 궁금하시다면 chatGPT에 셋팅한 스마트컨트렉트 코드를 올려놓고 그것이 동작할 수 있는 abi를 보내줘하면 그것에 맞는 코드를 알려줄 것입니다. 

추가 설명)

저는 해당 abi을 ConcerSeats.json으로 셋팅했습니다. 파일 위치는 Contract 폴더를 만들어서 넣었는데 참고하시는 분들은 편하신 위치에 넣고 연결만 잘해주시면 됩니다.

 

2. 실행하고자하는 파일에서 해당 abi json을 연결하고 performanceNFT contract address를 연결해서 해당 abi 맞게 mintTickets를 연결해주시면 됩니다. mintTickets는 발행을 의미합니다.

import Web3 from "web3";
import ConcertSeats from "../../../../Contract/ConcertSeats.json";

const web3 = new Web3(Web3.givenProvider || "http://localhost:8545");

const contract = new web3.eth.Contract(
  ConcertSeats.abi,
  [performanceNFT address]// "0x6...." 이형식으로 넣어야됨
 );
  
const seatNumbers = ["1", "2", "3", "4", "5"]; // 발행할 좌석 번호 배열
const qrCodes = ["123", "234", "456", "753", "895"]; // 발행할 QR 코드 배열
const price = [1, 1, 1, 1, 1]; // 발행할 가격 배열
  
async function handleMintTickets() {
  // 티켓 등록
  const accounts = await web3.eth.getAccounts();
  await contract.methods
    .mintTickets(seatNumbers, qrCodes, price)
    .send({ from: accounts[0], gasLimit: 3000000 });
}

추가 설명) 

await web3.eth.getAccounts() 는 현재 wallet address에 서명을 거치기 위함입니다. 블록체인 이벤트는 개인키가 중요하기 때문에 서명 없이 할 수 있는 건 해당 contract에 정보를 가져오는 것일 뿐 추가하는 것은 불가능합니다. 

await constract.methods.mintTickets(seatNumbers, qrCodes, price) 가 의미하는 것은 abi mitTickets를 보시면 이해하시기 편하실 것입니다. abi를 보시면 "inputs" 안에 seatNumbers, qrCodes, price로 구성되어있는 것을 확인하실 수 있습니다. 그리고 type도 지정되어 있어서 현재 넣는 값이 어떻게 넣고 있는 지 확인하시면서 값을 넣으시면 됩니다. 

gasLimit: 3000000 이부분은 개인적으로 딱 정해져있지 않고 다른 블록체인 이벤트는 500000으로도 충분히 이벤트가 진행이 되었는데 지금 해당 발행같은 경우에는 가스비가 부족하다는 에러가 나와서 3000000까지 테스트하면서 맞추게 되었습니다. 이 값에서 넘어가면 가스비 초과되었다는 에러가 나와서 이부분은 주의하시면서 가스비 조절하시길 바랍니다. 

    {
      "inputs": [
        {
          "internalType": "string[]",
          "name": "seatNumbers",
          "type": "string[]"
        },
        {
          "internalType": "string[]",
          "name": "qrCodes",
          "type": "string[]"
        },
        {
          "internalType": "uint256[]",
          "name": "price",
          "type": "uint256[]"
        }
      ],
      "name": "mintTickets",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },

signature

 

발행이 정상적으로 되었으면 해당 performanceNFT에는 블록이 하나 생성이 되었을 것입니다. 그리고 그 안에는 tokenId가 5개가 존재할 것입니다. 여기까지 되었다는 존재조건하에 진행하겠습니다.

 

1. 해당 코드에 발행자 wallet address와 performancNFT contract address를 맞게 넣습니다. 

  async function signMessage() {
    const message = web3.utils.soliditySha3(
      { type: "address", value: [발행자 wallet address] },
      { type: "uint256", value: 1 },
      { type: "address", value: [performanceNFT contract address] }
    );
    const signature = await web3.eth.personal.sign(
      message,
      [발행자 wallet address]
    );
    console.log("signature: ", signature);
    return signature;
  }

추가 설명) 이 설명을 하기 전에 배포할 때 스마트컨트렉트에서 signature 구성을 주의해달라고 말씀을 드린 적이 있습니다. 지금 현재 위에 코드를 보면  넣는 순서가 발행자 주소, tokenId, 스마트컨트렉트로 구성되어있는 것을 확인하실 수 있습니다.

  1. signatrue 구성과 스마트컨트렉트에 purchaseNFT signatre 구성 비교 
    bytes32 message = keccak256(
        abi.encodePacked(
            seller,
            tokenId,
            address(this)
        )
    ).toEthSignedMessageHash();

지금 위에 있는 코드는 스마트컨트렉트에서 purchaseNFT에 해당되는 signature 부분인데 나중에 구매자가 signature 받아서 처리를 할 때 signature을 분석하는 순서가 seller, tokenId, 스마트컨트렉트 순으로 되어있는 것으로 확인하실 수 있습니다. 여기에서 만약 순서가 잘못된다면 evm 에러가 발생되는 것을 확인하실 수 있습니다. 하지만 제가 알려드린 방법으로 하신다면 그부분에 대한 에러는 보지 않으실 것입니다. 

 

   2. 스마트컨트렉트에서 purchaseNFT signature을 keccak256으로 했다면 web3.utils.solidySha3으로 진행

 

여기까지 하셨다면 nft 발행과 signature이 정상적으로 된 것을 의미합니다. 이제 다음 단계는 구매자가 nft를 구매하는 방법을 알려드리겠습니다.

 

 

728x90
반응형
Comments