Sözleşmeniz için bir kullanıcı arayüzü oluşturma
Ethereum ekosisteminde ihtiyacımız olan bir özellik buldunuz. Bunu uygulamak için akıllı sözleşmeleri ve hatta belki zincir dışı çalışan bazı ilgili kodları yazdınız. Bu harika! Ne yazık ki, bir kullanıcı arayüzü olmadan hiç kullanıcınız olmayacak ve en son bir web sitesi yazdığınızda insanlar çevirmeli (dial-up) modemler kullanıyordu ve JavaScript henüz yeniydi.
Bu makale sizin için. Programlamayı ve belki biraz JavaScript ile HTML bildiğinizi, ancak kullanıcı arayüzü becerilerinizin paslanmış ve modası geçmiş olduğunu varsayıyorum. Birlikte basit ve modern bir uygulamanın üzerinden geçeceğiz, böylece günümüzde işlerin nasıl yapıldığını göreceksiniz.
Bu neden önemli
Teorik olarak, insanların sözleşmelerinizle etkileşime girmesi için sadece Etherscan (opens in a new tab) veya Blockscout (opens in a new tab) kullanmasını sağlayabilirsiniz. Bu, deneyimli Ethereum kullanıcıları için harikadır. Ancak biz bir milyar insana daha (opens in a new tab) hizmet etmeye çalışıyoruz. Bu, harika bir kullanıcı deneyimi olmadan gerçekleşmeyecektir ve kullanıcı dostu bir arayüz bunun büyük bir parçasıdır.
Greeter uygulaması
Modern kullanıcı arayüzünün nasıl çalıştığının arkasında pek çok teori vardır ve bunu açıklayan (opens in a new tab) pek çok iyi site (opens in a new tab) bulunur. Bu sitelerin yaptığı iyi işi tekrarlamak yerine, yaparak öğrenmeyi tercih ettiğinizi varsayacağım ve oynayabileceğiniz bir uygulamayla başlayacağım. İşleri halletmek için hâlâ teoriye ihtiyacınız var ve buna da değineceğiz; sadece kaynak dosyaları tek tek inceleyecek ve konulara geldikçe onları tartışacağız.
Kurulum
-
Uygulama Sepolia (opens in a new tab) test ağını kullanır. Gerekirse, Sepolia test ETH'si alın ve Sepolia'yı cüzdanınıza ekleyin (opens in a new tab).
-
GitHub deposunu klonlayın ve gerekli paketleri kurun.
git clone https://github.com/qbzzt/260301-modern-ui-web3.git cd 260301-modern-ui-web3 npm install -
Uygulama, performans sınırlamaları olan ücretsiz erişim noktalarını kullanır. Bir Hizmet olarak düğüm (Node as a service) sağlayıcısı kullanmak istiyorsanız,
src/wagmi.tsiçindeki URL'leri değiştirin. -
Uygulamayı başlatın.
npm run dev -
Uygulama tarafından gösterilen URL'ye gidin. Çoğu durumda bu http://localhost:5173/ (opens in a new tab) adresidir.
-
Hardhat'in Greeter'ının değiştirilmiş bir versiyonu olan sözleşme kaynak kodunu bir blokzincir gezgininde (opens in a new tab) görebilirsiniz.
Dosya incelemesi
index.html
Bu dosya, betik dosyasını içe aktaran bu satır dışında standart bir HTML şablonudur.
<script type="module" src="/src/main.tsx"></script>
src/main.tsx
Dosya uzantısı, bunun tür denetimini (opens in a new tab) destekleyen bir JavaScript uzantısı olan TypeScript (opens in a new tab) ile yazılmış bir React bileşeni (opens in a new tab) olduğunu gösterir. TypeScript, JavaScript'e derlenir, böylece onu istemci tarafında kullanabiliriz.
Bu dosya çoğunlukla ilgilenmeniz ihtimaline karşı açıklanmıştır. Genellikle bu dosyayı değil, src/App.tsx dosyasını ve onun içe aktardığı dosyaları değiştirirsiniz.
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React from 'react'
import ReactDOM from 'react-dom/client'
import { WagmiProvider } from 'wagmi'
İhtiyacımız olan kütüphane kodunu içe aktarın.
import App from './App.tsx'
Uygulamayı uygulayan React bileşenini içe aktarın (aşağıya bakın).
import { config } from './wagmi.ts'
Blokzincir yapılandırmasını içeren wagmi (opens in a new tab) yapılandırmasını içe aktarın.
const queryClient = new QueryClient()
React Query'nin (opens in a new tab) önbellek yöneticisinin yeni bir örneğini oluşturur. Bu nesne şunları depolayacaktır:
- Önbelleğe alınmış RPC çağrıları
- Sözleşme okumaları
- Arka planda yeniden getirme durumu
Önbellek yöneticisine ihtiyacımız var çünkü wagmi v3 dahili olarak React Query kullanır.
ReactDOM.createRoot(document.getElementById('root')!).render(
Kök React bileşenini oluşturun. render parametresi, hem HTML hem de JavaScript/TypeScript kullanan bir uzantı dili olan JSX (opens in a new tab)'tir. Buradaki ünlem işareti TypeScript bileşenine şunu söyler: "document.getElementById('root') öğesinin ReactDOM.createRoot için geçerli bir parametre olacağını bilmiyorsun, ama endişelenme - ben geliştiriciyim ve sana olacağını söylüyorum".
<React.StrictMode>
Uygulama bir React.StrictMode bileşeninin (opens in a new tab) içine giriyor. Bu bileşen, React kütüphanesine geliştirme sırasında yararlı olan ek hata ayıklama kontrolleri eklemesini söyler.
<WagmiProvider config={config}>
Uygulama aynı zamanda bir WagmiProvider bileşeninin (opens in a new tab) içindedir. Wagmi (bunu biz yapacağız) kütüphanesi (opens in a new tab), bir Ethereum merkeziyetsiz uygulaması yazmak için React kullanıcı arayüzü tanımlarını viem kütüphanesi (opens in a new tab) ile bağlar.
<QueryClientProvider client={queryClient}>
Ve son olarak, herhangi bir uygulama bileşeninin önbelleğe alınmış sorguları kullanabilmesi için bir React Query sağlayıcısı ekleyin.
<App />
Artık uygulama için kullanıcı arayüzünü fiilen uygulayan bileşene sahip olabiliriz. Bileşenin sonundaki />, XML standardına göre React'e bu bileşenin içinde herhangi bir tanım olmadığını söyler.
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)
Elbette diğer bileşenleri de kapatmalıyız.
src/App.tsx
import {
useConnect,
useConnection,
useDisconnect,
useSwitchChain
} from 'wagmi'
import { useEffect } from 'react'
import { Greeter } from './Greeter'
İhtiyacımız olan kütüphaneleri ve Greeter bileşenini içe aktarın.
const SEPOLIA_CHAIN_ID = 11155111
Sepolia zincir kimliği (chain ID).
function App() {
Bu, bir React bileşeni oluşturmanın standart yoludur: işlenmesi (render) gerektiğinde çağrılan bir işlev tanımlayın. Bu işlev tipik olarak TypeScript veya JavaScript kodu içerir ve ardından JSX kodunu döndüren bir return ifadesi gelir.
const connection = useConnection()
Adres ve chainId gibi mevcut bağlantıyla ilgili bilgileri almak için useConnection (opens in a new tab) kullanın.
Geleneksel olarak, React'te use... olarak adlandırılan işlevler kancalardır (hooks) (opens in a new tab). Bu işlevler yalnızca bileşene veri döndürmekle kalmaz; aynı zamanda bu veri değiştiğinde bileşenin yeniden işlenmesini (bileşen işlevi tekrar yürütülür ve çıktısı HTML'deki eskisinin yerini alır) sağlarlar.
const { connectors, connect, status, error } = useConnect()
Cüzdan bağlantısı hakkında bilgi almak için useConnect (opens in a new tab) kullanın.
const { disconnect } = useDisconnect()
Bu kanca (opens in a new tab) bize cüzdan bağlantısını kesme işlevini verir.
const { switchChain } = useSwitchChain()
Bu kanca (opens in a new tab) zincirleri değiştirmemizi sağlar.
useEffect(() => {
React kancası useEffect (opens in a new tab), harici bir sistemi senkronize etmek için bir değişkenin değeri her değiştiğinde bir işlev çalıştırmanıza olanak tanır.
if (connection.status === 'connected' &&
connection.chainId !== SEPOLIA_CHAIN_ID
) {
switchChain({ chainId: SEPOLIA_CHAIN_ID })
}
Bağlıysak ancak Sepolia blokzincirine bağlı değilsek, Sepolia'ya geçin.
}, [connection.status, connection.chainId])
Bağlantı durumu veya bağlantı chainId'si her değiştiğinde işlevi yeniden çalıştırın.
return (
<>
Bir React bileşeninin JSX'i tek bir HTML bileşeni döndürmek zorundadır. Birden fazla bileşenimiz olduğunda ve hepsini sarmak için bir kapsayıcıya ihtiyaç duymadığımızda, bunları tek bir bileşende birleştirmek için boş bir bileşen (<> ... </>) kullanırız.
<h2>Connection</h2>
<div>
status: {connection.status}
<br />
addresses: {JSON.stringify(connection.addresses)}
<br />
chainId: {connection.chainId}
</div>
Mevcut bağlantı hakkında bilgi sağlayın. JSX içinde {<expression>}, ifadeyi JavaScript olarak değerlendirmek anlamına gelir.
{connection.status === 'connected' && (
Sözdizimi {<condition> && <value>} means "if the condition is true, evaluate to the value; if it isn't, evaluate to false`".
Bu, JSX içine if ifadeleri koymanın standart yoludur.
<div>
<Greeter />
<hr />
JSX, HTML'den daha katı olan XML standardını izler. Bir etiketin karşılık gelen bir bitiş etiketi yoksa, onu sonlandırmak için sonunda bir eğik çizgi (/) olmalıdır.
Burada bu tür iki etiketimiz var: <Greeter /> (aslında sözleşmeyle konuşan HTML kodunu içerir) ve yatay bir çizgi için <hr /> (opens in a new tab).
<button type="button" onClick={disconnect}>
Disconnect
</button>
</div>
)}
Kullanıcı bu düğmeye tıklarsa, disconnect işlevini çağırın.
{connection.status !== 'connected' && (
Eğer bağlı değilsek, cüzdana bağlanmak için gerekli seçenekleri gösterin.
<div>
<h2>Connect</h2>
{connectors.map((connector) => (
connectors içinde bir bağlayıcı listemiz var. Bunu görüntülenecek bir JSX düğmeleri listesine dönüştürmek için map (opens in a new tab) kullanırız.
<button
key={connector.uid}
JSX'te "kardeş" etiketlerin (aynı ebeveynden gelen etiketler) farklı tanımlayıcılara sahip olması gerekir.
onClick={() => connect({ connector })}
type="button"
>
{connector.name}
</button>
))}
Bağlayıcı düğmeleri.
<div>{status}</div>
<div>{error?.message}</div>
</div>
)}
Ek bilgi sağlayın. <variable>?.<field> ifade sözdizimi JavaScript'e, değişken tanımlanmışsa o alanı değerlendirmesini söyler. Değişken tanımlanmamışsa, bu ifade undefined olarak değerlendirilir.
error.message ifadesi, hata olmadığında bir istisna (exception) oluşturur. error?.message kullanmak bu sorundan kaçınmamızı sağlar.
src/Greeter.tsx
Bu dosya, kullanıcı arayüzü işlevselliğinin çoğunu içerir. Normalde birden fazla dosyada bulunacak tanımları içerir, ancak bu bir eğitim olduğu için program, performans veya bakım kolaylığından ziyade ilk seferde anlaşılması kolay olacak şekilde optimize edilmiştir.
import {
useState,
useEffect,
} from 'react'
import { useChainId,
useAccount,
useReadContract,
useWriteContract,
useWatchContractEvent,
useSimulateContract
} from 'wagmi'
Bu kütüphane işlevlerini kullanıyoruz. Yine, kullanıldıkları yerlerde aşağıda açıklanmışlardır.
import { AddressType } from 'abitype'
abitype kütüphanesi (opens in a new tab), bize AddressType (opens in a new tab) gibi çeşitli Ethereum veri türleri için TypeScript tanımları sağlar.
let greeterABI = [
{ "type": "function", "name": "greet", ... },
{ "type": "function", "name": "setGreeting", ... },
{ "type": "event", "name": "SetGreeting", ... },
] as const // greeterABI
Greeter sözleşmesi için ABI.
Sözleşmeleri ve kullanıcı arayüzünü aynı anda geliştiriyorsanız, normalde bunları aynı depoya koyar ve Solidity derleyicisi tarafından oluşturulan ABI'yi uygulamanızda bir dosya olarak kullanırsınız. Ancak, sözleşme zaten geliştirilmiş olduğu ve değişmeyeceği için burada buna gerek yoktur.
TypeScript'e bunun gerçek bir sabit olduğunu söylemek için as const (opens in a new tab) kullanırız. Normalde, JavaScript'te const x = {"a": 1} belirttiğinizde, x içindeki değeri değiştirebilirsiniz, sadece ona atama yapamazsınız.
type AddressPerBlockchainType = {
[key: number]: AddressType
}
TypeScript güçlü bir şekilde türlendirilmiştir (strongly typed). Greeter sözleşmesinin farklı zincirlerde dağıtıldığı adresi belirtmek için bu tanımı kullanırız. Anahtar bir sayıdır (chainId) ve değer bir AddressType'dir (bir adres).
const contractAddrs : AddressPerBlockchainType = {
// Sepolia
11155111: '0xC87506C66c7896366b9E988FE0aA5B6dDE77CFfA'
}
Sepolia (opens in a new tab) üzerindeki sözleşmenin adresi.
Timer bileşeni
Timer bileşeni, belirli bir zamandan bu yana geçen saniye sayısını gösterir. Bu, kullanılabilirlik amaçları için önemlidir. Kullanıcılar bir şey yaptıklarında anında bir tepki beklerler. Blokzincirlerde bu genellikle imkansızdır çünkü bir işlem bir bloğa yerleştirilene kadar hiçbir şey olmaz. Çözümlerden biri, kullanıcının işlemi gerçekleştirmesinden bu yana ne kadar zaman geçtiğini göstermektir, böylece kullanıcı gereken sürenin makul olup olmadığına karar verebilir.
type TimerProps = {
lastUpdate: Date
}
Timer bileşeni, son eylemin zamanı olan lastUpdate adında bir parametre alır.
const Timer = ({ lastUpdate }: TimerProps) => {
const [_, setNow] = useState(new Date())
Bileşenin doğru çalışması için bir duruma (bileşene bağlı bir değişken) sahip olmamız ve onu güncellememiz gerekir. Ancak onu okumaya asla ihtiyacımız olmaz, bu yüzden bir değişken oluşturmakla uğraşmayın.
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000)
return () => clearInterval(id)
}, [])
setInterval (opens in a new tab) işlevi, bir işlevi periyodik olarak çalışacak şekilde zamanlamamızı sağlar. Bu durumda, her saniye. İşlev, durumu güncellemek için setNow çağrısı yapar, böylece Timer bileşeni yeniden işlenir. Bunu, bileşen her işlendiğinde değil, yalnızca bir kez gerçekleşmesi için boş bir bağımlılık listesiyle useEffect (opens in a new tab) içine sararız.
const secondsSinceUpdate = Math.floor(
(Date.now() - lastUpdate.getTime()) / 1000
)
return (
<span>{secondsSinceUpdate} seconds ago</span>
)
}
Son güncellemeden bu yana geçen saniye sayısını hesaplayın ve döndürün.
Greeter bileşeni
const Greeter = () => {
Son olarak, bileşeni tanımlamaya geçiyoruz.
const chainId = useChainId()
const account = useAccount()
Wagmi (opens in a new tab) sayesinde kullandığımız zincir ve hesap hakkında bilgiler. Bu bir kanca (use...) olduğu için, bu bilgi her değiştiğinde bileşen yeniden işlenir.
const greeterAddr = chainId && contractAddrs[chainId]
Zincir bilgimiz yoksa veya bu sözleşmenin olmadığı bir zincirdeysek undefined olan Greeter sözleşmesinin adresi.
const readResults = useReadContract({
address: greeterAddr,
abi: greeterABI,
functionName: "greet", // Argüman yok
})
useReadContract kancası (opens in a new tab), sözleşmenin (opens in a new tab) greet işlevini çağırır.
const [ currentGreeting, setCurrentGreeting ] =
useState("Please wait while we fetch the greeting from the blockchain...")
const [ newGreeting, setNewGreeting ] = useState("")
React'in useState kancası (opens in a new tab), değeri bileşenin bir işlemesinden diğerine kalıcı olan bir durum değişkeni belirlememizi sağlar. Başlangıç değeri parametredir, bu durumda boş dizedir.
useState kancası iki değerli bir liste döndürür:
- Durum değişkeninin mevcut değeri.
- Gerektiğinde durum değişkenini değiştirmek için bir işlev. Bu bir kanca olduğu için, her çağrıldığında bileşen yeniden işlenir.
Bu durumda, kullanıcının ayarlamak istediği yeni selamlama için bir durum değişkeni kullanıyoruz.
const [ lastSetterAddress, setLastSetterAddress ] = useState("")
Birden fazla kullanıcı aynı sözleşmeyi aynı anda kullanıyorsa, birbirlerinin selamlamalarının üzerine yazabilirler. Bu, kullanıcılara uygulama arızalıymış gibi görünecektir. Uygulama selamlamayı en son kimin ayarladığını gösterirse, kullanıcı bunun başka biri olduğunu ve uygulamanın doğru çalıştığını bilecektir.
const [ status, setStatus ] = useState("")
const [ statusTime, setStatusTime ] = useState(new Date())
Kullanıcılar eylemlerinin anında bir etkiye sahip olduğunu görmeyi severler. Ancak, bir blokzincirde durum böyle değildir. Bu durum değişkenleri, en azından kullanıcılara bir şeyler göstermemizi sağlar, böylece eylemlerinin devam ettiğini bilirler.
useEffect(() => {
if (readResults.data) {
setCurrentGreeting(readResults.data)
setStatus("Greeting fetched from blockchain")
}
}, [readResults.data])
Yukarıdaki readResults veriyi değiştirirse ve yanlış (false) bir değere (örneğin undefined) ayarlanmamışsa, mevcut selamlamayı blokzincirden okunanla güncelleyin. Ayrıca durumu da güncelleyin.
useWatchContractEvent({
address: greeterAddr,
abi: greeterABI,
eventName: 'SetGreeting',
chainId,
SetGreeting olaylarını dinleyin.
enabled: !!greeterAddr,
!!<value>, değer false ise veya undefined, 0 veya boş bir dize gibi yanlış (false) olarak değerlendirilen bir değerse, ifadenin genel olarak false olduğu anlamına gelir. Diğer herhangi bir değer için true olur. Bu, değerleri boolean'lara dönüştürmenin bir yoludur, çünkü greeterAddr yoksa olayları dinlemek istemeyiz.
onLogs: logs => {
const greetingFromContract = logs[0].args.greeting
setCurrentGreeting(greetingFromContract)
setLastSetterAddress(logs[0].args.sender)
updateStatus("Greeting updated by event")
},
})
Günlükleri (log) gördüğümüzde (ki bu yeni bir olay gördüğümüzde olur), bu selamlamanın değiştirildiği anlamına gelir. Bu durumda, currentGreeting ve lastSetterAddress değerlerini yeni değerlerle güncelleyebiliriz. Ayrıca, durum ekranını da güncellemek istiyoruz.
const updateStatus = (newStatus: string) => {
setStatus(newStatus)
setStatusTime(new Date())
}
Durumu güncellediğimizde iki şey yapmak istiyoruz:
- Durum dizesini güncellemek (
status) - Son durum güncelleme zamanını (
statusTime) şimdi olarak güncellemek.
const greetingChange = (evt) =>
setNewGreeting(evt.target.value)
Bu, yeni selamlama giriş alanındaki değişiklikler için olay işleyicisidir (event handler). evt parametresinin türünü belirtebilirdik, ancak TypeScript türü isteğe bağlı bir dildir. Bu işlev yalnızca bir kez, bir HTML olay işleyicisinde çağrıldığı için bunun gerekli olduğunu düşünmüyorum.
const { writeContractAsync } = useWriteContract()
Bir sözleşmeye yazma işlevi. writeContracts (opens in a new tab) ile benzerdir, ancak daha iyi durum güncellemeleri sağlar.
const simulation = useSimulateContract({
address: greeterAddr,
abi: greeterABI,
functionName: 'setGreeting',
args: [newGreeting],
account: account.address
})
İstemci perspektifinden bir blokzincir işlemi gönderme süreci şöyledir:
eth_estimateGas(opens in a new tab) kullanarak işlemi blokzincirdeki bir düğüme gönderin.- Düğümden bir yanıt bekleyin.
- Yanıt alındığında, kullanıcıdan işlemi cüzdan aracılığıyla imzalamasını isteyin. Bu adım, düğüm yanıtı alındıktan sonra gerçekleşmek zorundadır çünkü kullanıcıya imzalamadan önce işlemin gaz maliyeti gösterilir.
- Kullanıcının onaylamasını bekleyin.
- İşlemi bu kez
eth_sendRawTransaction(opens in a new tab) kullanarak tekrar gönderin.
Adım 2'nin algılanabilir bir zaman alması muhtemeldir, bu süre zarfında kullanıcılar komutlarının kullanıcı arayüzü tarafından alınıp alınmadığını ve neden henüz işlemi imzalamalarının istenmediğini merak edebilirler. Bu, zayıf bir kullanıcı deneyimi (UX) yaratır.
Çözümlerden biri, bir parametre her değiştiğinde eth_estimateGas göndermektir. Ardından, kullanıcı işlemi gerçekten göndermek istediğinde (bu durumda Update greeting düğmesine basarak), gaz maliyeti bilinir ve kullanıcı cüzdan sayfasını hemen görebilir.
return (
Artık nihayet döndürülecek asıl HTML'i oluşturabiliriz.
<>
<h2>Greeter</h2>
{currentGreeting}
Mevcut selamlamayı gösterin.
{lastSetterAddress && (
<p>Last updated by {
lastSetterAddress === account.address ? "you" : lastSetterAddress
}</p>
)}
Selamlamayı en son kimin ayarladığını biliyorsak, bu bilgiyi görüntüleyin. Greeter bu bilginin kaydını tutmaz ve SetGreeting olayları için geriye dönüp bakmak istemiyoruz, bu yüzden bunu yalnızca biz çalışırken selamlama değiştirildiğinde alırız.
<hr />
<input type="text"
value={newGreeting}
onChange={greetingChange}
/>
<br />
Bu, kullanıcının yeni bir selamlama ayarlayabileceği giriş metni alanıdır. Kullanıcı her tuşa bastığında, setNewGreeting öğesini çağıran greetingChange öğesini çağırırız. setNewGreeting, useState öğesinden geldiği için Greeter bileşeninin yeniden işlenmesine neden olur. Bu şu anlama gelir:
- Yeni selamlamanın değerini korumak için
valuebelirtmemiz gerekir, aksi takdirde varsayılan olan boş dizeye geri döner. simulationayrıcanewGreetingher değiştiğinde güncellenir, bu da doğru selamlama ile bir simülasyon alacağımız anlamına gelir. Bu önemli olabilir çünkü gaz maliyeti, dizenin uzunluğuna bağlı olan çağrı verisinin boyutuna bağlıdır.
<button disabled={!simulation.data}
Düğmeyi yalnızca işlemi göndermek için ihtiyacımız olan bilgiye sahip olduğumuzda etkinleştirin.
onClick={async () => {
updateStatus("Please confirm in wallet...")
Durumu güncelleyin. Bu noktada, kullanıcının cüzdanda onaylaması gerekir.
await writeContractAsync(simulation.data.request)
updateStatus("Transaction sent, waiting for greeting to change...")
}}
>
Update greeting
</button>
writeContractAsync yalnızca işlem gerçekten gönderildikten sonra döner. Bu, kullanıcıya işlemin blokzincire dahil edilmek üzere ne kadar süredir beklediğini göstermemizi sağlar.
<h4>Status: {status}</h4>
<p>Updated <Timer lastUpdate={statusTime} /> </p>
</>
)
}
Durumu ve güncellenmesinden bu yana ne kadar zaman geçtiğini gösterin.
export {Greeter}
Bileşeni dışa aktarın.
src/wagmi.ts
Son olarak, wagmi ile ilgili çeşitli tanımlar src/wagmi.ts içindedir. Burada her şeyi açıklamayacağım, çünkü bunların çoğu değiştirmeniz gerekmeyecek şablon kodlardır.
import { http, webSocket, createConfig, fallback } from 'wagmi'
import { sepolia } from 'wagmi/chains'
import { injected } from 'wagmi/connectors'
export const config = createConfig({
chains: [sepolia],
Wagmi yapılandırması, bu uygulama tarafından desteklenen zincirleri içerir. Mevcut zincirlerin listesini (opens in a new tab) görebilirsiniz.
connectors: [
injected(),
],
Bu bağlayıcı (opens in a new tab), tarayıcıda yüklü bir cüzdanla konuşmamızı sağlar.
transports: {
[sepolia.id]: http()
Viem ile birlikte gelen varsayılan HTTP uç noktası yeterince iyidir. Farklı bir URL istiyorsak, http("https:// hostname ") veya webSocket("wss:// hostname ") kullanabiliriz.
},
multiInjectedProviderDiscovery: false,
})
Başka bir blokzincir ekleme
Bugünlerde pek çok L2 ölçeklendirme çözümü (opens in a new tab) var ve viem'in henüz desteklemediği bazılarını desteklemek isteyebilirsiniz. Bunu yapmak için src/wagmi.ts dosyasını değiştirirsiniz. Bu talimatlar Optimism Sepolia'nın (opens in a new tab) nasıl ekleneceğini açıklar.
-
src/wagmi.tsdosyasını düzenleyinA. Viem'den
defineChaintürünü içe aktarın.import { defineChain } from 'viem'B. Ağ tanımını ekleyin. Optimism Sepolia için bunu yapmanıza aslında gerek yoktur, zaten
viemiçindedir (opens in a new tab), ancak bu şekildeviemiçinde olmayan bir blokzinciri nasıl ekleyeceğinizi öğrenirsiniz.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. Yeni zinciri
createConfigçağrısına ekleyin.export const config = createConfig({ chains: [sepolia, optimismSepolia], connectors: [ injected(), ], transports: { [optimismSepolia.id]: http(), [sepolia.id]: http() }, multiInjectedProviderDiscovery: false, }) -
Sepolia'ya otomatik geçişi yorum satırı yapmak için
src/App.tsxdosyasını düzenleyin. Bir üretim sisteminde, muhtemelen desteklediğiniz blokzincirlerin her birine bağlantılar içeren düğmeler gösterirsiniz./* useEffect(() => { if (connection.status === 'connected' && connection.chainId !== SEPOLIA_CHAIN_ID ) { switchChain({ chainId: SEPOLIA_CHAIN_ID }) } }, [connection.status, connection.chainId]) */ -
Uygulamanın yeni ağdaki sözleşmelerinizin adresini bildiğinden emin olmak için
src/Greeter.tsxdosyasını düzenleyin.const contractAddrs: AddressPerBlockchainType = { // Optimism Sepolia 11155420: "0x4dd85791923E9294E934271522f63875EAe5806f", // Sepolia 11155111: "0x7143d5c190F048C8d19fe325b748b081903E3BF0", } -
Tarayıcınızda.
A. ChainList (opens in a new tab)'e gidin ve zinciri cüzdanınıza eklemek için tablonun sağ tarafındaki düğmelerden birine tıklayın.
B. Uygulamada, blokzinciri değiştirmek için Disconnect (Bağlantıyı Kes) seçeneğine tıklayın ve ardından yeniden bağlanın. Bunu halletmenin daha güzel yolları vardır, ancak bunlar uygulama değişiklikleri gerektirir.
Sonuç
Elbette, Greeter için bir kullanıcı arayüzü sağlamak aslında umurunuzda değil. Kendi sözleşmeleriniz için bir kullanıcı arayüzü oluşturmak istiyorsunuz. Kendi uygulamanızı oluşturmak için şu adımları çalıştırın:
-
Bir wagmi uygulaması oluşturmayı belirtin.
npm create wagmi -
Devam etmek için
yyazın. -
Uygulamayı adlandırın.
-
React çerçevesini (framework) seçin.
-
Vite varyantını seçin.
Şimdi gidin ve sözleşmelerinizi tüm dünya için kullanılabilir hale getirin.
Çalışmalarımın daha fazlası için buraya bakın (opens in a new tab).