2

I am learning hardhat and ethers, so I made a create-react-app to interact with contracts on rinkeby. The react app works fine locally, but when I deploy it to Surge, I get an error and can't access the contracts anymore. I am not sure if this is because of ethers or the provider that metamask gives to ethers.

Here is the code that sets up the provider and signer by going through metamask:

  useEffect(() => {
    const init = async () => {
      const provider = await detectEthereumProvider();
      if (provider) {
        startApp(provider);
      } else {
        alert('Please install MetaMask!');
        setIsLoading(false);
        return;
      };

      async function startApp(provider) {
        if (provider !== window.ethereum) {
          alert('Do you have multiple wallets installed?');
          setIsLoading(false);
          return;
        };

        const ethersProvider = new ethers.providers.Web3Provider(provider);
        const signer = ethersProvider.getSigner();

        let signerAddress = await signer.getAddress();
        setCurrentMetaMaskAccount(signerAddress);

        const _myToken = new ethers.Contract(
          myTokenContractAddress.MyToken,
          MyToken.abi,
          signer
        );
        setMyToken(_myToken);

        const _faucet = new ethers.Contract(
          faucetContractAddress.Faucet,
          Faucet.abi,
          signer
        );
        setFaucet(_faucet);
      };
    };
    init();
  }, []);

The deployed React app produces this error:

Uncaught (in promise) Error: unknown account #0 (operation="getAddress", code=UNSUPPORTED_OPERATION, version=providers/5.1.2)

I have followed the Ether.js documentation for working with metamask:

const provider = new ethers.providers.Web3Provider(window.ethereum)
const signer = provider.getSigner()

What should I do to set up ethers.js and metamask so they work on a deployed react app?

2
  • This question is about how to interact with an already deployed contract, not about how to deploy a contract, so I would suggest editing the title to reflect that.
    – Nanolucas
    Apr 24 at 21:23
  • Nanolucas, you are correct. The original title reflected my thinking that the problem was with Ethers.js. Apr 25 at 4:20
1

The issue here is that you need to request access from metamask to the account before you can use it as a signer.

This boggled my mind for a long time since it was certainly not clear from the ethers.js documentation or from Metamask's documentation.

What worked for me was adding this code:

window.ethereum.on('accountsChanged', this.handleAccountsChanged);

window.ethereum.request({
    method: 'eth_requestAccounts'
})
//successful request will trigger accountsChanged event
.catch(this.handleError);

along with a few functions like these:

handleAccountsChanged = (accounts) => {
    if (accounts.length === 0) {
        // Wallet is locked or the user has not connected any accounts
        this.account = null;
        console.log('Please connect your wallet (Metamask, TrustWallet, etc).');
    } else if (accounts[0] !== this.account) {
        this.account = accounts[0];
    }

    this.initialised = true;
}

handleError = (error) => {
    // EIP 1193 userRejectedRequest error
    if (error.code === 4001) {
        console.log('Please connect your wallet (Metamask, TrustWallet, etc).');
    //other errors
    } else {
        console.log(error);
    }
}
1
  • Thanks for this! I ended up finding a solution, but as with your solution, it requires the RPC call to work. Apr 25 at 4:29
1

Here is a solution. The problem was not Ether.js as I originally thought. Permission is required, which is why MetaMask includes the RPC calls. In this solution I have only included what is needed to solve the question (so the Ethers contracts have been dropped).

let [provider, setProvider] = useState();
let [ethersProvider, setEthersProvider] = useState();
let [ethersSigner, setEthersSigner] = useState();
let [currentMetaMaskAccount, setCurrentMetaMaskAccount] = useState(null);

useEffect(() => {
  const init = async () => {
    //detect whether the browser is connected to a provider
    let ethereumProvider = await detectEthereumProvider();
    if (ethereumProvider) {
      setProvider(ethereumProvider);
      startApp(ethereumProvider);
    } else {
      alert('Please install MetaMask!');
      return;
    };

    async function startApp(_ethereumProvider) {
      //The provider detected by detectEthereumProvider() must be the same as window.ethereum
      if (_ethereumProvider !== window.ethereum) {
        alert('Do you have multiple wallets installed?');
        return;
      };

      //Check if a MetaMask account has permission to connect to app
      let accounts = await _ethereumProvider.request({ method: 'eth_accounts' });
        if (accounts.length > 0) {
          setCurrentMetaMaskAccount(accounts[0]);
        };

      let _ethersProvider = await new ethers.providers.Web3Provider(_ethereumProvider);
      setEthersProvider(_ethersProvider);
    };
  };
  init();
}, []);

const getAccounts = async () => {
  const accounts = await provider.request({ method: 'eth_requestAccounts' });
  await handleAccountsChanged(accounts);
};

function handleAccountsChanged(accounts) {
  if (accounts.length === 0) {
    console.log('Please connect to MetaMask.');
  } else if (accounts[0] !== currentMetaMaskAccount) {
    console.log('account[0]: ', accounts[0]);
    setCurrentMetaMaskAccount(accounts[0]);
  }
};
//Give a MetaMask account permission to interact with the app
const handleOnConnect = async () => {
  await getAccounts();
  provider.on('accountsChanged', handleAccountsChanged);

  let signer = await ethersProvider.getSigner();
  setEthersSigner(signer);
};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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