Vytvoření uživatelského rozhraní pro váš kontrakt
Našli jste funkci, kterou v ekosystému Etherea potřebujeme. Napsali jste chytré kontrakty k její implementaci a možná i nějaký související kód, který běží offchain. To je skvělé! Bohužel bez uživatelského rozhraní nebudete mít žádné uživatele a naposledy, když jste psali webové stránky, lidé používali vytáčené modemy a JavaScript byl novinkou.
Tento článek je pro vás. Předpokládám, že umíte programovat a možná znáte trochu JavaScript a HTML, ale vaše dovednosti v oblasti uživatelského rozhraní jsou zrezivělé a zastaralé. Společně si projdeme jednoduchou moderní aplikaci, abyste viděli, jak se to dělá dnes.
Proč je to důležité
Teoreticky byste mohli nechat lidi používat Etherscan (otevře se v nové kartě) nebo Blockscout (otevře se v nové kartě) k interakci s vašimi kontrakty. To je skvělé pro zkušené uživatele Etherea. My se ale snažíme obsloužit další miliardu lidí (otevře se v nové kartě). To se nestane bez skvělého uživatelského zážitku a přívětivé uživatelské rozhraní je jeho velkou součástí.
Aplikace Greeter
Za tím, jak funguje moderní uživatelské rozhraní, je spousta teorie a existuje mnoho dobrých stránek (otevře se v nové kartě), které to vysvětlují (otevře se v nové kartě). Místo opakování skvělé práce, kterou tyto stránky odvedly, budu předpokládat, že se raději učíte praxí, a začneme s aplikací, se kterou si můžete hrát. K dokončení věcí stále potřebujete teorii a dostaneme se k ní – jen půjdeme zdrojový soubor po zdrojovém souboru a budeme diskutovat o věcech, jak na ně narazíme.
Instalace
-
Aplikace používá testovací síť Sepolia (otevře se v nové kartě). Pokud je to nutné, získejte testovací ETH na síti Sepolia a přidejte si síť Sepolia do své peněženky (otevře se v nové kartě).
-
Naklonujte repozitář na GitHubu a nainstalujte potřebné balíčky.
git clone https://github.com/qbzzt/260301-modern-ui-web3.git cd 260301-modern-ui-web3 npm install -
Aplikace používá bezplatné přístupové body, které mají výkonnostní omezení. Pokud chcete použít poskytovatele uzlu jako služby (Node as a service), nahraďte adresy URL v
src/wagmi.ts. -
Spusťte aplikaci.
npm run dev -
Přejděte na adresu URL zobrazenou aplikací. Ve většině případů je to http://localhost:5173/ (otevře se v nové kartě).
-
Zdrojový kód kontraktu, upravenou verzi Greeteru z Hardhat, si můžete prohlédnout v prohlížeči blockchainu (otevře se v nové kartě).
Průvodce soubory
index.html
Tento soubor je standardní HTML šablona s výjimkou tohoto řádku, který importuje soubor skriptu.
<script type="module" src="/src/main.tsx"></script>
src/main.tsx
Přípona souboru naznačuje, že se jedná o komponentu React (otevře se v nové kartě) napsanou v TypeScript (otevře se v nové kartě), což je rozšíření JavaScriptu, které podporuje typovou kontrolu (otevře se v nové kartě). TypeScript se kompiluje do JavaScriptu, takže jej můžeme použít na straně klienta.
Tento soubor je vysvětlen hlavně pro případ, že by vás to zajímalo. Obvykle tento soubor neupravujete, ale upravujete src/App.tsx a soubory, které importuje.
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React from 'react'
import ReactDOM from 'react-dom/client'
import { WagmiProvider } from 'wagmi'
Importujte kód knihovny, který potřebujeme.
import App from './App.tsx'
Importujte komponentu React, která implementuje aplikaci (viz níže).
import { config } from './wagmi.ts'
Importujte konfiguraci Wagmi (otevře se v nové kartě), která zahrnuje konfiguraci blockchainu.
const queryClient = new QueryClient()
Vytvoří novou instanci správce mezipaměti React Query (otevře se v nové kartě). Tento objekt bude ukládat:
- RPC volání uložená v mezipaměti
- Čtení z kontraktu
- Stav opětovného načítání na pozadí
Správce mezipaměti potřebujeme, protože Wagmi v3 interně používá React Query.
ReactDOM.createRoot(document.getElementById('root')!).render(
Vytvořte kořenovou komponentu React. Parametrem pro render je JSX (otevře se v nové kartě), rozšiřující jazyk, který používá jak HTML, tak JavaScript/TypeScript. Vykřičník zde říká komponentě TypeScript: „nevíš, že document.getElementById('root') bude platným parametrem pro ReactDOM.createRoot, ale neboj se – já jsem vývojář a říkám ti, že bude“.
<React.StrictMode>
Aplikace se vkládá do komponenty React.StrictMode (otevře se v nové kartě). Tato komponenta říká knihovně React, aby vložila další ladicí kontroly, což je užitečné během vývoje.
<WagmiProvider config={config}>
Aplikace je také uvnitř komponenty WagmiProvider (otevře se v nové kartě). Knihovna Wagmi (kterou vytvoříme) (otevře se v nové kartě) propojuje definice uživatelského rozhraní React s knihovnou Viem (otevře se v nové kartě) pro psaní decentralizované aplikace (dapp) na Ethereu.
<QueryClientProvider client={queryClient}>
A nakonec přidejte poskytovatele React Query, aby jakákoli komponenta aplikace mohla používat dotazy uložené v mezipaměti.
<App />
Nyní můžeme mít komponentu pro aplikaci, která skutečně implementuje uživatelské rozhraní. /> na konci komponenty říká Reactu, že tato komponenta uvnitř nemá žádné definice, v souladu se standardem XML.
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)
Samozřejmě musíme uzavřít ostatní komponenty.
src/App.tsx
import {
useConnect,
useConnection,
useDisconnect,
useSwitchChain
} from 'wagmi'
import { useEffect } from 'react'
import { Greeter } from './Greeter'
Importujte knihovny, které potřebujeme, a také komponentu Greeter.
const SEPOLIA_CHAIN_ID = 11155111
ID řetězce Sepolia.
function App() {
Toto je standardní způsob vytvoření komponenty React: definujte funkci, která se volá, kdykoli je potřeba ji vykreslit. Tato funkce obvykle obsahuje kód v TypeScript nebo JavaScript, následovaný příkazem return, který vrací kód JSX.
const connection = useConnection()
Použijte useConnection (otevře se v nové kartě) k získání informací souvisejících s aktuálním připojením, jako je adresa a chainId.
Podle konvence se v Reactu funkce nazývané use... označují jako hooky (otevře se v nové kartě). Tyto funkce nejenže vracejí data komponentě; také zajišťují její opětovné vykreslení (funkce komponenty se znovu spustí a její výstup nahradí ten předchozí v HTML), když se tato data změní.
const { connectors, connect, status, error } = useConnect()
Použijte useConnect (otevře se v nové kartě) k získání informací o připojení peněženky.
const { disconnect } = useDisconnect()
Tento hook (otevře se v nové kartě) nám poskytuje funkci pro odpojení od peněženky.
const { switchChain } = useSwitchChain()
Tento hook (otevře se v nové kartě) nám umožňuje přepínat řetězce.
useEffect(() => {
Hook Reactu useEffect (otevře se v nové kartě) vám umožňuje spustit funkci, kdykoli se změní hodnota proměnné, za účelem synchronizace externího systému.
if (connection.status === 'connected' &&
connection.chainId !== SEPOLIA_CHAIN_ID
) {
switchChain({ chainId: SEPOLIA_CHAIN_ID })
}
Pokud jsme připojeni, ale ne k blockchainu Sepolia, přepněte na síť Sepolia.
}, [connection.status, connection.chainId])
Znovu spusťte funkci pokaždé, když se změní stav připojení nebo chainId připojení.
return (
<>
JSX komponenty React musí vracet jedinou HTML komponentu. Když máme více komponent a nepotřebujeme kontejner, který by je všechny obalil, použijeme prázdnou komponentu (<> ... </>), abychom je spojili do jedné komponenty.
<h2>Connection</h2>
<div>
status: {connection.status}
<br />
addresses: {JSON.stringify(connection.addresses)}
<br />
chainId: {connection.chainId}
</div>
Poskytněte informace o aktuálním připojení. V rámci JSX znamená {<expression>} vyhodnotit výraz jako JavaScript.
{connection.status === 'connected' && (
Syntaxe {<condition> && <value>} means "if the condition is true, evaluate to the value; if it isn't, evaluate to false`“.
Toto je standardní způsob, jak vkládat podmínky if do JSX.
<div>
<Greeter />
<hr />
JSX se řídí standardem XML, který je přísnější než HTML. Pokud značka nemá odpovídající koncovou značku, musí mít na konci lomítko (/), které ji ukončí.
Zde máme dvě takové značky, <Greeter /> (která ve skutečnosti obsahuje HTML kód komunikující s kontraktem) a <hr /> pro vodorovnou čáru (otevře se v nové kartě).
<button type="button" onClick={disconnect}>
Disconnect
</button>
</div>
)}
Pokud uživatel klikne na toto tlačítko, zavolejte funkci disconnect.
{connection.status !== 'connected' && (
Pokud nejsme připojeni, zobrazte potřebné možnosti pro připojení k peněžence.
<div>
<h2>Connect</h2>
{connectors.map((connector) => (
V connectors máme seznam konektorů. Použijeme map (otevře se v nové kartě), abychom jej přeměnili na seznam JSX tlačítek k zobrazení.
<button
key={connector.uid}
V JSX je nutné, aby „sourozenecké“ značky (značky, které pocházejí od stejného rodiče) měly různé identifikátory.
onClick={() => connect({ connector })}
type="button"
>
{connector.name}
</button>
))}
Tlačítka konektorů.
<div>{status}</div>
<div>{error?.message}</div>
</div>
)}
Poskytněte další informace. Syntaxe výrazu <variable>?.<field> říká JavaScriptu, že pokud je proměnná definována, má se vyhodnotit na toto pole. Pokud proměnná definována není, pak se tento výraz vyhodnotí jako undefined.
Výraz error.message by v případě, že nedojde k žádné chybě, vyvolal výjimku. Použití error?.message nám umožňuje tomuto problému předejít.
src/Greeter.tsx
Tento soubor obsahuje většinu funkcí uživatelského rozhraní. Zahrnuje definice, které by normálně byly ve více souborech, ale protože se jedná o tutoriál, je program optimalizován tak, aby byl snadno pochopitelný napoprvé, spíše než pro výkon nebo snadnou údržbu.
import {
useState,
useEffect,
} from 'react'
import { useChainId,
useAccount,
useReadContract,
useWriteContract,
useWatchContractEvent,
useSimulateContract
} from 'wagmi'
Používáme tyto funkce knihovny. Opět jsou vysvětleny níže tam, kde se používají.
import { AddressType } from 'abitype'
Knihovna abitype (otevře se v nové kartě) nám poskytuje definice TypeScript pro různé datové typy Etherea, jako je AddressType (otevře se v nové kartě).
let greeterABI = [
{ "type": "function", "name": "greet", ... },
{ "type": "function", "name": "setGreeting", ... },
{ "type": "event", "name": "SetGreeting", ... },
] as const // greeterABI
ABI pro kontrakt Greeter.
Pokud vyvíjíte kontrakty a uživatelské rozhraní současně, normálně byste je umístili do stejného repozitáře a použili ABI vygenerované kompilátorem Solidity jako soubor ve vaší aplikaci. Zde to však není nutné, protože kontrakt je již vyvinut a nebude se měnit.
Používáme as const (otevře se v nové kartě), abychom TypeScriptu řekli, že se jedná o skutečnou konstantu. Normálně, když v JavaScriptu zadáte const x = {"a": 1}, můžete změnit hodnotu v x, jen do ní nemůžete přiřazovat.
type AddressPerBlockchainType = {
[key: number]: AddressType
}
TypeScript je silně typovaný. Tuto definici používáme k určení adresy, kde je kontrakt Greeter nasazen napříč různými řetězci. Klíčem je číslo (chainId) a hodnotou je AddressType (adresa).
const contractAddrs : AddressPerBlockchainType = {
// Sepolia
11155111: '0xC87506C66c7896366b9E988FE0aA5B6dDE77CFfA'
}
Adresa kontraktu na síti Sepolia (otevře se v nové kartě).
Komponenta Timer
Komponenta Timer zobrazuje počet sekund od daného času. To je důležité z hlediska použitelnosti. Když uživatelé něco udělají, očekávají okamžitou reakci. V blockchainech je to často nemožné, protože se nic nestane, dokud není transakce umístěna do bloku. Jedním z řešení je ukázat, jak dlouho to je, co uživatel akci provedl, aby se mohl rozhodnout, zda je požadovaný čas přiměřený.
type TimerProps = {
lastUpdate: Date
}
Komponenta Timer přijímá jeden parametr, lastUpdate, což je čas poslední akce.
const Timer = ({ lastUpdate }: TimerProps) => {
const [_, setNow] = useState(new Date())
Potřebujeme mít stav (proměnnou vázanou na komponentu) a aktualizovat jej, aby komponenta fungovala správně. Nikdy jej ale nepotřebujeme číst, takže se neobtěžujte vytvářet proměnnou.
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000)
return () => clearInterval(id)
}, [])
Funkce setInterval (otevře se v nové kartě) nám umožňuje naplánovat pravidelné spouštění funkce. V tomto případě každou sekundu. Funkce volá setNow pro aktualizaci stavu, takže komponenta Timer bude znovu vykreslena. Zabalíme to do useEffect (otevře se v nové kartě) s prázdným seznamem závislostí, takže se to stane jen jednou, a ne při každém vykreslení komponenty.
const secondsSinceUpdate = Math.floor(
(Date.now() - lastUpdate.getTime()) / 1000
)
return (
<span>{secondsSinceUpdate} seconds ago</span>
)
}
Vypočítejte počet sekund od poslední aktualizace a vraťte jej.
Komponenta Greeter
const Greeter = () => {
Konečně se dostáváme k definici komponenty.
const chainId = useChainId()
const account = useAccount()
Informace o řetězci a účtu, který používáme, díky Wagmi (otevře se v nové kartě). Protože se jedná o hook (use...), komponenta se znovu vykreslí, kdykoli se tyto informace změní.
const greeterAddr = chainId && contractAddrs[chainId]
Adresa kontraktu Greeter, která je undefined, pokud nemáme informace o řetězci nebo jsme na řetězci bez tohoto kontraktu.
const readResults = useReadContract({
address: greeterAddr,
abi: greeterABI,
functionName: "greet", // Žádné argumenty
})
Hook useReadContract (otevře se v nové kartě) volá funkci greet kontraktu (otevře se v nové kartě).
const [ currentGreeting, setCurrentGreeting ] =
useState("Please wait while we fetch the greeting from the blockchain...")
const [ newGreeting, setNewGreeting ] = useState("")
Hook Reactu useState (otevře se v nové kartě) nám umožňuje specifikovat stavovou proměnnou, jejíž hodnota přetrvává z jednoho vykreslení komponenty do druhého. Počáteční hodnota je parametr, v tomto případě prázdný řetězec.
Hook useState vrací seznam se dvěma hodnotami:
- Aktuální hodnota stavové proměnné.
- Funkce pro úpravu stavové proměnné v případě potřeby. Protože se jedná o hook, při každém jeho zavolání se komponenta znovu vykreslí.
V tomto případě používáme stavovou proměnnou pro nový pozdrav, který chce uživatel nastavit.
const [ lastSetterAddress, setLastSetterAddress ] = useState("")
Pokud více uživatelů používá stejný kontrakt současně, mohou si navzájem přepisovat pozdravy. Uživatelům by to připadalo, jako by aplikace nefungovala správně. Pokud aplikace ukáže, kdo naposledy nastavil pozdrav, uživatel bude vědět, že to byl někdo jiný a že aplikace funguje správně.
const [ status, setStatus ] = useState("")
const [ statusTime, setStatusTime ] = useState(new Date())
Uživatelé rádi vidí, že jejich akce mají okamžitý účinek. Na blockchainu tomu tak ale není. Tyto stavové proměnné nám umožňují alespoň něco uživatelům zobrazit, aby věděli, že jejich akce probíhá.
useEffect(() => {
if (readResults.data) {
setCurrentGreeting(readResults.data)
setStatus("Greeting fetched from blockchain")
}
}, [readResults.data])
Pokud výše uvedený readResults změní data a není nastaven na nepravdivou hodnotu (například undefined), aktualizujte aktuální pozdrav na ten přečtený z blockchainu. Také aktualizujte stav.
useWatchContractEvent({
address: greeterAddr,
abi: greeterABI,
eventName: 'SetGreeting',
chainId,
Poslouchejte události SetGreeting.
enabled: !!greeterAddr,
!!<value> znamená, že pokud je hodnota false nebo hodnota, která se vyhodnotí jako nepravda, jako je undefined, 0 nebo prázdný řetězec, celkový výraz je false. Pro jakoukoli jinou hodnotu je to true. Je to způsob, jak převést hodnoty na booleovské hodnoty, protože pokud neexistuje žádná greeterAddr, nechceme poslouchat události.
onLogs: logs => {
const greetingFromContract = logs[0].args.greeting
setCurrentGreeting(greetingFromContract)
setLastSetterAddress(logs[0].args.sender)
updateStatus("Greeting updated by event")
},
})
Když vidíme protokoly (což se stane, když vidíme novou událost), znamená to, že pozdrav byl upraven. V takovém případě můžeme aktualizovat currentGreeting a lastSetterAddress na nové hodnoty. Také chceme aktualizovat zobrazení stavu.
const updateStatus = (newStatus: string) => {
setStatus(newStatus)
setStatusTime(new Date())
}
Když aktualizujeme stav, chceme udělat dvě věci:
- Aktualizovat řetězec stavu (
status) - Aktualizovat čas poslední aktualizace stavu (
statusTime) na aktuální čas.
const greetingChange = (evt) =>
setNewGreeting(evt.target.value)
Toto je obslužná rutina události pro změny ve vstupním poli nového pozdravu. Mohli bychom specifikovat typ parametru evt, ale TypeScript je jazyk s volitelným typováním. Vzhledem k tomu, že se tato funkce volá pouze jednou, v obslužné rutině události HTML, nemyslím si, že je to nutné.
const { writeContractAsync } = useWriteContract()
Funkce pro zápis do kontraktu. Je podobná writeContracts (otevře se v nové kartě), ale umožňuje lepší aktualizace stavu.
const simulation = useSimulateContract({
address: greeterAddr,
abi: greeterABI,
functionName: 'setGreeting',
args: [newGreeting],
account: account.address
})
Toto je proces odeslání blockchainové transakce z pohledu klienta:
- Odešlete transakci do uzlu v blockchainu pomocí
eth_estimateGas(otevře se v nové kartě). - Počkejte na odpověď od uzlu.
- Po obdržení odpovědi požádejte uživatele, aby transakci podepsal prostřednictvím peněženky. Tento krok se musí stát po obdržení odpovědi uzlu, protože uživateli se před podpisem zobrazí náklady na plyn (gas) transakce.
- Počkejte na schválení uživatelem.
- Odešlete transakci znovu, tentokrát pomocí
eth_sendRawTransaction(otevře se v nové kartě).
Krok 2 pravděpodobně zabere znatelné množství času, během kterého se uživatelé mohou divit, zda byl jejich příkaz přijat uživatelským rozhraním a proč ještě nejsou požádáni o podepsání transakce. To vytváří špatný uživatelský zážitek (UX).
Jedním z řešení je odeslat eth_estimateGas pokaždé, když se změní parametr. Pak, když uživatel skutečně chce odeslat transakci (v tomto případě stisknutím Update greeting), jsou náklady na plyn známé a uživatel může okamžitě vidět stránku peněženky.
return (
Nyní můžeme konečně vytvořit skutečné HTML, které se má vrátit.
<>
<h2>Greeter</h2>
{currentGreeting}
Zobrazte aktuální pozdrav.
{lastSetterAddress && (
<p>Last updated by {
lastSetterAddress === account.address ? "you" : lastSetterAddress
}</p>
)}
Pokud víme, kdo naposledy nastavil pozdrav, zobrazte tuto informaci. Greeter tyto informace neuchovává a my se nechceme ohlížet zpět na události SetGreeting, takže je získáme pouze tehdy, když se pozdrav změní během našeho běhu.
<hr />
<input type="text"
value={newGreeting}
onChange={greetingChange}
/>
<br />
Toto je vstupní textové pole, kde může uživatel nastavit nový pozdrav. Pokaždé, když uživatel stiskne klávesu, zavoláme greetingChange, což zavolá setNewGreeting. Protože setNewGreeting pochází z useState, způsobí to opětovné vykreslení komponenty Greeter. To znamená, že:
- Musíme specifikovat
value, abychom zachovali hodnotu nového pozdravu, protože jinak by se vrátil zpět na výchozí hodnotu, prázdný řetězec. simulationse také aktualizuje pokaždé, když se změnínewGreeting, což znamená, že získáme simulaci se správným pozdravem. To by mohlo být relevantní, protože náklady na plyn závisí na velikosti dat volání, která závisí na délce řetězce.
<button disabled={!simulation.data}
Tlačítko povolte až poté, co budeme mít informace potřebné k odeslání transakce.
onClick={async () => {
updateStatus("Please confirm in wallet...")
Aktualizujte stav. V tomto okamžiku musí uživatel potvrdit akci v peněžence.
await writeContractAsync(simulation.data.request)
updateStatus("Transaction sent, waiting for greeting to change...")
}}
>
Update greeting
</button>
writeContractAsync se vrátí až po skutečném odeslání transakce. To nám umožňuje ukázat uživateli, jak dlouho transakce čeká na zahrnutí do blockchainu.
<h4>Status: {status}</h4>
<p>Updated <Timer lastUpdate={statusTime} /> </p>
</>
)
}
Zobrazte stav a jak dlouho to je od jeho aktualizace.
export {Greeter}
Exportujte komponentu.
src/wagmi.ts
Nakonec jsou v src/wagmi.ts různé definice související s Wagmi. Nebudu zde vysvětlovat vše, protože většina z toho je šablona, kterou pravděpodobně nebudete muset měnit.
import { http, webSocket, createConfig, fallback } from 'wagmi'
import { sepolia } from 'wagmi/chains'
import { injected } from 'wagmi/connectors'
export const config = createConfig({
chains: [sepolia],
Konfigurace Wagmi zahrnuje řetězce podporované touto aplikací. Můžete se podívat na seznam dostupných řetězců (otevře se v nové kartě).
connectors: [
injected(),
],
Tento konektor (otevře se v nové kartě) nám umožňuje komunikovat s peněženkou nainstalovanou v prohlížeči.
transports: {
[sepolia.id]: http()
Výchozí HTTP koncový bod, který je dodáván s Viem, je dostatečně dobrý. Pokud chceme jinou adresu URL, můžeme použít http("https:// hostname ") nebo webSocket("wss:// hostname ").
},
multiInjectedProviderDiscovery: false,
})
Přidání dalšího blockchainu
V dnešní době existuje spousta řešení škálování na 2. vrstvě (L2) (otevře se v nové kartě) a možná budete chtít podpořit některá, která Viem ještě nepodporuje. Chcete-li to provést, upravte src/wagmi.ts. Tyto pokyny vysvětlují, jak přidat Optimism Sepolia (otevře se v nové kartě).
-
Upravte
src/wagmi.tsA. Importujte typ
defineChainz Viem.import { defineChain } from 'viem'B. Přidejte definici sítě. Pro Optimism Sepolia to ve skutečnosti dělat nemusíte, už je v
viem(otevře se v nové kartě), ale tímto způsobem se naučíte, jak přidat blockchain, který vviemnení.const optimismSepolia = defineChain({ id: 11_155_420, name: 'OP Sepolia', nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 }, rpcUrls: { default: { http: ['https://sepolia.optimism.io'], webSocket: ['wss://optimism-sepolia.drpc.org'], }, }, blockExplorers: { default: { name: 'Blockscout', url: 'https://optimism-sepolia.blockscout.com', apiUrl: 'https://optimism-sepolia.blockscout.com/api', } }, })C. Přidejte nový řetězec do volání
createConfig.export const config = createConfig({ chains: [sepolia, optimismSepolia], connectors: [ injected(), ], transports: { [optimismSepolia.id]: http(), [sepolia.id]: http() }, multiInjectedProviderDiscovery: false, }) -
Upravte
src/App.tsxtak, abyste zakomentovali automatické přepnutí na síť Sepolia. V produkčním systému byste pravděpodobně zobrazili tlačítka s odkazy na každý z blockchainů, které podporujete./* useEffect(() => { if (connection.status === 'connected' && connection.chainId !== SEPOLIA_CHAIN_ID ) { switchChain({ chainId: SEPOLIA_CHAIN_ID }) } }, [connection.status, connection.chainId]) */ -
Upravte
src/Greeter.tsx, abyste zajistili, že aplikace zná adresu pro vaše kontrakty na nové síti.const contractAddrs: AddressPerBlockchainType = { // Optimism Sepolia 11155420: "0x4dd85791923E9294E934271522f63875EAe5806f", // Sepolia 11155111: "0x7143d5c190F048C8d19fe325b748b081903E3BF0", } -
Ve vašem prohlížeči.
A. Přejděte na ChainList (otevře se v nové kartě) a klikněte na jedno z tlačítek na pravé straně tabulky, abyste přidali řetězec do své peněženky.
B. V aplikaci klikněte na Disconnect (Odpojit) a poté se znovu připojte, abyste změnili blockchain. Existují hezčí způsoby, jak to vyřešit, ale vyžadovaly by změny v aplikaci.
Závěr
Samozřejmě vás ve skutečnosti nezajímá poskytování uživatelského rozhraní pro Greeter. Chcete vytvořit uživatelské rozhraní pro své vlastní kontrakty. Chcete-li vytvořit vlastní aplikaci, proveďte tyto kroky:
-
Zadejte vytvoření aplikace Wagmi.
npm create wagmi -
Napište
ypro pokračování. -
Pojmenujte aplikaci.
-
Vyberte framework React.
-
Vyberte variantu Vite.
Nyní běžte a udělejte své kontrakty použitelné pro celý svět.