Ep1. Wallet Connect คืออะไร ประตูบานใหม่สู่ของโลก Web 3.0
1.Introduce
โลก blockchain ได้เดินทางมาถึงปี 2023 ผ่านเส้นทางที่หลากหลายและมีการเปลี่ยนแปลงเกิดขึ้นมากมาย ระหว่างทางผู้คนต่างพยายามสร้าง Ecosystem
ของตัวเองบ้างก็สร้าง chain ของตัวเอง บ้างก็สร้าง dApp, Wallet เพื่อรองรับ chain ที่หลากหลาย และเป็นรากฐานให้กับอาณาจักรของ Ecosystem
ที่คนเหล่านั้นสร้างขึ้นมา
ทำให้ทุกวันนี้จำนวนของ DApp, wallet และ chain ต่างๆ มีจำนวนมากมายมหาศาล สิ่งที่ตามมาทำให้เกิดความลำบากไม่ว่าจะเป็นผู้ใช้หรือผู้พัฒนา ในการย้ายไปแต่ละ ecosystem
เช่น Binance, Bitkub มี Hybrid wallet dapp ที่ทำหน้าที่เก็บ private key ให้กับผู้ใช้และสามารถใช้งาน DApp ได้ภายในตัวเปรียบเสมือน ecosystem
หนึ่ง แต่ก็มีข้อจำกัดที่แอปเหล่านั้นจะรองรับแค่ dapp ที่ผู้พัฒนาเขียนให้รองรับเท่านั้น และ DApp ของบุคคลที่สามที่ต้องการเชื่อมต่อกับกระเป๋าของ Binance หรือ Bitkub ก็ไม่สามารถ Integrate ได้เหมือนกัน นี่จึงเป็นข้อจำกัดของ Wallet และ DApp ส่วนใหญ่ในปัจจุบัน จึงเป็นที่มาของ Wallet Connect
ที่จะมาแก้ไขปัญหานี้
- DApp สามารถเชื่อมต่อกับ Wallet ใดๆ ก็ได้ที่รองรับ
Wallet Connect
- DApp ที่รองรับ
Wallet Connect
จะไม่ถูกยึดติดกับ platform อีกต่อไป - Wallet ไม่จำเป็นต้องอยู่ในรูปแบบของ extension หรือ browser app แบบ metamask อีกต่อไป
- สามารถสร้างกระเป๋าของตัวเองที่สามารถ sign, approve transaction ได้เหมือน metamask ไม่ว่าจะ platform ใดก็ตาม
- รองรับการพัฒนา Wallet และ DApp ด้วย React, React Native, Vanilla Js, Kotlin, Swift, Flutter, Unity
2.What is wallet connect?
WalletConnect
เป็นเทคโนโลยีที่ช่วยให้เราเชื่อมต่อ DApp และ Wallet ได้โดยไม่ยึดติดกับ Platform ทำงานโดยตั้งตัวเป็นตัวกลางชั้น Messaging layer ในการสื่อสารระหว่าง DApp กับ Wallet การเริ่มต้นเชื่อมต่อนี้ทำได้ผ่าน QR Code หรือ Deep link ที่ผู้ใช้สามารถสแกนหรือกด Link เพื่อเปิดแอปพลิเคชั่น เมื่อ DApp เชื่อมต่อกับ WalletConnect แล้วผู้ใช้จะได้รับการแจ้งเตือน Approved เพื่อยืนยันภายการอนุมัติ Wallet เป็นอันสิ้นสุดการเริ่มต้นเชื่อมต่อ และสามารถเริ่มทำธุรกรรมต่อไปได้
เนื่องจาก Wallet Connect
สามารถรองรับได้หลาย Platform การเริ่มต้นการเชื่อมต่อจะสามารถดำเนินการได้หลากหลายมากกว่าแต่ก่อนที่ต้องพึ่ง Metamask
อย่างเดียว ยกตัวอย่างเช่น
- เปิดบน desktop browser แล้วเชื่อมต่อด้วย wallet extension เช่น metamask
- เปิดบน desktop browser แล้วเชื่อมต่อด้วย QR Code ผ่าน กระเป๋าใดๆ บนมือถือที่รองรับ wallet connect
- เปิดบน mobile browser และเชื่อมต่อผ่านการเลือก wallet app ที่ติดตั้งไว้บนเครื่อง (จะเป็นเคสเดียวกับภาพ JFIN Staking)
3.Quick Start
หลังจากที่เราเข้าใจหน้าที่และบทบาทของ Wallet Connect
แล้ว เราจะมาสอนวิธีเชื่อมต่อ DApp เข้ากับ Wallet อย่างง่ายด้วย Wallet Connect
โดยใช้ Library web3modal ที่เป็น UI อเนกประสงค์พัฒนาอยู่บนรากฐานของ Wallet Connect
ควบคู่ไปกับ React โดยผู้ที่มี project react อยู่แล้วสามารถเริ่มทำตาม step ด้านล่างได้ทันที แต่ถ้าใครยังไม่มีให้ setup react project ก่อนนะคะ
ติดตั้ง package ที่จำเป็นสำหรับ web3modal ลงบน project react ของเรา
npm install @web3modal/ethereum @web3modal/react wagmi viem
เริ่มด้วยการ import Web3Modal
และ Wagmi
และตั้งค่าตามตัวอย่างด้านล่าง
import { EthereumClient, w3mConnectors, w3mProvider } from '@web3modal/ethereum'
import { Web3Modal } from '@web3modal/react'
import { configureChains, createConfig, WagmiConfig } from 'wagmi'
import { arbitrum, mainnet, polygon } from 'wagmi/chains'
const chains = [arbitrum, mainnet, polygon]
const projectId = 'YOUR_PROJECT_ID'
// ตั้งค่า provider
const { publicClient } = configureChains(chains, [w3mProvider({ projectId })])
const wagmiConfig = createConfig({
autoConnect: true,
connectors: w3mConnectors({ projectId, chains }),
publicClient
})
const ethereumClient = new EthereumClient(wagmiConfig, chains)
function App() {
return (
<>
<WagmiConfig config={wagmiConfig}>
<HomePage />
</WagmiConfig>
<Web3Modal projectId={projectId} ethereumClient={ethereumClient} />
</>
)
}
- ตั้งค่า chain ที่จะถูกรองรับใน wallet connect โดยสามารถดู chain ทั้งหมดได้ที่
chains
เหลือถ้าไม่มี chain ที่ต้องการเราสามารถที่จะ custom เองขึ้นได้custom
const chains = [arbitrum, mainnet, polygon]
- projectId เกิดจาก id ของ project เราที่สร้างบน cloud.walletconnect
const projectId = 'YOUR_PROJECT_ID'
WagmiConfig
เป็น component provider ของ wagmi ที่ทำหน้าที่เป็น interface กลางในการเชื่อมต่อระหว่างกระเป๋ากับ method บน web3
function App() {
return (
<>
<WagmiConfig config={wagmiConfig}>
<HomePage />
</WagmiConfig>
<Web3Modal projectId={projectId} ethereumClient={ethereumClient} />
</>
)
}
ถ้าเราไม่ถูกใจ theme เบื้องต้นของ web3modal
เราสามารถแต่ง theme
ของ modal และ button ได้ตามนี้
<Web3Modal
themeVariables={{
'--w3m-accent-color': '#ed0000', // Color used for buttons, icons, labels, etc.
'--w3m-accent-fill-color': '#fff', // Color used for text and icons inside elements with accent color background
'--w3m-background-color': ' #0b0d0f', // Background color to be used instead of default animated gradient
}}
/>
สามารถดูตัวเลือกทั้งหมดในการตกแต่งได้ที่ Theming
บางกรณีที่เราใช้ Custom chain หรือ Token ของตัวเองแล้วเราต้องการเปลี่ยนรูปภาพเหล่านั้นสามารถทำได้โดย
<Web3Modal
// กำหนดรูปภาพให้กับ chain id
chainImages: {
1: "/images/ethereum.webp",
137: "/images/polygon.webp",
};
// กำหนดรูปภาพให้กับ token
tokenImages: {
ETH: "/images/eth.webp",
AVAX: "/images/avax.webp",
};
/>
วางปุ่มเชื่อมต่อของ web3modal
ไว้ในจุดที่เราต้องการให้ผู้ใช้คลิ๊กเพื่อเชื่อมต่อกับกระเป๋า
ทีนี้เราก็สามารถเชื่อมกับกระเป๋าของเราและพร้อมที่จะทำธุรกรรมบนโลก web3 ได้แล้ว, แต่~ เราจะทำธุรกรรม read
write
ยังไงละแล้วถ้าเรามี contract แล้วเราจะเรียก method ของ contract เหล่านั้นยังไงละ?
4. Wagmi
Wagmi
เป็น library ที่รวบรวม method ต่างๆ สำหรับการมีปฎิสัมพันธ์กับ web3 เช่น ดู balance ของผู้ใช้, ดูจำนวน block ปัจจุบันของ chain, read write contract และอื่นๆ อีกมากมาย
หลังจากหัวข้อที่ 3 เราได้ติดตั้ง wagmiConfig
และเชื่อมต่อกระเป๋าได้แล้ว ทำให้เราสามารถเริ่มทำธุรกรรมต่างๆ ได้ทีนี้เรามาเริ่มทำจากสิ่งง่ายๆ อย่างการดูข้อมูลกระเป๋าของผู้ใช้กัน
Wallet Detail
import { useAccount } from 'wagmi'
function App() {
const { address, isConnecting, isDisconnected } = useAccount()
if (isConnecting) return <div>Connecting…</div>
if (isDisconnected) return <div>Disconnected</div>
return <div>{address}</div>
}
import { useBalance } from 'wagmi'
function App() {
const { data, isError, isLoading } = useBalance({
address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
})
if (isLoading) return <div>Fetching balance…</div>
if (isError) return <div>Error fetching balance</div>
return (
<div>
Balance: {data?.formatted} {data?.symbol}
</div>
)
}
Contract
อย่างแรกในการทำธุรกรรมกับ contract เราจำเป็นต้องมี interface สำหรับสื่อสารกับ contract ก่อน สิ่งนี้เรียกว่า ABI โดยปรกติเราจะเก็บในรูปแบบของไฟล์ json แต่เนื่องจาก wagmi จำเป็นต้องอ่าน type ของ ABI ด้วยเราจึงจำเป็นต้องเปลี่ยนไฟล์จาก json เป็น typescript
ในตัวอย่างนี้เราจะใช้ contract Wagmi Gotchi เพื่อเป็นตัวอย่างไปจนจบนะคะสามารถเข้าไปก็อป json มาไว้ในไฟล์ typescript เพื่อเอามาใช้ได้เลย
- Read Contract
เมื่อ read
เราจะใช้ hook ชื่อ useContractRead
โดยสิ่งจำเป็นที่เราต้องใส่เข้าไปในฟังก์ชั่นคือ address (contract address)
, abi (contract abi)
และ functionName
ซึ่งจะแสดง method ทั้งหมดที่เรากำหนดไว้ใน ABI
import wagmiGotchiABI from './abi/wagmiGotchiABI'
const alive = useContractRead({
address: '0xeCB504D39723b0be0e3a9Aa33D646642D1051EE1',
abi: wagmiGotchiABI,
functionName: 'getAlive',
})
- Write Contract
การ write จะมีความยุ่งยากมากกว่า read ระดับหนึ่งโดยอย่างแรกเราจะเรียก hook ชื่อว่า usePrepareContractWrite
เพื่อเตรียม config สำหรับการ write
const { config } = usePrepareContractWrite({
address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
abi: wagmiGotchiABI,
functionName: 'feed',
})
หลังจากได้ config แล้วเราจะใช้ hook ชื่อว่า useContractWrite
เพื่อเตรียม method สำหรับการเรียกใช้
const { config } = usePrepareContractWrite({
address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
abi: wagmiGotchiABI,
functionName: 'feed',
})
const { data, isLoading, isSuccess, write } = useContractWrite(config)
return (
<div>
<button disabled={!write} onClick={() => write?.()}>
Feed
</button>
{isLoading && <div>Check Wallet</div>}
{isSuccess && <div>Transaction: {JSON.stringify(data)}</div>}
</div>
)
Read Event (Logs)
มีหลายๆ กรณีที่ที่เราต้องการอ่าน event ย้อนหลังของ contract แต่จะมีความยุ่งยากตรงที่ wagmi ไม่ได้มี build-in function ในการอ่าน event มาให้เราจึงจำเป็นต้องไปใช้ library รากฐานของ wagmi นั่นคือ viem
ที่เราติดตั้ง package ไว้ตอนต้นนั่นเอง
import { getPublicClient, getContract } from 'wagmi/actions'
import { getAbiItem } from 'viem'
const publicClient = getPublicClient()
const contract = getContract({
address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
abi: wagmiGotchiABI,
})
// เลือก event จาก abi ที่จะใช้ในการอ่าน event (log)
const abiItem = getAbiItem({ abi: contract.abi, name: 'CaretakerLoved' })
// อ่าน event (log)
const logs = await publicClient.getLogs({
event: abiItem,
fromBlock: 'earliest', // เริ่มต้น
toBlock: 'latest' // ท้ายสุด
})
ทีนี้เราก็สามารถทำธุรกรรมได้ทั้ง read
write
และอ่าน logs
ได้แล้ว โดยตัวอย่างข้างต้นจะเป็นการเขียนในรูปแบบ hook แต่บางคนอาจต้องการเขียนเป็น function ธรรมดา ส่วนขยายเอง wagmi จึงมี wagmi/actions ที่เป็น function async ธรรมดาไว้เรียกใช้ได้ สามารถดูได้ที่ actions
หากผู้อ่านมีคำถามหรือข้อสงสัยอะไร หรือมีคำแนะนำ สามารถติดต่อผู้เขียนได้เลยค่ะ