Lompat ke konten utama

Building a user interface for your contract

TypeScript
react
vite
wagmi
frontend
Pemula
Ori Pomerantz
1 November 2023
17 menit baca

Anda menemukan fitur yang kita butuhkan di ekosistem Ethereum. Anda menulis kontrak pintar untuk mengimplementasikannya, dan mungkin juga beberapa kode terkait yang berjalan offchain. Ini luar biasa! Sayangnya, tanpa antarmuka pengguna, Anda tidak akan memiliki pengguna, dan terakhir kali Anda menulis situs web, orang-orang menggunakan modem dial-up dan JavaScript masih baru.

Artikel ini untuk Anda. Saya berasumsi Anda tahu pemrograman, dan mungkin sedikit JavaScript dan HTML, tetapi keterampilan antarmuka pengguna Anda sudah berkarat dan ketinggalan zaman. Bersama-sama kita akan membahas aplikasi modern yang sederhana sehingga Anda akan melihat bagaimana hal itu dilakukan saat ini.

Mengapa ini penting

Secara teori, Anda bisa saja meminta orang menggunakan Etherscan (opens in a new tab) atau Blockscout (opens in a new tab) untuk berinteraksi dengan kontrak Anda. Itu bagus untuk para pengguna Ethereum yang berpengalaman. Namun, kita mencoba melayani satu miliar orang lainnya (opens in a new tab). Ini tidak akan terjadi tanpa pengalaman pengguna yang luar biasa, dan antarmuka pengguna yang ramah adalah bagian besar dari hal tersebut.

Aplikasi Greeter

Ada banyak teori di balik cara kerja UI modern, dan banyak situs bagus (opens in a new tab) yang menjelaskannya (opens in a new tab). Daripada mengulangi pekerjaan luar biasa yang dilakukan oleh situs-situs tersebut, saya akan berasumsi bahwa Anda lebih suka belajar dengan mempraktikkannya dan memulai dengan aplikasi yang dapat Anda mainkan. Anda tetap membutuhkan teori untuk menyelesaikan sesuatu, dan kita akan membahasnya - kita hanya akan membahas file sumber demi file sumber, dan mendiskusikan berbagai hal saat kita menemukannya.

Instalasi

  1. Aplikasi ini menggunakan jaringan testnet Sepolia (opens in a new tab). Jika perlu, dapatkan ETH uji coba Sepolia dan tambahkan Sepolia ke dompet Anda (opens in a new tab).

  2. Klon repositori GitHub dan instal paket-paket yang diperlukan.

    1git clone https://github.com/qbzzt/260301-modern-ui-web3.git
    2cd 260301-modern-ui-web3
    3npm install
