iT邦幫忙

2022 iThome 鐵人賽

DAY 12
0
Web 3

Road Map To DApp Developer系列 第 12

【DAY12】 - Connect MetaMask Wallet!

  • 分享至 

  • xImage
  •  

Preface

昨天介紹了 Wallet 和 Provider 是怎麼與鏈上運作的,且上次在網頁上面做出了一個 Log in metamask 的按鈕,今天的目標就是讓網頁可以連結到 Metamask,並在同樣的地方顯示出其地址出來。

Log In flow

首先來講一下我的登入設計流程。

  1. 在網頁重新整理的時候便偵測是否已安裝 Metamask。
    • 若已安裝:顯示 Lon in Metamask 的按鍵。
    • 若沒有安裝:顯示 Please Install Metamask,並使其連到 Metamask 的下載頁面
  2. 在滑鼠移動到 Log in button 時會變色。
  3. 按下 Log in button 後會跳出 MetaMask 的解鎖畫面,解鎖登入。
  4. 成功登入後需要讀取 Metamask 地址的資料,並顯示在原本的按鈕上。

實作

首先要在頁面重整時,偵測瀏覽器上是否有安裝任何插件錢包。大致運作流程如下:

一、宣告一個 useState:isMetamaskInstalled=true

二、在網頁重新渲染(render)的時候,使用 useEffect() 來偵測這個使用者是否有安裝 Metamask。

  • 若已安裝: isMetaMaskInstalled 是 true
  • 若未安裝: setMetamaskInstalled(false)
const [isMetamaskInstalled, setMetamaskInstalled] = useState(true);
const [isConnected, setConnected] = useState(false);

useEffect(() => {
    if (typeof window.ethereum === 'undefined'){
      setMetamaskInstalled(false);
    }

},[]);

三、透過 isMetaMaskInstalled 來顯示 Log in button

使用的是 React 中 bool + tag 的語法。

return(
  ...
  {isMetaMaskInstalled && <button className='log-in-btn' onClick={ () => { connectAccount() }}>Log In Metamask</button>}
  ...
);

四、透過 connectAccount() 來連接 MetaMask

按下按鈕後會透過 window.ethereum.request({ method: 'eth_requestAccounts', }); 這個函式來 pop up MetaMask 的頁面

而此函式會 return 帳號地址,我們用 accounts 來接住他,並更新從 App.js 傳入的 propety--accounts(是一個useState)。

const connectAccount = async () => {
    // to detect whether the wallet is installed
    if (isConnected === false){
      if (window.ethereum) {
        const accounts = await window.ethereum.request({ 
          method: 'eth_requestAccounts',
        });
        setAccounts(accounts); 
        setConnected(true);
        setLogged(true);
      }
      alert("Successfully Logged in!")
    }
    console.log(isLogged);
  }

五、利用 isLogged 來顯示地址

這邊我宣告:

const [isLogged, setLogged] = useState(false);

並再登入成功後 setLogged(true),並在原本的 Log In Button 的邏輯中在加入 !isLogged,讓他在登入後不顯示。

{ !isLogged && (isMetamaskInstalled && <button className='log-in-btn' onClick={ () => { connectAccount() }}>Log In Metamask</button>) }

最後用一個 <div> 來顯示地址。

{ isLogged && <div>{ showAccount() }</div>}

但是原本的地址足足有 40 個字元這麼多!因此我又用 showAccount() 讓他縮減。

const showAccount = () => {
  const prefix = accounts[0].substr(0, 5);
  const suffix = accounts[0].substr(36, 40);
  return (prefix + "..." + suffix);
}

小插曲

我在 MetaMask 的官網上找到了更精確的方式來偵測是否安裝 MetaMask,就是 ethereum.isMetamask


const checkAccount = async () => {
  if(!window.ethereum.isMetamask) {
    setMetamaskInstalled(false);
    return;
  }
}

只要瀏覽器上安裝的插件並非 Metamask 就會請他左轉去安裝。但是這個方法在似乎行不通,會跳出

Uncaught TypeError: Cannot read properties of undefined (reading 'isMetaMask') 

這個錯誤。

因此推論需要先確認 window.ethereumdefined(也就是需要安裝其他種錢包),才能再進一步的確認 window.ethereum.isMetaMask 是否存在!

為了測試,我把 chrome 裡面的 MetaMask extension 刪除,改成安裝 coinbase 的錢包。

此時如果只有使用

if (typeof window.ethereum === 'undefined') 
    setMetaMaskInstalled(false);

在只有安裝 coinbase 的 chrome 裡面,會顯示 Log In MetaMask,而非 Please Install MetaMask

我推論除了 MetaMask 外,其他錢包也會透過 inject 一個 window.ethereum 的 object 給使用者,讓使用者來偵測他們的存在(並做出之後的動作)。

因此若只想要使用者使用 MetaMask 則要改成:

if (typeof window.ethereum === 'undefined') 
    setMetamaskInstalled(false);
    
else 
    if (!window.ethereum.isMetaMask) 
        setMetamaskInstalled(false);

這樣可以先確認 browser 裡面是沒有錢包(會產生 window.ethereum 的錢包),若有的話再確認其是否是 MetaMask,若不是則還是請她左轉安裝。

Presentation

以下是我的兩個實作影片!

  • 第一個是用 Edge,沒有安裝 MetaMask,可以點擊並跳出連結下載,下載後重新整理可以看到 Lon In MetaMask

  • 第二個使用 Brave 已安裝 MetaMask,登入後可以顯示地址。

Closing

今天實作的部分,大部分的 code 都是查詢不同的頁面,比對、偵錯、才慢慢寫出來的。而其中我最滿意的是,我實作出了一個完整的流程,並且在途中一一解決了很多困難(像是點下 link 後跳出一個新的分頁而不是在同一頁連結、怎麼使用 useEffect()useState() 等等)!

References


若有文章內有任何錯誤的地方歡迎指點與討論!非常感謝!

歡迎贊助窮困潦倒大學生
0xd8538ea74825080c0c80B9B175f57e91Ff885Cb4


上一篇
【DAY11】 - Wallet and Provider
下一篇
【DAY13】 - Mint button & Connect With Smart Contract
系列文
Road Map To DApp Developer30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言