Ethereum NFT 개발(feat. truffle)
시작하기에 앞서
- 중간중간 링크를 걸어 두었으니 참고하세요.
- solidity 문법과 openzeppelin 라이브러리는 안다는 가정하에 진행됩니다.
- 잘못되거나 문의가 있으면 댓글 달아주세요.
1. 로컬에서 개발
1-1. 프로젝트 생성 & 설정
npm install -g [email protected]
- 참고>Truffle 설치 & Ganache 연동
- 프로젝트 폴더 생성 후 truffle 초기화
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")
1-1. 프로젝트 생성 & 설정
npm install -g [email protected]
- 참고>Truffle 설치 & Ganache 연동
- 프로젝트 폴더 생성 후 truffle 초기화
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
회고
Ropsten - Migration 컨트랙트의 최신 TX
생각보다 내용 정리하는것이 쉽지 않았다.
그 이유는 내 머릿속에서 정리가 제대로 되지 않았기에 그런것같다.
중간중간 왜 그런지 모르고 넘어간 부분들이 있어서 찾아내느라 시간이 오래 걸렸다.
뭔가.. 정리를 하면서 공백이 있던 문서가 좀 더 채워진 느낌을 받는다.
생각 정리할 때 블로깅을 해봐야겠다.
truffle 이 너무 유용하다. 처음쓸때는 난해하고 어려웠는데 쓰면 쓸 수록 편리하다고 느낀다.
추가로 이더리움기반이 아닌 다른 블록체인네트워크에도 사용이 가능한지 알아보고 적용해보자. (truffle 한큐로 다 끝낼 수 있으면 너무 좋을것 같다)(같은 컴파일러가 아니면.. 어쩔수 없고..)
ERC721 코드를 좀 더 분석해서 전체 구조 파악이 좀 필요해보인다.
PS) 근데 블로그를 남들 보기 쉽게 정리하는건 또 다른 영역인것 같다.
PS) Key 관리 잘하자.
Author And Source
이 문제에 관하여(Ethereum NFT 개발(feat. truffle)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@repedore/truffle-Ethereum-NFT저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)