Ethereum NFT 개발(feat. truffle)

참고>github 링크

시작하기에 앞서

  • 중간중간 링크를 걸어 두었으니 참고하세요.
  • solidity 문법과 openzeppelin 라이브러리는 안다는 가정하에 진행됩니다.
  • 잘못되거나 문의가 있으면 댓글 달아주세요.

1. 로컬에서 개발

1-1. 프로젝트 생성 & 설정

npm install -g [email protected]
mkdir <project_name>
cd <project_name>
truffle init

1-2. 컨트랙트 작성

npm install @openzeppelin/contracts
  • contract 폴더 안에 <name>.sol 파일 작성 (이 프로젝트에선 MyNFTs.sol 임)
  • openzeppelin ERC721.sol 파일 참조
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(string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        _tokenIds.increment();

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

        return newItemId;
    }
}
  • mintNFT 함수는 넣어준 uri를 참조하는 토큰을 발행

1-3. 배포 스크립트 작성

  • migrations 폴더에 2_initial_mynfts.js 파일 생성 (규칙: {숫자}_{설명}.js)
    참고>Migrations.sol, 1_initial_migration.js 은 왜 있는지와 Migration 파일명 규칙
    - 요약) migration contract 는 last_completed_migration 에 마지막 배포 스트립트의 번호를 저장하고 나중에 컨트랙트가 수정되거나 새로운 컨트랙트를 배포할 때 기존 스크립트를 중복 실행 하지 않기 위함.
    - 추가로 이미 실행된 migration 을 추적하는데 도움이 됨.
const MyNFTs = artifacts.require('MyNFTs.sol'); // MyNFTs.sol 파일 추가

module.exports = function(deployer) {
	deployer.deploy(MyNFTs); // MyNFTs를 배포에 추가
};

1-4. ganache 네트워크 설정

ganache 의 default port는 7545
ganache-cli 의 default port는 8545

  • truffle project의 truffle-config.js 파일에서 networks 옵션 설정
    networks: {
        ganache: {				// 이름은 구별하기 쉽게 작성
         host: "127.0.0.1",
         port: 7545,			// ganache-cli 에 연결한다면 8545
         network_id: "*",       // Any network (default: none)
        },
    },

  // Configure your compilers
	compilers: {
		solc: {
			version: "0.8.7",
			settings: {
				evmVersion: "london",
			},
		},
	},

1-5. 컨트랙트 배포

  • ganache 를 실행
	truffle migrate --compile-all --network ganache
  • 모두 컴파일을 하고 ganache 네트워크에 배포
  • 명령어를 실행하면 컴파일을 먼저 한 후 ganache의 첫번째 계정으로 배포를 하는데 숫자 순서대로 배포를 시작한다.


    컨트랙트의 주소와 계정정보, 가스비 등등 다양한 출력을 확인 할 수 있다.
  • 컴파일시 중간의 경고는 생성자가 한번만 실행이 되므로 "표시"되지 않는다는것을 알려주는 경고 문구이다. (참고)

1-6. 컨트랙트 실행

	truffle console --network ganache
  • MyNFTs.deployed() 함수를 통해 MyNFTs 컨트랙트에 접근할 수 있다

  • truffle 이 배포한 MyNFTs 를 알 수 있는 방법 (log로 가지고 있다) (참고)

  • mintNFT 에 준비된 링크를 전달 (일반적으로 NFT는 그림파일의 경로를 담은 metadata의 경로를 넣어준다)
    참고>OpenSea Metadata 구조

instance.mintNFT("https://velog.io/@repedore")

  • 상속한 ERC721 contract의 tokenURI 함수를 사용하여 값 확인하기(ERC721.sol : 93 line)
instance.tokenURI(1)
  • 넣은 값이 잘 출력되는것을 확인

2. ropsten 테스트넷에 배포

2-1. ropsten 배포 설정

필수>Truffle과 Ethereum TestNet 연결하기 (feat. ropsten)

  • ropsten 네트워크와 연결 후 truffle console 로 진입.
truffle console --network ropsten

2-2. ropsten 배포

  • migrate 명령어를 입력해주면 ropsten 테스트 네트워크에 배포가 완료된다
    - migrate 실행 (온체인이라 시간이 걸림)

    - Migrations 배포중

    - MyNFTs 배포중

    - MyNFTs 의 mintNFT 함수 실행 & 결과 확인

    - 트랜잭션 해쉬값, 컨트랙트 주소, 실행한 계좌 주소 등등을 알 수 있다.

    ropsten - Contract 주소
    ropsten - TX 주소
  • 발행한 계정의 토큰 목록을 보면 MyNFTs(MNFT)의 TokenID 가 1인 토큰을 소유하고 있음을 알 수 있다.

