ทำปุ่ม Connect Wallet + Metamask ด้วย Rainbowkit + Wagmi

Web3 Apr 8, 2023

สวัสดีครับ โพสนี้มาลองทำเว็บ dApp เพื่อ Connect Wallet ด้วยการใช้ Rainbowkit กันนะครับ ก่อนหน้านี้ผมเคยโพสบทความการทำปุ่ม Connect Wallet ง่ายๆ ไว้ตามโพสด้านล่างนี้ เนื้อหาบทความนี้ก็เป็นบทความอัพเดทนะครับ

Devahoy - ทำปุ่ม Connect Wallet เชื่อมต่อ Blockchain ง่ายๆ ด้วย Rainbowkit
วิธีการทำปุ่ม Connect Wallet สำหรับ Decentralize App ปัจจุบันนั้นง่ายมาก และมีหลาย Library ให้เลือกใช้ เช่น - web3-react- Web3Modal- useDapp(https://

โดย ผมพยายามอธิบายบ่างส่วนเพิ่มเติม รวมถึงอัพเดทตัว Library จากบทความที่แล้วที่ใช้ Rainbowkit v0.7.1 เป็นปัจจุบัน 0.12.x แล้ว (จริงๆ ก็ไม่มีอะไรเปลี่ยนแปลงมาก เพราะบทความเราแค่ connect wallet ไม่ได้ sign transaction หรือ call contract)

ข้อดีของ Rainbowkit

  • ข้อแรก ผมว่ามันง่ายมาก ในการทำปุ่ม Connect Button ไม่กี่ขั้นตอนเท่านั้น
  • UI ก็พร้อมใช้งานเลย มี handle ต่างๆ เช่น auto connect, refresh, ดู list transaction ดู balance ต่างๆ
  • ปรับแต่ง chains เพิ่ม Wallets เองไม่ยาก (ใน Docs มีเขียนไว้หมดแล้ว)

มาลองสร้างโปรเจ็คกันดูครับ!

Step 1 - สร้างโปรเจ็ค

เริ่มแรก ทำการสร้างหน้าเว็บขึ้นมาก่อน เว็บเป็น React TypeScript ขึ้นโปรเจ็คด้วย Vite.js

npm create vite@latest hello-rainbowkit -- --template react-ts

ติดตั้ง wagmi, rainbowkit และ ethers.js v5

npm i @rainbow-me/rainbowkit wagmi ethers@5

Start server Vite.js

npm run dev

จะได้หน้าตาเริ่มต้นแบบนี้

ต่อมา ผมเพิ่มปุ่ม ConnectButton โดยการปรับ main.tsx ให้ใช้ WagmiConfig และ  RainbowKitProvider แบบนี้

ไฟล์ main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

import { WagmiConfig, configureChains, createClient } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import { publicProvider } from 'wagmi/providers/public'
import { getDefaultWallets, RainbowKitProvider } from '@rainbow-me/rainbowkit'

import '@rainbow-me/rainbowkit/styles.css'
import './index.css'

const { chains, provider } = configureChains(
  [mainnet, sepolia],
  [publicProvider()]
)

const { connectors } = getDefaultWallets({
  appName: 'Hello RainbowKit',
  chains,
})

const wagmiClient = createClient({
  autoConnect: true,
  connectors,
  provider,
})

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <WagmiConfig client={wagmiClient}>
      <RainbowKitProvider chains={chains}>
        <App />
      </RainbowKitProvider>
    </WagmiConfig>
  </React.StrictMode>
)

อธิบายจากโค๊ดด้านบน

  1. เริ่มต้น import css ของ rainbowkit ก่อน
import '@rainbow-me/rainbowkit/styles.css'

2. ทำการ config chains ที่จะมีให้เลือก และก็ Provider ที่เราต้องการ (ตัวอย่างใช้แค่ publicProvider ถ้าใครใช้ AlchemyProvider ก็สามารถเพิ่ม AlchemyProvider ได้

import { configureChains } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import { getDefaultWallets } from '@rainbow-me/rainbowkit'

const { chains, provider } = configureChains(
  [mainnet, sepolia],
  [publicProvider()]
)

const { connectors } = getDefaultWallets({
  appName: 'Hello RainbowKit',
  chains,
})

3. สร้าง wagmiClient โดยใช้ค่าจาก configureChains

import { createClient } from 'wagmi'

const wagmiClient = createClient({
  autoConnect: true,
  connectors,
  provider,
})

4. สุดท้าย wrap RainbowKitProvider กับ WagmiConfig

<WagmiConfig client={wagmiClient}>
  <RainbowKitProvider chains={chains}>
    <App />
  </RainbowKitProvider>
</WagmiConfig>

ทีนี้เมื่อเรามี RainbowKitProvider เรียบร้อยแล้ว เราก็สามารถใช้งาน ConnectButton ได้ โดยที่มี wagmi เป็น hooks เอาไว้จัดการเรื่อง connect, ดูข้อมูล balance, account, transactions อื่นๆ (จริงๆ ต้องบอกว่าส่วนใหญ่คือ wagmi มากกว่า ตัว RainbowKit เหมือนเป็น UI มาครอบอีกที)

Step 2 - เพิ่มปุ่ม Connect Button

ทีนี้ เราจะเพิ่มปุ่ม ConnectButton ก็คือต้องเปิดไฟล์ App.tsx และทำการแก้ไข ไปแทนที่ read the docs ที่เป็น default ตอนสร้างโปรเจ็คด้วย Vite

import { ConnectButton } from '@rainbow-me/rainbowkit'
 
function App() {
  return (
     ...
    <p className="read-the-docs">
      <ConnectButton />
    </p>
  )
}

เราก็จะได้ปุ่ม Connect Wallet แล้ว สามารถต่อ Metamask ได้ มี Popup เด้ง สวยงาม

สิ่งที่ต้องมี คือ ต้องติดตั้ง Metamask เป็น Extensions ก่อนนะครับ ไม่งั้น Popup ของ Rainbowkit จะไม่มีให้เลือก (ในตัวอย่างใช้ default ไม่ได้ custom wallet เอง)

Step 3 - Connect Contract

ขั้นตอนนี้ เราจะทำการ connect Contract เพื่อไปดึงข้อมูลนะครับ ตัวอย่าง ผมจะดึงข้อมูล จาก Mainnet (เนื่องจากแค่ read เลยไม่ต้องเสียอะไร) ตัวอย่างผมไปดึงเหรียญ​ USDT จริงๆ

เราจะใช้ wagmi hooks ชื่อ useContract  โดยต้องใช้ address และ abi ซึ่ง address หาได้จาก etherscan หรือ official ของ token นั้นๆ ส่วน abi ก็ใช้ default ERC20 ซึ่ง wagmi มีให้

  import { erc20ABI, useContract, useProvider } from 'wagmi'
  
  const provider = useProvider()
  const contract = useContract({
    address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
    abi: erc20ABI,
    signerOrProvider: provider,
  })

ลองดึง totalSupply ก่อนเลย

const totalSupply = await contract.totalSupply()

ลองดึง balance ของ user

const balance = await contract.balanceOf('<WALLET_ADDRESS>')

ค่าที่ได้ ก็จะเป็น BigNumber  เราก็เอามา formatUnits หรือแปลงให้เป็น number / string ก็แล้วแต่เราเนอะ

จริงๆ get balance ของ ERC20 ตัว wagmi ก็มี hooks เหมือนกัน ใช้อันนี้แทนได้

import { fetchBalance } from '@wagmi/core'

const balance = await fetchBalance({
  address: '<WALLET_ADDRESS>',
  token: '0xdac17f958d2ee523a2206206994597c13d831ec7',
})

เรื่องของ wagmi hooks ไว้บทความอื่นละกันนะครับ บทความนี้ขอเป็นตัวอย่างการทำ Connect Wallet ด้วย RainbowKit ก่อน

ไฟล์ Component ที่เอาไว้ดึงข้อมูล token มี useEffect เพื่ออ่านค่า contract และก็นำมา set state จากนั้น ก็ render ข้อมูล

import { useEffect, useState } from 'react'
import { ethers } from 'ethers'

import { erc20ABI, useContract, useProvider } from 'wagmi'

interface Token {
  name: string
  symbol: string
  decimals: number
  totalSupply: string
}

export default function TokenInfo() {
  const [token, setToken] = useState<Token>({
    name: '-',
    symbol: '-',
    decimals: 0,
    totalSupply: '0',
  })

  const provider = useProvider()
  const contract = useContract({
    address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
    abi: erc20ABI,
    signerOrProvider: provider,
  })

  useEffect(() => {
    readContract()
  }, [])

  const readContract = async () => {
    const symbol = (await contract?.symbol()) || '-'
    const name = (await contract?.name()) || '-'
    const decimals = (await contract?.decimals()) || 6
    const ts = (await contract?.totalSupply()) || 0

    const totalSupply = ethers.utils.formatUnits(ts, decimals)

    setToken({
      ...token,
      name,
      symbol,
      decimals,
      totalSupply,
    })
  }

  return (
    <div>
      <h2>Token Information</h2>

      <p>Name: {token.name}</p>
      <p>
        Total Supply : {token.totalSupply} {token.symbol}
      </p>
    </div>
  )
}

เพิ่ม Component <TokenInfo /> ที่ไฟล์ App.tsx ก็จะได้ดังภาพครับ

หวังว่าบทความนี้จะเป็นไอเดีย เป็นแนวทางให้เพื่อนๆ นำไปประยุกต์ใช้งานกันดูนะครับ สุดท้าย Source Code เอาไปดูเพิ่มเติมครับ

Source Code

เผื่อ ใครสนใจเริ่มเขียน Solidity / Smart Contract สามารถอ่านบทความก่อนหน้านี้ของผมได้ เพิ่มเติมครับ

Devahoy - เริ่มต้นเขียน Solidity ด้วย Hardhat
ช่วงนี้ผมค่อนข้างอินกับ Smart Contract เพราะกำลังอยู่ในช่วงหัดเขียน หัดลองครับ ก็เลยลองเขียนเป็นบทความเผื่อใครหลายๆ คนที่สนใจเหมือนๆกัน ตัวผมเองก็ยังเป็นมือใหม่มากๆบทความนี้จะพาไปลองส่องตัวโปรเจ็ค hardhat ว่าตัว sample project ของ hardhat มีอะไรบ้
ลองเขียนและ Deploy Smart Contract ด้วย Foundry
หลังจากที่หลายวันก่อนได้ลองใช้งาน Foundry และก็หัดใช้งานเบื้องต้นไป เผื่อจะเอามาแทนที่ Hardhat วันนี้วันหยุด ว่างๆ ก็เลยถือโอกาส ลองเล่น ลองเปลี่ยนมาลองใช้ Foundry ตั้งแต่เริ่ม เทส และ Deploy ดูว่าจะเป็นไง เลยกลายมาเป็นบทความนี้ครับ ติดตั้งและลองใช้

Happy Coding ❤️

Reference

RainbowKit
The best way to connect a wallet 🌈
wagmi: React Hooks for Ethereum
wagmi is a collection of React Hooks containing everything you need to start working with Ethereum.

Tags

Chai Phonbopit

เป็น Web Dev ทำงานมา 10 ปีหน่อยๆ ด้วยภาษา JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจ Web3, Crypto และ Blockchain เขียนบล็อกที่ https://devahoy.com