์ค๋์ ์์ฆ ๋์ธ ์ค์ ๋์ธ! ํซ ํ NFT๋ฅผ ๊ฐ๋ฐํ๊ณ ์ ํฉ๋๋ค.
truffle์ ์ฌ์ฉํ์ฌ ๊ธฐ๋ณธ ํ๊ฒฝ์ ๊ตฌ์ถ ํ ํ ์คํ์ ํ๋ฆฐ ํ๋ ์์ํฌ๋ฅผ ์ด์ฉํ์ฌ erc721๊ธฐ๋ณธ ์ฝ๋๋ฅผ ์ฌ์ฉํ๊ณ , ๊ทธ๋ฆผ์ ํ๋ ๊ณจ๋ผ Ganache์์ ํ
ํธ์คํธ ํ ์ด๋๋ฆฌ์ Ropsten ํ
์คํธ๋ท์ ๋ฐฐํฌํ์ฌ ์ด๋์ค์บ์์ ํด๋น ๋ด์ญ์ ํ์ธํ ์์ ์
๋๋ค.
๋จผ์ ๊ธฐ๋ณธ ๊ฐ๋ฐํ๊ฒฝ์ truffle์ ์ด์ฉํ๊ณ ์ด๋ฏธ์ง๋ฅผ ๋ฌด๋ฃ ์ ์ฅ์์ธ NFTStorage์ ์ ๋ก๋ํ์ฌ ์ด๋ก ์ป์ด์ง tokenURI๋ฅผ ํตํด ์ ์ผ๋ฌด์ดํ ํ ํฐ ERC721๋ฅผ ๋ง๋ค์ด ๋ณด๊ณ ์ํฉ๋๋ค.
# ํด๋์์ฑ
mkdir truffleERC721
# ํด๋์ง์
cd truffleERC721
# truffle ์ด๊ธฐํ
truffle init
# npm ์ด๊ธฐํ
npm init
# 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("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;
}
}
const Migrations = artifacts.require("Migrations");
const MyNFTs = artifacts.require("MyNFTs.sol");
module.exports = function (deployer) {
deployer.deploy(Migrations);
deployer.deploy(MyNFTs);
};
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
์์ฑ๋ ๋ฉํ๋ฐ์ดํฐ!!
์์ฑ๋ ๋น๊ทผ!
# ์ปดํ์ผ
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'
๋ฐฐํฌ์๋ฃ!
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();
Transaction Hash: 0x92a1325b955d26509d24f16421a1f5ce23e78e2f904f72ec6f0d583ddb9add70
์ง์ ๊ฐ๋ฐ์ ํ๋ฉด ์ข์์ ์ด ์ด๋ก ์ผ๋ก ์์์๋์ ๋น๊ตํด์ ๋ชธ์ ๋ฐฐ๋ ์ง์๋ค์ด ์๊ธด๋ค๋ ๊ฒ์
๋๋ค.
์ด๋ฒ์๋ ์ด๋ก ์ผ๋ก ๊ธฐ๋ณธ์ ์ต์ธ ํ ์ง์ ganache์ ropsten์ ํตํด ๋ฐฐํฌ๋ฅผ ํด๋ดค๋๋ฐ, MyNFTs์ปจํธ๋ํธ์์ mintNFTํจ์๊ฐ ์ด๋ป๊ฒ ์์ฑ์ด ๋๋์ง, ๊ทธ๋ฆฌ๊ณ mintNFT์ ๋ค์ด๊ฐ๋ tokenURI๋ ์ด๋ป๊ฒ ์์ฑํ๋์ง์ ๋ํ ์ข์ ํ์ต์ด ๋์์ต๋๋ค.
NFT๋ฅผ ์ง์ ์ด๋๋ฆฌ์ ๋คํธ์ํฌ์ ์ ์ฅํ๋ ๊ฒ์ด ์๋ IPFS๋ผ๋ ๋ค๋ฅธ ๋ถ์ฐ ์ ์ฅ์์ ์ ์ฅํ๊ณ ๊ทธ ์ด๋ฏธ์ง์ ๋ํ ๋ฉํ๋ฐ์ดํฐ ๋ํ ์ ์ฅํ์ฌ ๊ทธ URI๋ฅผ ๊ฐ์ ธ๋ค ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก NFT๋ฅผ ์์ฑํ๋ ๋ถ๋ถ์ด ์ด๋ฒ ํ์ต์ ๊ฐ์ฅ ํฐ ๋ฐฐ์์ด ์๋์๋ ์ถ์ต๋๋ค.
๋ฐฐํฌํ๊ณ ์ ์์ ํ์ฌ ์ฌ๋ฐฐํฌ๊ฐ ๋ถ๊ฐ๋ฅํ๊ฒ ๋ง์์ ธ์๋ค๋ ์ฌ์ค์ ์์์ต๋๋ค. ์ฝ๋๋ฅผ ์ ํ์ธํ์ฌ ์ปจํธ๋ํธ๋ฅผ ๋ค ๋ฐ๊ณ ๋ค์ ๋ฐฐํฌํ๋ ์ผ์ด ์๊ฒ ํ ๊ฒ์ ๋๋ค.