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 May 19 '19 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 May 20 '19 at 0:35

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 May 20 '19 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 May 20 '19 at 16:51

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

  • 1
    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 May 18 '19 at 22:22

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.