如何在刷新页面后检查是否连接了Metamask

16

我的dApp需要连接MetaMask。文档中有两个粗暴的解决方案:要求用户每次手动点击连接按钮或者在页面加载后弹出连接确认窗口。我想实现唯一便捷的解决方案:第一次用户通过点击连接按钮并与MetaMask弹出窗口交互手动连接,然后我的dApp检测到连接仍然建立并使用该连接。我找不到解决方案,但我在其他dApps(例如Capture the ether)中看到了这个方法。

import detectEthereumProvider from '@metamask/detect-provider';

const provider = await detectEthereumProvider(); 

if (provider) {
  connect(provider)
} else {
  // kind of "Install the MetaMask please!"
}

function connect(provider) {
  // How to check if the connection is here
  if (//connection established) {
    // Show the user connected account address
  } else {
    // Connect
    provider.request({ method: "eth_requestAccounts" })
      .then // some logic
  }
}
6个回答

19

我终于找到了一个可能的解决方案,结果证明它像应该的那样简单。在以太坊JSON-RPC中有一个eth_accounts方法,允许我们查询可用账户而无需实际请求它们。这样,我们可以检查metamask是否仍然连接(如果有任何账户),避免自动请求或每次手动点击“连接”的需要。简单的示例实现可以是:

// detect provider using @metamask/detect-provider
detectEthereumProvider().then((provider) => {
  if (provider && provider.isMetaMask) {
    provider.on('accountsChanged', handleAccountsChanged);
    // connect btn is initially disabled
    $('#connect-btn').addEventListener('click', connect);
    checkConnection();
  } else {
    console.log('Please install MetaMask!');
  }
});

function connect() {
  ethereum
    .request({ method: 'eth_requestAccounts' })
    .then(handleAccountsChanged)
    .catch((err) => {
      if (err.code === 4001) {
        console.log('Please connect to MetaMask.');
      } else {
        console.error(err);
      }
    });
}

function checkConnection() {
  ethereum.request({ method: 'eth_accounts' }).then(handleAccountsChanged).catch(console.error);
}

function handleAccountsChanged(accounts) {
  console.log(accounts);

  if (accounts.length === 0) {
    $('#connection-status').innerText = "You're not connected to MetaMask";
    $('#connect-btn').disabled = false;
  } else if (accounts[0] !== currentAccount) {
    currentAccount = accounts[0];
    $('#connection-status').innerText = `Address: ${currentAccount}`;
    $('#connect-btn').disabled = true;
  }
}

3
使用window.onload来初始化isConnected()函数,当网页加载时。如果连接成功,浏览器控制台将返回一个钱包地址。
     window.onload = (event) => {
        isConnected();
     };
            
      async function isConnected() {
         const accounts = await ethereum.request({method: 'eth_accounts'});       
         if (accounts.length) {
            console.log(`You're connected to: ${accounts[0]}`);
         } else {
            console.log("Metamask is not connected");
         }
      }

1

我假设你已经在以太坊提供者API的Metamask文档中找到了内容。这个章节指定了三个步骤来使你的应用程序工作:

  • 检测以太坊提供者(window.ethereum)
  • 检测用户连接的以太坊网络
  • 获取用户的以太坊账户

你的代码片段完成了第一步——检测提供者。 根据这个章节,要检测网络,你可以使用以下代码:

const chainId = await ethereum.request({ method: 'eth_chainId' });
handleChainChanged(chainId);

ethereum.on('chainChanged', handleChainChanged);

function handleChainChanged(_chainId) {
  window.location.reload();
}

最关键的部分 - 获取用户帐户。

let currentAccount = null; 

function handleAccountsChanged(accounts) {
  if (accounts.length === 0) {
    console.log('Please connect to MetaMask.');
  } else if (accounts[0] !== currentAccount) {
    currentAccount = accounts[0];
  }
}

document.getElementById('connectButton', connect);

