๐ง๐ปโ๐ป truffle ์ด์ฉํ ERC-721๊ฐ๋ฐ๊ธฐ
>>> Github์ฃผ์ Github_truffleERC721
๐ ๊ฐ๋ฐ๋ชฉํ
์ค๋์ ์์ฆ ๋์ธ ์ค์ ๋์ธ! ํซ ํ NFT๋ฅผ ๊ฐ๋ฐํ๊ณ ์ ํฉ๋๋ค.
truffle์ ์ฌ์ฉํ์ฌ ๊ธฐ๋ณธ ํ๊ฒฝ์ ๊ตฌ์ถ ํ ํ ์คํ์ ํ๋ฆฐ ํ๋ ์์ํฌ๋ฅผ ์ด์ฉํ์ฌ erc721๊ธฐ๋ณธ ์ฝ๋๋ฅผ ์ฌ์ฉํ๊ณ , ๊ทธ๋ฆผ์ ํ๋ ๊ณจ๋ผ Ganache์์ ํ
ํธ์คํธ ํ ์ด๋๋ฆฌ์ Ropsten ํ
์คํธ๋ท์ ๋ฐฐํฌํ์ฌ ์ด๋์ค์บ์์ ํด๋น ๋ด์ญ์ ํ์ธํ ์์ ์
๋๋ค.
๋จผ์ ๊ธฐ๋ณธ ๊ฐ๋ฐํ๊ฒฝ์ truffle์ ์ด์ฉํ๊ณ ์ด๋ฏธ์ง๋ฅผ ๋ฌด๋ฃ ์ ์ฅ์์ธ NFTStorage์ ์ ๋ก๋ํ์ฌ ์ด๋ก ์ป์ด์ง tokenURI๋ฅผ ํตํด ์ ์ผ๋ฌด์ดํ ํ ํฐ ERC721๋ฅผ ๋ง๋ค์ด ๋ณด๊ณ ์ํฉ๋๋ค.
๐ ganache์ NFT๋ฐฐํฌ
1. truffle ์ด์ฉํ ๊ฐ๋ฐํ๊ฒฝ ๊ตฌ์ถํ๊ธฐ
- ๋จผ์ , ํ๋ก์ ํธ ํดํฐ๋ฅผ ์์ฑํ ํ truffle๊ณผ npm ์ด๊ธฐํ๋ฅผ ํฉ๋๋ค.
# ํด๋์์ฑ
mkdir truffleERC721
# ํด๋์ง์
cd truffleERC721
# truffle ์ด๊ธฐํ
truffle init
# npm ์ด๊ธฐํ
npm init
- ์ด ํ ํ๋ก์ ํธ ํด๋์์ truffle-config.js ํ์ผ์ ์ด์ด ์๋ฆฌ๋ํฐ ์ปดํ์ผ ๋ฒ์ ์ ๊ฐ์ธ์ด ์ฌ์ฉํ๋ ๋ฒ์ ๊ณผ ๋ง์ถฐ์ฃผ๊ณ , ๋คํธ์ํฌ๋ฅผ ์ค์ ํด ์ฃผ๋๋ฐ, ๋จผ์ ๊ฐ๋์๋ฅผ ์ด์ฉํด ํ
์คํธ ํ ๊ฒ ์ด๋ฏ๋ก ๊ฐ๋์์ port ๋ฒํธ 7545๋ฅผ ์
๋ ฅํด ์ค๋๋ค.
2. openzeppelin์ ์ด์ฉํ์ฌ NFT๋ฐํํ ์ค๋งํธ ์ปจํธ๋ํธ ์์ฑํ๊ธฐ
- openzeppelin ์ค์น
openzeppelin์ ์ค๋งํธ ์ปจํธ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ๋ฐํ๋ ํ์ฌ๋ก ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์์ฝ๊ฒ ์ฌ๋ฌ ํ ํฐ๋ค์ ๋ฐํํ ์ ์์ต๋๋ค.
# openzeppelin ์ค์น
npm install @openzeppelin/contracts
# ํด๋์์ฑ
mkdir truffleERC721
# ํด๋์ง์
cd truffleERC721
# truffle ์ด๊ธฐํ
truffle init
# npm ์ด๊ธฐํ
npm init
openzeppelin์ ์ค๋งํธ ์ปจํธ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ๋ฐํ๋ ํ์ฌ๋ก ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์์ฝ๊ฒ ์ฌ๋ฌ ํ ํฐ๋ค์ ๋ฐํํ ์ ์์ต๋๋ค.
# openzeppelin ์ค์น
npm install @openzeppelin/contracts
- 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("zekeNFTs", "ZNFT") {}
function mintNFT(string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
- ๋ฐฐํฌ๋ฅผ ์ํด migrations/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);
};
3. NFTStorage์ด์ฉํ์ฌ NFT๋ก ๋ฐํํ ์ด๋ฏธ์ง ์ ๋ก๋ํ๊ณ tokenURI๊ฐ์ ธ์ค๊ธฐ
-
nft.storage์ ๊ฐ์ ํ๊ณ APIKeys์์ ํค๋ฅผ ์์ฑํฉ๋๋ค.
-
NFT๋ก ๋ฐํํ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํฉ๋๋ค.
-
๋ฉํ๋ฐ์ดํฐ๋ฅผ nft.storage์ ์ฌ๋ ค์ฃผ๊ธฐ
npm install nft.storage
ํ๋ก์ ํธ ํด๋ ์์ nft.jsํ์ผ ์์ฑํ๊ณ ์ฝ๋ ์ฝ์
import { NFTStorage, File } from "nft.storage";
import fs from "fs";
const apiKey = "Your API KEY";
const client = new NFTStorage({ token: apiKey });
const metadata = await client.store({
name: "woodang",
description: "carrot is cute",
image: new File([fs.readFileSync("./woodang.png")], "woodang.png", {
type: "image/png ",
}),
attributes: [
{ trait_type: "skin", value: "flash" },
{
trait_type: "face",
value: "frightened",
},
],
});
console.log(metadata.url);
// ipfs://bafyreib4pff766vhpbxbhjbqqnsh5emeznvujayjj4z2iu533cprgbz23m/metadata.json
์์ฑ๋ ๋ฉํ๋ฐ์ดํฐ!!
์์ฑ๋ ๋น๊ทผ!
4. ์ปดํ์ผํ ๋ฐฐํฌํ๊ธฐ
# ์ปดํ์ผ
truffle compile
# ๋ฐฐํฌ
truffle deployed
# ์ฝ์ ์ ์ํ์ฌ ์์ฑํ token metadata URI๋ก NFT๋ฐํ
truffle console
truffle(ganache)> instance = await MyNFTs.deployed
truffle(ganache)> instance.name() // 'zekeNFTs'
truffle(ganache)> instance.symbol() // 'ZNFT'
๋ฐฐํฌ์๋ฃ!
๐ Ropsten์ NFT๋ฐฐํฌ
1. ganache๋ฐฐํฌ์ ์ฌ์ฉํ ํ๋ก์ ํธ์์ truffle-config.js์์
- ๋๋ชจ๋ ์ง๊ฐ ์ฌ์ฉ์ผ๋ก ์ธํด @truffle/hdwallet-provider ์ค์น ํ์
npm install @truffle/hdwallet-provider
- ๋คํธ์ํฌ ์์
const HDWalletProvider = require("@truffle/hdwallet-provider");
const infuraKey = "YOUR INFURA KEY";
const fs = require("fs");
const mnemonic = fs.readFileSync(".secret").toString().trim();
npm install @truffle/hdwallet-provider
const HDWalletProvider = require("@truffle/hdwallet-provider");
const infuraKey = "YOUR INFURA KEY";
const fs = require("fs");
const mnemonic = fs.readFileSync(".secret").toString().trim();
2. truffle์ด์ฉํ์ฌ ๋ฐฐํฌ
3. ๋ฐฐํฌํ truffle console ์คํํ์ฌ mintNFTํจ์๋ก NFT๋ฐํ
Transaction Hash: 0x92a1325b955d26509d24f16421a1f5ce23e78e2f904f72ec6f0d583ddb9add70
๐ ๊ฐ๋ฐํ๊ณ
KEEP
์ง์ ๊ฐ๋ฐ์ ํ๋ฉด ์ข์์ ์ด ์ด๋ก ์ผ๋ก ์์์๋์ ๋น๊ตํด์ ๋ชธ์ ๋ฐฐ๋ ์ง์๋ค์ด ์๊ธด๋ค๋ ๊ฒ์
๋๋ค.
์ด๋ฒ์๋ ์ด๋ก ์ผ๋ก ๊ธฐ๋ณธ์ ์ต์ธ ํ ์ง์ ganache์ ropsten์ ํตํด ๋ฐฐํฌ๋ฅผ ํด๋ดค๋๋ฐ, MyNFTs์ปจํธ๋ํธ์์ mintNFTํจ์๊ฐ ์ด๋ป๊ฒ ์์ฑ์ด ๋๋์ง, ๊ทธ๋ฆฌ๊ณ mintNFT์ ๋ค์ด๊ฐ๋ tokenURI๋ ์ด๋ป๊ฒ ์์ฑํ๋์ง์ ๋ํ ์ข์ ํ์ต์ด ๋์์ต๋๋ค.
NFT๋ฅผ ์ง์ ์ด๋๋ฆฌ์ ๋คํธ์ํฌ์ ์ ์ฅํ๋ ๊ฒ์ด ์๋ IPFS๋ผ๋ ๋ค๋ฅธ ๋ถ์ฐ ์ ์ฅ์์ ์ ์ฅํ๊ณ ๊ทธ ์ด๋ฏธ์ง์ ๋ํ ๋ฉํ๋ฐ์ดํฐ ๋ํ ์ ์ฅํ์ฌ ๊ทธ URI๋ฅผ ๊ฐ์ ธ๋ค ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก NFT๋ฅผ ์์ฑํ๋ ๋ถ๋ถ์ด ์ด๋ฒ ํ์ต์ ๊ฐ์ฅ ํฐ ๋ฐฐ์์ด ์๋์๋ ์ถ์ต๋๋ค.
PROBLEM
๋ฐฐํฌํ๊ณ ์ ์์ ํ์ฌ ์ฌ๋ฐฐํฌ๊ฐ ๋ถ๊ฐ๋ฅํ๊ฒ ๋ง์์ ธ์๋ค๋ ์ฌ์ค์ ์์์ต๋๋ค. ์ฝ๋๋ฅผ ์ ํ์ธํ์ฌ ์ปจํธ๋ํธ๋ฅผ ๋ค ๋ฐ๊ณ ๋ค์ ๋ฐฐํฌํ๋ ์ผ์ด ์๊ฒ ํ ๊ฒ์ ๋๋ค.
TRY
- ์คํ์จ ํ ์คํธ๋ท์๋ ๋ฐฐํฌํ์ฌ ํ ์คํธ๋ท์ NFT๊ฐ ์ ์ฌ๋ผ์ค๋์ง ํ์ธํ์ฌ ๋ณผ ์์ ์ ๋๋ค.
- ์ฌ๊ธฐ์ ๋ฉ์ถ์ง ์๊ณ ๋ค์์๋ ํ์ฌ ํซ๐ฅํ ๋๋คNFT์ ๋ํด ๊ฐ๋ฐํด๋ณด๊ณ ํ๊ณ ๋ก์ ์์ฑ ์์ ์ ๋๋ค.
Author And Source
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(๐ง๐ปโ๐ป truffle ์ด์ฉํ ERC-721๊ฐ๋ฐ๊ธฐ), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://velog.io/@yoonique_garage/truffleERC721์ ์ ๊ท์: ์์์ ์ ๋ณด๊ฐ ์์์ URL์ ํฌํจ๋์ด ์์ผ๋ฉฐ ์ ์๊ถ์ ์์์ ์์ ์ ๋๋ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค