30

When a user changes to a different account in metamask, is there a way to detect it asynchronously in code?

I currently use

this.web3.eth.getAccounts((err, accs) => {   
  this.account = accs[0];
});  

but when the account is changed, it still picks up the previous one. Refreshing the page is not a way to go. Did anyone face this and has a solution?

bbusdriver
  • 1,144
  • 2
  • 18
  • 33
  • 2
    Unfortunately, the method most people used is polling for changes using setIntervals. The only other option is checking if accounts match before each call. – ReyHaynes Mar 15 '18 at 12:50

7 Answers7

36

As suggested by Metamask FAQs this might be an option:

var account = web3.eth.accounts[0];
var accountInterval = setInterval(function() {
  if (web3.eth.accounts[0] !== account) {
    account = web3.eth.accounts[0];
    updateInterface();
  }
}, 100);

edit

In the newer version metamask exposes an event that could be used to detect whether there's an account change as per new doc:

window.ethereum.on('accountsChanged', function (accounts) {
  // Time to reload your interface with accounts[0]!
})
Jan Klimo
  • 113
  • 3
qbsp
  • 4,367
  • 2
  • 15
  • 26
34

From the MetaMask docs:

window.ethereum.on('accountsChanged', function (accounts) {
  // Time to reload your interface with accounts[0]!
})

window.ethereum.on('networkChanged', function (networkId) { // Time to reload your interface with the new networkId })

As @Sr.PEDRO has noted, ethereum.publicConfigStore will not work in the future. In fact, it will be removed entirely. See this GitHub comment for details.

You can also prevent MetaMask from automatically reloading web page:

window.onbeforeunload = function() {
  return "Prevent reload"
}

This only applies if you use the window.web3 object injected by MetaMask, which is also scheduled to be removed.

rekmarks
  • 3
  • 2
rstormsf
  • 4,337
  • 2
  • 25
  • 42
23

Using web3 version 1.0.0, the metamask provider exposes an 'update' event you can listen to on it's publicConfigStore.

web3.currentProvider.publicConfigStore.on('update', callback);

Your callback will be passed an object with 'selectedAddress' and 'networkVersion' whenever those attributes change.

Brian Ethier
  • 331
  • 1
  • 2
  • 2
  • 3
    Hi Brian, where can I find documentation about the update event? Have searched the web3 1.0 docs. Thanks. – Vindberg Nov 23 '18 at 19:38
  • On the new metamask version the name has changed, now you have to use web3.currentProvider._publicConfigStore.on('update', callback); But this is probably not going to be supported by metamask in the future so i would recomend using window.ethereum events https://medium.com/metamask/breaking-changes-to-the-metamask-inpage-provider-b4dde069dd0a – Sr.PEDRO Jul 21 '20 at 13:26
5

You could put in useEffect a listenEvent like below:

useEffect(() => {
    async function listenMMAccount() {
      window.ethereum.on("accountsChanged", async function() {
        // Time to reload your interface with accounts[0]!
        accounts = await web3.eth.getAccounts();
        // accounts = await web3.eth.getAccounts();
        console.log(accounts);
      });
    }
    listenMMAccount();
  }, []);
brunillopu
  • 333
  • 1
  • 3
  • 8
1

You use chainChanged event but you have to consider 3 things:

  • You have to set listener globally once. Either in navbar, or if using provider in provider or in next.js in _app component

  • When chain changed, you should reload the page, recommended by metamask:

We strongly recommend reloading the page on chain changes, unless you have good reason not to.

  • after setting a listener for chainChanged event, you should remove the listener

here is an example in react/next.js

  useEffect(() => {
   async function initWeb3() {
     try {
      const provider = new ethers.providers.Web3Provider(
        window.ethereum );
      setListener(window.ethereum);
      // then add logic here
    } catch (error: any) {
      // handle error
    }
  }
   initWeb3();
   return () => removeListener(window.ethereum);
  }, []);

// this has to be set once globally. metamask suggests const setListener = (ethereum) => { ethereum.on("chainChanged", pageReload); }; const removeListener = (ethereum) => { ethereum.removeListener("chainChanged", pageReload); };

function pageReload() { window.location.reload(); }

Yilmaz
  • 1,580
  • 10
  • 24
1

Detect change in Metamask account

 useEffect(() => {
    if (window.ethereum) {
  window.ethereum.on("accountsChanged", () => {
    window.location.reload();
  });
}

});

0

Yes, when a user account changes on Metamask, you can detect and update it on your interface asynchronously without the need to reload your page as follows, for an example:

const checkCurrentAccount = () => {
 if (window.ethereum) {
   window.ethereum.on("accountsChanged", async () => {
    const accounts = await window.ethereum.request({
      method: "eth_accounts",
    });
    setCurrentAccount(accounts[0]);
  });
 }
};

useEffect(() => { checkCurrentAccount(); }, []);