블록체인 실습

모나드 빌더 부트캠프 - Phase 1 - 3일차 정리

knowing_j1n 2025. 4. 13. 15:25

안녕하세요.

오늘은 지난 4월 3일에 진행된 모나드 부트캠프 3일차 내용을 정리 및 복습해 보겠습니다.

 

3일차 복습

 

3일차 : 

 

  1. .env 사용
  2. ERC-721 (NFT) 컨트랙트 배포 

아래 제공한 코드는 기초님께서 작성해주신 코드를 인용하였습니다.

https://t.me/gichocoin

 

기초코인수급자

You can view and join @gichocoin right away.

t.me

 


.env : 환경 변수(Environment Variables)를 저장하기 위해 사용되는 파일

 

프로젝트를 진행할 때 서버주소, DB 정보, 고유 API KEY 등 숨겨줘야 하는 정보들이 존재한다.
.env 파일은 애플리케이션의 설정값과 민감한 정보를 효율적으로 관리하기 위한 도구로, 다양한 환경에서 동일한 코드를 실행할 수 있도록 지원한다. 이를 통해 보안성과 유연성을 높이고, 프로젝트의 유지보수를 간소화할 수 있습니다.

 

EIP & ERC

 

EIP - Ethereum Improvement Proposal : 이더리움 네트워크의 개선을 제안하는 문서 

ERC - Ethereum Request for Comment : EIP의 한 유형으로 스마트 컨트랙트 및 앱 개발을 위한 표준

하드포크를 통해 변경 사항을 적용

 

포크 : 쉽게 말해 블록체인 규칙이 변경되는 지점

하드포크 : 블록체인의 규칙을 완전히 새롭게 변경하는 업데이트, 하위 호완성이 없다. 체인이 영구적으로 둘로 나뉜다.

 

ERC-20

Ethereum 'The DAO' 이후 ICO 붐이 시작되면서 수백개의 토큰이 난립했다.

인터페이스도 제각각이어서 호완성이 부족했고 거래소와 지갑 통합에 어려움을 겪었다.

 

그래서 나온게 ERC-20

Fabian Vogelsteller 와 Vitalik Buterin이 제안

Ethereum 기반 토큰에 대한 기본 인터페이스 정의

ERC-20 호환 토큰은 모두 동일한 함수 명세를 가진다.

 

대부분의 토큰들은 기본 표준을 상속한 뒤 커스터마이징 한다.

 

ERC-721 : NFT

 

ERC-20은 모든 토큰은 동등한 가치라는 전제를 가진다 - Fungible Token

자산 중에는 고유한 것들이 많다. 그래서 디지털 자산도 고유한 것이 필요하다.

그래서 나온게 721 -> NFT

 

핵심 구조 :

고유성을 가진다 : TokenID는 유일하다.

단위전송 불가 : 0.5 NFT는 존재하지 않는다.

아트, 티켓등 고유한 것을 표현하기 위해 사용

 

 

기본 코드 구성

 

ownerOf : NFT의 소유자가 누구인지 반환하는 함수

safeTransferFrom : NFT를 from 주소에서 to 주소로 안전하게 전송하는 함수

mint : 새로운 NFT를 발행(mint)하는 함수

tokenURI : 특정 NFT의 메타데이터 URI를 반환하는 함수

 

대부분의 NFT는 Metadata (NFT가 가지는 특징)을 JSON 형식으로 IPFS등에 저장

TMI = 반드시 TokenID 순서대로 MINT 되지 않는 경우도 있다.

 

대표적인 NFT 마켓 플레이스 :

Opensea : https://opensea.io/

MagicEden : https://magiceden.io/

 

Magic Eden - NFT Marketplace

Magic Eden is the leading community-centric NFT marketplace. Home to the next generation of creators. Discover the best and latest NFT collections today.

magiceden.io

 

URI - Uniform Resource Identifier : 통합 자원 식별자 -> NFT 에서는 어딘가 저장되어있는 JSON 형태를 의미

JSON : Key - value 방식의 딕셔너리 형태로 구성되어 있다.

 

ERC-721의 확장

 

ERC-721Enumerable (Enumerable : 열거할 수 있는) :

토큰의 전체 목록 및 소유자별 토큰 목록을 열거할 수 있도록 추가

외부에서 토큰 목록 조회, 전체 공급량 및 소유자별 토큰 접근 가능하다.

추가 가스 비용이 든다. - 스마트 컨트랙트 에서는 복잡할 수록 가스비가 많이 든다.

 

 

ERC-721Burnable : 소유자 또는 승인된 계정이 토큰을 영구적으로 소각할 수 있도록 하는 확장.

NFT를 영구적으로 제거하여 총 공급량을 줄이고, 특정 토큰을 더 이상 사용할 수 없도록 만들 수 있다.

 

ERC-1155 : Multi-Token

하나의 컨트랙트로 여러개의 Fungible/NFT 토큰 관리

