How to check if Metamask is connected after page refreshing

Viewed 7432

My dApp have to connect to MetaMask. There are two rude solutions in the docs: make user to click connect btn every time manually or just pop up connection confirmation after page load. I want to implement the only convenient solution: first time user connect manually by clicking the connect btn and interacting with MetaMask popup and then my dApp detect that connection is still established and use this connection. I can't find the solution, but i saw this in other dApps (Capture the ether for example) I use:

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 Answers

I finally found a possible solution and it turned out to be as simple as it should be. There is an eth_accounts method in Ethereum JSON-RPC which allow us to ask for available accounts without actually requesting them. This way we can check if metamask is still connected (if there are any accounts) and avoid auto requesting or need of manually clicking "connect" every time. Simple example implementation could be:

// 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;
  }
}

I assume you have already found Metamask docs on Ethereum Provider API. This section specifies three steps you need to do to make your app work:

  • Detect the Ethereum provider (window.ethereum)
  • Detect which Ethereum network the user is connected to
  • Get the user's Ethereum account(s)

Your snippet does the first part - it detects the provider. As per this section, to detect network you can use the following code

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

ethereum.on('chainChanged', handleChainChanged);

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

And the most crucial part - fetching user account.

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);
      }
    });

After the user logs in the first time, Metamask won't show the pop-up next time.

Use window.onload to initiate the isConnected() function when the webpage is loaded. The browser console will return a wallet address if it is connected.

     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");
         }
      }

I think it's help you. In some case you noticedethereum.window.once('connect',()=>{}) is not worked and then disconnect event too.. i also face this problem and i don't know how to get userAccount address automatically after refresh so i started research on many youtube video and metamask api document. finally i got the answer.

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>
  </>
   )
 }                                
  
 

This would get you the wallet address. returns false if not connected.

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

basic call from the DOM:

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

if using react:

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

Try using window.ethereum._state.account it will show array of accounts if connected else it will show an empty array, and use the length property to further access if connected to metamask or not.

Related