¿Cómo puedo detectar el cambio de cuenta en Metamask?

Cuando un usuario cambia a una cuenta diferente en metamask, ¿hay alguna forma de detectarlo de forma asíncrona en el código?

actualmente uso

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

pero cuando se cambia la cuenta, sigue recogiendo la anterior. Actualizar la página no es un camino a seguir. ¿Alguien se enfrentó a esto y tiene una solución?

Desafortunadamente, el método que usa la mayoría de la gente es buscar cambios usando setIntervals. La única otra opción es verificar si las cuentas coinciden antes de cada llamada.

Respuestas (6)

Como lo sugieren las preguntas frecuentes de Metamask , esta podría ser una opción:

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

editar

En la versión más nueva, metamask expone un evento que podría usarse para detectar si hay un cambio de cuenta según el nuevo documento :

window.ethereum.on('accountsChanged', function (accounts) {
  // Time to reload your interface with accounts[0]!
})
Desafortunadamente, en este momento, la verificación en un bucle es el único método admitido.
Sin embargo, tenga cuidado, tuve un problema en el que siempre coincidía con la condición if porque la cuenta que obtenía siempre estaba en minúsculas.
Uso y sugiero usar el evento onFocus para manejar este problema.
¿Existe una solución similar para la extensión TronLink (tronWeb)?

De los documentos de MetaMask :

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

Como ha señalado @Sr.PEDROethereum.publicConfigStore , no funcionará en el futuro. De hecho, se eliminará por completo. Vea este comentario de GitHub para más detalles.

También puede evitar que MetaMask vuelva a cargar automáticamente la página web:

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

Esto solo se aplica si usa el window.web3objeto inyectado por MetaMask, que también está programado para ser eliminado .

¿Existe una solución similar para la extensión TronLink (tronWeb)?
La URL de los documentos ha cambiado, vaya a docs.metamask.io/guide/ethereum-provider.html#table-of-contents

Usando web3 versión 1.0.0, el proveedor de metamask expone un evento de 'actualización' que puede escuchar en su publicConfigStore.

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

Su devolución de llamada pasará a un objeto con 'selectedAddress' y 'networkVersion' cada vez que esos atributos cambien.

Hola Brian, ¿dónde puedo encontrar documentación sobre el evento de actualización? He buscado en los documentos web3 1.0. Gracias.
En la nueva versión de metamask, el nombre ha cambiado, ahora debe usar web3.currentProvider._publicConfigStore.on('update', callback);Pero esto probablemente no sea compatible con metamask en el futuro, por lo que recomendaría usar window.ethereum events medium.com/metamask/…

Podría poner en usoEfecto un evento de escucha como el siguiente:

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();
  }, []);

Usas chainChangedevent pero tienes que considerar 3 cosas:

  • Tienes que configurar el oyente globalmente una vez. Ya sea en la barra de navegación, o si usa el proveedor en el proveedor o en next.js en el componente _app

  • Cuando cambia la cadena, debe volver a cargar la página, recomendado por metamask:

Recomendamos encarecidamente volver a cargar la página en los cambios de cadena, a menos que tenga una buena razón para no hacerlo.

  • después de configurar un oyente para el chainChangedevento, debe eliminar el oyente

aquí hay un ejemplo en 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();
  }

Detectar cambio en la cuenta de Metamask

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