Лучший шаблон для обнаружения учетной записи web3 по умолчанию при использовании метамаски

Контекст: я хочу использовать блоки для отображения идентификатора на странице, я получаю учетную запись по умолчанию из web3, для этого пользователь должен войти в метамаску с выбранным адресом из своего кошелька.

Проблема: веб-приложение, похоже, не обнаруживает объект web3 в событии загрузки страницы, что является рекомендуемым местом для его обнаружения.

Код: ниже вдохновлен рекомендациями по адресу:

https://github.com/MetaMask/metamask-plugin/issues/1158

https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#partly_sunny-web3---ethereum-browser-environment-check

У меня постоянно прерывистое поведение, иногда web3 есть, а иногда нет, единственное решение, которое я могу придумать, это иметь таймер, но мне это кажется слишком упрощенным, я бы предпочел что-то более элегантное.

Вопрос: есть ли лучшее решение для обнаружения defaultAccount из web3 при загрузке страницы?

 function startApp() { 
        GenerateIdenticon();  
}  


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

// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {

    // Use Mist/MetaMask's provider
    window.web3 = new Web3(web3.currentProvider); 
    if (web3.currentProvider.isMetaMask === true) {
        if (typeof web3.eth.defaultAccount === 'undefined') {
            document.body.innerHTML = '<body><h1>Oops! Your browser does not support Ethereum Ðapps.</h1></body>';   
        }
        else {
            startApp();
        }
    }
    else {
         alert('No web3? Please use google chrome and metamask plugin to enter this Dapp!', null, null);
        // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
       window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

Ответы (6)

Единственный метод, который более надежен, чем accountsмассив на странице, — это web3.eth.getAccounts(accounts => console.log(accounts[0])).

Это асинхронно запросит массив учетных записей и перезвонит, когда он будет доступен.

Большое спасибо DanF, действительно, использование этого метода всегда возвращает желаемое состояние.
Альтернатива: web3.eth.getAccounts().then(...).
Поскольку я не мог получить эту работу, я обнаружил, что getAccountsметод возвращает два (2) аргумента accounts, а второй. Другими словами: web3.eth.getAccounts((error, accounts) => console.log(accounts[0])).
Есть ли риск залить провайдера, если он будет запрашивать эту информацию каждые 100 секунд?
Это не серьезный риск, поскольку поставщик учетных записей является локальным, поэтому это не сетевой вызов, а просто проверка значения на том же компьютере.
консоль chrome показывает «неопределенное» значение

Вот что я использую в своем Dapp. Кажется, это работает очень хорошо.

function getWeb3(callback) {
  if (typeof window.web3 === 'undefined') {
    // no web3, use fallback
    console.error("Please use a web3 browser");
  } else {
    // window.web3 == web3 most of the time. Don't override the provided,
    // web3, just wrap it in your Web3.
    var myWeb3 = new Web3(window.web3.currentProvider); 

    // the default account doesn't seem to be persisted, copy it to our
    // new instance
    myWeb3.eth.defaultAccount = window.web3.eth.defaultAccount;

    callback(myWeb3);
  }
}

Используется так:

function startApp(web3) {
  // ...
}

window.addEventListener('load', function() {
  getWeb3(startApp);
});

Ключевое отличие состоит в том, что я копирую defaultAccountиз исходного window.web3экземпляра. Вы заметите, что web3.eth.defaultAccountв Javascript API нет кода, специфичного для MetaMask. MetaMask, кажется, заполняется, defaultAccountкак только вы разблокируете свой кошелек.

спасибо @0xcaff, но, как упомянул DeviateFish, у вас все еще есть та же проблема с учетными записями по умолчанию, в любом случае, я думаю, вы правы в том, что у вас нет кода, связанного с метамаской, и вы просто полагаетесь на стандартные вызовы web3.

Проверка web3.eth.accounts[0]каждые 100 миллисекунд в некоторых случаях намного быстрее, чем web3.eth.getAccounts():

// Option 1:
web3.eth.getAccounts(console.log);

// Option 2:
(function loop() {
  if (web3.eth.accounts[0]) {
    console.log(web3.eth.accounts[0]);
  } else {
    setTimeout(loop, 100);
  }
}());

При использовании прокси Option 2для меня это было на 35 секунд (!) быстрее, чем Option 1.


Что касается web3.eth.defaultAccount, в документации MetaMask говорится, что его не следует использовать для определения того, какая учетная запись выбрана пользователем в данный момент.


Кстати, согласно документации MetaMask, вам нужно будет использовать setTimeout/ setIntervalв любом случае для прослушивания выбранных изменений учетной записи :

var account = web3.eth.accounts[0];
setInterval(function () {
  if (web3.eth.accounts[0] !== account) {
    account = web3.eth.accounts[0];
    updateInterface();
  }
}, 100);

Пара вещей, которые я заметил при использовании MetaMask.

  1. defaultAccountне всегда заселен. Безопаснее использовать web3.eth.accounts[0]в качестве проверки, выбрана ли учетная запись. Вам нужно будет периодически опрашивать это значение, если вы хотите знать, изменяется ли выбранная учетная запись.

  2. Это loadсобытие также не всегда кажется надежным в некоторых моих опытах. В конце концов я решил это, но у меня нет кода, чтобы продемонстрировать, как это сделать. Я обновлю этот пост позже, если этого accounts[0]фрагмента недостаточно.

спасибо @DeviateFish, действительно, я не мог убедиться, что defaultAccount заполняется каждый раз, но DanF рекомендовал использовать getAccounts с функцией обратного вызова. Кроме того, для смены учетных записей было бы неплохо иметь обратный вызов часов всякий раз, когда изменяется учетная запись, не так ли?
Это действительно было бы неплохо, но такого в настоящее время нет в web3 (или через RPC, насколько я могу судить). Использование асинхронных методов, как правило, предпочтительнее, поэтому я рад, что это работает для вас!

Если вы используете React и столкнулись с этой проблемой, вам нужно сделать только это:

1. Установите веб3:

npm install web3 --save

2. Проверяем, есть ли MetaMask:

import 'Web3' from 'web3';

// ...

componentDidMount() {

  // Check if Web3 has been injected by the browser (MetaMask).
  // (since 'web3' is global, we need to use 'window')
  if (window.web3 && window.web3.currentProvider.isMetaMask) {
    window.web3.eth.getAccounts((error, accounts) => {

      // Do whatever you need to.
      this.setState({wallet: accounts[0]});
    });
  } else {
    console.log('MetaMask account not detected :(');
  }
}
В этот момент мое свойство currentProvider возвращает значение null. Есть ли обратный вызов или что-то, что я могу использовать, когда он завершится?
@user339946 возвращается, nullкогда вы разблокировали Metamask?
Да, метамаска разблокирована. Похоже, что на данный момент объект web3 еще не готов для компонента. Любые идеи?
Как вы проверяете мобильные кошельки, такие как Trust или Status? Я могу поймать метамаску просто, но ни один из мобильных кошельков.
Я не знаю @JamboDev :(

Не бессовестный штекер.

Недавно начал использовать: https://www.blocknative.com/

Они предлагают хорошие шаблоны адаптации с MetaMask.