1
23. Aplikasi ini menggunakan titik akses gratis, yang memiliki batasan kinerja. Jika Anda ingin menggunakan penyedia [Node sebagai layanan](/developers/docs/nodes-and-clients/nodes-as-a-service/), ganti URL di [`src/wagmi.ts`](#wagmi-ts).
3
44. Mulai aplikasi.
5
6 ```sh
7 npm run dev
  1. Buka URL yang ditampilkan oleh aplikasi. Dalam kebanyakan kasus, URL tersebut adalah http://localhost:5173/ (opens in a new tab).

  2. Anda dapat melihat kode sumber kontrak, versi modifikasi dari Greeter milik Hardhat, di penjelajah blok (opens in a new tab).

Panduan file

index.html

File ini adalah boilerplate HTML standar kecuali untuk baris ini, yang mengimpor file skrip.

1<script type="module" src="/src/main.tsx"></script>

src/main.tsx

Ekstensi file menunjukkan bahwa ini adalah komponen React (opens in a new tab) yang ditulis dalam TypeScript (opens in a new tab), sebuah ekstensi dari JavaScript yang mendukung pemeriksaan tipe (opens in a new tab). TypeScript dikompilasi menjadi JavaScript, sehingga kita dapat menggunakannya di sisi klien.

File ini sebagian besar dijelaskan jika Anda tertarik. Biasanya Anda tidak memodifikasi file ini, melainkan src/App.tsx dan file-file yang diimpornya.

1import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2import React from 'react'
3import ReactDOM from 'react-dom/client'
4import { WagmiProvider } from 'wagmi'

Impor kode pustaka yang kita butuhkan.

1import App from './App.tsx'

Impor komponen React yang mengimplementasikan aplikasi (lihat di bawah).

1import { config } from './wagmi.ts'

Impor konfigurasi wagmi (opens in a new tab), yang mencakup konfigurasi blockchain.

1const queryClient = new QueryClient()

Membuat instans baru dari manajer cache React Query (opens in a new tab). Objek ini akan menyimpan:

  • Panggilan RPC yang di-cache
  • Pembacaan kontrak
  • Status pengambilan ulang di latar belakang

Kita membutuhkan manajer cache karena wagmi v3 menggunakan React Query secara internal.

1ReactDOM.createRoot(document.getElementById('root')!).render(

Buat komponen React root. Parameter untuk render adalah JSX (opens in a new tab), bahasa ekstensi yang menggunakan HTML dan JavaScript/TypeScript. Tanda seru di sini memberi tahu komponen TypeScript: "Anda tidak tahu bahwa document.getElementById('root') akan menjadi parameter yang valid untuk ReactDOM.createRoot, tetapi jangan khawatir - saya adalah pengembangnya dan saya memberi tahu Anda bahwa itu akan ada".

1 <React.StrictMode>

Aplikasi ini akan berada di dalam komponen React.StrictMode (opens in a new tab). Komponen ini memberi tahu pustaka React untuk menyisipkan pemeriksaan debugging tambahan, yang berguna selama pengembangan.

1 <WagmiProvider config={config}>

Aplikasi ini juga berada di dalam komponen WagmiProvider (opens in a new tab). Pustaka wagmi (we are going to make it) (opens in a new tab) menghubungkan definisi UI React dengan pustaka viem (opens in a new tab) untuk menulis aplikasi terdesentralisasi Ethereum.

1 <QueryClientProvider client={queryClient}>

Dan terakhir, tambahkan penyedia React Query sehingga komponen aplikasi apa pun dapat menggunakan kueri yang di-cache.

1 <App />

Sekarang kita dapat memiliki komponen untuk aplikasi, yang sebenarnya mengimplementasikan UI. Tanda /> di akhir komponen memberi tahu React bahwa komponen ini tidak memiliki definisi apa pun di dalamnya, sesuai dengan standar XML.

1 </QueryClientProvider>
2 </WagmiProvider>
3 </React.StrictMode>,
4)

Tentu saja, kita harus menutup komponen lainnya.

src/App.tsx

1import {
2 useConnect,
3 useConnection,
4 useDisconnect,
5 useSwitchChain
6} from 'wagmi'
7
8import { useEffect } from 'react'
9import { Greeter } from './Greeter'
Tampilkan semua

Impor pustaka yang kita butuhkan, serta komponen Greeter.

1const SEPOLIA_CHAIN_ID = 11155111

ID chain Sepolia.

1function App() {

Ini adalah cara standar untuk membuat komponen React: tentukan fungsi yang dipanggil setiap kali perlu dirender. Fungsi ini biasanya berisi kode TypeScript atau JavaScript, diikuti oleh pernyataan return yang mengembalikan kode JSX.

1 const connection = useConnection()

Gunakan useConnection (opens in a new tab) untuk mendapatkan informasi terkait koneksi saat ini, seperti alamat dan chainId.

Berdasarkan konvensi, dalam React fungsi yang disebut use... adalah hooks (opens in a new tab). Fungsi-fungsi ini tidak hanya mengembalikan data ke komponen; mereka juga memastikan komponen tersebut dirender ulang (fungsi komponen dieksekusi lagi, dan outputnya menggantikan yang sebelumnya di HTML) ketika data tersebut berubah.

1 const { connectors, connect, status, error } = useConnect()

Gunakan useConnect (opens in a new tab) untuk mendapatkan informasi tentang koneksi dompet.

1 const { disconnect } = useDisconnect()

Hook ini (opens in a new tab) memberi kita fungsi untuk memutuskan koneksi dari dompet.

1 const { switchChain } = useSwitchChain()

Hook ini (opens in a new tab) memungkinkan kita beralih chain.

1 useEffect(() => {

Hook React useEffect (opens in a new tab) memungkinkan Anda menjalankan fungsi setiap kali nilai variabel berubah untuk menyinkronkan sistem eksternal.

1 if (connection.status === 'connected' &&
2 connection.chainId !== SEPOLIA_CHAIN_ID
3 ) {
4 switchChain({ chainId: SEPOLIA_CHAIN_ID })
5 }

Jika kita terhubung, tetapi tidak ke blockchain Sepolia, beralihlah ke Sepolia.

1 }, [connection.status, connection.chainId])

Jalankan ulang fungsi setiap kali status koneksi atau chainId koneksi berubah.

1 return (
2 <>

JSX dari komponen React harus mengembalikan satu komponen HTML. Ketika kita memiliki beberapa komponen dan tidak memerlukan wadah untuk membungkus semuanya, kita menggunakan komponen kosong (<> ... </>) untuk menggabungkannya menjadi satu komponen.

1 <h2>Connection</h2>
2 <div>
3 status: {connection.status}
4 <br />
5 addresses: {JSON.stringify(connection.addresses)}
6 <br />
7 chainId: {connection.chainId}
8</div>

Berikan informasi tentang koneksi saat ini. Di dalam JSX, {<expression>} berarti mengevaluasi ekspresi sebagai JavaScript.

1 {connection.status === 'connected' && (

Sintaks {<condition> && <value>} berarti "jika kondisinya true, evaluasi menjadi nilai tersebut; jika tidak, evaluasi menjadi false`".

Ini adalah cara standar untuk meletakkan pernyataan if di dalam JSX.

1 <div>
2 <Greeter />
3 <hr />

JSX mengikuti standar XML, yang lebih ketat daripada HTML. Jika sebuah tag tidak memiliki tag penutup yang sesuai, tag tersebut harus memiliki garis miring (/) di bagian akhir untuk mengakhirinya.

Di sini kita memiliki dua tag semacam itu, <Greeter /> (yang sebenarnya berisi kode HTML yang berkomunikasi dengan kontrak) dan <hr /> untuk garis horizontal (opens in a new tab).

1 <button type="button" onClick={disconnect}>
2 Disconnect
3 </button>
4</div>
5 )}

