Building a React app, MetaMask injects web3, I get accounts[0] from getAccounts(), then pass it to my contract functions together with other parameters. I want my modifiers to lock incoming function calls based on comparisons between 1) msg.sender 2) the account MetaMask injected in my web3 and 3) contract owner.

When debugging an uncaught "MetaMask - RPC Error: Internal JSON-RPC error." I noticed my checks to be extremely valuable: 1) and 2) did not match. Metamask is injecting the account I selected (Account 3 in the picture below) in web3, but calls the following Solidity function (modified for debugging) using Account 1 (the first one I setup in MetaMask, see picture below, and first one in Ganache) as the msg.sender. If I console.log the msg.sender and web3 addresses in JS I obtain two different values.

ethAddress: "0x478205E668883bba86cCa3eFB8C489A7c4d27cE7"
sender: "0x81eCa3fA3727BdC7B8774c26B57152bb62C0834D"

This behaviour could lead to unpredictable things if left uncontrolled. Any clue why? Is it a bug?

    function findMe(address _ethAddress) public view returns(address sender,address ethAddress) { //bool foundMe
            return (
    //        require(msg.sender == _ethAddress);
    //        if (users[_ethAddress].found) {
    //            return true;
    //        } else {
    //            return false;
    //        }

enter image description here

  • are you sure that your app connected to metamask and not to Ganache?
    – Majd TL
    Commented May 19, 2019 at 9:55
  • My dApp uses Metamask to inject the web3 package, plus the addresses I imported in Metamask to serve as "sender" address (supposedly), then connects to the localhost blockchain served by Ganache. But I have no issues with Ganache now, just with Metamask.
    – Elio
    Commented May 20, 2019 at 0:35

2 Answers 2


When a function is called outside a transaction then the msg.sender is unreliable because it is not signed. It can be null for instance.

A public view or pure function should't rely on msg.sender being correct. If it is required it is better to explicitly pass an address as parameter.

In same frameworks when you create the instance of your contract it sets some default parameters like gas limit and sender. Perhaps when switching accounts this object is not reset and it will continue to point to the first account.

  • 1
    Thanks, that makes it clearer regarding signing the transaction. I didn't test .send()ing transactions without a {from:} though, I just hope it doesn't use a random account, as well. I refresh the page every time (but do not restart the browser) after changing account on MetaMask, so I was supposing the web3 injected in the new (refreshed) page would contain the new address I chose in MetaMask, but it doesn't work like that. Wouldn't you want MM to update the address, though?
    – Elio
    Commented May 20, 2019 at 11:57
  • Yes, Metamask should return the new account selected. But it doesn't know how a framework is managing its objects. In any case it was just a guess, without testing your code I can't determine why it is using the old account.
    – Ismael
    Commented May 20, 2019 at 16:51

You can provide the address with {from: 'address'} when calling your function

  • 2
    I had used that on the .send() methods, not on the .call() ones. Thanks, it does indeed solve the issue here, but I still fail to understand why that happens, or why that would happen at all. Why does MetaMask decide on its own which account to use on the backend? Imagine this on mainnet: you think you selected your test account with 0.01 ETH, but the transaction goes through using your main account with, say, 100 ETH. And if not only MetaMask is buggy, but also the dApp you are using, you might get your main account drained. Isn't this a security issue?
    – Elio
    Commented May 18, 2019 at 22:22

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.