2-3. (추가) OpenSea 제공 Metadata로 NFT 만들기

  • Opensea 는 Rinkeby 테스트 네트워크를 사용
  • truffle-config.js 에 Rinkeby 네트워크 추가
  • infura 에서 RINKEBY로 변경하고 ENDPOINTS 가져옴
  • HDWalletProvider 의 <ENDPOINTS> 란에 ROPSTEN 에서 생성한 ENDPOINTS 를 적는다
		rinkeby: {
			provider: () => new HDWalletProvider(mnemonic, '<ENDPOINTS>'),
			network_id: 4,
			gas: 4500000,
			gasPrice: 10000000000,
		},
  • 주의) rinkeby 계정을 다른걸 쓴다면 개인키를 다시 .secret 파일에 써줘야함.

  • truffle console 접속

	truffle console --network rinkeby 
  • rinkeby 에는 배포 안했으니 배포
	migrate

  • 발행한 컨트랙트 불러오기
	instance = await MyNFTs.deployed()
    instance.name()			// 잘 불러왔나 확인
  • OpenSea 제공 Metadata 로 mintNFT 하기 (링크)
	instance.mintNFT('https://opensea-creatures-api.herokuapp.com/api/creature/3')

OpenSea 확인
rinkeby - Contract 주소(Verify & Publish)
rinkeby - TX 주소

2-4. (추가) Contract Verify & Publish

  • 상속받은 Contract를 Verify & Publish 하려면 상속받은 코드를 다 올려주어야 함.
  • 마지막으로 했던 rinkeby 네트워크에만 Verify & Publish 함.
npm install -D truffle-plugin-verify

참고>github truffle-plugin-verify

  • Etherscan Api Site 에 접속하여 회원가입 후 API Keys 를 클릭

  • 왼쪽 Other -> API Keys 를 클릭하고 "+ Add" 를 눌러 키를 추가하고 "API Key Token" 을 복사한다.

  • truffle-config.js 에 plugins 와 api_keys 를 다음과 같은 구조로 추가하고 Etherscan key를 붙여넣음.

  • Verify 시작

truffle run verify MyNFTs --network rinkeby

  • Verifying 하는데 약간의 시간이 걸린다. 기다리면 Pass - Verified 됨.

  • Pass - Verifying 뒤 경로는 MyNFTs Contract 의 code 를 가르킨다. (링크)

  • 상속한 모든 코드와 작성한 코드가 다 올라가 있는것을 확인 할 수 있음.

  • 실수로 Etherscan API Key 를 외부로 빼지않고 truffle-config.js 에 그대로 포함시켜서 git에 올려버렸다. 무료이고 중요하지는 않아서 당장 조치는 취하지 않겠지만 언젠가 키를 삭제 할 수 있으니 꼭 개인키를 받아서 사용하고 나같은 실수를 하지 않길 바란다.

보충

  • 컨트랙트 생성 트랜잭션 -> Owner가 컨트랙트 실행(mintNFT) 트랜잭션 -> Owner 계정에 ERC-721(NFT)발행 순이다.
  • Migration 컨트랙트의 최신 트랜잭션을 보면 Input Data에 complete Data가 2인 것을 알 수 있다.

    Ropsten - Migration 컨트랙트의 최신 TX

회고

생각보다 내용 정리하는것이 쉽지 않았다.
그 이유는 내 머릿속에서 정리가 제대로 되지 않았기에 그런것같다.
중간중간 왜 그런지 모르고 넘어간 부분들이 있어서 찾아내느라 시간이 오래 걸렸다.
뭔가.. 정리를 하면서 공백이 있던 문서가 좀 더 채워진 느낌을 받는다.
생각 정리할 때 블로깅을 해봐야겠다.

truffle 이 너무 유용하다. 처음쓸때는 난해하고 어려웠는데 쓰면 쓸 수록 편리하다고 느낀다.
추가로 이더리움기반이 아닌 다른 블록체인네트워크에도 사용이 가능한지 알아보고 적용해보자. (truffle 한큐로 다 끝낼 수 있으면 너무 좋을것 같다)(같은 컴파일러가 아니면.. 어쩔수 없고..)
ERC721 코드를 좀 더 분석해서 전체 구조 파악이 좀 필요해보인다.

PS) 근데 블로그를 남들 보기 쉽게 정리하는건 또 다른 영역인것 같다.
PS) Key 관리 잘하자.

좋은 웹페이지 즐겨찾기