Jika pengguna mengklik tombol ini, panggil fungsi disconnect.

1 {connection.status !== 'connected' && (

Jika kita tidak terhubung, tampilkan opsi yang diperlukan untuk terhubung ke dompet.

1 <div>
2 <h2>Connect</h2>
3 {connectors.map((connector) => (

Di connectors kita memiliki daftar konektor. Kita menggunakan map (opens in a new tab) untuk mengubahnya menjadi daftar tombol JSX untuk ditampilkan.

1 <button
2 key={connector.uid}

Dalam JSX, tag "saudara" (tag yang diturunkan dari induk yang sama) perlu memiliki pengidentifikasi yang berbeda.

1 onClick={() => connect({ connector })}
2 type="button"
3 >
4 {connector.name}
5 </button>
6 ))}

Tombol-tombol konektor.

1 <div>{status}</div>
2 <div>{error?.message}</div>
3
4</div>
5 )}

Berikan informasi tambahan. Sintaks ekspresi <variable>?.<field> memberi tahu JavaScript bahwa jika variabel tersebut didefinisikan, evaluasi ke bidang tersebut. Jika variabel tidak didefinisikan, maka ekspresi ini dievaluasi menjadi undefined.

Ekspresi error.message, ketika tidak ada kesalahan, akan memunculkan pengecualian. Menggunakan error?.message memungkinkan kita menghindari masalah ini.

