如何使用 Web3-react 保持 MetaMask 与 UI 的持久连接?

How to keep MetaMask connection to the UI persistent with Web3-react?

我正在使用 web3-react,我不知道如何在浏览器刷新时保持与 MetaMask 钱包的连接。

这是代码:

// define the injectedConnectors

const injectedConnector = new InjectedConnector({
  supportedChainIds: [
    1, // Mainet
    3, // Ropsten
    4, // Rinkeby
    5, // Goerli
    42, // Kovan
  ],
})

const { chainId, account, activate, active } = useWeb3React()
// activate the wallet
activate(injectedConnector)
console.log(account)
// all good. 

到目前为止,一切正常,我激活了我的 MetaMask 钱包,并且我正确地记录了帐户,并且 active 变量是一个布尔值,它会变为 true。 问题是,当我刷新页面时,active 变为 false,我失去了 UI 与 MetaMask 钱包之间的连接。当然,将 active 保存到浏览器中不会改变任何内容,因为连接依赖于 active 布尔值。 文档缺少此类信息。 任何帮助将不胜感激。

终于找到解决办法了! 我试图使用官方库中的示例使用 ... 但由于某种原因它没有工作,尽管没有出现错误。 然后我偶然发现了一个遇到同样问题的人,然后 post 在 reddit 上编辑并得到了一个适合我的好答案。 这是 link 到 post:https://www.reddit.com/r/ethdev/comments/nw7iyv/displaying_connected_wallet_after_browser_refresh/h5uxl88/?context=3 这是 post:

中的代码

首先创建一个包含名为 connectors.js:

的注入连接器的文件
import { InjectedConnector } from '@web3-react/injected-connector'
export const Injected = new InjectedConnector({ supportedNetworks: [1, 3, 4, 5, 42] })

然后创建一个组件来检查用户是否已经激活了钱包:

import React, { useEffect, useState } from 'react'
import { injected } from '../connectors'
import { useWeb3React } from '@web3-react/core'

function MetamaskProvider({ children }) {
  const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React()
  const [loaded, setLoaded] = useState(false)
  useEffect(() => {
    injected
      .isAuthorized()
      .then((isAuthorized) => {
        setLoaded(true)
        if (isAuthorized && !networkActive && !networkError) {
          activateNetwork(injected)
        }
      })
      .catch(() => {
        setLoaded(true)
      })
  }, [activateNetwork, networkActive, networkError])
  if (loaded) {
    return children
  }
  return <>Loading</>
}

export default MetamaskProvider

然后将 MetamaskProvider 包裹在您希望钱包在刷新时激活的组件周围:

 return (
    <ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
      <StylesProvider injectFirst>
        <Paper>
          <Router>
            <Web3ReactProvider getLibrary={getLibrary}>
              <MetamaskProvider>
              {...children components}
              </MetamaskProvider>
            </Web3ReactProvider>
          </Router>
        </Paper>
      </StylesProvider>
    </ThemeProvider>
  );

其实很简单。您可以只将连接地址存储在本地存储中,当用户单击断开连接按钮时,然后从本地存储中删除该地址。基本上我们使用的条件是,如果本地存储中有一个帐户,那么我们在加载时连接,如果没有,那么我们必须手动单击连接按钮。考虑下面的代码。请注意,理想情况下,您应该将逻辑编写为钩子并在主应用程序中使用钩子,然后将 useWeb3React() 返回的“活动”状态传递给道具。但为了这个例子的目的,我只是将连接逻辑放在一个文件中以使其更容易阅读

import React, { useState, useEffect } from 'react';
import Web3 from 'web3';
import detectEthereumProvider from '@metamask/detect-provider';
import { useWeb3React } from "@web3-react/core"
import { InjectedConnector } from '@web3-react/injected-connector'

//declare supportated chains
export const injected = new InjectedConnector({
  supportedChainIds: [1, 3, 4, 5, 42, 1337, 43114],
})


export default function connButton() {

var web3;
var accounts;
var connected

const [loading, setLoading] = useState(false)

//here we can destructure out various things from web3React such as
//active (which is true if the user is connected and false otherwise)
//activate and deactiveate which we use to instansiate and break the users
//connection
const { active, account, library, connector, activate, deactivate } = useWeb3React()

//set up an elemnt in local storage that we use to hold the connected account
var acc = localStorage.getItem("account")



//function that initialises web3.js
const connectWalletHandler = () => {
    if (window.ethereum && window.ethereum.isMetaMask) {
        console.log('MetaMask Here!');
        web3 = new Web3(window.ethereum);

        window.ethereum.request({ method: 'eth_requestAccounts'})
        
    } else {
        console.log('Need to install MetaMask');
        // setErrorMessage('Please install MetaMask browser extension to interact');
    }
    console.log(web3.eth.currentProvider)
}

//function that is called on page load if and only if their exists and
//item for the user accoun tin local storage
async function connectOnLoad() {

     try {
        
        //here we use activate to create the connection
        await activate(injected)
        connected = true
      } catch (ex) {
        console.log(ex)
      }

      //we use web3.eth to get the accounts to store it in local storage
      var accounts1 = await web3.eth.getAccounts();
      acc = localStorage.setItem("account", accounts1);
}

//here we use a useEffect so that on page load we can check if there is
//an account in local storage. if there is we call the connect onLoad func
//above which allows us to presist the connection and i also call connectWalletHandler
which sets up web3.js so we can call web3.eth.getAccounts()
useEffect(() => {

        
    if (acc != null) {
    connectOnLoad()
    }
    connectWalletHandler()
}, [])


//however in the case where there is no item in local storage we use this
//function to connect which is called when we click the connect button. its
//essentially the same but we check if local storage is null if it is we activate
//if its not then we disconnect. And when we disconnect we remove the acccount from local storage
async function connectOnClick() {
    
    if (localStorage.getItem("account") == null) {

        setLoading(true);
        try {
            await activate(injected)
            connected = true
        } catch (ex) {
            console.log(ex)
        }
        // window.location.reload();
        var accounts1 = await web3.eth.getAccounts();
        console.log(accounts1)
        acc = localStorage.setItem("account", accounts1);
        console.log(acc)
        setTimeout(function(){
            setLoading(false)
         }, 1600);//wait 2 seconds
        
    } else {

        disconnect();
        connected = false
    }

}

async function disconnect() {
    try {
    deactivate()
    localStorage.removeItem("account");
    } catch (ex) {
    console.log(ex)
    }
}



return (
  
    //remember the active boolean from useReactWeb3() stores a bool
    //depending on if the user is or is not connected there for we can 
    //use this as a conditon to render the button saying "Connect Wallet"
    or displaying their address as the text.
    <div>
          {active ? <button onClick={connectOnClick}>{account.substring(0, 6)}...{account.substring(account.length - 4)}</button> : <button onClick={connectOnClick}>Connect Wallet</button>}
    </div>
  );
}

然后在您的 app.js 中记得将您的整个应用包裹在标签中。请记住,这意味着您需要将 web3React 导入 app.js 以及