1

Brief explanation: I tried to embed some interactivity to a website using plain js/html. I deploy a smart contract using hardhat-deploy plug-in (my contract gets deployed when i spin up the hardhat node) and then try to tinker with my site using Metamask and test accounts that hardhat gives you. Here's my BaseContract instance:

const account0 = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; // Hardhat account
const signer = new ethers.Wallet(account0, provider);
const elections = new ethers.Contract(contractAddress, abi, signer);

For some reasons, whenever it comes to execute something related to calling read functions using that BaseContract my Metamask pops up and tries to connect. Using a debugger, i set some breakpoints, and when it tries to execute this line here:

const votingTime = await elections.getVotingTime();
// getVotingTime is a 'read' method

All of a sudden, Metamask pops up and tries to connect. So i wonder, how to fix it?

Thanks for any help in advance!

2
  • Can you show the code of getVotingTime?
    – Mila A
    Commented Feb 13 at 13:13
  • 1
    function getVotingTime() public pure returns (uint256) { return (VOTING_TIME);}, where VOTING_TIME is a constant
    – andr0-0
    Commented Feb 13 at 13:19

2 Answers 2

1

I managed to resolve the problem! Here's what i changed:

Code before:

const account0 = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545/", 31337);
const userProvider = new ethers.BrowserProvider(window.ethereum);
const userSigner = await userProvider.getSigner();
const signer = new ethers.Wallet(account0, provider);
const elections = new ethers.Contract(contractAddress, abi, signer);

and after:

const account0 = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545/", 31337);
// const userProvider = new ethers.BrowserProvider(window.ethereum);
// const userSigner = await userProvider.getSigner();
const signer = new ethers.Wallet(account0, provider);
const elections = new ethers.Contract(contractAddress, abi, signer); // works fine with the signer instead of provider btw

let userSigner;

I populate userSigner variable later on when it comes to using eth_requestAcounts method:

// this happens inside of my custom `connect()` function
...
userAccount = (await window.ethereum.request({ method: 'eth_requestAccounts' }))[0];
const userProvider = new ethers.BrowserProvider(window.ethereum);
userSigner = await userProvider.getSigner();
...
0

For read-only static methods, the contract should be instantiated by passing a provider instead of a signer.

Because if you pass the signer, Metamask will pop up each time unless you're connected already, or, in some cases, it'll pop up every time just to confirm the transaction.

It's a known behavior explained here: (https://docs.ethers.org/v5/api/contract/contract/#Contract-connect).

To call static methods, consider instantiating the contract like this instead:

const elections = new ethers.Contract(contractAddress, abi, provider);

See this example:

// A Human-Readable ABI; for interacting with the contract, we
// must include any fragment we wish to use
const abi = [
    // Read-Only Functions
    "function balanceOf(address owner) view returns (uint256)",
    "function decimals() view returns (uint8)",
    "function symbol() view returns (string)",

    // Authenticated Functions
    "function transfer(address to, uint amount) returns (bool)",

    // Events
    "event Transfer(address indexed from, address indexed to, uint amount)"
];

// This can be an address or an ENS name
const address = "0x70ff5c5B1Ad0533eAA5489e0D5Ea01485d530674";

// Read-Only; By connecting to a Provider, allows:
// - Any constant function
// - Querying Filters
// - Populating Unsigned Transactions for non-constant methods
// - Estimating Gas for non-constant (as an anonymous sender)
// - Static Calling non-constant methods (as anonymous sender)
const erc20 = new ethers.Contract(address, abi, provider);

// Read-Write; By connecting to a Signer, allows:
// - Everything from Read-Only (except as Signer, not anonymous)
// - Sending transactions for non-constant functions
const erc20_rw = new ethers.Contract(address, abi, signer);

If you need both read and write access, but you don't want to overwhelm the UX with requesting Metamask to connect each time, you can consider doing something like this:

  1. Use const elections = new ethers.Contract(contractAddress, abi, provider); for read-only methods;
  2. Use const electionsMutable = elections.connect(signer); for write methods;
4
  • 1
    That doesn't work for me though. Strangely enough, my Metamask pops up even when it comes to this line: const userSigner = await userProvider.getSigner();, where userProvider is const userProvider = new ethers.BrowserProvider(window.ethereum);
    – andr0-0
    Commented Feb 13 at 13:57
  • Hmm, and does that behavior repeat again when you do accept the request to connect Metamask to your website?
    – Mila A
    Commented Feb 13 at 14:17
  • Well, problem actually disappears as fast as connect to Metamask. What would such strange behavior mean? I tried to delete Metamask's cache and to clear the activity tab, neither helped
    – andr0-0
    Commented Feb 13 at 15:53
  • It must be related to this thing: github.com/ethers-io/ethers.js/issues/… Metamask popping up is an expected behavior for const userSigner = await userProvider.getSigner();, however it's odd that it pops up when you call static methods.
    – Mila A
    Commented Feb 14 at 7:51

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.