src/Greeter.tsx

File ini berisi sebagian besar fungsionalitas UI. Ini mencakup definisi yang biasanya berada di beberapa file, tetapi karena ini adalah tutorial, program ini dioptimalkan agar mudah dipahami pada kali pertama, daripada kinerja atau kemudahan pemeliharaan.

1import {
2 useState,
3 useEffect,
4 } from 'react'
5import { useChainId,
6 useAccount,
7 useReadContract,
8 useWriteContract,
9 useWatchContractEvent,
10 useSimulateContract
11 } from 'wagmi'
Tampilkan semua

Kita menggunakan fungsi-fungsi pustaka ini. Sekali lagi, fungsi-fungsi tersebut dijelaskan di bawah ini di mana mereka digunakan.

1import { AddressType } from 'abitype'

Pustaka abitype (opens in a new tab) memberi kita definisi TypeScript untuk berbagai tipe data Ethereum, seperti AddressType (opens in a new tab).

1let greeterABI = [
2 { "type": "function", "name": "greet", ... },
3 { "type": "function", "name": "setGreeting", ... },
4 { "type": "event", "name": "SetGreeting", ... },
5] as const // greeterABI // greeterABI

ABI untuk kontrak Greeter. Jika Anda mengembangkan kontrak dan UI pada saat yang sama, Anda biasanya akan meletakkannya di repositori yang sama dan menggunakan ABI yang dihasilkan oleh kompiler Solidity sebagai file dalam aplikasi Anda. Namun, ini tidak diperlukan di sini karena kontrak sudah dikembangkan dan tidak akan berubah.

Kita menggunakan as const (opens in a new tab) untuk memberi tahu TypeScript bahwa ini adalah konstanta yang sebenarnya. Biasanya, ketika Anda menentukan dalam JavaScript const x = {"a": 1}, Anda dapat mengubah nilai dalam x, Anda hanya tidak dapat menetapkan ulang nilainya.

1type AddressPerBlockchainType = {
2 [key: number]: AddressType
3}

TypeScript diketik dengan kuat (strongly typed). Kita menggunakan definisi ini untuk menentukan alamat di mana kontrak Greeter diterapkan di berbagai chain. Kuncinya adalah angka (chainId), dan nilainya adalah AddressType (sebuah alamat).

1const contractAddrs : AddressPerBlockchainType = {
2 // Sepolia // Sepolia
3 11155111: '0xC87506C66c7896366b9E988FE0aA5B6dDE77CFfA'
4}

Alamat kontrak di Sepolia (opens in a new tab).

Komponen Timer

Komponen Timer menunjukkan jumlah detik sejak waktu tertentu. Ini penting untuk tujuan kegunaan. Ketika pengguna melakukan sesuatu, mereka mengharapkan reaksi langsung. Dalam blockchain, ini sering kali tidak mungkin karena tidak ada yang terjadi sampai transaksi ditempatkan di dalam blok. Salah satu solusinya adalah dengan menunjukkan sudah berapa lama sejak pengguna melakukan tindakan tersebut, sehingga pengguna dapat memutuskan apakah waktu yang dibutuhkan masuk akal.

1type TimerProps = {
2 lastUpdate: Date
3}

Komponen Timer mengambil satu parameter, lastUpdate, yang merupakan waktu dari tindakan terakhir.

