Mayıs 2023'te bir grup Ethereum cüzdan geliştiricisi, dapp'lerle bağlantı kurmak için yeni bir endüstri standardı oluşturmak amacıyla bir araya geldi. Bundan önce yaygın uygulama, cüzdanların web tarayıcılarındaki window.ethereum global nesnesi aracılığıyla bir EIP-1193 sağlayıcısı sunmasıydı. Bu teknik, kullanıcıların genellikle tek bir cüzdana sahip olduğu ve bağlandığı ilk dönemlerde iyi çalışıyordu.
Ekosistem büyüdükçe ve kullanıcılar aynı anda birden fazla cüzdan kullanmaya başladıkça, dapp geliştiricilerinin bir kullanıcının hangi cüzdana bağlı olduğunu tespit etme sürecini doğru şekilde yönetmesi ya da birden fazla cüzdan yüklüyken kullanıcılara hangi cüzdanı kullanacaklarını seçme imkânı sunması güçleşti.
Ekim 2023'te, artık EIP-6963 veya Multi-Wallet Injected Provider Discovery olarak bilinen bu standart kabul edildi. O tarihten bu yana cüzdan geliştiricileri, EIP-6963'ü cüzdan sağlayıcılarını keşfetmenin varsayılan yolu olarak sunmaya başladı. MetaMask da bu konuda bir istisna değildir; bu standardın MetaMask cüzdan API'si ve MetaMask SDK tarafından desteklenmesini sağladık.
Bunun yanı sıra, window.ethereum'a dayanan dapp'lerin desteğinin süreceği güvence altına alınmıştır. Ancak, tercih edilen sağlayıcı keşif mekanizması olarak tüm dapp'lere EIP-6963 desteğinin eklenmesini öneriyoruz. Bunu yapmanın en kolay yolu, EIP-6963'ü destekleyen üçüncü taraf kütüphanelerle entegrasyon sağlamaktır. Mevcut kütüphanelerin büyüyen listesi için MetaMask belgelerini ziyaret edin.
Bu eğitim, mevcut üçüncü taraf kütüphaneleri kullanmamaya karar verirseniz dapp'inize EIP-6963 desteği eklemenin temel adımlarını size gösterecektir. React ve TypeScript kullanılmaktadır; ancak herhangi bir JavaScript geliştiricisi bu adımları takip edebilmeli ve tercih ettiği framework'te uygulayabilmelidir.
Web3 Cüzdanları için Desteklenen Arayüzleri ve Türleri Tanımlayın
Dapp'inizde çoklu cüzdan keşfini desteklemenin ilk adımı, iyileştirme önerisinde belirtilen tüm desteklenen arayüzleri ve türleri tanımlamaktır. Bu arayüzler ve türler, Ethereum cüzdan sağlayıcılarını yönetmek için yapılandırılmış ve standartlaştırılmış bir yol sunarak birden fazla Ethereum cüzdanının entegrasyonunu ve keşfini kolaylaştırır.
interface EIP6963ProviderInfo {
walletId: string;
uuid: string;
name: string;
icon: string;
}
interface EIP1193Provider {
isStatus?: boolean;
host?: string;
path?: string;
sendAsync?: (request: { method: string, params?: Array<unknown> }, callback: (error: Error | null, response: unknown) => void) => void;
send?: (request: { method: string, params?: Array<unknown> }, callback: (error: Error | null, response: unknown) => void) => void;
request: (request: { method: string, params?: Array<unknown> }) => Promise<unknown>;
}
interface EIP6963ProviderDetail {
info: EIP6963ProviderInfo;
provider: EIP1193Provider;
}
type EIP6963AnnounceProviderEvent = {
detail: {
info: EIP6963ProviderInfo;
provider: EIP1193Provider;
}
}
Her arayüzün ve türün ne anlama geldiğini anlamanıza yardımcı olmak için açıklamalar ekledim. Daha ayrıntılı bir açıklama için lütfen iyileştirme önerisine başvurun.
Olaylara Abone Olun ve Yeni Bağlantılardan Haberdar Olun
Bir sonraki adım, yeni cüzdan bağlantıları tespit edildiğinde dapp'inizi uyaracak bir mekanizma oluşturmaktır. Unutmayın, EIP-6963'ün amacı birden fazla cüzdan arasında kesintisiz bağlantı ve geçiş sağlamaktır. React'te bunu aşağıdaki örnek kodla gerçekleştirebiliriz.
declare global {
interface WindowEventMap {
"eip6963:announceProvider": CustomEvent<EIP6963AnnounceProviderEvent>;
}
}
let providers: EIP6963ProviderDetail[] = [];
export const store = {
value: () => providers,
subscribe: (callback: () => void) => {
function onAnnouncement(event: EIP6963AnnounceProviderEvent) {
if (providers.some(p => p.info.uuid === event.detail.info.uuid)) return;
providers = [...providers, event.detail];
callback();
}
window.addEventListener("eip6963:announceProvider", onAnnouncement as EventListener);
window.dispatchEvent(new Event("eip6963:requestProvider"));
return () => window.removeEventListener("eip6963:announceProvider", onAnnouncement as EventListener);
}
}
Yukarıdaki kod örneğinin açıklaması:
WindowEventMap'i "eip6963:announceProvider" adlı özel bir olayla genişleterek yeni Ethereum cüzdan sağlayıcılarının kullanılabilirliğini duyurmak için global bir olay türü oluşturuyoruz.
Tespit edilen tüm cüzdan sağlayıcılarını takip etmek için kullanılan, adlandırılmış sağlayıcılar dizisiyle temsil edilen harici bir store başlatıyoruz. Bu dizi, cüzdan sağlayıcısı ve ilgili Ethereum sağlayıcısı hakkında bilgi içeren EIP6963ProviderDetail arayüzüne uyan nesneleri depolar.
Store nesnesi içindeki subscribe fonksiyonu aracılığıyla bir abonelik mekanizması sunuyoruz. Bu fonksiyon "eip6963:announceProvider" olayını dinler ve harici store'u yeni duyurulan sağlayıcıların ayrıntılarıyla günceller. Yinelenen girişleri önlemek amacıyla sağlayıcıların yalnızca store'da bulunmuyorsa eklenmesini sağlar.
Store nesnesi iki temel işlev sunar: 1. value fonksiyonu, harici store'un mevcut durumunu, yani tespit edilen cüzdan sağlayıcılarının listesini almayı sağlar.
subscribe fonksiyonu, bileşenlerin veya uygulamanın diğer bölümlerinin store'daki değişikliklere abone olmasını sağlar. Yeni bir sağlayıcı duyurulup store'a eklendiğinde, abone olan öğeler bir callback mekanizması aracılığıyla bilgilendirilir ve güncellenmiş sağlayıcı listesine tepki verebilirler.
Şimdi, yerel durumu yukarıdaki store.tsx'te tanımlanan harici store ile senkronize etmek için bir useSyncExternalStore hook'u tanımlayalım:
import { useSyncExternalStore } from "react";
import { store } from "./store";
export const useSyncProviders = ()=> useSyncExternalStore(store.subscribe, store.value, store.value)
Tespit Edilen Her Web3 Cüzdan Sağlayıcısı için Dinamik Olarak Butonlar Görüntüleyin
Tespit edilen her cüzdan sağlayıcısı için dinamik olarak bir buton oluşturmak amacıyla useSyncProviders hook'unu kullanan bir React bileşeni tanımlayalım:
import { useState } from 'react';
import { useSyncProviders } from '../hooks/useSyncProviders';
import { formatAddress } from '~/utils';
export const DiscoverWalletProviders = () => {
const [selectedWallet, setSelectedWallet] = useState<EIP6963ProviderDetail | undefined>();
const [userAccount, setUserAccount] = useState<string>('');
const providers = useSyncProviders();
const handleConnect = async (providerWithInfo: EIP6963ProviderDetail) => {
const accounts = await providerWithInfo.provider.request({ method: 'eth_requestAccounts' }).catch(console.error);
if (accounts && accounts[0]) {
setSelectedWallet(providerWithInfo);
setUserAccount(accounts[0]);
}
};
return (
<>
<h2>Wallets Detected:</h2>
<div>
{providers.length > 0 ? (
providers.map((provider) => (
<button key={provider.info.uuid} onClick={() => handleConnect(provider)}>
<img src={provider.info.icon} alt={provider.info.name} />
<div>{provider.info.name}</div>
</button>
))
) : (
<div>There are no announced providers.</div>
)}
</div>
<hr />
<h2>{userAccount ? 'Wallet Selected' : 'No Wallet Selected'}</h2>
{userAccount && (
<div>
<img src={selectedWallet!.info.icon} alt={selectedWallet!.info.name} />
<div>{selectedWallet!.info.name}</div>
<div>({formatAddress(userAccount)})</div>
</div>
)}
</>
);
};
Yukarıdaki React bileşeninin açıklaması:
Şu anda seçili olan Ethereum cüzdan sağlayıcısını ve kullanıcının hesap adresini takip etmek için sırasıyla selectedWallet ve userAccount olmak üzere iki state hook kullanıyoruz.
Mevcut her Ethereum cüzdan sağlayıcısını dinamik olarak tespit etmek için useSyncProviders hook'unu kullanıyoruz. Bu hook, her biri sağlayıcının bilgilerini ve Ethereum sağlayıcı nesnesini içeren EIP6963ProviderDetail arayüzüne uyan bir sağlayıcılar dizisi döndürür.
Bir cüzdan sağlayıcısı butonu tıklandığında çağrılan handleConnect fonksiyonunu tanımlıyoruz. Bu fonksiyon, kullanıcıdan hesap erişimi istemek için eth_requestAccounts metoduyla sağlayıcının request metodunu kullanır. Erişim verilirse ilk hesap adresi userAccount'ta, sağlayıcının ayrıntıları ise selectedWallet'ta saklanır.
Son olarak, tespit edilen her cüzdan sağlayıcısı için sağlayıcının adını ve simgesini gösteren bir buton listesi oluşturuyoruz.
Bileşenimiz tanımlandıktan sonra son adım bu bileşeni render etmektir.
import './App.css'
import { DiscoverWalletProviders } from './components/DiscoverWalletProviders'
function App() {
return (
<>
<DiscoverWalletProviders/>
</>
)
}
export default App
Bu birkaç adımla dapp'imizde temel düzeyde EIP-6963 desteğini başarıyla uyguladık. Az önce oluşturduğumuz işlevselliğin görsel bir temsili için aşağıya bakın. Görselde yer alan dapp'in tüm kodu GitHub'da bulunabilir.
EIP-6963 ve Web3 Cüzdanlarının Geleceği
Bu standardın cüzdan geliştiricileri ve üçüncü taraf bağlantı kütüphaneleri tarafından daha geniş çapta benimsenmesini görmeye devam ettikçe, özellikle yeni kullanıcıların katılımında UX'i iyileştirme açısından Ethereum topluluğuna açtığı çeşitli kullanım senaryoları konusunda heyecan duyuyoruz.
MetaMask olarak, EIP-6963'ün daha da geniş çapta benimsenmesini sağlamak için diğer cüzdan geliştiricileriyle, dapp geliştiricileriyle ve üçüncü taraf bağlantı kütüphaneleriyle iş birliği yapmak için sabırsızlanıyoruz. Bu standardı destekleme çalışmalarımız hakkında daha fazla bilgi için lütfen MetaMask belgelerine başvurun.