錢包喚起時的混亂連結錢包是進入 Web3 世界的關鍵一步,Web3 用戶經常需要在一些 DApp 網站上連接金錢

作者:ZAN Team

原文標題: Web3 新手系列:點擊 MetaMask 誤喚起其他錢包?錢包衝突解決方案現狀

封面: Photo by  Shubham's Web3  on  Unsplash

錢包喚起時的混亂

連結錢包是進入 Web3 世界的關鍵一步,Web3 用戶經常需要在一些 DApp 網站上連接錢包。但是,只是這個簡單的動作,也可能對使用者造成嚴重的不便。

連接錢包

想像一下這樣一個場景:一位新入門的 Web3 用戶(出於好奇,他安裝了許多個插件錢包),訪問了某個 DApp 網站,並且想要使用自己的瀏覽器插件錢包來連接它,但是當他們點擊網站提供的「Connect Wallet」按鈕,並選擇某個錢包以便想使用它來連接 DApp 時,可能會發現彈出的錢包並不是自己選擇的。這很可能會讓他感到慌張和窒息,以為是自己的電腦中了病毒,所以才執行了他意料之外的操作。

區塊鏈錢包是連接區塊鏈的重要入口,而為了佔據這個入口,各個錢包使用了它們能想到的各種方式。其中最讓 DApp 開發者以及 DApp 用戶頭痛的要數各錢包對全域變數的竄改。

在目前的瀏覽器錢包實作邏輯中,都有透過向瀏覽器注入全域變數來暴露錢包提供的功能(例如以太坊平台的錢包會將其提供的功能注入到上 window.ethereum ),以便 DApp 可以呼叫錢包提供的方法來與之互動。

只是,由於許多錢包都會將自己注入到同一個 window.ethereum 變數上,就導致在後面註冊的錢包會覆蓋之前註冊的錢包,以至於只能通過這種方式只能喚起最後註冊的那個。

有時候 DApp 用戶為了可以正常使用自己想要使用的錢包,只好臨時將其他錢包外掛程式停用,或是直接只安裝某一個錢包。這樣一來,反而與錢包開發者最初的想法大相逕庭了。而新錢包哪怕做的更出色,也很難吸引到已經使用其他錢包的用戶。

有朋友可能會奇怪,為什麼一定要注入到同一個變數中呢?假設有兩個錢包:A 和 B,其實只要 A 將自己注入到 window.a,B 將自己注入到 window.b,想要喚起哪個錢包,就調用其對應的對像中提供的方法,就不會發生上述想要調用 A 卻反而將 B 喚起的問題。這樣確實可以解決競爭問題,但是,問題在於,如此以來,假如 DApp 將要支持多個錢包連接,就必須將開發者想要適配的所有錢包名稱全部預先定義在代碼中,並且在用戶選中某個錢包時,呼叫該錢包的相關方法。導致相關程式碼維護起來相當麻煩。而統一將錢包注入到同一個物件上,則可以免於這個麻煩。

解決方案

為了脫離上述的兩難困境,社區中有兩個相似的標準。

以太坊的解決方案:EIP-6963

以太坊社群在 2023 年 5 月提出了 EIP-6963 提案。

其中的基本邏輯很簡單,就是捨棄全域變量,轉而使用約定的事件,來解決錢包註冊與發現的問題。
具體來說,插件錢包載入成功後,觸發統一的 eip6963:announceProvider  事件,通知 DApp 有新的錢包可用。而 DApp 則透過監聽此事件,來得知自己目前可用的錢包有哪些。

這樣,透過一套抽象的事件監聽邏輯,避免了直接使用全域變數所造成的問題,並且能夠自動發現目前使用者環境中可用的錢包。如此一來,兩難自解。

社區標準:Wallet Standard

EIP-6963 是以太坊生態標準,但不只以太坊,其他鏈平台也會有類似的問題。例如 Solana 鏈的錢包,普遍會將自己注入到 window.solana  變數上,同樣會造成競爭情境。