function connect() {
  ethereum
    .request({ method: 'eth_requestAccounts' })
    .then(handleAccountsChanged)
    .catch((err) => {
      if (err.code === 4001) {
        console.log('Please connect to MetaMask.');
      } else {
        console.error(err);
      }
    });

用户第一次登录后,Metamask将不会再次显示弹出窗口。


谢谢您的回答!所以用户每次都必须点击连接,即使连接已经建立(如扩展弹出窗口中所示)。我想检测一下Metamask是否已经连接到该网站。是的,我可以使用window.ethereum.request({ method: 'eth_requestAccounts' }),但对我来说有一个问题:如果Metamask没有连接,它将显示一个弹出窗口,但我希望它能够静默地发生。只有当用户点击“连接”时才显示弹出窗口。 - Данила Алексеев
如果Metamask未连接,则无法在不显示弹出窗口的情况下连接它。再次检查最后一个链接,其中包含EIP-1193连接网站的方法。用户将不得不在第一次打开网站时与Metamask进行交互,但下一次他将自动登录。 - d3mage
我明白你的意思,但问题有些不同。如果我将 window.ethereum.request({ method: 'eth_requestAccounts' }) 用作按钮单击处理程序,则用户必须在每次页面重新加载后都点击该按钮。如果我在页面加载时使用此方法,则弹出窗口将出现而无需用户请求(是的,仅在第一次)。期望的行为是:用户第一次点击“连接”按钮并与弹出窗口交互,但然后用户不必再次单击该按钮,因为我知道(如何知道是个问题)连接仍然建立。 - Данила Алексеев
当我说“连接仍然建立”时,我的意思是如果我请求账户,那么我只需在没有用户交互的情况下收到它们。我们可以在metamask扩展窗口的左上角看到连接状态。但是我们如何在我们的代码中看到它呢? - Данила Алексеев
请注意我说的话。检查链接,有两种方法:一种是我在这里发布的(EIP-1102),另一种是将自动请求页面加载的帐户(EIP-1193)。如果用户已将帐户连接到网站,则请求帐户不会显示弹出窗口。 - d3mage
显示剩余3条评论

1

我认为这会对你有所帮助。在某些情况下,你可能会注意到ethereum.window.once('connect',()=>{})无法正常工作,然后断开连接事件也是如此。我也遇到了这个问题,不知道如何在刷新后自动获取用户账户地址,所以我开始研究许多YouTube视频和MetaMask API文档。最终,我找到了答案。

import React, {useState,useEffect} from 'react';
 import { ethers} from 'ethers';
function App(){ 
 let [userAccount,setUserAccount] = useState({
  isConnect:false,
  Account:""
 })
 let isItConnect = async()=>{
  let provider = new ethers.providers.Web3Provider(window.ethereum);
let accounts = await provider.send("eth_requestAccounts",[]);
console.log(accounts.length)
if(accounts.length>0){
  return {
    status:true,
   userAddress:accounts[0]
  }
}
else{
  return {
    status:false,
    userAddress:""
  }
}
}
 let connect = async()=>{
let Status = await isItConnect();
 localStorage.setItem('isConnected',Status.status)
 setUserAccount((prev)=>{
return {...prev,Account:Status.userAddress}
})

}

window.ethereum.on('accountsChanged',async()=>{
localStorage.removeItem('isConnected');
setUserAccount((prev)=>{
 return {...prev,Account:""}
})
connect()
})

   useEffect(()=>{
let status = localStorage.getItem('isConnected')
if(status){
connect()
}
 if(status === null){
  if(window.ethereum.selectedAddress === null){
  console.log('welcome User!')
  }
   else{
  connect()
  }
 }
 },[])
 return  (
<>
 {userAccount.Account===""&& <button onClick={connect}>Connect Metamask!
  </button>}
   {userAccount.Account !==""&& <>
  <p>{userAccount.Account}</p>
  <p>Connected</p>
  </>
   )
 }                                
  
 

1
尝试使用 window.ethereum._state.account,如果连接成功,它将显示账户数组,否则将显示空数组,并使用长度属性进一步访问是否连接到Metamask。

实际上是window.ethereum._state.accounts - Rahul

0

以下代码可以获取钱包地址。如果未连接,则返回 false。

const getAccount = async () =>  await window.ethereum.request({method: 'eth_accounts'})[0] || false;

从DOM的基本调用:

window.onload = event => {
   const account = getAccount();
   console.log(account ? `You're connected to: ${accounts}` : 'Metamask is not connected');
};  

如果使用 React:

componentDidMount() { 
   const account = getAccount();
   console.log(account ? `You're connected to: ${accounts}` : 'Metamask is not connected');
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接