메타마스크 연동 클라이언트에서 컨트랙트 이용해보기 [토이 프로젝트]

알락·2022년 11월 14일
2

! 주의 / 위에서 사용된 주소로 암호화폐를 전송할 시 영원히 사라질 수 있으니 사용하지 말 것을 당부드립니다.

동기

실제로 클라이언트에서 어떻게 컨트랙트와 상호작용할 수 있는지 살펴보려고 한다. 특히 이번에는 이더리움 기반 지갑으로 유명한 메타마스크를 사용해봤다. 메타마스크를 통해서 로컬 이더리움 테스트넷의 주소를 가져와 사용할 수 있어서 더욱더 클라이언트 사용 시나리오에 근접할 수 있게 되었다.


프로젝트 개요

이미 많은 테스트넷에 구현되어있는 Faucet 스마트 컨트랙트를 배포하고, 클라이언트가 해당 컨트랙트에 암호화폐를 기부하거나 가져와서 쓸 수 있는 웹 프로그램이다.


아키텍쳐

React 클라이언트에 web3.js 라이브러리를 사용했다. 스마트 컨트랙트 작성은 solidity를 이용하였다. 테스트넷은 ganache를 통해 생성했고, 컨트랙트 배포 및 테스트넷 접근은 truffle을 이용하였다.


구현

⌞ 스마트 컨트랙트 작성

// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.6.0 <=0.9.0;

contract Faucet {

    receive() external payable{}

    function withdraw(uint withdraw_amount) public {
        require(withdraw_amount <= 1 ether);
        payable(msg.sender).transfer(withdraw_amount);
    }   
}

위에 작성된 컨트랙트를 ganache를 통해 생성된 테스트넷에 truffle을 이용하여 배포해준다. 그리고 truffle을 이용하였다면 build 폴더 -> contracts 폴더에 배포한 컨트랙트의 json 파일이 생성된다. 여기에는 만든 컨트랙트 관련한 여러 데이터가 저장되는데, 그 중에는 배포된 컨트랙트의 API 격인 ABI가 있으니 잘 챙겨둬야 한다.

⌞ 메타마스크 설정

ganache로 만든 테스트넷에 메타마스크를 연동해야한다. 또한 ganache 테스트넷이 만들어질 때 생성된 계정 주소 하나를 메타마스크 지갑에 가져와 사용하자. truffle에서 컨트랙트를 작성하거나 트랜잭션을 보낼 때 기본값으로 설정되어 있는 제일 첫 번째 account 주소를 가져오면 편하다. 메타마스크에 등록할 때는 해당 주소의 개인키를 대신해서 기입해주면 된다.

[메타마스크 처음 모습]

[ganache 로컬 네트워크 적용]

[계정 가져오기]

[등록된 계정 모습]

⌞ 클라이언트 작성

[web3.js]

import {useEffect, useState} from 'react';
import Web3 from "web3/dist/web3.min.js";


const useWeb3 = ()=>{
    const [account, setAccount] = useState(null);
    const [web3, setWeb3] = useState(null);

    useEffect(()=>{
    
        (async ()=>{
          if (!window.ethereum) return;
          
          const [address] = await window.ethereum.request({
            method: 'eth_requestAccounts',
          })
          
          setAccount(address);

          const web3 = new Web3('ws://localhost:8545');
          setWeb3(web3);

        })();
      }, []);

      return [web3, account];
};

export default useWeb3;

위 코드는 미리 web3를 받아오는 작업을 하는 함수 useWeb3를 정의한다. 사실 React의 Hook을 모티브로 구현한 것 같다. window.ethereum의 갑작스러운 등장에 당황스러울텐데, 이는 브라우저에서 작동하고 있는 메타마스크가 제공해준다. request가 실행되면 메타마스크에서 해당 웹페이지와 연동을 할 건지, 브라우저 어플이 확인을 한다. 현재 메타마스크에 연동되어있는 주소값을 요청했기에, 문제 없으면 메타마스크에 등록했던 주소값을 가져올 수 있다.

[app.js]

import './App.css';
import useWeb3 from "./hooks/useWeb3";
import {useState, useEffect} from "react";
import Faucet from "./contracts/Faucet.json";

function App() {
  const [web3, account] = useWeb3();
  const [faucet, setFaucet] = useState(null);
  const [number, setNumber] = useState(0);
  const contractAddress = "Faucet의 배포된 주소"

  const giveBtnClickHandler = async ()=>{
    if(number > 0){
      const result = await web3.eth.sendTransaction({from:account, to:contractAddress, value:number});
      console.log(result);
    }
  }

  const receiveBtnClickHandler = async ()=>{
    if(number > 0){
      const result = await faucet.methods.withdraw(number).send({from:account});
      console.log(result);
    }
  }

  const numChangeHandler = (e)=>{
    setNumber(e.target.value);
  }

  useEffect(()=>{
    if (!web3) return;

    const contract = new web3.eth.Contract(Faucet.abi, contractAddress)
    setFaucet(contract);
  }, [web3])

  return (
    <div className="App">
      <div className="container">
        <div className="box">
          <div>  
            <p>내 주소</p>
            { account ? <p>{account}</p>
              : <p>메타마스크와 연결이 안되었습니다.</p>
            }
          </div>
          <div className="input-box">
            <input placeholder="숫자를 입력하세요 (1 wei)" type="number" value={number} onChange={numChangeHandler}/>
          </div>
          <button type="button" className="btn" onClick={giveBtnClickHandler}>Give Ether</button>
          <button type="button" className="btn" onClick={receiveBtnClickHandler}>Receive Ether</button>
        </div>
      </div>
    </div>
  );
}

export default App;

먼저 useEffect 부분을 확인해보자. 거기서 배포된 컨트랙트와의 접점을 만들어주는 작업을 한다. web3.eth.Contract는 마치 네트워크에 배포된 컨트랙트를 클라이언트에서 객체처럼 사용할 수 있게 도와준다. 재료는 아까 중요하다고 얘기했던 스마트 컨트랙트 배포 후 만들어진 json 파일과 네트워크의 해당 컨트랙트의 주소값이다. 여기서 연결한 Faucet 컨트랙트는 App 컴포넌트의 상태값 faucet에 할당된다.
receiveBtnClickHandler를 살펴보면 facuet를 통해 컨트랙트의 함수에 접근, 실행시키고 있는 모습을 확인할 수 있다.


배운 점

  • 배포된 스마트 컨트랙트와 클라이언트가 상호작용할 수 있게 하는 접점이 어떻게 만들어지는지 확인했다.
  • 메타마스크를 테스트넷에 연결하는 방법, 테스트넷에서 이용가능한 주소값을 가져와서 사용하는 방법을 알았다.
  • web3의 대략적인 사용방법을 터득했다.

참고

profile
블록체인 개발 공부 중입니다, 프로그래밍 공부합시다!

4개의 댓글

comment-user-thumbnail
2022년 11월 15일

전부 이해하진 못했지만 ganache, truffle의 쓰임새와 웹앱에서 메타마스크 연동하는 방법 참고해보고 갑니다.

1개의 답글