Erstellen einer Benutzeroberfläche für Ihren Smart Contract
Sie haben eine Funktion gefunden, die wir im Ethereum-Ökosystem benötigen. Sie haben die Smart Contracts geschrieben, um sie zu implementieren, und vielleicht sogar einigen zugehörigen Code, der Off-Chain ausgeführt wird. Das ist großartig! Leider werden Sie ohne eine Benutzeroberfläche keine Benutzer haben, und das letzte Mal, als Sie eine Website geschrieben haben, benutzten die Leute Einwahlmodems und JavaScript war neu.
Dieser Artikel ist für Sie. Ich gehe davon aus, dass Sie programmieren können und vielleicht ein bisschen JavaScript und HTML kennen, aber dass Ihre Fähigkeiten im Bereich Benutzeroberflächen eingerostet und veraltet sind. Gemeinsam werden wir eine einfache moderne Anwendung durchgehen, damit Sie sehen, wie das heutzutage gemacht wird.
Warum dies wichtig ist
Theoretisch könnten Sie die Leute einfach Etherscan (opens in a new tab) oder Blockscout (opens in a new tab) verwenden lassen, um mit Ihren Verträgen zu interagieren. Das ist großartig für erfahrene Ethereans. Aber wir versuchen, einer weiteren Milliarde Menschen (opens in a new tab) zu dienen. Dies wird ohne eine großartige Benutzererfahrung nicht passieren, und eine freundliche Benutzeroberfläche ist ein großer Teil davon.
Greeter-Anwendung
Es gibt viel Theorie darüber, wie moderne UIs funktionieren, und viele gute Websites (opens in a new tab), die das erklären (opens in a new tab). Anstatt die hervorragende Arbeit dieser Websites zu wiederholen, gehe ich davon aus, dass Sie lieber durch Ausprobieren lernen und mit einer Anwendung beginnen, mit der Sie spielen können. Sie brauchen die Theorie trotzdem, um Dinge zu erledigen, und wir werden dazu kommen – wir gehen einfach Quelldatei für Quelldatei durch und besprechen die Dinge, wenn wir auf sie stoßen.
Installation
-
Die Anwendung verwendet das Sepolia (opens in a new tab)-Testnet. Falls erforderlich, holen Sie sich Sepolia-Test-ETH und fügen Sie Sepolia zu Ihrem Wallet hinzu (opens in a new tab).
-
Klonen Sie das GitHub-Repository und installieren Sie die erforderlichen Pakete.
1git clone https://github.com/qbzzt/260301-modern-ui-web3.git2cd 260301-modern-ui-web33npm install
1
23. Die Anwendung verwendet kostenlose Zugangspunkte, die Leistungseinschränkungen aufweisen. Wenn Sie einen Anbieter für [Blockchain-Knoten als Dienstleistung (Node as a service)](/developers/docs/nodes-and-clients/nodes-as-a-service/) verwenden möchten, ersetzen Sie die URLs in [`src/wagmi.ts`](#wagmi-ts).3
44. Starten Sie die Anwendung.5
6 ```sh7 npm run dev-
Navigieren Sie zu der von der Anwendung angezeigten URL. In den meisten Fällen ist das http://localhost:5173/ (opens in a new tab).
-
Sie können den Quellcode des Vertrags, eine modifizierte Version von Hardhats Greeter, in einer Blocksuchmaschine sehen (opens in a new tab).
Dateidurchlauf
index.html
Diese Datei ist ein Standard-HTML-Boilerplate, mit Ausnahme dieser Zeile, die die Skriptdatei importiert.
1<script type="module" src="/src/main.tsx"></script>src/main.tsx
Die Dateierweiterung zeigt an, dass es sich um eine React-Komponente (opens in a new tab) handelt, die in TypeScript (opens in a new tab) geschrieben wurde, einer Erweiterung von JavaScript, die Typprüfung (opens in a new tab) unterstützt. TypeScript wird zu JavaScript kompiliert, sodass wir es auf der Client-Seite verwenden können.
Diese Datei wird hauptsächlich für den Fall erklärt, dass Sie interessiert sind. Normalerweise ändern Sie diese Datei nicht, sondern src/App.tsx und die Dateien, die sie importiert.
1import { QueryClient, QueryClientProvider } from '@tanstack/react-query'2import React from 'react'3import ReactDOM from 'react-dom/client'4import { WagmiProvider } from 'wagmi'Importieren Sie den benötigten Bibliothekscode.
1import App from './App.tsx'Importieren Sie die React-Komponente, die die Anwendung implementiert (siehe unten).
1import { config } from './wagmi.ts'Importieren Sie die wagmi (opens in a new tab)-Konfiguration, die die Blockchain-Konfiguration enthält.
1const queryClient = new QueryClient()Erstellt eine neue Instanz des Cache-Managers von React Query (opens in a new tab). Dieses Objekt speichert:
- Zwischengespeicherte RPC-Aufrufe
- Vertragslesevorgänge
- Status des erneuten Abrufens im Hintergrund
Wir benötigen den Cache-Manager, da wagmi v3 intern React Query verwendet.
1ReactDOM.createRoot(document.getElementById('root')!).render(Erstellen Sie die React-Stammkomponente. Der Parameter für render ist JSX (opens in a new tab), eine Erweiterungssprache, die sowohl HTML als auch JavaScript/TypeScript verwendet. Das Ausrufezeichen hier sagt der TypeScript-Komponente: „Du weißt nicht, dass document.getElementById('root') ein gültiger Parameter für ReactDOM.createRoot sein wird, aber keine Sorge – ich bin der Entwickler und ich sage dir, dass es so sein wird“.
1 <React.StrictMode>Die Anwendung wird in eine React.StrictMode-Komponente (opens in a new tab) eingefügt. Diese Komponente weist die React-Bibliothek an, zusätzliche Debugging-Prüfungen einzufügen, was während der Entwicklung nützlich ist.
1 <WagmiProvider config={config}>Die Anwendung befindet sich auch in einer WagmiProvider-Komponente (opens in a new tab). Die wagmi-Bibliothek (we are going to make it) (opens in a new tab) verbindet die React-UI-Definitionen mit der viem-Bibliothek (opens in a new tab) zum Schreiben einer dezentralisierten Anwendung für Ethereum.
1 <QueryClientProvider client={queryClient}>Fügen Sie schließlich einen React Query-Anbieter hinzu, damit jede Anwendungskomponente zwischengespeicherte Abfragen verwenden kann.
1 <App />Jetzt können wir die Komponente für die Anwendung haben, die tatsächlich die Benutzeroberfläche implementiert. Das /> am Ende der Komponente teilt React mit, dass diese Komponente gemäß dem XML-Standard keine Definitionen in sich hat.
1 </QueryClientProvider>2 </WagmiProvider>3 </React.StrictMode>,4)Natürlich müssen wir die anderen Komponenten schließen.
src/App.tsx
1import {2 useConnect,3 useConnection,4 useDisconnect,5 useSwitchChain6} from 'wagmi'7
8import { useEffect } from 'react'9import { Greeter } from './Greeter'Importieren Sie die benötigten Bibliotheken sowie die Greeter-Komponente.
1const SEPOLIA_CHAIN_ID = 11155111Die Sepolia-Chain-ID.
1function App() {Dies ist der Standardweg, um eine React-Komponente zu erstellen: Definieren Sie eine Funktion, die aufgerufen wird, wann immer sie gerendert werden muss. Diese Funktion enthält typischerweise TypeScript- oder JavaScript-Code, gefolgt von einer return-Anweisung, die den JSX-Code zurückgibt.
1 const connection = useConnection()Verwenden Sie useConnection (opens in a new tab), um Informationen zur aktuellen Verbindung zu erhalten, wie z. B. die Adresse und die chainId.
Konventionsgemäß werden in React Funktionen, die use... heißen, als Hooks (opens in a new tab) bezeichnet. Diese Funktionen geben nicht nur Daten an die Komponente zurück; sie stellen auch sicher, dass sie neu gerendert wird (die Komponentenfunktion wird erneut ausgeführt und ihre Ausgabe ersetzt die vorherige im HTML), wenn sich diese Daten ändern.
1 const { connectors, connect, status, error } = useConnect()Verwenden Sie useConnect (opens in a new tab), um Informationen über die Wallet-Verbindung zu erhalten.
1 const { disconnect } = useDisconnect()Dieser Hook (opens in a new tab) gibt uns die Funktion, die Verbindung zum Wallet zu trennen.
1 const { switchChain } = useSwitchChain()Dieser Hook (opens in a new tab) lässt uns Chains wechseln.
1 useEffect(() => {Der React-Hook useEffect (opens in a new tab) ermöglicht es Ihnen, eine Funktion auszuführen, wann immer sich der Wert einer Variablen ändert, um ein externes System zu synchronisieren.
1 if (connection.status === 'connected' &&2 connection.chainId !== SEPOLIA_CHAIN_ID3 ) {4 switchChain({ chainId: SEPOLIA_CHAIN_ID })5 }Wenn wir verbunden sind, aber nicht mit der Sepolia-Blockchain, wechseln Sie zu Sepolia.
1 }, [connection.status, connection.chainId])Führen Sie die Funktion jedes Mal erneut aus, wenn sich entweder der Verbindungsstatus oder die chainId der Verbindung ändert.
1 return (2 <>Das JSX einer React-Komponente muss eine einzelne HTML-Komponente zurückgeben. Wenn wir mehrere Komponenten haben und keinen Container benötigen, um sie alle zu umschließen, verwenden wir eine leere Komponente (<> ... </>), um sie zu einer einzigen Komponente zu kombinieren.
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 9</div>Stellen Sie Informationen über die aktuelle Verbindung bereit. Innerhalb von JSX bedeutet {<expression>}, dass der Ausdruck als JavaScript ausgewertet wird.
1 {connection.status === 'connected' && (Die Syntax {<condition> && <value>} bedeutet: „Wenn die Bedingung true ist, werte zum Wert aus; wenn nicht, werte zu false aus“.
Dies ist der Standardweg, um if-Anweisungen in JSX einzufügen.
1 <div>2 <Greeter />3 <hr />JSX folgt dem XML-Standard, der strenger ist als HTML. Wenn ein Tag kein entsprechendes End-Tag hat, muss es am Ende einen Schrägstrich (/) haben, um es zu beenden.
Hier haben wir zwei solcher Tags, <Greeter /> (das tatsächlich den HTML-Code enthält, der mit dem Vertrag kommuniziert) und <hr /> für eine horizontale Linie (opens in a new tab).
1 <button type="button" onClick={disconnect}>2 Disconnect3 </button>4 5</div>6 )}Wenn der Benutzer auf diese Schaltfläche klickt, rufen Sie die Funktion disconnect auf.
1 {connection.status !== 'connected' && (Wenn wir nicht verbunden sind, zeigen Sie die erforderlichen Optionen an, um eine Verbindung zum Wallet herzustellen.
1 <div>2 <h2>Connect</h2>3 {connectors.map((connector) => (In connectors haben wir eine Liste von Konnektoren. Wir verwenden map (opens in a new tab), um sie in eine Liste von JSX-Schaltflächen zur Anzeige umzuwandeln.
1 <button2 key={connector.uid}In JSX ist es erforderlich, dass „Geschwister“-Tags (Tags, die vom selben übergeordneten Element abstammen) unterschiedliche Identifikatoren haben.
1 onClick={() => connect({ connector })}2 type="button"3 >4 {connector.name}5 </button>6 ))}Die Konnektor-Schaltflächen.
1 <div>{status}</div>2 <div>{error?.message}</div>3 4</div>5 )}Stellen Sie zusätzliche Informationen bereit. Die Ausdruckssyntax <variable>?.<field> teilt JavaScript mit, dass, wenn die Variable definiert ist, sie zu diesem Feld ausgewertet werden soll. Wenn die Variable nicht definiert ist, wird dieser Ausdruck zu undefined ausgewertet.
Der Ausdruck error.message würde, wenn kein Fehler vorliegt, eine Ausnahme auslösen. Die Verwendung von error?.message lässt uns dieses Problem vermeiden.
src/Greeter.tsx
Diese Datei enthält den Großteil der UI-Funktionalität. Sie enthält Definitionen, die sich normalerweise in mehreren Dateien befinden würden, aber da dies ein Tutorial ist, ist das Programm darauf optimiert, beim ersten Mal leicht verständlich zu sein, anstatt auf Leistung oder Wartungsfreundlichkeit.
1import {2 useState,3 useEffect,4 } from 'react'5import { useChainId,6 useAccount,7 useReadContract,8 useWriteContract,9 useWatchContractEvent,10 useSimulateContract11 } from 'wagmi'Wir verwenden diese Bibliotheksfunktionen. Auch diese werden unten erklärt, wo sie verwendet werden.
1import { AddressType } from 'abitype'Die abitype-Bibliothek (opens in a new tab) stellt uns TypeScript-Definitionen für verschiedene Ethereum-Datentypen zur Verfügung, wie z. B. 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 // greeterABIDie ABI für den Greeter-Vertrag.
Wenn Sie die Verträge und die Benutzeroberfläche gleichzeitig entwickeln, würden Sie sie normalerweise im selben Repository ablegen und die vom Solidity-Compiler generierte ABI als Datei in Ihrer Anwendung verwenden. Dies ist hier jedoch nicht erforderlich, da der Vertrag bereits entwickelt ist und sich nicht ändern wird.
Wir verwenden as const (opens in a new tab), um TypeScript mitzuteilen, dass dies eine echte Konstante ist. Normalerweise können Sie in JavaScript bei der Angabe von const x = {"a": 1} den Wert in x ändern, Sie können ihm nur nichts Neues zuweisen.
1type AddressPerBlockchainType = {2 [key: number]: AddressType3}TypeScript ist streng typisiert. Wir verwenden diese Definition, um die Adresse anzugeben, an der der Greeter-Vertrag über verschiedene Chains hinweg bereitgestellt wird. Der Schlüssel ist eine Zahl (die chainId), und der Wert ist ein AddressType (eine Adresse).
1const contractAddrs : AddressPerBlockchainType = {2 // Sepolia3 11155111: '0xC87506C66c7896366b9E988FE0aA5B6dDE77CFfA'4}Die Adresse des Vertrags auf Sepolia (opens in a new tab).
Timer-Komponente
Die Timer-Komponente zeigt die Anzahl der Sekunden seit einer bestimmten Zeit an. Dies ist für die Benutzerfreundlichkeit wichtig. Wenn Benutzer etwas tun, erwarten sie eine sofortige Reaktion. Bei Blockchains ist dies oft unmöglich, da nichts passiert, bis eine Transaktion in einem Block platziert wird. Eine Lösung besteht darin, anzuzeigen, wie lange es her ist, seit der Benutzer die Aktion ausgeführt hat, damit der Benutzer entscheiden kann, ob die benötigte Zeit angemessen ist.
1type TimerProps = {2 lastUpdate: Date3}Die Timer-Komponente nimmt einen Parameter entgegen, lastUpdate, der die Zeit der letzten Aktion darstellt.
1const Timer = ({ lastUpdate }: TimerProps) => {2 const [_, setNow] = useState(new Date())Wir müssen einen Zustand (eine an die Komponente gebundene Variable) haben und ihn aktualisieren, damit die Komponente korrekt funktioniert. Aber wir müssen ihn nie lesen, also machen wir uns nicht die Mühe, eine Variable zu erstellen.
1 useEffect(() => {2 const id = setInterval(() => setNow(new Date()), 1000)3 return () => clearInterval(id)4 }, [])Die Funktion setInterval (opens in a new tab) ermöglicht es uns, eine Funktion so zu planen, dass sie regelmäßig ausgeführt wird. In diesem Fall jede Sekunde. Die Funktion ruft setNow auf, um den Zustand zu aktualisieren, sodass die Timer-Komponente neu gerendert wird. Wir verpacken dies in useEffect (opens in a new tab) mit einer leeren Abhängigkeitsliste, damit es nur einmal passiert und nicht jedes Mal, wenn die Komponente gerendert wird.
1 const secondsSinceUpdate = Math.floor(2 (Date.now() - lastUpdate.getTime()) / 10003 )4
5 return (6 <span>{secondsSinceUpdate} seconds ago</span>7 )8}Berechnen Sie die Anzahl der Sekunden seit der letzten Aktualisierung und geben Sie sie zurück.
Greeter-Komponente
1const Greeter = () => {Schließlich können wir die Komponente definieren.
1 const chainId = useChainId()2 const account = useAccount()Informationen über die Chain und das Konto, das wir verwenden, mit freundlicher Unterstützung von wagmi (opens in a new tab). Da dies ein Hook (use...) ist, wird die Komponente neu gerendert, wann immer sich diese Informationen ändern.
1 const greeterAddr = chainId && contractAddrs[chainId] Die Adresse des Greeter-Vertrags, die undefined ist, wenn wir keine Chain-Informationen haben oder uns auf einer Chain ohne diesen Vertrag befinden.
1 const readResults = useReadContract({2 address: greeterAddr,3 abi: greeterABI,4 functionName: "greet", // Keine Argumente5 })Der useReadContract-Hook (opens in a new tab) ruft die greet-Funktion des Vertrags (opens in a new tab) auf.
1 const [ currentGreeting, setCurrentGreeting ] = 2 useState("Please wait while we fetch the greeting from the blockchain...")3 const [ newGreeting, setNewGreeting ] = useState("")Der useState-Hook (opens in a new tab) von React ermöglicht es uns, eine Zustandsvariable anzugeben, deren Wert von einem Rendering der Komponente zum nächsten erhalten bleibt. Der Anfangswert ist der Parameter, in diesem Fall die leere Zeichenfolge.
Der useState-Hook gibt eine Liste mit zwei Werten zurück:
- Den aktuellen Wert der Zustandsvariablen.
- Eine Funktion, um die Zustandsvariable bei Bedarf zu ändern. Da dies ein Hook ist, wird die Komponente bei jedem Aufruf erneut gerendert.
In diesem Fall verwenden wir eine Zustandsvariable für die neue Begrüßung, die der Benutzer festlegen möchte.
1 const [ lastSetterAddress, setLastSetterAddress ] = useState("")Wenn mehrere Benutzer denselben Vertrag gleichzeitig verwenden, könnten sie die Begrüßungen der anderen überschreiben. Dies würde für die Benutzer so aussehen, als ob die Anwendung nicht richtig funktioniert. Wenn die Anwendung anzeigt, wer die Begrüßung zuletzt festgelegt hat, weiß der Benutzer, dass es jemand anderes war und dass die Anwendung ordnungsgemäß funktioniert.
1 const [ status, setStatus ] = useState("")2 const [ statusTime, setStatusTime ] = useState(new Date())Benutzer sehen gerne, dass ihre Aktionen eine sofortige Wirkung haben. Auf einer Blockchain ist dies jedoch nicht der Fall. Diese Zustandsvariablen ermöglichen es uns, den Benutzern zumindest etwas anzuzeigen, damit sie wissen, dass ihre Aktion im Gange ist.
1 useEffect(() => {2 if (readResults.data) {3 setCurrentGreeting(readResults.data)4 setStatus("Greeting fetched from blockchain")5 }6 }, [readResults.data])Wenn readResults oben die Daten ändert und sie nicht auf einen falschen Wert (z. B. undefined) gesetzt sind, aktualisieren Sie die aktuelle Begrüßung auf die von der Blockchain gelesene. Aktualisieren Sie außerdem den Status.
1 useWatchContractEvent({2 address: greeterAddr,3 abi: greeterABI,4 eventName: 'SetGreeting',5 chainId,Hören Sie auf SetGreeting-Ereignisse.
1 enabled: !!greeterAddr,!!<value> bedeutet, dass, wenn der Wert false ist oder ein Wert, der als falsch ausgewertet wird, wie undefined, 0 oder eine leere Zeichenfolge, der Ausdruck insgesamt false ist. Für jeden anderen Wert ist er true. Es ist eine Möglichkeit, Werte in Boolesche Werte umzuwandeln, denn wenn es keine greeterAddr gibt, möchten wir nicht auf Ereignisse hören.
1 onLogs: logs => {2 const greetingFromContract = logs[0].args.greeting3 setCurrentGreeting(greetingFromContract)4 setLastSetterAddress(logs[0].args.sender)5 updateStatus("Greeting updated by event")6 },7 })Wenn wir Protokolle sehen (was passiert, wenn wir ein neues Ereignis sehen), bedeutet dies, dass die Begrüßung geändert wurde. In diesem Fall können wir currentGreeting und lastSetterAddress auf die neuen Werte aktualisieren. Außerdem möchten wir die Statusanzeige aktualisieren.
1 const updateStatus = (newStatus: string) => {2 setStatus(newStatus)3 setStatusTime(new Date())4 }Wenn wir den Status aktualisieren, möchten wir zwei Dinge tun:
- Die Statuszeichenfolge aktualisieren (
status) - Die Zeit der letzten Statusaktualisierung (
statusTime) auf jetzt aktualisieren.
1 const greetingChange = (evt) =>2 setNewGreeting(evt.target.value)Dies ist der Event-Handler für Änderungen am Eingabefeld für die neue Begrüßung. Wir könnten den Typ des Parameters evt angeben, aber TypeScript ist eine Sprache mit optionaler Typisierung. Da diese Funktion nur einmal in einem HTML-Event-Handler aufgerufen wird, halte ich dies nicht für erforderlich.
1 const { writeContractAsync } = useWriteContract()Die Funktion zum Schreiben in einen Vertrag. Sie ähnelt writeContracts (opens in a new tab), ermöglicht aber bessere Statusaktualisierungen.
1 const simulation = useSimulateContract({2 address: greeterAddr,3 abi: greeterABI,4 functionName: 'setGreeting',5 args: [newGreeting],6 account: account.address 7 })Dies ist der Prozess zum Einreichen einer Blockchain-Transaktion aus der Perspektive der Anwendung:
- Senden Sie die Transaktion an einen Blockchain-Knoten in der Blockchain unter Verwendung von
eth_estimateGas(opens in a new tab). - Warten Sie auf eine Antwort vom Blockchain-Knoten.
- Wenn die Antwort empfangen wird, bitten Sie den Benutzer, die Transaktion über das Wallet zu signieren. Dieser Schritt muss erfolgen, nachdem die Antwort des Blockchain-Knotens empfangen wurde, da dem Benutzer vor dem Signieren die Gaskosten der Transaktion angezeigt werden.
- Warten Sie auf die Genehmigung des Benutzers.
- Senden Sie die Transaktion erneut, diesmal unter Verwendung von
eth_sendRawTransaction(opens in a new tab).
Schritt 2 wird wahrscheinlich eine spürbare Zeit in Anspruch nehmen, in der sich Benutzer möglicherweise fragen, ob ihr Befehl von der Benutzeroberfläche empfangen wurde und warum sie noch nicht aufgefordert werden, die Transaktion zu signieren. Das führt zu einer schlechten Benutzererfahrung (UX).
Eine Lösung besteht darin, eth_estimateGas jedes Mal zu senden, wenn sich ein Parameter ändert. Wenn der Benutzer dann tatsächlich die Transaktion senden möchte (in diesem Fall durch Drücken von Update greeting), sind die Gaskosten bekannt und der Benutzer kann die Wallet-Seite sofort sehen.
1 return (Jetzt können wir endlich das eigentliche HTML erstellen, das zurückgegeben werden soll.
1 <>2 <h2>Greeter</h2>3 {currentGreeting}Zeigen Sie die aktuelle Begrüßung an.
1 {lastSetterAddress && (2 <p>Last updated by {3 lastSetterAddress === account.address ? "you" : lastSetterAddress4 }</p>5 )}Wenn wir wissen, wer die Begrüßung zuletzt festgelegt hat, zeigen Sie diese Informationen an. Greeter verfolgt diese Informationen nicht, und wir möchten nicht nach SetGreeting-Ereignissen in der Vergangenheit suchen, daher erhalten wir sie nur, wenn die Begrüßung geändert wird, während wir ausgeführt werden.
1 <hr /> 2 <input type="text"3 value={newGreeting}4 onChange={greetingChange}5 /> 6 <br />Dies ist das Eingabetextfeld, in dem der Benutzer eine neue Begrüßung festlegen kann. Jedes Mal, wenn der Benutzer eine Taste drückt, rufen wir greetingChange auf, was setNewGreeting aufruft. Da setNewGreeting von useState stammt, wird die Greeter-Komponente neu gerendert. Das bedeutet, dass:
- Wir
valueangeben müssen, um den Wert der neuen Begrüßung beizubehalten, da er sonst auf den Standardwert, die leere Zeichenfolge, zurückgesetzt würde. simulationebenfalls jedes Mal aktualisiert wird, wenn sichnewGreetingändert, was bedeutet, dass wir eine Simulation mit der korrekten Begrüßung erhalten. Dies könnte relevant sein, da die Gaskosten von der Größe der Aufrufdaten abhängen, die wiederum von der Länge der Zeichenfolge abhängen.
1 <button disabled={!simulation.data}Aktivieren Sie die Schaltfläche erst, wenn wir die Informationen haben, die wir zum Senden der Transaktion benötigen.
1 onClick={async () => {2 updateStatus("Please confirm in wallet...")Aktualisieren Sie den Status. An diesem Punkt muss der Benutzer im Wallet bestätigen.
1 await writeContractAsync(simulation.data.request)2 updateStatus("Transaction sent, waiting for greeting to change...")3 }}4 >5 Update greeting6 </button>7
writeContractAsync kehrt erst zurück, nachdem die Transaktion tatsächlich gesendet wurde. Dadurch können wir dem Benutzer anzeigen, wie lange die Transaktion darauf gewartet hat, in die Blockchain aufgenommen zu werden.
1 <h4>Status: {status}</h4>2 <p>Updated <Timer lastUpdate={statusTime} /> </p>3 </>4 )5}Zeigen Sie den Status an und wie lange es her ist, seit er aktualisiert wurde.
1export {Greeter}Exportieren Sie die Komponente.
src/wagmi.ts
Schließlich befinden sich verschiedene Definitionen im Zusammenhang mit wagmi in src/wagmi.ts. Ich werde hier nicht alles erklären, da das meiste davon Boilerplate ist, das Sie wahrscheinlich nicht ändern müssen.
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],Die wagmi-Konfiguration enthält die von dieser Anwendung unterstützten Chains. Sie können die Liste der verfügbaren Chains (opens in a new tab) einsehen.
1 connectors: [2 injected(),3 ],Dieser Konnektor (opens in a new tab) ermöglicht es uns, mit einem im Browser installierten Wallet zu kommunizieren.
1 transports: {2 [sepolia.id]: http()Der Standard-HTTP-Endpunkt, der mit Viem geliefert wird, ist gut genug. Wenn wir eine andere URL möchten, können wir http("https:// hostname ") oder webSocket("wss:// hostname ") verwenden.
1 },2 multiInjectedProviderDiscovery: false,3})Hinzufügen einer weiteren Blockchain
Heutzutage gibt es viele L2-Skalierungslösungen (opens in a new tab), und Sie möchten vielleicht einige unterstützen, die viem noch nicht unterstützt. Dazu ändern Sie src/wagmi.ts. Diese Anweisungen erklären, wie Sie Optimism Sepolia (opens in a new tab) hinzufügen.
-
Bearbeiten Sie
src/wagmi.tsA. Importieren Sie den Typ
defineChainaus viem.1import { defineChain } from 'viem'
1
2 B. Fügen Sie die Netzwerkdefinition hinzu. Sie müssen dies für Optimism Sepolia nicht wirklich tun, [es ist bereits in `viem` enthalten](https://github.com/wevm/viem/blob/main/src/chains/definitions/optimismSepolia.ts), aber auf diese Weise lernen Sie, wie Sie eine Blockchain hinzufügen, die nicht in `viem` enthalten ist.3
4 ```ts5 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 })C. Fügen Sie die neue Chain zum Aufruf von createConfig hinzu.
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})-
Bearbeiten Sie
src/App.tsx, um den automatischen Wechsel zu Sepolia auszukommentieren. Auf einem Produktionssystem würden Sie wahrscheinlich Schaltflächen mit Links zu jeder der von Ihnen unterstützten Blockchains anzeigen.1/* useEffect(() => {2 if (connection.status === 'connected' &&3 connection.chainId !== SEPOLIA_CHAIN_ID4 ) {5 switchChain({ chainId: SEPOLIA_CHAIN_ID })6 }7}, [connection.status, connection.chainId]) */89101112131415
1
23. Bearbeiten Sie `src/Greeter.tsx`, um sicherzustellen, dass die Anwendung die Adresse für Ihre Verträge im neuen Netzwerk kennt.3
4 ```ts5 const contractAddrs: AddressPerBlockchainType = {6 // Optimism Sepolia7 11155420: "0x4dd85791923E9294E934271522f63875EAe5806f",8
9 // Sepolia10 11155111: "0x7143d5c190F048C8d19fe325b748b081903E3BF0",11 }-
In Ihrem Browser.
A. Navigieren Sie zu ChainList (opens in a new tab) und klicken Sie auf eine der Schaltflächen auf der rechten Seite der Tabelle, um die Chain zu Ihrem Wallet hinzuzufügen.
B. Klicken Sie in der Anwendung auf Disconnect (Trennen) und stellen Sie dann die Verbindung wieder her, um die Blockchain zu wechseln. Es gibt elegantere Möglichkeiten, dies zu handhaben, aber sie würden Änderungen an der Anwendung erfordern.
Fazit
Natürlich ist es Ihnen nicht wirklich wichtig, eine Benutzeroberfläche für Greeter bereitzustellen. Sie möchten eine Benutzeroberfläche für Ihre eigenen Verträge erstellen. Um Ihre eigene Anwendung zu erstellen, führen Sie diese Schritte aus:
-
Geben Sie an, dass eine wagmi-Anwendung erstellt werden soll.
1npm create wagmi
1
22. Geben Sie `y` ein, um fortzufahren.3
43. Benennen Sie die Anwendung.5
64. Wählen Sie das **React**-Framework aus.7
85. Wählen Sie die **Vite**-Variante aus.9
10Gehen Sie nun hin und machen Sie Ihre Verträge für die weite Welt nutzbar.11
12[Sehen Sie hier für weitere meiner Arbeiten](https://cryptodocguy.pro/).Letzte Aktualisierung der Seite: 3. März 2026