Fungible : 대체 가능 , Non-Fungible : 대체 불가능

 

ERC-721의 단점을 해결하기 위해 나온 버전

NFT 마다 별도 컨트랙트 배포 or TokenID 관리가 필요하다 -> 대량 발행 시. 가스비 + 복잡도 증가

동일 아이템 다수 발행하는 경우 : 두 표준을 동시에 써야하는 비효율 발생한다,

 

20과 721을 동시 관리 할 수 있는 표준 = ERC-1155

 


 

실습 :

모나드 테스트넷에 NFT 배포하기

 

openzepplin 이용하여 쉽게 코드 생성 가능

OpenZeppelin Contracts Wizard

 

OpenZeppelin Contracts Wizard

An interactive smart contract generator based on OpenZeppelin Contracts.

wizard.openzeppelin.com

 

.env 설치

 

아래 코드 입력

npm i dotenv

 

실행할 폴더에 .env 파일 생성 후 아래 코드 입력

PRIVATE_KEY_1= 본인 키

 

config.js 파일 생성 후 아래 코드 입력

require("dotenv").config();

module.exports = {
    PRIVATE_KEY_1: process.env.PRIVATE_KEY_1,
    RPC_URL: "https://testnet-rpc.monad.xyz",
    // 다른 설정들도 여기에 추가 가능
};

 

openzepplin 이동

 

1. 생성할 NFT 이름 , 티커 정하기

2. 포함할 요소들을 클릭하여 선택하기

3. 코드 복사하기

 

Pinata 로그인

https://pinata.cloud/

 

Pinata | Crypto's file storage

Add IPFS file uploads and retrieval in minutes so you can focus on your app — because you’ve got better things to code than infrastructure.

pinata.cloud

 

생성할 이미지 업로드

이미지 업로드

업로드한 이미지 URL 확인

https://gold-reasonable-cicada-753.mypinata.cloud/ipfs/QmToNVak6f1CAfedpVMTqvR92cXFAmiCSTKnScbp4Pp6CN

 

본인 도메인 주소 확인

도메인 주소 확인

 

 

deployAndMint.js 생성

NFT(Non-Fungible Token) 를 생성하고 배포하며, 메타데이터를 저장하고 실제 NFT를 민팅(mint)하는 전체 과정을 자동화한 스크립트 파일

 

// utils.js 파일에서 필요한 함수들을 불러옵니다.
// - deployNFTContract: NFT 컨트랙트를 배포하는 함수
// - mintNFT: NFT를 민팅하는 함수
// - createMetadata: 메타데이터 JSON 객체를 생성하는 함수
// - saveMetadata: 메타데이터를 파일로 저장하는 함수
const { deployNFTContract, mintNFT, createMetadata, saveMetadata } = require("./utils");

// config.js에서 환경 변수들을 불러옵니다.
// 보통 이 파일에는 RPC URL과 프라이빗 키 같은 민감한 정보가 들어 있습니다.
const config = require("./config");

// 메인 실행 함수 정의
async function main() {
    // ----------------------------
    // 1. NFT 컨트랙트 배포 단계
    // ----------------------------
    console.log("Deploying NFT contract...");

    // deployNFTContract() 함수를 실행하여 스마트 컨트랙트를 블록체인에 배포합니다.
    // 이 함수는 배포된 컨트랙트의 정보(예: 주소)를 포함한 객체를 반환합니다.
    const { address: contractAddress } = await deployNFTContract();

    // 배포된 컨트랙트 주소 출력
    console.log("Contract deployed to:", contractAddress);

    // ----------------------------------
    // 2. 메타데이터 생성 및 파일 저장 단계
    // ----------------------------------
    console.log("Creating metadata...");

    // NFT에 사용할 메타데이터를 생성합니다.
    // - 첫 번째 인자: NFT 이름
    // - 두 번째 인자: 설명
    // - 세 번째 인자: 이미지 URL (IPFS 주소 등)
    const metadata = createMetadata(
        "", // NFT 이름
        "", // 설명
        "" // 이미지 URL
    );

    // 위에서 만든 메타데이터를 JSON 파일로 저장합니다.
    saveMetadata(metadata, "./meta_prac.json");

    // 저장 완료 메시지 출력
    console.log("Metadata saved to meta_prac.json");

    // ----------------------------
    // 3. NFT 민팅(Minting) 단계
    // ----------------------------
    console.log("Minting NFT...");

    // provider를 설정하여 블록체인 네트워크에 연결합니다.
    const provider = new ethers.JsonRpcProvider(config.RPC_URL);

    // 지갑 객체를 만듭니다 (프라이빗 키 필요). 이 지갑으로 NFT를 민팅합니다.
    const wallet = new ethers.Wallet(config.PRIVATE_KEY_1, provider);

    // mintNFT 함수를 사용하여 NFT를 발행합니다.
    // - 첫 번째 인자: 배포된 NFT 컨트랙트 주소
    // - 두 번째 인자: NFT를 받을 주소 (지갑 주소)
    // - 세 번째 인자: 메타데이터가 저장된 JSON URL (IPFS에 업로드된 메타데이터 링크)
    const tx = await mintNFT(
        contractAddress,
        wallet.address,
        "https://example.com/meta_prac.json" // 실제로는 IPFS URL을 넣는 것이 일반적입니다
    );

    // 트랜잭션 해시 출력
    console.log("NFT minted! Transaction hash:", tx.hash);
}

