wagmi.sh hooks 만들어보기

2024. 7. 25. 07:50UX Engineer 이야기
seonju.lee

들어가며

올해 그룹에서 Web 3에 대한 내부 프로젝트를 시작하게 되면서, 내부 환경 세팅 및 관련 라이브러리 사용에 대해 고민하던 중, WAGMI.sh라는 라이브러리를 우연히 알게 되었습니다. 비록 실제 프로젝트에 투입되지는 않았고 해당 라이브러리를 사용하지는 않았지만, 공부를 통해 WAGMI.sh의 내부 동작 방식을 이해하고 분석하는 좋은 기회를 얻게 되어 학습 경험을 공유하고자 합니다.

WAGMI.sh란,

블록체인 기술과 상호작용을 하는 애플리케이션을 위해 설계된 유틸리티 라이브러리입니다.
여기서 WAGMI 는 “We’re All Gonna Make It”의 약자로 암호화폐와 web 3 커뮤니티에서 자주 사용되는 표현인데,
이더리움 개발과 일반적인 작업을 단순화하는 훅을 제공합니다.
Wagmi.sh에서 제공하는 훅은 총 31개로 주요 기능은 다음과 같습니다.

  • useAccount : 사용자 지갑 계정 가지고 오기
  • useBalance : 스마트 계약에서 데이터 가져오기
  • useSendTransaction : 트랜잭션을 전송하기

 

커스텀  만들어보기

useAccount : 사용자 지갑 계정 가지고 오기

// wagmi.sh
Import { useAccount } from ‘wagmi’;

function App() {
  const account = useAccount()
}

wagmi를 설치하면 아래 코드처럼 간단한 호출을 통해서 사용자 지갑 계정 정보를 가져올 수 있지만, 아래와 같은 과정으로 직접 만들어 볼 수도 있습니다. (생각보다 어렵지 않아요.)

  1. 이더리움 (Ethereum) 브라우저 환경에서 제공하는 API인 request 함수를 이용합니다.
    window.ethereum.request({ method: 'eth_requestAccounts' });
  2. 이더리움 브라우저 환경에서 현재 사용하고 있는 브라우저와의 상호작용을 위한 Web 3 인스턴스를 생성해 줍니다.
    const web3Instance = new Web 3(window.ethereum);


    (web 3 인스턴스란, 우리가 자바스크립트에서 생성자 함수인 new Date()를 이용해 인스턴스를 생성하여 내장 객체를 사용하는 것을 떠올리면 쉽게 이해할 수 있습니다)
  3. 생성된 web 3 인스턴스 내장 객체 중 eth 객체의 다양한 함수 중 “getAccounts” 이용해서 연결된 계정 목록을 가져옵니다.
    const accounts = await web3Instance.eth.getAccounts();

계정을 갖고 오기 위한 전체 코드로 확인이 가능합니다. [ jsfield에서 확인하기 ]

Import Web3 from ‘web3’;

// 계정정보 갖고 오는 컴포넌트
const AccountInfo = () => {
	const [web3, setWeb3] = useState(null);
	const [account, setAccount] = useState(null);
	const connectWallet = async () => {
         if (window.ethereum) {
             try {
                await window.ethereum.request({ method: 'eth_requestAccounts' });
                const web3Instance = new Web3(window.ethereum);
                const accounts = await web3Instance.eth.getAccounts();
                setWeb3(web3Instance);
                setAccount(accounts[0]);
             } catch (error) {
                 console.log("메타마스크 연결에 실패했습니다.");
             }
        } else {
           alert("메타마스트 연결해주세요.");
        }
  };
	return (
		<div>{account}</div> 
	)
}

 

useBalance : 스마트 계약에서 데이터 가져오기

// wagmi.sh
import { useBalance } from 'wagmi'

