Development of ERC-721 using Truffle

ERC-20이 '대체 가능'한 토큰을 발행한다면 ERC-721은 '대체 불가능'한 토큰을 발행한다. 오늘은 ERC-721을 사용해 NFT를 발행하는 실습을 진행한다.


GitHub, Etherscan(smart contract)

github repository
smart contract


개발환경

truffle, opensea testnet, etherscan rinkeby, pinata


테스트 네트워크 설정

truffle-config.js 파일에서 몇가지 설정이 필요하다.
1. solc
작성할 컨트랙트의 솔리디티, 컴파일 버전을 확인 후 수정한다. "0.8.7", "london"

compilers: {
  solc: {
    version: "0.8.7",
    evmVersion: "london",
  },
},
  1. HDWalletProvider
const HDWalletProvider = require("@truffle/hdwallet-provider");
const fs = require("fs");
const mnemonic = fs.readFileSync(".secret").toString().trim();

.secret파일을 만들어 mnemonic 키를 담아준다.
추가적으로 .gitignore.secret 파일을 작성해야 github repository에 .secret 파일이 올라가지 않는다.

  1. networks: rinkeby 사용을 위해 기존 ropsten으로 작성된 부분을 주석해제 후 변경해준다. 참고
rinkeby: {
	provider: () =>
		new HDWalletProvider(
			mnemonic,
			`https://rinkeby.infura.io/v3/{infura에서 받은 api key}`, 1), // 3번째 인자는 메타마스크 계정의 인덱스이다. 
	network_id: 4, // Rinkeby's id
	gas: 4500000,
	gasPrice: 10000000000,
	confirmations: 2, 
	timeoutBlocks: 200,
	skipDryRun: true, 
},

smart contract 작성

vs code에 MyNFTs.sol 파일을 만들어 아래 코드를 작성한다. openzeppelin 라이브러리를 사용하기 때문에 추가적으로 설치가 필요하다.

npm install @openzeppelin/contracts
//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

contract MyNFTs is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() public ERC721("MyNFTs", "MNFT") {}

    function mintNFT(address recipient, string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

migration 파일 설정

컨트랙트 배포를 위해 migration 파일 설정이 필요하다.
migration폴더 아래 기존 파일인 1_initial_migration.js를 아래의 내용처럼 수정한다.

const Migrations = artifacts.require('Migrations');
const MyNFTs = artifacts.require('MyNFTs.sol'); // 추가

module.exports = function (deployer) {
	deployer.deploy(Migrations);
	deployer.deploy(MyNFTs); // 추가
};

smart contract 컴파일, 배포

오픈씨 테스트넷에 NFT를 올리기 위해 network는 rinkeby를 사용한다. 터미널에서 다음 명령어를 작성하면 컴파일 후 배포까지 이루어진다.

truffle migrate --compile-all --network rinkeby

터미널에서 컴파일 후 배포까지 진행되는 모습을 확인할 수 있다.

MyNFTs의 transaction hash를 복사해 이더스캔에서 검색해보면 아래처럼 확인이 가능하다.

contract verify & publish

contract verify&publish를 진행하면 이더스캔에서 함수를 작동시킬 수 있다. 방금 확인한 transaction에서 contract주소를 클릭하면 아래와 같이 나온다.

contract 부분을 클릭하여 verify를 진행한다.

contract code를 작성할 때 truffle-flattener를 사용해 import했던 openzeppelin의 코드를 전부 넣어주어야 한다. 다시 vs code로 돌아가 flat폴더를 만들고 터미널에 다음 명령어를 순차적으로 입력한다.

npm install truffle-flattener --save
truffle-flattener ./contracts/MyNFTs.sol > ./flat/MyNFTs.sol

flat폴더의 MyNFTs.sol을 확인하면 openzeppelin의 모든 코드가 들어와 있다. 전체 코드를 복사해서 다시 이더스캔의 verify를 진행하면 된다.
그러나 복사한 소스코드를 입력해도 에러가 발생했다.

truffle-flattener를 사용했더니 SPDX 부분이 중복으로 들어가 생긴 문제였다. sol-merger를 사용하면 중복되는 SPDX를 알아서 제거해준다고 했지만 적용이 되지 않아 수동으로 중복된 부분을 제거했다.

verify & publish가 완료되면 이더스캔에서 함수를 실행할 수 있다. 다음 작업을 통해 mintNFT함수를 사용해 NFT를 발행해볼 수 있다.


metadata file 생성

mintNFT함수에는 두 가지 인자가 들어간다. 첫 번째는 NFT를 받는 사람의 주소이고, 두 번째는 tokenURI이다. 여기서는 tokenUI 생성 작업을 진행한다.
우선 vs code로 돌아가 test.json파일을 만들어준다. 그리고 아래와 같이 입력한다.

{
	"name": "Loyal Duck #001",
	"description": "Made on December 18, 2021",
	"image": "https://imagedelivery.net/v7-TZByhOiJbNM9RaUdzSA/2c6f93b0-891f-42b4-a017-bcf85c03cb00/public",
	"attributes": [
		{
			"trait_type": "TYPE",
			"value": "Cute"
		}
	]
}

이후 생성한 json 파일을 pinata에 업로드시킨다. 이전 실습에서는 nft.storage를 사용했으나 storage providers가 정해지는데 48시간이 걸려서 대신 피나타를 사용했다.

파일 업로드 후 CID 부분을 복사한다.

mintNFT

이더스캔으로 돌아와 mintNFT를 마저 진행한다.
첫 번째 인자(recipient)에 NFT를 받을 주소를 입력하고 두 번째 인자(tokenURI)에 피나타에서 받아온 CID를 입력 후 Write를 클릭 후 가스비를 결제한다.

OpenSea Testnets

앞서 mintNFT함수를 실행해 NFT를 발행했다. 확인을 위해
오픈씨 테스트넷으로 이동한다. 아래 동작영상으로 확인이 가능하다.
metadata로 작성된 속성들이 오픈씨 테스트넷에서 잘 보이고 있다.


개발 회고 (KPT)

KEEP(작업 및 코드적으로 좋았던 경험)

예전 실습 때 미뤄둔 부분들이 많았는데 이번 실습을 통해 거의 모든 부분들을 해결해볼 수 있어 매우 유익했다. 특히 네트워크 환경을 ganache에서 rinkeby로 변경해 설정하다보니 지갑 설정 및 가스비 오류 가 있었는데 이런 오류들을 해결하는 과정이 재밌었다. metadata를 직접 작성하는 부분도 NFT가 만들어진 과정을 더 구체하게 볼 수 있어 좋았던 부분이다. 개인적으로 NFT 발행이 가장 즐거운 작업이었다.

PROBLEM(작업 및 코드에서 좋지 않았던 경험)

작업하며 블로그를 동시에 작성하는 편이 좋았을 것 같다. 모든 작업이 끝나고 정리하니 작업보다 더 많은 시간을 블로깅에 할애했다.

TRY(앞으로 시도해볼 액션 아이템 설정)

현재는 함수를 한번 실행할 때 하나의 NFT를 발행하고 있다. 그러나 NFT를 다량으로 발행하는 작업이 가능하다고 하여 추가적으로 학습 후 NFT를 다량으로 발행하는 코드를 작성해 보고 싶다.

좋은 웹페이지 즐겨찾기