// 메인 함수 실행 + 에러 핸들링
main()
    .then(() => process.exit(0)) // 성공 시 종료
    .catch((error) => {
        console.error(error); // 에러 출력
        process.exit(1); // 실패 시 종료
    });

 

 

utils.js 생성

하드햇 + 이더리움(Ethers.js) 환경에서 NFT를 배포하고, 민팅하며, 메타데이터 생성과 저장까지 전체 과정을 자동화한 스크립트

// 필요한 라이브러리 불러오기
const { ethers } = require("ethers");      // ethers.js는 이더리움과 상호작용을 위한 라이브러리
const hre = require("hardhat");            // Hardhat은 스마트 컨트랙트 개발 환경
const config = require("./config");    // RPC URL, 프라이빗 키 등의 설정 불러오기
const fs = require("fs");                  // 파일 입출력을 위한 Node.js 모듈

// NFT 컨트랙트 배포 함수
async function deployNFTContract() {
    await hre.run("compile");  // 하드햇에서 컨트랙트 컴파일 실행
    const artifact = await hre.artifacts.readArtifact("생성할 NFT 이름"); // 컴파일 결과 읽기
    const abi = artifact.abi;
    const bytecode = artifact.bytecode;

    const provider = new ethers.JsonRpcProvider(config.RPC_URL); // 네트워크 연결
    const wallet = new ethers.Wallet(config.PRIVATE_KEY_1, provider); // 지갑 연결

    // 컨트랙트 팩토리 생성 → 배포
    const contractFactory = new ethers.ContractFactory(abi, bytecode, wallet);
    const contract = await contractFactory.deploy(wallet.address); // 생성자 인자로 소유자 주소 전달
    await contract.waitForDeployment(); // 배포가 블록에 포함될 때까지 기다림

    // 컨트랙트 정보 리턴
    return {
        contract,
        address: contract.target, // 배포된 주소
        abi
    };
}

// NFT 민팅 함수
async function mintNFT(contractAddress, to, tokenURI) {
    const provider = new ethers.JsonRpcProvider(config.RPC_URL);
    const wallet = new ethers.Wallet(config.PRIVATE_KEY_1, provider);

    const artifact = await hre.artifacts.readArtifact("생성할 NFT 이름"); // ABI 확보
    const contract = new ethers.Contract(contractAddress, artifact.abi, wallet); // 컨트랙트 연결

    const tx = await contract.safeMint(to, tokenURI); // 민팅 실행
    await tx.wait(); // 블록에 포함될 때까지 대기
    return tx; // 트랜잭션 객체 반환
}

// 메타데이터 생성성 함수
function createMetadata(name, description, image) {
    return {
        name,
        description,
        image
    };
}

// 메타데이터 저장 함수수
function saveMetadata(metadata, filename) {
    fs.writeFileSync(filename, JSON.stringify(metadata, null, 2));
}

// 모듈 내보내기
module.exports = {
    deployNFTContract,  // 컨트랙트 배포 함수
    mintNFT,           // NFT 민팅 함수
    createMetadata,    // 메타데이터 생성 함수
    saveMetadata       // 메타데이터 저장 함수
};

 

 

터미널에 명령어 입력

npx hardhat run scripts/"생성한 파일(deployAndMint) 이름".js --network monad

 

실행하면 배포된 CA와 NFT의 해시값을 알 수 있습니다.

 

결과값

 

모나드 익스플로러에 배포된 주소를 검색하면 아래와 같이 민팅된 NFT를 확인할 수 있습니다.

 

NFT CA : 0xa09b551Be560BC9741e7018CF89720039edEd65E

배포한 CA 주소 : 0xa5b04999185ccd2784b42f229f9bc3458cdfc1c3e06fed4129c6feae28578aa2

Hash : 0x150de53e1cd985206ce866da908012dc3f7e0ca8d03b466dc627f90bdb545e60

 

 

https://testnet.monadexplorer.com/nft/0xa09b551Be560BC9741e7018CF89720039edEd65E/0?tab=Overview

 

MonadExplorer | Monad Explorer

MonadExplorer is Layer 1 Monad blockchain explorer, where you can explore transactions, blocks, accounts, tokens and dive into immersive Monad data visualization through Block Explorer, Token Data Analysis, Statistics tools.

testnet.monadexplorer.com

 

이상으로 복습을 마치도록 하겠습니다.