1const Timer = ({ lastUpdate }: TimerProps) => {
2 const [_, setNow] = useState(new Date())

Kita perlu memiliki status (variabel yang terikat pada komponen) dan memperbaruinya agar komponen berfungsi dengan benar. Namun kita tidak pernah perlu membacanya, jadi tidak perlu repot-repot membuat variabel.

1 useEffect(() => {
2 const id = setInterval(() => setNow(new Date()), 1000)
3 return () => clearInterval(id)
4 }, [])

Fungsi setInterval (opens in a new tab) memungkinkan kita menjadwalkan fungsi untuk berjalan secara berkala. Dalam hal ini, setiap detik. Fungsi ini memanggil setNow untuk memperbarui status, sehingga komponen Timer akan dirender ulang. Kita membungkus ini di dalam useEffect (opens in a new tab) dengan daftar dependensi kosong sehingga ini hanya akan terjadi sekali, bukan setiap kali komponen dirender.

1 const secondsSinceUpdate = Math.floor(
2 (Date.now() - lastUpdate.getTime()) / 1000
3 )
4
5 return (
6 <span>{secondsSinceUpdate} seconds ago</span>
7 )
8}

Hitung jumlah detik sejak pembaruan terakhir dan kembalikan nilainya.

Komponen Greeter
1const Greeter = () => {

Akhirnya, kita dapat mendefinisikan komponen tersebut.

1 const chainId = useChainId()
2 const account = useAccount()

Informasi tentang chain dan akun yang kita gunakan, berkat wagmi (opens in a new tab). Karena ini adalah hook (use...), komponen dirender ulang setiap kali informasi ini berubah.

1 const greeterAddr = chainId && contractAddrs[chainId]

Alamat kontrak Greeter, yang bernilai undefined jika kita tidak memiliki informasi chain, atau kita berada di chain tanpa kontrak tersebut.

1 const readResults = useReadContract({
2 address: greeterAddr,
3 abi: greeterABI,
4 functionName: "greet", // No arguments // Tidak ada argumen
5 })

Hook useReadContract (opens in a new tab) memanggil fungsi greet dari kontrak tersebut (opens in a new tab).

1 const [ currentGreeting, setCurrentGreeting ] =
2 useState("Please wait while we fetch the greeting from the blockchain...")
3 const [ newGreeting, setNewGreeting ] = useState("")

Hook useState (opens in a new tab) dari React memungkinkan kita menentukan variabel status, yang nilainya bertahan dari satu rendering komponen ke rendering lainnya. Nilai awalnya adalah parameter, dalam hal ini string kosong.

Hook useState mengembalikan daftar dengan dua nilai:

  1. Nilai saat ini dari variabel status.
  2. Fungsi untuk memodifikasi variabel status saat dibutuhkan. Karena ini adalah hook, setiap kali dipanggil, komponen akan dirender lagi.

Dalam hal ini, kita menggunakan variabel status untuk sapaan baru yang ingin ditetapkan oleh pengguna.

1 const [ lastSetterAddress, setLastSetterAddress ] = useState("")

Jika beberapa pengguna menggunakan kontrak yang sama pada saat yang sama, mereka mungkin menimpa sapaan satu sama lain. Ini akan terlihat oleh pengguna seolah-olah aplikasi tidak berfungsi. Jika aplikasi menunjukkan siapa yang terakhir menetapkan sapaan, pengguna akan tahu bahwa itu adalah orang lain dan bahwa aplikasi berfungsi dengan benar.

1 const [ status, setStatus ] = useState("")
2 const [ statusTime, setStatusTime ] = useState(new Date())

Pengguna suka melihat bahwa tindakan mereka memiliki efek langsung. Namun, di blockchain, hal ini tidak terjadi. Variabel status ini memungkinkan kita setidaknya menampilkan sesuatu kepada pengguna sehingga mereka akan tahu bahwa tindakan mereka sedang diproses.

1 useEffect(() => {
2 if (readResults.data) {
3 setCurrentGreeting(readResults.data)
4 setStatus("Greeting fetched from blockchain")
5 }
6 }, [readResults.data])

Jika readResults di atas mengubah data dan tidak diatur ke nilai salah (misalnya undefined), perbarui sapaan saat ini ke sapaan yang dibaca dari blockchain. Selain itu, perbarui statusnya.

1 useWatchContractEvent({
2 address: greeterAddr,
3 abi: greeterABI,
4 eventName: 'SetGreeting',
5 chainId,

Dengarkan peristiwa SetGreeting.

1 enabled: !!greeterAddr,

!!<value> berarti bahwa jika nilainya false, atau nilai yang dievaluasi sebagai salah, seperti undefined, 0, atau string kosong, ekspresi secara keseluruhan adalah false. Untuk nilai lainnya, itu adalah true. Ini adalah cara untuk mengonversi nilai menjadi boolean, karena jika tidak ada greeterAddr, kita tidak ingin mendengarkan peristiwa.

1 onLogs: logs => {
2 const greetingFromContract = logs[0].args.greeting
3 setCurrentGreeting(greetingFromContract)
4 setLastSetterAddress(logs[0].args.sender)
5 updateStatus("Greeting updated by event")
6 },
7 })

Ketika kita melihat log (yang terjadi ketika kita melihat peristiwa baru), itu berarti sapaan telah dimodifikasi. Dalam hal itu, kita dapat memperbarui currentGreeting dan lastSetterAddress ke nilai baru. Selain itu, kita ingin memperbarui tampilan status.

1 const updateStatus = (newStatus: string) => {
2 setStatus(newStatus)
3 setStatusTime(new Date())
4 }

Ketika kita memperbarui status, kita ingin melakukan dua hal:

  1. Memperbarui string status (status)
  2. Memperbarui waktu pembaruan status terakhir (statusTime) menjadi sekarang.
1 const greetingChange = (evt) =>
2 setNewGreeting(evt.target.value)

Ini adalah penangan peristiwa untuk perubahan pada bidang input sapaan baru. Kita bisa menentukan tipe parameter evt, tetapi TypeScript adalah bahasa dengan tipe opsional. Karena fungsi ini hanya dipanggil sekali, dalam penangan peristiwa HTML, saya rasa itu tidak perlu.

1 const { writeContractAsync } = useWriteContract()

Fungsi untuk menulis ke kontrak. Ini mirip dengan writeContracts (opens in a new tab), tetapi memungkinkan pembaruan status yang lebih baik.

1 const simulation = useSimulateContract({
2 address: greeterAddr,
3 abi: greeterABI,
4 functionName: 'setGreeting',
5 args: [newGreeting],
6 account: account.address
7 })

Ini adalah proses untuk mengirimkan transaksi blockchain dari perspektif klien:

  1. Kirim transaksi ke node di blockchain menggunakan eth_estimateGas (opens in a new tab).
  2. Tunggu respons dari node.
  3. Ketika respons diterima, minta pengguna untuk menandatangani transaksi melalui dompet. Langkah ini harus terjadi setelah respons node diterima karena pengguna akan ditunjukkan biaya gas dari transaksi sebelum menandatanganinya.
  4. Tunggu pengguna untuk menyetujui.
  5. Kirim transaksi lagi, kali ini menggunakan eth_sendRawTransaction (opens in a new tab).

Langkah 2 kemungkinan akan memakan waktu yang cukup lama, di mana pengguna mungkin bertanya-tanya apakah perintah mereka diterima oleh antarmuka pengguna dan mengapa mereka belum diminta untuk menandatangani transaksi. Hal itu menciptakan pengalaman pengguna (UX) yang buruk.

Salah satu solusinya adalah mengirimkan eth_estimateGas setiap kali parameter berubah. Kemudian, ketika pengguna benar-benar ingin mengirim transaksi (dalam hal ini dengan menekan Update greeting), biaya gas sudah diketahui, dan pengguna dapat segera melihat halaman dompet.

1 return (

Sekarang kita akhirnya dapat membuat HTML aktual untuk dikembalikan.

1 <>
2 <h2>Greeter</h2>
3 {currentGreeting}

Tampilkan sapaan saat ini.

1 {lastSetterAddress && (
2 <p>Last updated by {
3 lastSetterAddress === account.address ? "you" : lastSetterAddress
4 }</p>
5 )}

Jika kita tahu siapa yang terakhir menetapkan sapaan, tampilkan informasi tersebut. Greeter tidak melacak informasi ini, dan kita tidak ingin melihat kembali peristiwa SetGreeting, jadi kita hanya mendapatkannya setelah sapaan diubah saat kita sedang berjalan.

1 <hr />
2 <input type="text"
3 value={newGreeting}
4 onChange={greetingChange}
5 />
6 <br />

Ini adalah bidang teks input di mana pengguna dapat menetapkan sapaan baru. Setiap kali pengguna menekan tombol, kita memanggil greetingChange, yang memanggil setNewGreeting. Karena setNewGreeting berasal dari useState, ini menyebabkan komponen Greeter dirender ulang. Ini berarti bahwa:

  • Kita perlu menentukan value untuk menyimpan nilai sapaan baru, karena jika tidak, nilainya akan kembali ke default, yaitu string kosong.
  • simulation juga diperbarui setiap kali newGreeting berubah, yang berarti kita akan mendapatkan simulasi dengan sapaan yang benar. Ini bisa relevan karena biaya gas bergantung pada ukuran data panggilan, yang bergantung pada panjang string.
1 <button disabled={!simulation.data}

Hanya aktifkan tombol setelah kita memiliki informasi yang kita butuhkan untuk mengirim transaksi.

1 onClick={async () => {
2 updateStatus("Please confirm in wallet...")

Perbarui status. Pada titik ini, pengguna perlu mengonfirmasi di dompet.

1 await writeContractAsync(simulation.data.request)
2 updateStatus("Transaction sent, waiting for greeting to change...")
3 }}
4 >
5 Update greeting
6 </button>
7

writeContractAsync hanya mengembalikan nilai setelah transaksi benar-benar dikirim. Ini memungkinkan kita menunjukkan kepada pengguna sudah berapa lama transaksi menunggu untuk dimasukkan ke dalam blockchain.

1 <h4>Status: {status}</h4>
2 <p>Updated <Timer lastUpdate={statusTime} /> </p>
3 </>
4 )
5}

