BlockChain

OpenSea Clone Coding Project - Whale

es_0409 2022. 4. 18. 16:04

프로젝트 개요

지난 일주일 간 첫 번째 프로젝트인 오픈씨 클론코딩, <whale>을 작업했다.

내가 맡은 파트는 프론트 + web3로써, 전체적인 프론트 디자인 구상 및 구현, 그리고 지갑 연동 및 트랜잭션 전송을 위한 코드를 작성했다.

전체 구성도

 클라이언트에서 구현하고자 한 기능은 메인 페이지를 제외하고 총 5가지로, NFT 민팅 기능, 구매 기능, 판매 기능, 그리고 본인이 소유한 NFT를 확인할 수 있는 Mypage, 모든 NFT를 확인할 수 있는 Explore페이지로 구성되어 있다.

Mint, Sell, Buy 페이지의 경우 web3-contract를 이용해 Robsten 네트워크 상에 올라가 있는 컨트랙트와 직접 소통하도록 구현했다. 서버는 이 정보를 주기적으로 읽어와서 DB테이블을 업데이트한다. 

그 외에 MyPage, Explore의 경우 서버에 get요청과 필요 시 쿼리문을 이용해 필요한 정보를 DB에서 받아보도록 설계하였다.


목업

* optional로 표시된 목업은 기본 기능 구현 단계에 포함되지 않은 advanced 레벨에 해당하는 부분이다.


MySQL Tables

MySQL scheme

DB는 주기적으로 NFT에 관련한 정보를 업데이트한다. 업데이트하는 순서는 다음과 같다.

1. 컨트랙트와 소통하여 모든 NFT의 소유권 정보 읽어오기

2. 컨트랙트와 소통하여 모든 NFT의 메타데이터 정보 읽어오기

3. 가져온 정보를 토대로 NFTs Table 업데이트

4. 컨트랙트와 소통하여 모든 거래 정보 읽어오기

5. 가져온 거래 정보를 토대로 NFTs Table 업데이트

이 모든 과정이 반복적으로 이루어지므로 서버는 주기적으로 업데이트된 DB 테이블을 받아볼 수 있다.


API

작성한 api문서는 아래 페이지에서 확인할 수 있다.

https://www.notion.so/API-ver-1-1-8dc8da7ccb6140b6a03aa34c8ce993b7


Contract

프로젝트에 사용된 스마트 컨트랙트는 모두 ropsten Test Network에 배포되었으며 기능은 다음과 같다.

 

1. NFT 컨트랙트 (Whale NFT)

openzeppelin을 사용한 ERC721 컨트랙트, 기존의 ERC721컨트랙트와 기능은 동일하나 mintNFT, mintNFTMyself(민팅 하려는 지갑 주소 넣을 필요 없음)이 추가되어 있음

 

2. NFT 거래용 컨트랙트 (TransferWhaleNFT)

NFT 거래에 필요한 sell, buy함수가 구현된 컨트랙트.

roomCount와 roomInfo가 읽기 전용 함수로 구현 되어 있어서 거래 정보를 받아올 수 있도록 구현되어있다.

판매자가 이 컨트랙트 주소로 Whale NFT에서 approve를 실행시켜 주면, 판매 등록이 가능하다.

구매자가 나타나면 roomNumber를 이용하여 구매프로세스가 이루어지도록 한다.

 

두 컨트랙트는 모두 아래 주소에서 확인 가능하다.

https://ropsten.etherscan.io/address/0xe23e30b939a085a2d92f50c803f574c58912162b#writeContract

 

WhaleNFT | Address 0xe23e30b939a085a2d92f50c803f574c58912162b | Etherscan

The Contract Address 0xe23e30b939a085a2d92f50c803f574c58912162b page allows users to view the source code, transactions, balances, and analytics for the contract address. Users can also interact and make transactions to the contract directly on Etherscan.

ropsten.etherscan.io

https://ropsten.etherscan.io/address/0xd5e92129477fd369c14411919a6833c05c214275#code

 

TransferWhaleNFT | Address 0xd5e92129477fd369c14411919a6833c05c214275 | Etherscan

The Contract Address 0xd5e92129477fd369c14411919a6833c05c214275 page allows users to view the source code, transactions, balances, and analytics for the contract address. Users can also interact and make transactions to the contract directly on Etherscan.

ropsten.etherscan.io


Client

모든 react 소스 코드는 material-ui와 styled-component를 혼용하여 작성되었다.

지갑 연결

헤더의 지갑 모양 아이콘을 클릭하면 메타마스크 지갑을 연결할 수 있도록 팝업이 뜬다. 연결된 지갑 주소는 window.ethereum.selectedAddress를 통해 접근 가능하며, 지갑을 연결하고 나면 Mypage에서 아래와 같이 자신이 민팅했거나, 구매하여 들고 있는 NFT들을 확인 가능하다.

Mypage

모든 페이지에는 최상단에 라우팅을 위한 헤더가 렌더링된다. 

Main
Explore

Main과 Explore의 경우 서버에 get요청을 보내 NFT리스트를 받아오도록 구현하였다. 서버로부터 받는 응답은 다음과 같다.

[
	{
		"token_id" : "1",
		"owner" : "0xF1fB7114Dc87CeB4efC0D6a5dbc05b042412745F",
		"name" : "doge1",
		"description" : "doge is cool, wow",
		"image_link" : "https://bafkreihbecelxnbh7qczs5wb2r2xpfyx2zfyzc46ecr7m3guzy6fecxslm.ipfs.nftstorage.link/",
		"price" : "1.2",
		"room_number" : "2"
	},
	... // 이외의 여러 개의 메시지
]

이 중 필요한 정보를 이용하여 클라이언트단에서 렌더링되도록 구성하였다.

 

Mint

header의 create탭을 클릭하면 민팅 페이지로 라우팅된다.

Mint

mint 페이지에서는 원하는 사진과 콜렉션 네임, 그리고 explain을 첨부하여 민팅이 가능하다. 

NFT의 메타데이터와 이미지는 infura IPFS상에 저장되도록 하였다. 일단, 사진을 등록하면 곧바로 IPFS에 이미지가 업로드되어 이미지 링크를 변수에 저장하도록 하였다. 그렇게 IPFS에 업로드된 이미지는 메타 데이터로 구성되어 또 다시 IPFS상에 업로드된다.

// 파일 업로드 시 실행되는 함수
  async function onChange(e) {
    const file = e.target.files[0];
    try {
      const added = await client.add(file);
      const url = `https://ipfs.infura.io/ipfs/${added.path}`;
      updateFileUrl(url); // fileUrl이라는 state에 저장
      console.log(url);
    } catch (error) {
      console.log("Error uploading file: ", error);
    }
  }
  
  // CREATE 버튼 클릭 시 실행되는 함수
   async function storeNFT() {
    let metadata = {
      name: name,
      description: description,
      image: fileUrl, // state로 저장해 두었던 IPFS상의 FILE url을 메타데이터에 넣어줌
    };

    metadata = JSON.stringify(metadata);

    try {
      const added = await client.add(metadata);
      const url = `https://ipfs.infura.io/ipfs/${added.path}`;
      // console.log(url);
      mintNFT(url); // 민팅 함수로 전달
    } catch (e) {
      console.log(e);
    }
  }

CREATE버튼을 누르면 sroteNFT가 실행되며, storeNFT에서는 mintNFT에 메타 데이터를 전달하여 실행시킨다. 코드는 아래와 같다.

async function mintNFT(nfturl) {
    try {
      console.log(nfturl);
      const abi = nftABI;
      const address = "0xe23E30b939a085a2d92f50C803F574c58912162B";
      Contract.setProvider(
        "https://ropsten.infura.io/v3/6df37bdfbb1e4dcd8db19ac839911a1b"
      );
      window.contract = new Contract(abi, address);
      const transactionParameters = {
        to: address, // Required except during contract publications.
        from: window.ethereum.selectedAddress, // must match user's active address.
        data: window.contract.methods.mintNFTMyself(nfturl).encodeABI(), //make call to NFT smart contract
      };
      //sign transaction via Metamask
      try {
        const txHash = await window.ethereum.request({
          method: "eth_sendTransaction",
          params: [transactionParameters],
        });
        return {
          success: true,
          status:
            "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" +
            txHash,
        };
      } catch (error) {
        return {
          success: false,
          status: "😥 Something went wrong: " + error.message,
        };
      }
    } catch (e) {
      console.log(e);
    }
  }

 

from : 메타마스크에 연결된 지갑 주소

to : contract Address(whaleNFT)

data : contract 함수 실행 데이터

 

위의 세 가지 정보를 트랜잭션 파라미터에 입력하여 블록체인에 요청을 보내면 트랜잭션이 성공적으로 전송됨을 확인할 수 있다.

 

Sell & Buy

Mypage에서 자신이 들고 있는 NFT를 판매하려면 해당 아이템을 클릭해서 판매 페이지로 이동하면 된다.

Sell

여기서 원하는 가격 정보를 입력하고 (eth 단위) approve를 실행시켜 줘야 판매 등록이 가능하다.

Sell 버튼을 누르면 컨트랙트의 sell 함수가 실행되어 컨트랙트 상에서 거래를 위한 room을 생성하고 구매자가 나타날 때 까지 Explore탭에 가격이 등록된 채로 대기하게 된다. 

Approve - Sell이 정상적으로 수행되었다면 Explore탭에서 다음과 같이 가격이 등록된 NFT를 확인할 수 있다.

 

이렇게 판매 등록된 NFT를 구매하려면 해당 아이템을 클릭해서 구매 페이지로 이동하면 된다.

Buy

구매자가 BUY NOW버튼을 클릭하면 클라이언트에서는 서버에서 받아온 roomNumber를 이용해 TransferWhaleNFT 컨트랙트의 buy 함수를 실행한다. 이 때 구매자가 직접 가격을 입력하는것이 아니라, 마찬가지로 서버에서 응답으로 받아온 데이터를 갖고 있다가, 트랜잭션 파라미터의 value에 다음과 같이 입력해준다.

     const transactionParameters = {
        value: web3.utils.toHex(
          new BN(web3.utils.toWei(curItem[0].price.toString()))
        ),
        to: address.transferWhaleNFTAddress, // Required except during contract publications.
        from: window.ethereum.selectedAddress, // must match user's active address.
        data: window.contract.methods
          .buy(curItem[0].room_number) // 미완성
          .encodeABI(), //make call to NFT smart contract
      };
      const txHash = await window.ethereum.request({
        method: "eth_sendTransaction",
        params: [transactionParameters],
      });

value는 반드시 16진수 string형태로 입력해 주어야 하기 때문에, web3.utils를 사용하여 인풋 타입을 맞춰주었다. 또한, 컨트랙트에는 ETH가 아닌 wei단위로 입력이 되기 때문에, BigNumber모듈을 활용하여 오버플로우를 방지하였다. 구매가 완료되면 price는 0으로 처리되기 때문에 explore탭에서 다음과 같이 확인할 수 있다.

 


마치며..

아쉬운 점

 이번 프로젝트에서 프론트를 담당하면서, 기능적으로 많은 것을 담지 못한 아쉬움이 남는다. opensea에서는 콜렉션과 지갑 주소에 대해서 검색 기능을 제공하지만, 해당 기능을 우리 프로젝트에 담지 못했고 또 collection 구매 상세 페이지에서 NFT의 특징에 따른 희귀도 구현 등 조금 더 다양한 기능이 들어갔다면 더욱 그럴 듯한 NFT 마켓이 되었을 것 같다.

 

좋았던 점 

 팀원들과의 소통

우리 팀은 팀원들과 소통이 좋은 편이었다. 우리 팀은 애초에 프로젝트 기획 당시에 서로 지켜야 하는 룰을 설정하고 시작했다. 회의는 하루 2번 진행했는데, 많은것 같지만 결코 많은 숫자가 아니었다. 그날 그날 서로의 진행 상황을 활발히 공유함으로써 모든 과정이 순탄하게 지나갈 수 있었다. 팀원들과 문제가 생길 시에는 곧바로 공유하기로 했는데, 이 덕분에 어려운 상황이 닥쳐도 함께 빠르게 해결해 나갈 수 있었다. 협력에 대해 몸으로 느낄 수 있는 프로젝트였으며, 모두가 현명하게 잘 해결한 것 같아 뿌듯하다.

프론트에 좀 더 능숙해지기

 프론트를 맡으며, web3,css,react Hook과 씨름할 일이 예상대로 생겼다. 리액트 프로젝트를 진행할 때 마다 렌더링 문제를 겪곤 했는데, 이번에도 마찬가지로 같은 문제가 생겼다. 하지만 이런 문제를 해결해 나감으로써 리액트에 조금 더 능숙해질 수 있는 계기가 되었다.

section 4에서 web3를 많이 다뤄보지 못한 아쉬움이 남았었는데, 이번 프로젝트를 진행하며 web3에 대해 좀 더 능숙해질 수 있었다. 트랜잭션 전송과 web3 contract를 다뤄보며 많은 공부를 하게 됐으며 다음 프로젝트를 좀 더 원활히 진행할 수 있도록 토대를 마련한 느낌이 들었다.

 


github

https://github.com/codestates/beb-03-whale

 

GitHub - codestates/beb-03-whale

Contribute to codestates/beb-03-whale development by creating an account on GitHub.

github.com

Notion Page

https://www.notion.so/whale-1b61c2e5eae04b79ad841dfb08f73096