2

I am trying to figure out the best solution to keep a wallet connection persistent when a page refreshes on Ethers. So far, the only answers I've found have been through Web3-react and not Ethers.

Here is my context for getting the connect accounts, and I import the account state and connectWallet function into all components that need them to run transactions. Additionally, my provider is coming from a configs file that connects to an Alchemy provider:

import { createContext, useContext, useState } from "react";
import { provider } from "../utils/configs";

const WalletContext = createContext(null);

const WalletProvider = ({ children }) => {
  const [account, setAccount] = useState(null);
  const [signer, setSigner] = useState();

  function connectWallet() {
    try {
      window.ethereum.request({
        method: "eth_requestAccounts",
      });
      const signerAccount = provider.getSigner();
      setSigner(signerAccount);
      signerAccount.getAddress().then((address) => setAccount(address));
    } catch (error) {
      console.error("Error connecting to wallet", error);
      alert("Error connecting to wallet");
    }
  }

  const walletActions = { connectWallet };

  return (
    <WalletContext.Provider value={{ account, ...walletActions }}>
      {children}
    </WalletContext.Provider>
  );
};

const useWallet = () => useContext(WalletContext);

const useWalletActions = () => {
  const { connectWallet } = useWallet();

  return { connectWallet };
};

export { WalletProvider, useWallet, useWalletActions };

I've then wrapped the Provider around my App.js so the user only needs to log in once.

But when I refresh, I have to constantly log back in to gain a connection and that doesn't seem very user-friendly for the dApp.

Can anyone provide a short explanation on how I can keep the wallet connection persistent when refreshing? If so, is there a minimum amount of time when that connection stays persistent? Like if after 5 minutes, the account needs to be connected again.

2 Answers 2

1

have you tried to query "eth_accounts" inside a useEffect, I think this should work.

  useEffect(() => {
       
    window.ethereum.request({
    method: "eth_accounts",
  });
  const signerAccount = provider.getSigner();
  setSigner(signerAccount);
  signerAccount.getAddress().then((address) => setAccount(address));

  }, []);

Hope it helps!

3
  • I'll check it right now! Thanks!
    – OhItsPM
    Commented Apr 12, 2023 at 0:14
  • That worked! Amazing how something so simple could work that well
    – OhItsPM
    Commented Apr 12, 2023 at 0:17
  • Thks, please can you vote for the answer and mark the question as solved? That would help others looking for the same problem.
    – donoso.eth
    Commented Apr 12, 2023 at 5:45
0

I noticed that when I use the useEffect hook inside the connectWallet(), the connectWallet function will be called on initial page load because of the empty dependency array [] without the user clicking on the button, and if the user was connected before the reload, the contract instance might not be accessible to the UI after and you have to connect again. Which isn't a good UX. Just in case you experienced such. What you can do is to persist the connection.

Here is the implementation in code :

1. Set a localStorage variable when connecting the wallet

  function connectWallet() {
    try {
      localStorage.setItem("isWalletConnected", true); // Create a localstorage 
// variable to indicate whether the user has been connected to a wallet before
      window.ethereum.request({
        method: "eth_requestAccounts",
      });
      const signerAccount = provider.getSigner();
      setSigner(signerAccount);
      signerAccount.getAddress().then((address) => setAccount(address));
    } catch (error) {
      console.error("Error connecting to wallet", error);
      alert("Error connecting to wallet");
    }
  }

2. Listen for Metamask events on accountsChanged (this also serves for disconnection) on our Dapp and remove the variable from localStorage or set it to false, anyone that suits you

            // Listen for account changes
        window.ethereum.on("accountsChanged", async (accounts) => { // or provider.on if your Dapp supports EIP 6963
          if (accounts.length === 0) {
            // No accounts are connected
            localStorage.removeItem("isWalletConnected");
            setAccount(""); //Update the account state
            setContract(null);  //assuming you are managing state of the contract 
            setProvider(null); //assuming you are managing state of the contract
          } else {
            // At least one account is connected
            localStorage.setItem("isWalletConnected", true);
            const signer = await provider.getSigner();
            const address = await signer.getAddress();
            setAccount(address);
          }
        });

3. On page load, check if the variable in local storage is true. If it is, this means the user was previously connected to a wallet before page refresh, so we need to reconnect them. This should be outside the connectWallet() function

  useEffect(() => {
    const connectWalletOnPageLoad = async () => {
      const isWalletConnected = localStorage.getItem("isWalletConnected");
      if (isWalletConnected === "true") {
        await connectWallet();
      }
    };
    connectWalletOnPageLoad();
  }, []);

I hope this helps!

cheers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.