Tampilkan status dan sudah berapa lama sejak status tersebut diperbarui.

1export {Greeter}

Ekspor komponen.

src/wagmi.ts

Terakhir, berbagai definisi terkait wagmi ada di src/wagmi.ts. Saya tidak akan menjelaskan semuanya di sini, karena sebagian besar adalah boilerplate yang kemungkinan tidak perlu Anda ubah.

1import { http, webSocket, createConfig, fallback } from 'wagmi'
2import { sepolia } from 'wagmi/chains'
3import { injected } from 'wagmi/connectors'
4
5export const config = createConfig({
6 chains: [sepolia],

Konfigurasi wagmi mencakup chain yang didukung oleh aplikasi ini. Anda dapat melihat daftar chain yang tersedia (opens in a new tab).

1 connectors: [
2 injected(),
3 ],

Konektor ini (opens in a new tab) memungkinkan kita berkomunikasi dengan dompet yang diinstal di browser.

1 transports: {
2 [sepolia.id]: http()

Titik akhir HTTP default yang disertakan dengan Viem sudah cukup baik. Jika kita menginginkan URL yang berbeda, kita dapat menggunakan http("https:// hostname ") atau webSocket("wss:// hostname ").

1 },
2 multiInjectedProviderDiscovery: false,
3})