function App() {
  const result = useBalance({
    address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
  })
}

 

  1. 생성자 함수를 이용해서 스마트 컨트랙트 인스턴스를 생성합니다.
    const contract = new web3.eth.Contract(abi, tokenAddress);
  2. 생성한 인스턴스 "balanceOf" 함수를 이용해서 특정 계정의 토큰 잔액을 조회할 수 있습니다.
    const balance = await contract.methods.balanceOf(account).call();
  3. Web3.js 유틸리티로 조회한 토큰 잔액 값을 Wei 단위에서 Ether 단위로 변환해 줍니다. (스마트 컨트랙트에서 토큰 잔액이나 트랜잭션 금액을 조회할 때는 보통 Wei 단위로 반환되는데, ether 단위로 변환해서 확인하는 게 일반적이라고 합니다. 1 ether = 10^18)
    web3.utils.fromWei(balance, 'ether')

abi(Application Binary Interface) 란?
ABI는 Application Binary Interface의 약자로, 런타임 시 바이너리 코드와 상호작용을 하기 위한 인터페이스이다. ABI는 바이너리 형태로 되어있는 스마트 컨트랙트가 어떤 인터페이스를 가졌는지 알려주는 역할을 한다.

계정에 있는 잔액 정보를 갖고 오는 전체 코드로 확인이 가능합니다. [ jsfield에서 확인하기 ]

import { useState } from 'react';
import Web3 from 'web3';

const abi = [
  {
    "constant": true,
    "inputs": [
      {
        "name": "_owner",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "name": "balance",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  }
];

const useBalance = (web3, tokenAddress) => {
  const [balance, setBalance] = useState(null);

  const getBalance = async (account) => {
    if (web3 && tokenAddress) {
      const contract = new web3.eth.Contract(abi, tokenAddress);
      const balance = await contract.methods.balanceOf(account).call();
      setBalance(web3.utils.fromWei(balance, 'ether'));
    }
  };

  return { balance, getBalance };
}

export default useBalance;

 

useSendTransaction : 트랜잭션을 전송하기

// wagmi.sh
import { useSendTransaction } from 'wagmi'
import { parseEther } from 'viem'

function App() {
  const { sendTransaction } = useSendTransaction()

  return (
    <button
      onClick={() =>
        sendTransaction({
          to: '0xd2135CfB216b74109775236E36d4b433F1DF507B',
          value: parseEther('0.01'),
        })
      }
    >
      Send transaction
    </button>
  )
}
  1. transaction 객체를 만들어서 보내는 지갑 주소와, 받는 지갑 주소, 전송할 양을 먼저 정의를 해줍니다.
    (전송할 이더의 양은 이더의 작은 단위인 Wei 단위로 정의해야 합니다)
    const transaction = { from: from, to: to, value: web3.utils.toWei(amount, 'ether'), };
  2. web3 인스턴스 내 sendTransaction 메서드를 이용하면 트랜잭션을 전송할 수 있습니다. 
    web3.eth.sendTransaction(transaction);

트랜잭션을 전송하는 전체 코드로 확인이 가능합니다. [ jsfield에서 확인하기 ]

import Web3 from 'web3';

const sendTransaction = async (web3, from, to, amount) => {
  try {
    const transaction = {
      from: from,
      to: to,
      value: web3.utils.toWei(amount, 'ether'),
    };
    await web3.eth.sendTransaction(transaction);
  } catch (error) {
    console.error("Transaction error:", error);
  }
}

 

마치며

처음 개발을 시작할 때는 슬라이드, 탭, 아코디언 UI 같은 컴포넌트도 라이브러리를 사용해 구현했습니다. 그러나 동작 원리를 이해하기 위해 직접 만들어보는 경험을 통해, 이제는 필요한 기능을 라이브러리 없이도 어렵지 않게 구현할 수 있게 되었습니다.
WAGMI.sh와 같은 많은 훅의 내부 구성을 모두 이해하지 못하더라도, 시간을 내어 하나씩 훑어보는 것은 좋은 학습 경험이 될 것 같습니다.

읽어주셔서 감사합니다.

이 글은 pxd XE Group Blog에서도 보실 수 있습니다.

참고문서