'Ethers.js returns the same wallet address even if I switch accounts
I'm using Ethers.js to allow users to connect their Metamask wallets to my app. Here's the code that I have:
import { ethers } from "ethers"
async function connect() {
const provider = new ethers.providers.Web3Provider(window.ethereum, "any")
await provider.send("eth_requestAccounts", [])
const signer = provider.getSigner()
const address = await signer.getAddress()
// Always prints the address that I first connected with
console.log(address)
}
The issue is that once I have connected one of my Metamask accounts, then I always get its wallet address even if I switch to another Metamask account and try to connect it as well.
Why is that and how should I fix this?
Solution 1:[1]
Correct answer:
To get the current account, get the 0
address of eth_requestAccounts
:
let accounts = await provider.send("eth_requestAccounts", []);
let account = accounts[0];
To update it automatically, listen to the accountsChanged
event:
provider.on('accountsChanged', function (accounts) {
account = accounts[0];
});
Code:
import { ethers } from "ethers"
async function connect() {
const provider = new ethers.providers.Web3Provider(window.ethereum, "any");
let accounts = await provider.send("eth_requestAccounts", []);
let account = accounts[0];
provider.on('accountsChanged', function (accounts) {
account = accounts[0];
console.log(address); // Print new address
});
const signer = provider.getSigner();
const address = await signer.getAddress();
console.log(address);
}
Old (incorrect) answer:
I believe that the issue is that you need to re-instantiate both the signer and the provider when the account is switched.
In addition, if I read the docs correctly, it's better practice to instantiate the signer after eth_requestAccounts
is successfully called.
Solution 2:[2]
async function connect() {
try{
// detecting provider and requesting metamask usually handled in separate function but I m displaying on your implementation
const provider = new ethers.providers.Web3Provider(window.ethereum, "any")
if (provider){
window.ethereum?.request({ method: "eth_requestAccounts" });
const signer = provider.getSigner()
const address = await signer.getAddress()
}
// Always prints the the address that I first connected with
console.log(address)
}catch(){
// handle the error
}}
The problem is you are not detecting account change. Assuming you are storing window.ethereum
as useState, you could write this useEffect
useEffect(() => {
ethereum?.on("accountsChanged", handleAccountChange);
return () => {
ethereum?.removeListener("accountsChanged", handleAccountChange);
};
});
then:
const handleAccountChange = (...args) => {
// you can console to see the args
const accounts = args[0] ;
// if no accounts that means we are not connected
if (accounts.length === 0) {
console.log("Please connect to metamask");
// our old data is not current connected account
// currentAccount account that you already fetched and assume you stored it in useState
} else if (accounts[0] !== currentAccount) {
// if account changed you should update the currentAccount so you return the updated the data
// assuming you have [currentAccount,setCurrentAccount]=useState
// however you are tracking the state currentAccount, you have to update it. in case of redux you have to dispatch update action etc
setCurrentAccount(accounts[0)
}
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Yilmaz |