Menambahkan blockchain lain

Saat ini ada banyak solusi peningkatan L2 (opens in a new tab), dan Anda mungkin ingin mendukung beberapa yang belum didukung oleh viem. Untuk melakukannya, Anda memodifikasi src/wagmi.ts. Instruksi ini menjelaskan cara menambahkan Optimism Sepolia (opens in a new tab).

  1. Edit src/wagmi.ts

    A. Impor tipe defineChain dari viem.

    1import { defineChain } from 'viem'
1
2 B. Tambahkan definisi jaringan. Anda sebenarnya tidak perlu melakukan ini untuk Optimism Sepolia, [itu sudah ada di `viem`](https://github.com/wevm/viem/blob/main/src/chains/definitions/optimismSepolia.ts), tetapi dengan cara ini Anda belajar cara menambahkan blockchain yang tidak ada di `viem`.
3
4 ```ts
5 const optimismSepolia = defineChain({
6 id: 11_155_420,
7 name: 'OP Sepolia',
8 nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
9 rpcUrls: {
10 default: {
11 http: ['https://sepolia.optimism.io'],
12 webSocket: ['wss://optimism-sepolia.drpc.org'],
13 },
14 },
15 blockExplorers: {
16 default: {
17 name: 'Blockscout',
18 url: 'https://optimism-sepolia.blockscout.com',
19 apiUrl: 'https://optimism-sepolia.blockscout.com/api',
20 }
21 },
22 })
Tampilkan semua

C. Tambahkan chain baru ke panggilan createConfig.

1export const config = createConfig({
2 chains: [sepolia, optimismSepolia],
3 connectors: [
4 injected(),
5 ],
6 transports: {
7 [optimismSepolia.id]: http(),
8 [sepolia.id]: http()
9 },
10 multiInjectedProviderDiscovery: false,
11})
Tampilkan semua
  1. Edit src/App.tsx untuk mengomentari peralihan otomatis ke Sepolia. Pada sistem produksi, Anda mungkin akan menampilkan tombol dengan tautan ke masing-masing blockchain yang Anda dukung.

    1/* useEffect(() => {
    2 if (connection.status === 'connected' &&
    3 connection.chainId !== SEPOLIA_CHAIN_ID
    4 ) {
    5 switchChain({ chainId: SEPOLIA_CHAIN_ID })
    6 }
    7}, [connection.status, connection.chainId]) */
    8/*
    9useEffect(() => {
    10 if (connection.status === 'connected' &&
    11 connection.chainId !== SEPOLIA_CHAIN_ID
    12 ) {
    13 switchChain({ chainId: SEPOLIA_CHAIN_ID })
    14 }
    15}, [connection.status, connection.chainId])
    16*/
    Tampilkan semua
1
23. Edit `src/Greeter.tsx` untuk memastikan bahwa aplikasi mengetahui alamat untuk kontrak Anda di jaringan baru.
3
4 ```ts
5 const contractAddrs: AddressPerBlockchainType = {
6 // Optimism Sepolia // Optimism Sepolia
7 11155420: "0x4dd85791923E9294E934271522f63875EAe5806f",
8
9 // Sepolia // Sepolia
10 11155111: "0x7143d5c190F048C8d19fe325b748b081903E3BF0",
11 }
Tampilkan semua
  1. Di browser Anda.

    A. Buka ChainList (opens in a new tab) dan klik salah satu tombol di sisi kanan tabel untuk menambahkan chain ke dompet Anda.

    B. Di aplikasi, Disconnect (Putuskan koneksi) lalu sambungkan kembali untuk mengubah blockchain. Ada cara yang lebih baik untuk menangani ini, tetapi itu akan memerlukan perubahan aplikasi.

Kesimpulan

Tentu saja, Anda tidak terlalu peduli tentang menyediakan antarmuka pengguna untuk Greeter. Anda ingin membuat antarmuka pengguna untuk kontrak Anda sendiri. Untuk membuat aplikasi Anda sendiri, jalankan langkah-langkah ini:

  1. Tentukan untuk membuat aplikasi wagmi.

    1npm create wagmi
1
22. Ketik `y` untuk melanjutkan.
3
43. Beri nama aplikasi.
5
64. Pilih kerangka kerja **React**.
7
85. Pilih varian **Vite**.
9
10Sekarang pergilah dan buat kontrak Anda dapat digunakan oleh dunia luas.
11
12[Lihat di sini untuk karya saya yang lain](https://cryptodocguy.pro/).
Tampilkan semua

Pembaruan terakhir halaman: 3 Maret 2026

Apakah tutorial ini membantu?