那麼可否讓 Solana 生態也能實現這個標準呢?雖然 EIP-6963 只是為了解決以太坊生態的錢包發現問題,但其中所蘊含的想法其實可以套用在所有鏈平台。那麼,我們能否再進一步,提供一套通用的標準,讓所有區塊鏈平台的錢包和 DApp 實現,讓所有鏈平台的開發者和使用者都能享受到 EIP-6963 所提供的便利?理論上是完全沒有問題的,而且已經有開發者在這麼做了,也就是 Wallet Standard 

Wallet Standard 所做的核心工作,在於提供了兩個函數:registerWallet  和 getWallets,前者用於錢包,後者用於 DApp。

錢包調用 registerWallet,傳入一個錢包對象,這個對像上封裝了錢包提供的功能(例如 Connect 方法,用於連接錢包)。函數內部會先觸發一個 RegisterWalletEvent 事件,事件的參數其實是一個回呼函數,用來讓 DApp 監聽到 RegisterWalletEvent 事件時調用,而這個回呼函數其實會將 wallet 物件傳入,於是 DApp 就可以拿到錢包對象引用,也就可以與錢包互動了。

export function registerWallet(wallet: Wallet): void {
    const callback: WindowRegisterWalletEventCallback = ({ register }) => register(wallet);
    try {
        (window as WalletEventsWindow).dispatchEvent(new RegisterWalletEvent(callback));
    } catch (error) {
        console.error('wallet-standard:register-wallet event could not be dispatched\n', error);
    }
    try {
        (window as WalletEventsWindow).addEventListener('wallet-standard:app-ready', ({ detail: api }) =>
            callback(api)
);
    } catch (error) {
        console.error('wallet-standard:app-ready event listener could not be added\n', error);
    }
}

DApp 開發者沒有必要自行寫入監聽、接收錢包物件的程式碼,這部分也已經被 Wallet Standard 內建到 getWallets  當中。但是,getWallets 只是監聽了事件,具體要怎麼處理事件,還是需要開發者考慮。例如取得到的 Wallets 放到哪裡?有些錢包在 DApp 加載前就已經加載,而另一些錢包可能在之後才加載,這些錢包的狀態如何維護? Wallet Standard 針對以上細節問題,同時提供了 @wallet-standard/react  包,開發者直接使用它提供的 React Hooks 就可以獲取到想要的數據,包括錢包列表、當前連接的錢包、錢包提供的方法等。

Wallet Standard Features

除了最基本的取得 Wallet 物件外,Wallet Standard 也定義了一些 Features 格式。

實際上,錢包都具有一些最基本的功能,例如連接、監聽錢包事件等。 Wallet Standard 提供了 standard:connectstandard:events  等 Features,錢包供應商實現這些特性後,DApp 可以直接根據這些值來判斷錢包是否支援某些操作。

上面提到的"standard:*" 是它內建定義的特性,實際上它們的值並沒有特別強硬的要求,所以可以隨意擴展。不同的連結平台也會有其獨特的特性,例如 Solana,直接約定"solana:*" 即可。 Solana 平台常見的 Features 包括 solana:signTransactionsolana:signMessage  等。

Wallet Standard 現狀

目前實現了 Wallet Standard 標準的項目其實不多,值得一提的有 Solana 和 Sui。

在 Ant Design Web3 的 Solana 適配器中,也支援適應了 Wallet Standard 的錢包的自動檢測,開發者基本上只需要通過一個 autoAddRegisteredWallets  開啟即可,不需要配置一大堆的錢包元數據,開發體驗和用戶使用體驗直線上升。

ZAN.TOP 連接錢包的邏輯在早期同樣遇到相同的問題,不過現在,得益於 Ant Design Web3 提供的配置,很輕鬆就適應了 EIP-6963 標準。大家在 https://zan.top/personal/account?chInfo=ch_wxdyh 綁定位址時應該已經體驗到這一點了。

各區塊鏈生態的實現

目前各個區塊鏈平台對 Wallet Standard(或 EIP-6963)標準的態度並不相同,這裡舉幾個例子:

Bitcoin

