В этом посте вы узнаете, как просто подключить ваше фронтенд-приложение к кошельку MetaMask, используя простой код JavaScript.
Цель состоит в том, чтобы продемонстрировать, насколько это просто, без всякого шума библиотек типа React.
Давайте начнем.
Создаем новую песочницу Vanilla JavaScript Sandbox
Для начала откроем https://codesandbox.io/ и создадим новую песочницу для VanillaJS.
Вы можете удалить все содержимое файла index.js
, чтобы начать все с чистого листа.
Теперь мы готовы к работе!
Понимание window.ethereum
Чтобы подключиться к кошельку MetaMask, нам необходимо программно проверить, установлено ли у пользователя расширение MetaMask в браузере. Для этого мы можем просто вставить этот код в наш файл index.js
:
if (typeof window.ethereum !== "undefined") {
console.log("MetaMask is installed!");
} else {
console.log("MetaMask is NOT installed!");
}
После вставки проверьте вкладку Console в вашей песочнице, вы должны увидеть сообщение console.log
. Если у вас не установлен MetaMask, убедитесь в этом, посетив официальный сайт и выбрав правильную версию для вашего браузера.
Продолжаем, что же такое window.ethereum
вообще? Если не вдаваться в подробности, расширение браузера внедряет этот объект в ваш браузер. Этот объект ethereum
более формально называется Ethereum Provider, который, как следует из названия, предоставляет вам API для доступа к сетям на основе Ethereum.
Если вам интересно узнать об API Ethereum Provider, читайте подробнее здесь.
Проверка наличия у браузера пользователя доступа к кошельку MetaMask
Теперь давайте создадим кнопку, которая будет сообщать нам, готовы ли мы подключиться к MetaMask или нет на основе того, доступен ли window.ethereum
или нет.
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<button id="connect-btn">Connect Wallet</button>
<script src="src/index.js"></script>
</body>
</html>
// index.js
function isEthereumAvailable() {
return window.ethereum !== "undefined"
}
const connectButton = document.getElementById('connect-btn')
function init() {
if (isEthereumAvailable()) {
connectButton.textContent = 'Connect Wallet'
connectButton.removeAttribute('disabled')
} else {
connectButton.textContent = 'Ethereum not available. Please install MetaMask!'
connectButton.setAttribute('disabled', true)
}
}
init()
Отлично, теперь у нас есть кнопка, которая будет отключена и скажет пользователю пойти и установить MetaMask в случае, если он недоступен. В противном случае будет отображаться «Подключить кошелек».
Отображение всплывающего окна MetaMask для запроса разрешений на просмотр учетных записей пользователей
Теперь, когда у нас есть кнопка, нам нужно убедиться, что при нажатии на нее у пользователя появится всплывающее окно MetaMask.
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<button id="connect-btn">Connect Wallet</button>
<-- Add this line so we can display the connected account -->
<div id="account">Account not connected</div>
<-- Add this line so we can display errors -->
<div id="error"></div>
<script src="src/index.js"></script>
</body>
</html>
function isEthereumAvailable() {
return window.ethereum !== "undefined";
}
const connectButton = document.getElementById("connect-btn");
// + Add this two elements to give feedback to the user
const accountElement = document.getElementById("account");
const errorElement = document.getElementById("error");
// + Add this function to request the user accounts
// This will display the MetaMask popup to the user
async function getAccounts() {
return window.ethereum.request({
method: "eth_requestAccounts"
});
}
// + Add the connect function that will be triggered by the connectButton
function connect() {
connectButton.textContent = "Loading...";
errorElement.textContent = "";
return getAccounts().then(showAccount).catch(showError);
}
// + Display the selected wallet address
function showAccount(accounts) {
if (accounts.length > 0) {
accountElement.textContent = 'Account: ' + accounts[0];
connectButton.textContent = "Connected";
}
}
// + Displays an error to the user when trying to connect the wallet
function showError(err) {
connectButton.textContent = "Connect Wallet";
errorElement.textContent = err.message;
}
function init() {
if (isEthereumAvailable()) {
connectButton.textContent = "Connect Wallet";
connectButton.removeAttribute("disabled");
// + Add this line to add the connect function to the button
connectButton.addEventListener("click", connect);
} else {
connectButton.textContent = "Ethereum not available";
connectButton.setAttribute("disabled", true);
}
}
init();
Обнаружение и отображение текущей сети
Давайте добавим новый элемент в наш index.html
.
... same as before
<body>
<button id="connect-btn">Connect Wallet</button>
<div id="account">Account not connected</div>
<div id="error"></div>
<!-- Add this line so we can display the connected network -->
<div id="chain"></div>
<script src="src/index.js"></script>
</body>
В наш файл index.js
мы добавим пару новых функций:
... same as before
// Add this after errorElement variable
const chainElement = document.getElementById("chain");
async function getChainId() {
const chainId = await window.ethereum.request({ method: "eth_chainId" });
return chainId;
}
// We will use this to display a friendly name for the chain,
// as the getChainId method will give us the chainId.
function getChainName(chainId) {
switch (chainId) {
case "0x1":
return "Ethereum Main Network";
case "0x3":
return "Ropsten Test Network";
case "0x4":
return "Rinkeby Test Network";
case "0x5":
return "Goerli Test Network";
case "0x2a":
return "Kovan Test Network";
default:
default: "Chain not supported"
}
}
function showChain(chainId) {
chainElement.textContent = getChainName(chainId);
}
А в нашей функции connect
обновим ее, чтобы она выглядела следующим образом:
function connect() {
connectButton.textContent = "Loading...";
chainElement.textContent = "";
errorElement.textContent = "";
return getAccounts()
.then(showAccount)
.then(getChainId)
.then(showChain)
.catch(showError);
}
Вот и все, конечный результат должен выглядеть следующим образом: