react NFT application 서사 3장

Poo·2021년 12월 15일
1

하루만에 다할려니까 너무 힘들다....

Web3.js 로 NFT 받기

이전 블로깅에서 나는 shipNFT 컨트랙트를 만들었다 자 이제 컨트랙트 안에서 NFT를 발행해보겠다. 이전 ha에서 머리털 빠지게 nft storage를 썼는데 그렇게 어렵게 할 필요가 읎드라는것! 흘러가듯이 흘러가보자!

ERC721 컨트랙트 주소를 입력받아, 해당 컨트랙트에서 발행받은 토큰을 가져와보자

App.js

먼저 주소를 입력 받는다. newErc721addr 상태는 입력받은 컨트랙트 주소를 저장한다. 컨트랙트 주소를 입력하고 버튼을 누르면 addNewErc721Token() 이 실행이 된다.

function App() {
	const [newErc721addr, setNewErc721Addr] = useState();
	// 생략
	const addNewErc721Token = () => {}
	return (
	// 생략
	<div className="newErc721">
	    <input
	        type="text"
	        onChange={(e) => {
	            setNewErc721addr(e.target.value);  
                // 입력받을 때마다 newErc721addr 갱신
	        }}
	    ></input>
	    <button onClick={addNewErc721Token}>add new erc721</button>
	</div>
	)
}

addNewErc721Token() 함수에서는 web3.js를 사용해 컨트랙트 객체를 생성하고, 컨트랙트 객체를 통해 컨트랙트 함수를 호출하여 계정이 가지고 있는 토큰의 목록을 저장한다.
web3.eth.Contract()를 사용해 컨트랙트 객체를 만든다. 컨트랙트 객체를 만들기 위해서는 컨트랙트의 ABI와 주소가 필요하다.

const addNewErc721Token = async () => {
    const tokenContract = await new web3.eth.Contract(
        erc721Abi,
        newErc721addr
    );
} 

컨트랙트 객체를 통해 컨트랙트 함수를 실행하자.
컨트랙트 이름 심볼 총 발행량을 가져오자. 뭐 더가져오고 싶은게 있으면 추가하라.
contract 객체를 통해 함수를 실행할 때는 뒤에 call()을 붙혀야 한다고 한다.

const addNewErc721Token = async () => {
	  const tokenContract = await new web3.eth.Contract(
	      erc721Abi,
	      newErc721addr
	  );
	  const name = await tokenContract.methods.name().call();
	  const symbol = await tokenContract.methods.symbol().call();
	  const totalSupply = await tokenContract.methods.totalSupply().call();
}

사용자 토큰을 찾는 로직은
1. 토큰의 총 발행량만큼 반복문을 돈다.
2. Contract.methods.ownerOf() 를 통해 각 토큰의 오너 주소를 받아온다.
3. 해당 주소가 dApp으로 연결한 계정 주소와 같은지 확인한다.
4. 같다면, Contract.methods.tokenURI() 를 사용해 해당 토큰의 URI 값을 가져온다.
5. 토큰 정보를 저장한다.

function App () {
	const [erc721list, setErc721list] = useState([]);  // 자신의 NFT 정보를 저장할 토큰

	const addNewErc721Token = async () => {
		// 생략
			let arr = [];
		  for (let i = 1; i <= totalSupply; i++) {
		      arr.push(i);
		  }
		  
		  for (let tokenId of arr) {
		      let tokenOwner = await tokenContract.methods
		          .ownerOf(tokenId)
		          .call();
		      if (String(tokenOwner).toLowerCase() === account) {
		          let tokenURI = await tokenContract.methods
		              .tokenURI(tokenId)
		              .call();
		          setErc721list((prevState) => {
		              return [...prevState, { name, symbol, tokenId, tokenURI }];
		          });
		      }
		  }
	}
}

컨트랙트 함수 호출은 비동기적으로 실행되기 때문에, 반복문을 사용하기 위해서는 for...of 를 사용한다.

토큰리스트 컴포넌트는 자신이 가지고 있는 모든 토큰 목록을 출력하고
erc721은 토큰 중 721만 출력한다
받아온 토큰 목록 721list를 출력하기 위해서 해당 상태를 721로 내려보내야 한다
이를 위해

compnents/TokenList.js, Erc721.js 생성

src 폴더 안에 components '폴더'를 만들어 주고 TokenList.js '파일'을 생성해준다. 그리고 코드는 이렇게

import Erc721 from "./Erc721";

function TokenList({erc721list }) {
    return (
        <div className="tokenlist">
            <Erc721 erc721list={erc721list}  />
        </div>
    );
}

export default TokenList;

같은 폴더 내에 Erc721.js 파일을 생성하고 이렇게

function Erc721({ erc721list }) {
    return (
        <div className="erc721list">
            {erc721list.map((token) => {
                return (
                    <div className="erc721token">
                        Name: <span className="name">{token.name}</span>(
                        <span className="symbol">{token.symbol}</span>)
                        
                        <div className="nft">id: {token.tokenId}</div>
                        <img src={token.tokenURI} width={300} />
                    </div>
                );
            })}
        </div>
    );
}

export default Erc721;

마지막으로 App.js 에서 토큰리스트 컴포넌트를 추가해주자

// 생략
import TokenList from "./components/TokenList";

function App () {
	// 생략
	return (
		<div className="App">
			// 생략
			<TokenList erc721list={erc721list} />
		</div>
	)
}

블로깅 최하단에 코드 전문을 따로 쓰겠다.

마무리

npm run start

해주고

메타마스크 로그인
롭스텐 네트워크 진입

connect wallet 해주고
컨트랙트 주소를 입력해주자

컨트랙트 주소는
remix ide 터미널에서 트랜잭션 성공 후 클릭 하면 보이는
to 부분을 참고 하거나

롭스텐 이더스캔에서 자기 지갑 주소 검색 후
발행된 nft 클릭
컨트랙트 부분에서 해쉬값을 복사 해주면 되겠다.

완성

자 그러면 어떻게 뜨냐!!

힘들다 힘들어

코드 전문

App.js

import './App.css';
import { useState, useEffect } from "react";
import Web3 from 'web3';
import React from "react"
import erc721Abi from "./erc721Abi"
import TokenList from "./components/TokenList";

function App() {
  const [erc721list, setErc721list] = useState([]); 
  const [newErc721addr, setNewErc721Addr] = useState();

  const [web3, setWeb3] = useState();
    useEffect(() => {
        if (typeof window.ethereum !== "undefined") { // window.ethereum이 있다면
            try {
                const web = new Web3(window.ethereum);  // 새로운 web3 객체를 만든다
                setWeb3(web);
            } catch (err) {
                console.log(err);
            }
        }
    }, []);

  const [account, setAccount] = useState('');
	// ...
  
  const connectWallet = async () => {
    const accounts = await window.ethereum.request({
        method: "eth_requestAccounts",
         });

    setAccount(accounts[0]);
};

const addNewErc721Token = async () => {
  const tokenContract = await new web3.eth.Contract(
      erc721Abi,
      newErc721addr
  );
    const name = await tokenContract.methods.name().call();
	  const symbol = await tokenContract.methods.symbol().call();
	  const totalSupply = await tokenContract.methods.totalSupply().call();

    let arr = [];
		  for (let i = 1; i <= totalSupply; i++) {
		      arr.push(i);
		  }
      for (let tokenId of arr) {
        let tokenOwner = await tokenContract.methods
            .ownerOf(tokenId)
            .call();
        if (String(tokenOwner).toLowerCase() === account) {
            let tokenURI = await tokenContract.methods
                .tokenURI(tokenId)
                .call();
            setErc721list((prevState) => {
                return [...prevState, { name, symbol, tokenId, tokenURI }];
            });
        }
      }

} 

  return (
    <div className="App">
      {/* <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header> */}
            <button
                className="metaConnect"
                onClick={() => {
                    connectWallet();
                }}
            >
                connect to MetaMask
            </button>
        <div className="userInfo">주소: {account}</div>  
        // 연결된 계정 주소를 화면에 출력
        <div className="newErc721">
        <input
            type="text"
            onChange={(e) => {
                setNewErc721Addr(e.target.value);  
                // 입력받을 때마다 newErc721addr 갱신
            }}
        ></input>
        <button onClick={addNewErc721Token}>add new erc721</button>
      </div>    
      <TokenList erc721list={erc721list} />
    </div>
  );
}
export default App;
profile
죽을 때 까지 공부하다 죽을 인생, 로봇공학자에서 블록체인 개발자가 되기 까지

0개의 댓글