Truffle을 사용해 Ropsten Testnet에 ERC-721 NFT 배포하기 (Opensea 클론코딩)

0xWonTiger·2022년 8월 11일
0
post-thumbnail

목표

👉 OpenSea 클론코딩에 사용되는 NFT Create 기능을 위한 Solidity 컨트랙트 코드

  • Truffle에서 Infura API를 이용해 Ropsten 네트워크에 컨트랙트를 배포
  • NFT Recipient Address / ENDPOINT URL을 입력받아 NFT가 민팅되는 함수를 구현

개발 흐름도

  • Truffle 개발환경 설정 (Ropsten testnet과의 연결)
  • Solidity 컨트랙트 코드 작성
  • Truffle만을 사용하여 네트워크에 배포, verify, publish까지 진행
  • 컨트랙트 코드를 사용해 이더스캔에서 NFT 민팅을 진행

1. Truffle 개발 환경 설정

1) 프로젝트 루트 디렉토리에서 contract 폴더를 생성하고, truffle 개발 환경을 세팅한다

mkdir contract
cd contract
truffle init
npm init

2) truffle-config.js 설정

truffle-config.js 파일에서 Infura를 사용하기 위한 설정을 추가해야 한다. Infura와의 연결에 대한 내용이 최상단에 기본적으로 주석처리되어 있으며, 주석처리를 해제하고 진행하면 된다.

배포에 필요한 정보는 다음과 같다

  • Metamask 테스트넷 계정: 기존에 사용하던 지갑말고 새로 생성해서 사용하자. 프로젝트용으로 쓰다보니 니모닉 유출의 우려가 있다.
  • 니모닉 코드: Metamask 지갑을 생성할때 받은 니모닉 코드를 사용
  • Infura API Key (ropsten)
  • Etherscan API Key

하단의 HDWalletProvider와 fs, mnemonic이 선언된 주석을 해제하고 @truffle/hdwallet-provider를 설치한다

npm install @truffle/hdwallet-provider

니모닉은 .secret이란 파일을 호출해 사용하니 contract폴더에 .secret 파일을 생성하고,

니모닉이 git에는 올라가지 않도록 .gitignored.secret을 추가한다.

network에 주석처리되어 있는 코드중 ropsten 내역을 해제하고,
provider에는 Infura API를, 최하단의 etherscan에는 etherscan API Key를 넣는다

truffle-plugin-verify
여기에서 사용되는 truffle-plugin-verify는 truffle에서 컨트랙트 코드를 배포한 후 etherscan에서 해야할 작업인 코드 검증 및 퍼블리시를 truffle 단계에서 자동으로 해주는 플러그인이다.
이 플러그인을 사용하지 않고 단순히 컨트랙트 코드를 배포하고 etherscan에서 코드 검증을 하려고 하면 오류가 나기 때문에(Remix에서 컴파일 후 검증을 하라는 에러가 뜬다) 사용하는 것이 좋다

npm install -D truffle-plugin-verify

이렇게 truffle과 내가 가진 메타마스크 지갑, ropsten 네트워크의 연결이 완료되었다.

2. 컨트랙트 코드 작성

1) 컨트랙트 코드

실제로 배포할 ERC-721 컨트랙트 코드를 작성해야 한다.
OpenZeppelin의 ERC721 라이브러리를 활용하기 위해서 OpenZeppelin을 프로젝트 모듈로 설치하고 진행한다

npm install @openzeppelin/contracts

contract 폴더에 contracts라는 폴더가 있는데, 그 곳에 sol파일을 생성하고 컨트랙트 코드를 작성한다. ERC721코드는 다양한 함수가 있지만 민팅 함수는 구현되어 있지 않으므로 라이브러리를 불러오고, 민팅을 구현하는 함수를 작성해줘야 한다.

MyERC721.sol이라는 파일을 생성하고, 하단의 솔리디티 코드를 붙여주었다

NFT name은 deepNFT, symbol은 DNFT이다.

// 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";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

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

    constructor() ERC721("deepNFT", "DNFT") {}

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

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

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

        return newItemId;
    }
}

❗️민팅함수에서 변경한 부분

  • import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
    해당 컨트랙트로 민팅이 된 전체 갯수를 확인할 수 있는 totalSupply 함수를 사용하기 위해 ERC721Enumerable.sol을 import해야 한다.
  • function mintNFT(address recipient, string memory tokenURI) public onlyOwner returns (uint256)
    mintNFT에 부여된 onlyOwner 부분을 삭제해 누구나 해당 컨트랙트로 민팅이 가능하게 변경했다.

2) Migration 코드

migrations 폴더의 1_initial_migraion.js 코드를 수정하여 내가 제작한 MyERC721.sol 코드가 배포되게끔 설정한다

3. Truffle로 ropsten 네트워크에 컨트랙트 배포

contract 루트 디렉토리에서 truffle을 사용해 배포를 진행하면 되는데, 로컬이 아니기 때문에 어느정도의 시간이 소요된다

truffle migrate --compile-all --network ropsten

성공적으로 배포되었다면 터미널의 결과는 아래와 같다.

배포 후 컨트랙트 코드의 검증(verify)를 위해서 이더스캔으로 가지 않고, truffle-plugin-verify를 실행한다.

성공하면 위와 같은 이미지가 나오고, etherscan에서 별도로 verify&publish를 진행하지 않아도 자동으로 검증까지 완료가 된다.

4. Etherscan에서 NFT 발행

해당 NFT 토큰을 받을 주소와, NFT에 사용될 이미지 엔드포인트 URI를 넣고 민팅을 진행할 수 있다.

완료가 되면, tokenId 1을 가진 트랜잭션을 확인할 수 있다


📕 개발 회고

  • Truffle을 사용하면 몇몇 특정 플러그인을 필수로 사용해야 하고 Infura/Etherscan API를 필요로 하기 때문에 Remix를 사용하는 것이 절차나 과정면에서 훨씬 더 빠르게 작업할 수 있을 것 같다. 다시 한다면 Remix.

  • 누구나 해당 컨트랙트로 민팅이 가능하도록 컨트랙트 코드의 mintNFT함수에서 onlyOwner를 삭제했는데, 생각해보면 같은 컨트랙트 코드 안에서 다양한 지갑을 통해 NFT 발행하면 그 안의 이미지나 내용이 다르더라도 결국 1개의 NFT 프로젝트로 묶이는 듯 하다. 그래서 실제로 create NFT 기능을 만드려면 create와 함께 새로운 프로젝트명을 가진 컨트랙트 코드를 먼저 배포하는 것까지 고려해야 한다.

  • 즉 총 Supply부터 실제 NFT 프로젝트에 필요한 양식들을 모두 구현해야 할텐데 그 정도 구현할줄 알면 LaunchMyNFT와 같은 서비스와 동일하게 되니 수수료만으로 떼돈(...)을 벌 수 있겠다.

  • Create기능이 아닌 Mint와 My Page만 간단히 구현하고 -> 리액트/CSS 작업에 참가했으면 프론트를 담당했던 동기분에게 도움을 드렸을 수 있었을텐데 그걸 못한게 아쉬운 부분으로 남는다.

참고 레퍼런스

profile
Blockchain & Crypto Enthusiast

0개의 댓글