2

MetaMask provides the following way currently to find a provider for their upcoming 2020 breaking change:

// Running on the page, in the browser
// This API will go live in Q1 2020
// It will be the only API available after a 6-week deprecation period

if (!ethereum || !ethereum.isMetaMask) {
  throw new Error('Please install MetaMask.')
}

/*********************************************************/
/* Handle chain (network) and chainChanged, per EIP 1193 */
/*********************************************************/

let currentChainId = null
ethereum.send('eth_chainId')
  .then(handleChainChanged)
  .catch(err => console.error(err)) // This should never happen

ethereum.on('chainChanged', handleChainChanged)

function handleChainChanged (chainId) {

  if (currentChainId !== chainId) {

    currentChainId = chainId
    // Run any other necessary logic...
  }
}

/**********************************************************/
/* Handle user accounts and accountsChanged, per EIP 1193 */
/**********************************************************/

let currentAccount = null
ethereum.send('eth_accounts')
  .then(handleAccountsChanged)
  .catch(err => {
    // In the future, maybe in 2020, this will return a 4100 error if
    // the user has yet to connect
    if (err.code === 4100) { // EIP 1193 unauthorized error
      console.log('Please connect to MetaMask.')
    } else {
      console.error(err)
    }
  })

// Note that this event is emitted on page load.
// If the array of accounts is non-empty, you're already
// connected.
ethereum.on('accountsChanged', handleAccountsChanged)

// For now, 'eth_accounts' will continue to always return an array
function handleAccountsChanged (accounts) {

  if (accounts.length === 0) {

    // MetaMask is locked or the user has not connected any accounts
    console.log('Please connect to MetaMask.')

  } else if (accounts[0] !== currentAccount) {

    currentAccount = accounts[0]
    // Run any other necessary logic...
  }
}

/***********************************/
/* Handle connecting, per EIP 1102 */
/***********************************/

// You should only attempt to connect in response to user interaction,
// such as a button click. Otherwise, you're popup-spamming the user
// like it's 1999.
// If you can't retrieve the user's account(s), you should encourage the user
// to initiate a connection attempt.
document.getElementById('connectButton', connect)

function connect () {

  // This is equivalent to ethereum.enable()
  ethereum.send('eth_requestAccounts')
    .then(handleAccountsChanged)
    .catch(err => {
      if (err.code === 4001) { // EIP 1193 userRejectedRequest error
        console.log('Please connect to MetaMask.')
      } else {
        console.error(err)
      }
    })
}

I am struggling to get my frontend to recognize a web3.js provider that is not MetaMask. The above code works perfectly with the current version of MetaMask, but does not work in any other Web3 browsers. If I inject my own web3.js, I get Can't find variable: Web3 and Can't find variable: ethereum MetaMask's documentation is highly confusing, especially in light of the fact that it still injects v0.2 of web3.js while forcing you soon to completely change the way you find a provider. I don't even know which version of web3.js the above code is for, 1.2 or 2.0. It is all unclear from all their convoluted press releases and blogs that seem to come from a different person on the team.

How can I make the above code work for other web3.js browsers like Cipher or Parity?

Currently I do this with v0.2:

if (typeof web3 !== 'undefined') {
    console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
    window.web3 = new Web3(web3.currentProvider);
    web3.eth.defaultAccount = web3.eth.accounts[0];
} else {
    console.log('No Web3 Detected, Using HTTP Provider')
    web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/xxxxxxx"));
    alert("You are not logged into a Web 3.0 enabled wallet such as MetaMask. You will be able to see the current status of the contract via Infura Web 3.0 services, but you will not be able to interact with the Dapp. Please login to a Web 3.0 wallet and refresh the page to use all functions.");
}

1 Answer 1

2
+50

See this tutorial and example in Status's documentation. https://status.im/developer_tools/run_on_status/eip-1102.html

The idea is to maintain backward compatibility while also introducing and supporting EIP-1102 if/when the browser/metamask/etc supports it. In other words, add EIP-1102 without breaking existing provider support.

It looks for EIP-1102 and if it is not there, then it falls back through other methods. That gives the user options and a UI that works with MetaMask/Status and EIP-1102, a legacy injected provider or their own node.

Please pardon the crudeness of the example app. It is intentionally so to avoid obfuscating the salient points of accomplishing this. For this reason, no cosmetic concerns or front-end framework is used except good old jQuery to put something on the screen.

const Web3 = require("web3");
const $ = require("jquery");

window.addEventListener('load', async function() {

    if (typeof ethereum !== 'undefined') {
        // Supports EIP-1102 injected Ethereum providers.
        window.web3 = new Web3(ethereum);
    } else if (typeof web3 !== 'undefined') {
        // Supports legacy injected Ethereum providers.
        window.web3 = new Web3(web3.currentProvider);
    } else {
        // Your preferred fallback.
        window.web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); 
    }

    try {
        $("#connected").html(`You are connected to network ${await window.web3.eth.net.getId()}`);
    } catch(error) {
        $("#connected").html(`You are have no access to Ethereum: ${error}`);
    }

    $("#otherAddress").change(async function() {
        try {
            $("#otherBalance").html(await window.web3.eth.getBalance($(this).val()));
        } catch(error) {
            $("#otherBalance").html(error);
        }
    });

    const displayMyAccounts = accounts => {
        try {
            if (accounts.length == 0) {
                $("#myAddresses").html("<tr><td colspan='2'>You have no addresses</td></tr>");
            } else {
                $("#myAddresses").html("");
                accounts.forEach(async myAddress => $("#myAddresses").append(`<tr>
                        <td>${myAddress}</td>
                        <td>${await window.web3.eth.getBalance(myAddress)}</td>
                    </tr>`)
                );
            }
        } catch(error) {
            $("#myAddresses").html(error);
        }
    };

    if (typeof ethereum !== 'undefined') {
        $("#allowMyAddresses").click(async () => {
            try {
                displayMyAccounts(await ethereum.enable());
            } catch(error) {
                $("#allowStatus").html("You did not allow to access your addresses");
            }
        });
    } else {
        try {
            displayMyAccounts(await window.web3.eth.getAccounts());
        } catch(error) {
            $("#myAddresses").html(`Failed to get your addresses: ${error}`);
        }        
    }

});

For clarity, where the tutorial says "in Status" you can interpret that as "in your browser with Status, MetaMask, or any other arrangement that supports EIP-1102." The example is tested with MetaMask. The hints about navigating to permissions will be a little different, of course, but the gist is the same because EIP-1102 is clear about the flow and representation of permissions.

Hope it helps.

3
  • Thank you. Ideally someone from MetaMask will answer their intent too. It is such a mess on the communication. I didn't see that tutorial in my detailed search, so thank you for providing it. Commented Jan 7, 2020 at 3:30
  • The docs will be updated at least one more time before they come out of beta, very soon. Any feedback you have about the experience would be very welcome. We would like that be a go-to doc on this topic. Thanks for accepting my answer. Commented Jan 7, 2020 at 5:02
  • Thay code example works with metamask, and yes, it would be great if someone there weighed in as well. Commented Jan 7, 2020 at 5:05

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.