比特幣目前為止似乎沒有類似的標準,有一個實現了 Wallet Standard 標準的項目,但並沒有引起太多關注,現在也很久沒有提交新的程式碼。

目前開發者只能手動維護狀態,或使用一些開發包來輔助工作。例如在 Ant Design Web3 中的 Bitcoin 適配器實作中,針對不同的錢包,會從不同的全域變數上獲取,並存到統一的狀態。這其實是等於函式庫開發者將繁瑣的狀態維護工作接手了。而且,這僅僅解決了錢包衝突問題,無法自動感知可用錢包的問題仍然存在。

Ethereum

以太坊平台已經有了 EIP-6963 標準,相關庫和錢包也大多提供了支援。

Solana

如上文,官方提供了實現:https://github.com/solana-labs/wallet-standard

Sui

Sui 目前已經對 Wallet Standard 提供了實現,在官方文件上可以找到使用方法:https://docs.sui.io/standards/wallet-standard

DApps 開發庫的支持

wagm

i wagmi 透過 mipd (https://github.com/wevm/mipd) 庫對 EIP-6963 提供了支持,具體方式可以查看 wagmi 的文檔。

RainbowKit

RainbowKit(https://www.rainbowkit.com/)內部邏輯是基於 wagmi,所以也已經對 EIP-6963 提供了內建支援。

Ant Design Web3

Ant Design Web3(https://web3.ant.design/) 的 Ethereum 和 Solana 轉接器對這兩個標準都進行了非常好的支持,並且開發者開啟起來非常便攜。

對以太坊 DApp 開發者而言,只需要增加 eip6963 配置即可,注意其中與 EIP-6963 相關的在 23-25 行:App.tsx

import { ConnectButton, Connector } from '@ant-design/web3';
import {
  MetaMask,
  OkxWallet,
  WagmiWeb3ConfigProvider,
} from '@ant-design/web3-wagmi';
import { QueryClient } from '@tanstack/react-query';
import { createConfig, http } from 'wagmi';
import { mainnet } from 'wagmi/chains';

const queryClient = new QueryClient();

const config = createConfig({
  chains: [mainnet],
  transports: {
    [mainnet.id]: http(),
  },
});

const App: React.FC = () => {
  return (
    <WagmiWeb3ConfigProvider
      eip6963={{
        autoAddInjectedWallets: true,
      }}
      ens
      wallets={[
        MetaMask(),
        OkxWallet(),
      ]}
      config={config}
      queryClient={queryClient}
    >
      <Connector modalProps={{ mode: 'simple' }}>
        <ConnectButton quickConnect />
      </Connector>
    </WagmiWeb3ConfigProvider>
  );
};

export default App;

而如果你是 Solana 生態的 DApp 開發者,方式也是類似的。它提供了 autoAddRegisteredWallets 屬性:App.tsx

import React from 'react';
import { ConnectButton, Connector } from '@ant-design/web3';
import {
  OKXWallet,
  PhantomWallet,
  SolanaWeb3ConfigProvider,
  WalletConnectWallet,
} from '@ant-design/web3-solana';

const App: React.FC = () => {
  return (
    <SolanaWeb3ConfigProvider
      autoAddRegisteredWallets
      balance
      wallets={[PhantomWallet(), OKXWallet(), WalletConnectWallet()]}
      walletConnect={{ projectId: YOUR_WALLET_CONNECT_PROJECT_ID }}
    >
      <Connector modalProps={{ mode: 'simple', group: false }}>
        <ConnectButton quickConnect />
      </Connector>
    </SolanaWeb3ConfigProvider>
  );
};

export default App;

總結

EIP-6963 和 Wallet Standard 可大幅改善用戶連接錢包的體驗,並降低新錢包供應商的進入門檻。希望以後能有更多鏈平台以及錢包、DApp 開發者可以提供或實現相關標準,這有利於 Web3 朝向更好的方向發展。

免責聲明:作為區塊鏈資訊平台,本站所發布文章僅代表作者及來賓個人觀點,與 Web3Caff 立場無關。文章內的資訊僅供參考,均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。