1.Introduction Wallet Connect
โลก 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 แบบ seamless กับกระเป๋าใดๆ ก็ได้ที่รองรับ 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 กับ metamask mobile
เนื่องจาก 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
หลังจากที่เราเข้าใจหน้าที่และบทบาทของ Wallet Connect แล้ว เราจะมาสอนวิธีเชื่อมต่อ DApp เข้ากับ Wallet อย่างง่ายด้วย Wallet Connect โดยใช้ Library web3modal ที่เป็น UI อเนกประสงค์พัฒนาอยู่บนรากฐานของ Wallet Connect ควบคู่ไปกับ React โดยผู้ที่มี project react อยู่แล้วสามารถเริ่มทำตาม step ด้านล่างได้ทันที แต่ถ้าใครยังไม่มีให้ setup react project ก่อนนะคะ ติดตั้ง package ที่จำเป็นสำหรับ web3modal ลงบน project react ของเรา ติดตั้ง 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 }} />
สามารถดูตัวเลือกทั้งหมดในการตกแต่งได้ที่ Themeing
บางกรณีที่เราใช้ Custon chain หรือ Token ของตัวเองแล้วเราต้องการเปลี่ยนรูปภาพเหล่านั้นสามารถทำได้โดย
<Web3Modal // กำหนดรูปภาพให้กับ chain id chainImages: { 1: "/images/ethereum.webp", 137: "/images/polygon.webp", }; // กำหนดรูปภาพให้กับ token tokenImages: { ETH: "/images/eth.webp", AVAX: "/images/avax.webp", }; />
Icon ของ Token และ Chain ถูกเปลี่ยนเป็น JFIN วางปุ่มเชื่อมต่อของ web3modal ไว้ในจุดที่เราต้องการให้ผู้ใช้คลิ๊กเพื่อเชื่อมต่อกับกระเป๋า
เมื่อกดปุ่มเชื่อมต่อแล้วจะแสดง popup เลือกกระเป๋าที่ต้องการเชื่อมต่อกระเป๋าได้แล้ว
ทีนี้เราก็สามารถเชื่อมกับกระเป๋าของเราและพร้อมที่จะทำธุรกรรมบนโลก web3 ได้แล้ว, แต่~ เราจะทำธุรกรรม read write ยังไงละแล้วถ้าเรามี contract แล้วเราจะเรียก method ของ contract เหล่านั้นยังไงละ?
4. Wagmi
Wagmi เป็น library ที่รวบรวม method ต่างๆ สำหรับการมีปฎิสัมพันธ์กับ web3 เช่น ดู balance ของผู้ใช้, ดูจำนวน block ปัจจุบันของ chain, read write contract และอื่นๆ อีกมากมาย
หลังจากหัวข้อที่ 3 เราได้ติดตั้ง wagmiConfig และเชื่อมต่อกระเป๋าได้แล้ว ทำให้เราสามารถเริ่มทำธุรกรรมต่างๆ ได้ทีนี้เรามาเริ่มทำจากสิ่งง่ายๆ อย่างการดูข้อมูลกระเป๋าของผู้ใช้กัน
Wallet Detail
- อ่าน address กระเป๋า
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> }
- อ่าน balance กระเป๋า
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
ตัวอย่างไฟล์ ABI
ในตัวอย่างนี้เราจะใช้ 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
หากผู้อ่านมีคำถามหรือข้อสงสัยอะไร หรือมีคำแนะนำ สามารถติดต่อผู้เขียนได้เลยค่ะ
ที่มา: https://vasin-peach.medium.com/wallet-connect-ศูนย์กลางใหม่ของโลก-web-3-0-1f0f987fdae4