Смарт-контракт Hello World для початківців — повний стек
Цей посібник для вас, якщо ви новачок у розробці блокчейнів і не знаєте, з чого почати або як розгортати смарт-контракти та взаємодіяти з ними. Ми пройдемо процес створення та розгортання простого смарт-контракту в тестовій мережі Goerli за допомогою MetaMask (opens in a new tab), Solidity (opens in a new tab), Hardhat (opens in a new tab) та Alchemy (opens in a new tab).
Щоб завершити цей посібник, вам знадобиться обліковий запис Alchemy. Зареєструйте безкоштовний обліковий запис (opens in a new tab).
Якщо у вас виникнуть запитання, сміливо ставте їх у Discord Alchemy (opens in a new tab)!
Частина 1. Створення та розгортання смарт-контракту за допомогою Hardhat
Підключення до мережі Ethereum
Існує багато способів робити запити до ланцюга Ethereum. Для простоти ми будемо використовувати безкоштовний обліковий запис на Alchemy, платформі для розробників блокчейну та API, яка дає змогу взаємодіяти з ланцюгом Ethereum без необхідності запускати власний вузол. Alchemy також має інструменти розробника для моніторингу та аналітики; ми скористаємося ними в цьому посібнику, щоб зрозуміти, що відбувається «під капотом» під час розгортання нашого смарт-контракту.
Створення застосунку та ключа API
Створивши обліковий запис Alchemy, ви можете згенерувати ключ API, створивши застосунок. Це дасть вам змогу робити запити до тестової мережі Goerli. Якщо ви не знайомі з тестовими мережами, ви можете прочитати посібник Alchemy щодо вибору мережі (opens in a new tab).
На інформаційній панелі Alchemy знайдіть спадний список Apps на панелі навігації та натисніть Create App.
Дайте своєму застосунку назву «Hello World» і напишіть короткий опис. Виберіть Staging як своє середовище та Goerli як мережу.
Примітка: обов’язково виберіть Goerli, інакше цей посібник не працюватиме.
Натисніть Create app. Ваш застосунок з’явиться в таблиці нижче.
Створення облікового запису Ethereum
Вам потрібен обліковий запис Ethereum для надсилання та отримання транзакцій. Ми будемо використовувати MetaMask, віртуальний гаманець у браузері, який дає змогу користувачам керувати адресою свого облікового запису Ethereum.
Ви можете завантажити та створити обліковий запис MetaMask безкоштовно тут (opens in a new tab). Під час створення облікового запису, або якщо у вас вже є обліковий запис, обов’язково переключіться на «тестову мережу Goerli» у верхньому правому куті (щоб ми не мали справу з реальними грошима).
Крок 4. Додайте ефір (ether) із крана (Faucet)
Щоб розгорнути смарт-контракт у тестовій мережі, вам знадобляться несправжні ETH. Щоб отримати ETH у мережі Goerli, перейдіть до крана Goerli та введіть адресу свого облікового запису Goerli. Зауважте, що останнім часом крани Goerli можуть бути дещо ненадійними. Перегляньте сторінку тестових мереж, щоб побачити список варіантів, які можна спробувати:
Примітка: через перевантаження мережі це може зайняти деякий час. ``
Крок 5. Перевірка балансу
Щоб ще раз перевірити, чи є ETH у вашому гаманці, зробімо запит eth_getBalance (opens in a new tab) за допомогою інструмента-композитора від Alchemy (opens in a new tab). Це поверне кількість ETH в ваш гаманець. Щоб дізнатися більше, перегляньте короткий посібник від Alchemy про те, як використовувати інструмент-композитор (opens in a new tab).
Введіть адресу свого облікового запису MetaMask і натисніть Send Request. Ви побачите відповідь, схожу на фрагмент коду нижче.
1{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }Примітка: цей результат у wei, а не в ETH. Wei використовується як найменша одиниця ефіру.
Фух! Наші "гроші" все ще там.
Крок 6. Ініціалізація проєкту
Спочатку нам потрібно буде створити папку для нашого проєкту. Перейдіть до командного рядка та введіть наступне.
1mkdir hello-world2cd hello-worldТепер, коли ми всередині папки нашого проєкту, ми використаємо npm init для ініціалізації проєкту.
Якщо у вас ще не встановлено npm, дотримуйтесь цих інструкцій, щоб встановити Node.js та npm (opens in a new tab).
Для цілей цього посібника не має значення, як ви відповісте на запитання щодо ініціалізації. Ось як ми це зробили для довідки:
1назва пакета: (hello-world)2версія: (1.0.0)3опис: смарт-контракт hello world4точка входу: (index.js)5тестова команда:6репозиторій git:7ключові слова:8автор:9ліцензія: (ISC)1011Збираюся записати в /Users/.../.../.../hello-world/package.json:1213{14 "name": "hello-world",15 "version": "1.0.0",16 "description": "смарт-контракт hello world",17 "main": "index.js",18 "scripts": {19 "test": "echo \"Помилка: тест не вказано\" && exit 1"20 },21 "author": "",22 "license": "ISC"23}Показати всеПідтвердіть package.json, і можемо продовжувати!
Крок 7. Завантаження Hardhat
Hardhat є середовищем розробки для компіляції, розгортання, тестування та налагодження вашого програмного забезпечення Ethereum. Це допомагає розробникам створювати смарт-контракти та dapp локально перед розгортанням у реальний ланцюжок.
Усередині нашого проєкту hello-world запустіть:
1npm install --save-dev hardhatПерегляньте цю сторінку, щоб дізнатися більше про інструкції з установки (opens in a new tab).
Крок 8. Створення проєкту Hardhat
У папці нашого проєкту hello-world запустіть:
1npx hardhatПотім ви маєте побачити вітальне повідомлення та вибір подальших бажаних дій. Оберіть "Створити порожній hardhat.config.js":
1888 888 888 888 8882888 888 888 888 8883888 888 888 888 88848888888888 8888b. 888d888 .d88888 88888b. 8888b. 8888885888 888 "88b 888P" d88" 888 888 "88b "88b 8886888 888 .d888888 888 888 888 888 888 .d888888 8887888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888910👷 Ласкаво просимо до Hardhat v2.0.11 👷1112Що ви хочете зробити? …13Створити зразок проєкту14❯ Створити порожній hardhat.config.js15ВийтиПоказати всеЦе згенерує файл hardhat.config.js у проєкті. Ми використаємо це пізніше в посібнику, щоб вказати налаштування для нашого проєкту.
Крок 9. Додавання папок проєкту
Щоб проєкт був упорядкований, створимо дві нові папки. У командному рядку перейдіть до кореневого каталогу вашого проєкту hello-world і введіть:
1mkdir contracts2mkdir scriptscontracts/— тут ми будемо зберігати файл коду нашого смарт-контракту «hello world».scripts/— тут ми будемо зберігати скрипти для розгортання та взаємодії з нашим контрактом.
Крок 10. Написання нашого контракту
Ви, можливо, запитуєте себе, коли ми вже будемо писати код? Час настав!
Відкрийте проєкт hello-world у вашому улюбленому редакторі. Смарт-контракти найчастіше пишуться на Solidity, яку ми й будемо використовувати для написання нашого смарт-контракту.
- Перейдіть до папки
contractsі створіть новий файл під назвоюHelloWorld.sol - Нижче наведено зразок смарт-контракту Hello World, який ми будемо використовувати для цього посібника. Скопіюйте вміст нижче у файл
HelloWorld.sol.
Примітка: обов’язково прочитайте коментарі, щоб зрозуміти, що робить цей контракт.
1// Визначає версію Solidity, використовуючи семантичне версіонування.2// Дізнатися більше: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma3pragma solidity >=0.7.3;45// Визначає контракт з назвою `HelloWorld`.6// Контракт — це набір функцій і даних (його стан). Після розгортання контракт знаходиться за певною адресою в блокчейні Ethereum. Дізнатися більше: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html7contract HelloWorld {89 // Викликається, коли викликається функція оновлення10 // Події смарт-контракту — це спосіб, за допомогою якого ваш контракт повідомляє, що щось сталося в блокчейні, вашому зовнішньому застосунку, який може «слухати» певні події та реагувати на них, коли вони відбуваються.11 event UpdatedMessages(string oldStr, string newStr);1213 // Оголошує змінну стану `message` типу `string`.14 // Змінні стану — це змінні, значення яких постійно зберігаються в сховищі контракту. Ключове слово `public` робить змінні доступними ззовні контракту та створює функцію, яку інші контракти або клієнти можуть викликати для доступу до значення.15 string public message;1617 // Подібно до багатьох класових об'єктно-орієнтованих мов, конструктор — це спеціальна функція, яка виконується лише під час створення контракту.18 // Конструктори використовуються для ініціалізації даних контракту. Дізнатися більше:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors19 constructor(string memory initMessage) {2021 // Приймає строковий аргумент `initMessage` та встановлює значення у змінну сховища контракту `message`).22 message = initMessage;23 }2425 // Публічна функція, яка приймає строковий аргумент і оновлює змінну сховища `message`.26 function update(string memory newMessage) public {27 string memory oldMsg = message;28 message = newMessage;29 emit UpdatedMessages(oldMsg, newMessage);30 }31}Показати всеЦе базовий смарт-контракт, який зберігає повідомлення після створення. Його можна оновити, викликавши функцію update.
Крок 11. Підключення MetaMask і Alchemy до вашого проєкту
Ми створили гаманець MetaMask, обліковий запис Alchemy і написали наш смарт-контракт, тепер час з’єднати всі три компоненти.
Кожна транзакція, надіслана з вашого гаманця, вимагає підпису з використанням вашого унікального приватного ключа. Щоб надати нашій програмі цей дозвіл, ми можемо безпечно зберігати наш приватний ключ у файлі середовища. Ми також будемо зберігати тут ключ API для Alchemy.
Щоб дізнатися більше про надсилання транзакцій, перегляньте цей посібник (opens in a new tab) про надсилання транзакцій за допомогою web3.
Спочатку встановіть пакет dotenv у каталог вашого проєкту:
1npm install dotenv --saveПотім створіть файл .env у кореневому каталозі проєкту. Додайте до нього свій приватний ключ MetaMask та URL-адресу HTTP API Alchemy.
Ваш файл середовища має називатися .env, інакше він не буде розпізнаний як файл середовища.
Не називайте його process.env або .env-custom чи якось інакше.
- Дотримуйтесь цих інструкцій (opens in a new tab), щоб експортувати свій приватний ключ
- Дивіться нижче, щоб отримати URL-адресу HTTP Alchemy API
Ваш .env повинен виглядати так:
1API_URL = "https://eth-goerli.alchemyapi.io/v2/ваш-api-ключ"2PRIVATE_KEY = "ваш-приватний-ключ-metamask"Щоб підключити їх до нашого коду, ми будемо посилатися на ці змінні в нашому файлі hardhat.config.js на кроці 13.
Крок 12: Встановлення Ethers.js
Ethers.js — це бібліотека, яка спрощує взаємодію та надсилання запитів до Ethereum, огортаючи стандартні методи JSON-RPC (opens in a new tab) більш зручними для користувача методами.
Hardhat дає змогу інтегрувати плагіни (opens in a new tab) для додаткових інструментів і розширеної функціональності. Ми скористаємося плагіном Ethers (opens in a new tab) для розгортання контракту.
У каталозі вашого проєкту надрукуйте:
npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"Крок 13. Оновлення hardhat.config.js
Ми вже додали кілька залежностей і плагінів, тепер нам потрібно оновити hardhat.config.js, щоб наш проєкт знав про них усі.
Оновіть ваш hardhat.config.js так, щоб він виглядав наступним чином:
1/**2 * @type import('hardhat/config').HardhatUserConfig3 */45require("dotenv").config()6require("@nomiclabs/hardhat-ethers")78const { API_URL, PRIVATE_KEY } = process.env910module.exports = {11 solidity: "0.7.3",12 defaultNetwork: "goerli",13 networks: {14 hardhat: {},15 goerli: {16 url: API_URL,17 accounts: [`0x${PRIVATE_KEY}`],18 },19 },20}Показати всеКрок 14. Компіляція нашого контракту
Щоб переконатися, що все працює, скомпілюймо наш контракт. Завдання compile є одним із вбудованих завдань Hardhat.
З командного рядка запустіть:
npx hardhat compileВи можете отримати попередження про SPDX license identifier not provided in source file, але не варто про це турбуватися — сподіваємось, усе інше виглядає добре! Якщо ні, ви завжди можете написати повідомлення в Discord-каналі Alchemy (opens in a new tab).
Крок 15. Написання нашого скрипта розгортання
Тепер, коли контракт написано, і файл конфігурації готовий до запуску, настав час написати скрипт розгортання контракту.
Перейдіть до папки scripts/ і створіть новий файл deploy.js, додавши до нього такий вміст:
1async function main() {2 const HelloWorld = await ethers.getContractFactory("HelloWorld")34 // Почати розгортання, повертаючи проміс, який перетворюється на об'єкт контракту5 const hello_world = await HelloWorld.deploy("Hello World!")6 console.log("Контракт розгорнуто за адресою:", hello_world.address)7}89main()10 .then(() => process.exit(0))11 .catch((error) => {12 console.error(error)13 process.exit(1)14 })Показати всеHardhat чудово пояснює, що робить кожен із цих рядків коду, у своєму посібнику з контрактів (opens in a new tab), ми використали їхні пояснення тут.
1const HelloWorld = await ethers.getContractFactory("HelloWorld")ContractFactory в ethers.js — це абстракція, яка використовується для розгортання нових смарт-контрактів, тому HelloWorld тут — це фабрика (opens in a new tab) для екземплярів нашого контракту hello world. При використанні плагіна hardhat-ethers екземпляри ContractFactory та Contract за замовчуванням підключаються до першого підписанта (власника).
1const hello_world = await HelloWorld.deploy()Виклик deploy() для ContractFactory запустить розгортання та поверне Promise, який перетвориться на об'єкт Contract. Це об'єкт, у якого є метод для кожної з наших функцій смартконтракту.
Крок 16: Розгортання нашого контракту
Ми нарешті готові розгорнути наш розумний контракт! Перейдіть до командного рядка та запустіть:
npx hardhat run scripts/deploy.js --network goerliТоді ви повинні побачити щось на кшталт:
Контракт розгорнуто за адресою: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570Будь ласка, збережіть цю адресу. Ми будемо використовувати її пізніше в цьому посібнику.
Якщо ми перейдемо на Etherscan для Goerli (opens in a new tab) і знайдемо адресу нашого контракту, ми повинні побачити, що він був успішно розгорнутий. Транзакція буде виглядати приблизно так:
Адреса From має збігатися з адресою вашого облікового запису MetaMask, а адреса To буде вказувати на створення контракту. Якщо ми клацнемо на транзакцію, ми побачимо адресу нашого контракту в полі To.
Вітаємо! Ви щойно розгорнули смарт-контракт у тестовій мережі Ethereum.
Щоб зрозуміти, що відбувається «під капотом», перейдімо на вкладку «Провідник» на нашій інформаційній панелі Alchemy (opens in a new tab). Якщо у вас є кілька застосунків Alchemy, обов’язково відфільтруйте їх за застосунком і виберіть Hello World.
Тут ви побачите кілька методів JSON-RPC, які Hardhat/Ethers виконали для нас «під капотом», коли ми викликали функцію .deploy(). Два важливі методи тут — це eth_sendRawTransaction (opens in a new tab), який є запитом на запис нашого контракту в ланцюг Goerli, та eth_getTransactionByHash (opens in a new tab), який є запитом на читання інформації про нашу транзакцію за заданим хешем. Щоб дізнатися більше про надсилання транзакцій, перегляньте наш посібник про надсилання транзакцій за допомогою Web3.
Частина 2. Взаємодія з вашим смарт-контрактом
Тепер, коли ми успішно розгорнули смарт-контракт у мережі Goerli, давайте дізнаємося, як з ним взаємодіяти.
Створення файлу interact.js
Це файл, у якому ми напишемо наш скрипт взаємодії. Ми будемо використовувати бібліотеку Ethers.js, яку ви раніше встановили в Частині 1.
Усередині папки scripts/ створіть новий файл з назвою interact.js та додайте наступний код:
1// interact.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESSОновіть свій файл .env
Ми будемо використовувати нові змінні середовища, тому нам потрібно визначити їх у файлі .env, який ми створили раніше.
Нам потрібно буде додати визначення для нашого Alchemy API_KEY та CONTRACT_ADDRESS, де був розгорнутий ваш смарт-контракт.
Ваш файл .env повинен виглядати приблизно так:
# .envAPI_URL = "https://eth-goerli.alchemyapi.io/v2/<ваш-api-ключ>"API_KEY = "<ваш-api-ключ>"PRIVATE_KEY = "<ваш-приватний-ключ-metamask>"CONTRACT_ADDRESS = "0x<адреса вашого контракту>"Отримайте ABI вашого контракту
Наш контракту — це інтерфейс для взаємодії з нашим смарт-контрактом. Hardhat автоматично генерує ABI і зберігає його у HelloWorld.json. Щоб використовувати ABI, нам потрібно буде розібрати вміст, додавши наступні рядки коду до нашого файлу interact.js:
1// interact.js2const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")Якщо ви хочете побачити ABI, ви можете вивести його в консоль:
1console.log(JSON.stringify(contract.abi))Щоб побачити ваш ABI, надрукований у консолі, перейдіть до терміналу та запустіть:
npx hardhat run scripts/interact.jsСтворення екземпляру вашого контракту
Щоб взаємодіяти з нашим контрактом, нам потрібно створити екземпляр контракту в нашому коді. Для цього з Ethers.js нам потрібно буде працювати з трьома поняттями:
- Provider (Постачальник) - постачальник вузлів, який дає вам доступ на читання та запис до блокчейну
- Signer (Підписант) - представляє обліковий запис Ethereum, який може підписувати транзакції
- Contract (Контракт) - об'єкт Ethers.js, що представляє конкретний контракт, розгорнутий в мережі
Ми використаємо ABI контракту з попереднього кроку для створення екземпляра нашого контракту:
1// interact.js23// Постачальник4const alchemyProvider = new ethers.providers.AlchemyProvider(5 (network = "goerli"),6 API_KEY7)89// Підписант10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1112// Контракт13const helloWorldContract = new ethers.Contract(14 CONTRACT_ADDRESS,15 contract.abi,16 signer17)Показати всеДізнайтеся більше про постачальників, підписантів та контракти в документації ethers.js (opens in a new tab).
Читання початкового повідомлення
Пам'ятаєте, коли ми розгортали наш контракт з initMessage = "Hello world!"? Тепер ми прочитаємо це повідомлення, збережене в нашому смарт-контракті, і виведемо його в консоль.
У JavaScript асинхронні функції використовуються при взаємодії з мережами. Щоб дізнатися більше про асинхронні функції, прочитайте цю статтю на Medium (opens in a new tab).
Використовуйте код нижче, щоб викликати функцію message у нашому смарт-контракті та прочитати початкове повідомлення:
1// interact.js23// ...45async function main() {6 const message = await helloWorldContract.message()7 console.log("Повідомлення: " + message)8}9main()Показати всеПісля запуску файлу за допомогою npx hardhat run scripts/interact.js у терміналі ми повинні побачити таку відповідь:
1Повідомлення: Hello world!Вітаємо! Ви щойно успішно прочитали дані смарт-контракту з блокчейну Ethereum, так тримати!
Оновлення повідомлення
Замість того, щоб просто читати повідомлення, ми також можемо оновити повідомлення, збережене в нашому смарт-контракті, за допомогою функції update! Круто, чи не так?
Щоб оновити повідомлення, ми можемо безпосередньо викликати функцію update на нашому екземплярі об'єкта Contract:
1// interact.js23// ...45async function main() {6 const message = await helloWorldContract.message()7 console.log("Повідомлення: " + message)89 console.log("Оновлення повідомлення...")10 const tx = await helloWorldContract.update("Це нове повідомлення.")11 await tx.wait()12}13main()Показати всеЗверніть увагу, що в рядку 11 ми робимо виклик .wait() для повернутого об'єкта транзакції. Це гарантує, що наш скрипт чекає, поки транзакція буде видобута в блокчейні, перш ніж вийти з функції. Якщо виклик .wait() не буде включений, скрипт може не побачити оновлене значення message в контракті.
Прочитати нове повідомлення
Ви повинні бути в змозі повторити попередній крок, щоб прочитати оновлене значення message. Приділіть хвилинку і подивіться, чи зможете ви внести необхідні зміни, щоб вивести це нове значення!
Якщо вам потрібна підказка, ось як на даний момент повинен виглядати ваш файл interact.js:
1// interact.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS67const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")89// провайдер - Alchemy10const alchemyProvider = new ethers.providers.AlchemyProvider(11 (network = "goerli"),12 API_KEY13)1415// підписант - ви16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1718// екземпляр контракту19const helloWorldContract = new ethers.Contract(20 CONTRACT_ADDRESS,21 contract.abi,22 signer23)2425async function main() {26 const message = await helloWorldContract.message()27 console.log("Повідомлення: " + message)2829 console.log("Оновлення повідомлення...")30 const tx = await helloWorldContract.update("це нове повідомлення")31 await tx.wait()3233 const newMessage = await helloWorldContract.message()34 console.log("Нове повідомлення: " + newMessage)35}3637main()Показати всеТепер просто запустіть скрипт, і ви повинні побачити старе повідомлення, статус оновлення та нове повідомлення, виведені у вашому терміналі!
npx hardhat run scripts/interact.js --network goerli
1Повідомлення: Hello World!2Оновлення повідомлення...3Нове повідомлення: Це нове повідомлення.Під час виконання цього скрипта ви можете помітити, що крок Оновлення повідомлення... займає деякий час, перш ніж завантажиться нове повідомлення. Це пов'язано з процесом майнінгу; якщо вам цікаво відстежувати транзакції під час їхнього майнінгу, відвідайте мемпул Alchemy (opens in a new tab), щоб побачити статус транзакції. Якщо транзакцію відхилено, корисно також перевірити Goerli Etherscan (opens in a new tab) і знайти хеш вашої транзакції.
Частина 3. Публікація вашого смарт-контракту на Etherscan
Ви виконали всю важку роботу, щоб втілити в життя свій смарт-контракт; тепер настав час поділитися ним зі світом!
Підтвердивши свій смарт-контракт на Etherscan, будь-хто зможе переглянути ваш вихідний код і взаємодіяти з вашим смарт-контрактом. Почнімо!
Крок 1: Створіть ключ API у своєму акаунті Etherscan
Ключ API Etherscan необхідний для підтвердження того, що ви є власником смарт-контракту, який ви намагаєтеся опублікувати.
Якщо у вас ще немає акаунта Etherscan, зареєструйтеся (opens in a new tab).
Після входу в систему знайдіть своє ім'я користувача на панелі навігації, наведіть на нього курсор і виберіть кнопку Мій профіль.
На сторінці вашого профілю ви повинні побачити бічну панель навігації. На бічній панелі навігації виберіть API Keys. Далі натисніть кнопку «Додати», щоб створити новий ключ API, назвіть свій застосунок hello-world і натисніть кнопку Створити новий ключ API.
Ваш новий ключ API повинен з'явитися в таблиці ключів API. Скопіюйте ключ API до буфера обміну.
Далі, нам потрібно додати ключ API Etherscan до нашого файлу .env.
Після його додавання ваш файл .env повинен виглядати так:
1API_URL = "https://eth-goerli.alchemyapi.io/v2/ваш-api-ключ"2PUBLIC_KEY = "ваша-публічна-адреса-акаунту"3PRIVATE_KEY = "ваша-приватна-адреса-акаунту"4CONTRACT_ADDRESS = "адреса-вашого-контракту"5ETHERSCAN_API_KEY = "ваш-ключ-etherscan"Смарт-контракти, розгорнуті за допомогою Hardhat
Встановіть hardhat-etherscan
Публікація вашого контракту на Etherscan за допомогою Hardhat є простою. Спочатку вам потрібно буде встановити плагін hardhat-etherscan, щоб почати. hardhat-etherscan автоматично перевірить вихідний код смарт-контракту та ABI на Etherscan. Щоб додати це, у каталозі hello-world запустіть:
1npm install --save-dev @nomiclabs/hardhat-etherscanПісля встановлення додайте наступний вираз на початку вашого файлу hardhat.config.js і додайте опції конфігурації Etherscan:
1// hardhat.config.js23require("dotenv").config()4require("@nomiclabs/hardhat-ethers")5require("@nomiclabs/hardhat-etherscan")67const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env89module.exports = {10 solidity: "0.7.3",11 defaultNetwork: "goerli",12 networks: {13 hardhat: {},14 goerli: {15 url: API_URL,16 accounts: [`0x${PRIVATE_KEY}`],17 },18 },19 etherscan: {20 // Ваш ключ API для Etherscan21 // Отримайте його на https://etherscan.io/22 apiKey: ETHERSCAN_API_KEY,23 },24}Показати всеПеревірте свій смарт-контракт на Etherscan
Переконайтеся, що всі файли збережені, а всі змінні .env налаштовані правильно.
Запустіть завдання verify, передавши адресу контракту та мережу, в якій він розгорнутий:
1npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'Переконайтеся, що DEPLOYED_CONTRACT_ADDRESS — це адреса вашого розгорнутого смарт-контракту в тестовій мережі Goerli. Також останній аргумент ('Hello World!') має бути таким самим рядковим значенням, яке використовувалося на етапі розгортання в частині 1.
Якщо все пройде добре, ви побачите наступне повідомлення у вашому терміналі:
1Джерельний код контракту успішно надіслано2contracts/HelloWorld.sol:HelloWorld за адресою 0xdeployed-contract-address3для перевірки на Etherscan. Очікування результату перевірки...456Контракт HelloWorld успішно перевірено на Etherscan.7https://goerli.etherscan.io/address/<адреса-контракту>#contractsВітаємо! Код вашого смарт-контракту є на Etherscan!
Перегляньте свій смарт-контракт на Etherscan!
Коли ви перейдете за посиланням, наданим у вашому терміналі, ви зможете побачити код свого смарт-контракту та ABI, опубліковані на Etherscan!
Вау - ти це зробив, чемпіоне! Тепер будь-хто може викликати ваш смарт-контракт або писати в нього! Ми з нетерпінням чекаємо, що ви створите далі!
Частина 4 - Інтеграція вашого смарт-контракту з фронтендом
До кінця цього посібника ви дізнаєтеся, як:
- Підключити гаманець MetaMask до вашого dapp
- Читання даних з вашого смарт-контракту за допомогою Alchemy Web3 (opens in a new tab) API
- Підписувати транзакції Ethereum за допомогою MetaMask
Для цього dapp ми будемо використовувати React (opens in a new tab) як наш фронтенд-фреймворк; однак важливо зазначити, що ми не будемо витрачати багато часу на розбір його основ, оскільки ми переважно зосередимося на впровадженні функціональності Web3 у наш проєкт.
Як попередня умова, ви повинні мати початкове розуміння React. Якщо ні, ми рекомендуємо пройти офіційний Вступ до React (opens in a new tab).
Клонуйте стартові файли
Спочатку перейдіть до репозиторію hello-world-part-four на GitHub (opens in a new tab), щоб отримати стартові файли для цього проєкту та клонувати цей репозиторій на свій локальний комп'ютер.
Відкрийте клонований репозиторій локально. Зверніть увагу, що він містить дві папки: starter-files та completed.
starter-files- ми будемо працювати в цьому каталозі, ми підключимо інтерфейс користувача до вашого гаманця Ethereum та смарт-контракту, який ми опублікували на Etherscan у Частині 3.completedмістить повністю завершений посібник і повинен використовуватися лише як довідник, якщо ви застрягли.
Далі, відкрийте свою копію starter-files у вашому улюбленому редакторі коду, а потім перейдіть до папки src.
Весь код, який ми будемо писати, буде знаходитися в папці src. Ми будемо редагувати компонент HelloWorld.js та файли JavaScript util/interact.js, щоб надати нашому проєкту функціональність Web3.
Перевірте стартові файли
Перш ніж ми почнемо кодувати, давайте розглянемо, що нам надано в стартових файлах.
Запустіть ваш проєкт на React
Давайте почнемо із запуску проєкту на React у нашому браузері. Перевага React полягає в тому, що коли наш проєкт запущений у браузері, будь-які збережені зміни будуть оновлюватися в браузері в реальному часі.
Щоб запустити проєкт, перейдіть до кореневого каталогу папки starter-files і запустіть npm install у вашому терміналі, щоб встановити залежності проєкту:
cd starter-filesnpm installПісля завершення інсталяції запустіть npm start у вашому терміналі:
npm startЦе повинно відкрити http://localhost:3000/ (opens in a new tab) у вашому браузері, де ви побачите фронтенд для нашого проєкту. Він повинен складатися з одного поля (місця для оновлення повідомлення, що зберігається у вашому смарт-контракті), кнопки «Підключити гаманець» та кнопки «Оновити».
Якщо ви спробуєте натиснути будь-яку кнопку, ви помітите, що вони не працюють — це тому, що нам ще потрібно запрограмувати їх функціональність.
Компонент HelloWorld.js
Давайте повернемося до папки src у нашому редакторі та відкриємо файл HelloWorld.js. Дуже важливо, щоб ми розуміли все в цьому файлі, оскільки це основний компонент React, над яким ми будемо працювати.
У верхній частині цього файлу ви помітите кілька операторів імпорту, які необхідні для запуску нашого проєкту, включаючи бібліотеку React, хуки useEffect та useState, деякі елементи з ./util/interact.js (ми опишемо їх детальніше пізніше!), та логотип Alchemy.
1// HelloWorld.js23import React from "react"4import { useEffect, useState } from "react"5import {6 helloWorldContract,7 connectWallet,8 updateMessage,9 loadCurrentMessage,10 getCurrentWalletConnected,11} from "./util/interact.js"1213import alchemylogo from "./alchemylogo.svg"Показати всеДалі у нас є змінні стану, які ми будемо оновлювати після певних подій.
1// HelloWorld.js23//Змінні стану4const [walletAddress, setWallet] = useState("")5const [status, setStatus] = useState("")6const [message, setMessage] = useState("Немає з'єднання з мережею.")7const [newMessage, setNewMessage] = useState("")Ось що представляє кожна зі змінних:
walletAddress— рядок, у якому зберігається адреса гаманця користувачаstatus- рядок, який зберігає корисне повідомлення, що направляє користувача, як взаємодіяти з dappmessage- рядок, який зберігає поточне повідомлення у смарт-контрактіnewMessage- рядок, який зберігає нове повідомлення, яке буде записано в смарт-контракт
Після змінних стану ви побачите п'ять нереалізованих функцій: useEffect , addSmartContractListener, addWalletListener, connectWalletPressed та onUpdatePressed. Нижче ми пояснимо, що вони роблять:
1// HelloWorld.js23//викликається лише один раз4useEffect(async () => {5 //TODO: реалізувати6}, [])78function addSmartContractListener() {9 //TODO: реалізувати10}1112function addWalletListener() {13 //TODO: реалізувати14}1516const connectWalletPressed = async () => {17 //TODO: реалізувати18}1920const onUpdatePressed = async () => {21 //TODO: реалізувати22}Показати всеuseEffect(opens in a new tab)- це хук React, який викликається після рендерингу вашого компонента. Оскільки в нього передано порожній масив[](див. рядок 4), він буде викликаний лише під час першого рендерингу компонента. Тут ми завантажимо поточне повідомлення, що зберігається в нашому смарт-контракті, викличемо наші слухачі смарт-контракту та гаманця, і оновимо наш інтерфейс, щоб відобразити, чи вже підключений гаманець.addSmartContractListener- ця функція налаштовує слухача, який буде стежити за подієюUpdatedMessagesнашого контракту HelloWorld та оновлювати наш інтерфейс, коли повідомлення змінюється в нашому смарт-контракті.addWalletListener- ця функція налаштовує слухача, який виявляє зміни в стані гаманця MetaMask користувача, наприклад, коли користувач відключає свій гаманець або змінює адреси.connectWalletPressed- ця функція буде викликана для підключення гаманця MetaMask користувача до нашого dapp.onUpdatePressed- ця функція буде викликана, коли користувач захоче оновити повідомлення, що зберігається в смарт-контракті.
Ближче до кінця цього файлу ми маємо інтерфейс користувача нашого компонента.
1// HelloWorld.js23//інтерфейс нашого компонента4return (5 <div id="container">6 <img id="logo" src={alchemylogo}></img>7 <button id="walletButton" onClick={connectWalletPressed}>8 {walletAddress.length > 0 ? (9 "Підключено: " +10 String(walletAddress).substring(0, 6) +11 "..." +12 String(walletAddress).substring(38)13 ) : (14 <span>Підключити гаманець</span>15 )}16 </button>1718 <h2 style={{ paddingTop: "50px" }}>Поточне повідомлення:</h2>19 <p>{message}</p>2021 <h2 style={{ paddingTop: "18px" }}>Нове повідомлення:</h2>2223 <div>24 <input25 type="text"26 placeholder="Оновіть повідомлення у вашому смарт-контракті."27 onChange={(e) => setNewMessage(e.target.value)}28 value={newMessage}29 />30 <p id="status">{status}</p>3132 <button id="publishButton" onClick={onUpdatePressed}>33 Оновити34 </button>35</div>36 37</div>38)Показати всеЯкщо ви уважно переглянете цей код, ви помітите, де ми використовуємо наші різні змінні стану в нашому інтерфейсі:
- У рядках 6-12, якщо гаманець користувача підключений (тобто
walletAddress.length > 0), ми відображаємо скорочену версіюwalletAddressкористувача в кнопці з ID "walletButton;" в іншому випадку вона просто говорить "Підключити гаманець". - У рядку 17 ми відображаємо поточне повідомлення, що зберігається в смарт-контракті, яке міститься в рядку
message. - У рядках 23-26 ми використовуємо контрольований компонент (opens in a new tab) для оновлення нашої змінної стану
newMessage, коли змінюється ввід у текстовому полі.
Крім наших змінних стану, ви також побачите, що функції connectWalletPressed та onUpdatePressed викликаються при натисканні кнопок з ID publishButton та walletButton відповідно.
Нарешті, давайте розберемося, куди додається цей компонент HelloWorld.js.
Якщо ви перейдете до файлу App.js, який є головним компонентом в React, що діє як контейнер для всіх інших компонентів, ви побачите, що наш компонент HelloWorld.js вставлено в рядку 7.
І останнє, але не менш важливе, давайте розглянемо ще один наданий вам файл, interact.js.
Файл interact.js
Оскільки ми хочемо дотримуватися парадигми M-V-C (opens in a new tab), нам знадобиться окремий файл, який містить усі наші функції для керування логікою, даними та правилами нашого dapp, а потім мати змогу експортувати ці функції до нашого фронтенду (наш компонент HelloWorld.js).
👆🏽Це і є точне призначення нашого файлу interact.js!
Перейдіть до папки util у вашому каталозі src, і ви помітите, що ми включили файл з назвою interact.js, який буде містити всі наші функції та змінні для взаємодії зі смарт-контрактом та гаманцем.
1// interact.js23//export const helloWorldContract;45export const loadCurrentMessage = async () => {}67export const connectWallet = async () => {}89const getCurrentWalletConnected = async () => {}1011export const updateMessage = async (message) => {}Показати всеВи помітите, що на початку файлу ми закоментували об'єкт helloWorldContract. Пізніше в цьому посібнику ми розкоментуємо цей об'єкт і створимо екземпляр нашого смарт-контракту в цій змінній, яку ми потім експортуємо в наш компонент HelloWorld.js.
Чотири нереалізовані функції після нашого об'єкта helloWorldContract роблять наступне:
loadCurrentMessage- ця функція обробляє логіку завантаження поточного повідомлення, що зберігається в смарт-контракті. Вона зробить виклик читання до смарт-контракту Hello World за допомогою API Alchemy Web3 (opens in a new tab).connectWallet- ця функція підключить MetaMask користувача до нашого dapp.getCurrentWalletConnected- ця функція перевірить, чи обліковий запис Ethereum вже підключено до нашого dapp під час завантаження сторінки, і відповідно оновить наш інтерфейс.updateMessage- ця функція оновить повідомлення, що зберігається в смарт-контракті. Вона зробить виклик запису до смарт-контракту Hello World, тому гаманець MetaMask користувача повинен буде підписати транзакцію Ethereum, щоб оновити повідомлення.
Тепер, коли ми розуміємо, з чим маємо справу, давайте з'ясуємо, як читати з нашого смарт-контракту!
Крок 3: Читання з вашого смарт-контракту
Щоб читати з вашого смарт-контракту, вам потрібно успішно налаштувати:
- API-з'єднання з ланцюгом Ethereum
- Завантажений екземпляр вашого смарт-контракту
- Функція для виклику функції вашого смарт-контракту
- Слухач для відстеження оновлень, коли дані, які ви читаєте зі смарт-контракту, змінюються
Це може здатися великою кількістю кроків, але не хвилюйтеся! Ми проведемо вас через кожен з них крок за кроком! :)
Встановлення API-з'єднання з ланцюгом Ethereum
Отже, пам'ятаєте, як у частині 2 цього посібника ми використовували наш ключ Alchemy Web3 для читання з нашого смарт-контракту (opens in a new tab)? Вам також знадобиться ключ Alchemy Web3 у вашому dapp для читання з ланцюга.
Якщо у вас його ще немає, спочатку встановіть Alchemy Web3 (opens in a new tab), перейшовши до кореневого каталогу вашого starter-files і запустивши наступне у вашому терміналі:
1npm install @alch/alchemy-web3Alchemy Web3 (opens in a new tab) — це оболонка для Web3.js (opens in a new tab), що надає розширені методи API та інші важливі переваги, щоб полегшити ваше життя як розробника web3. Він розроблений так, щоб вимагати мінімальної конфігурації, щоб ви могли одразу почати використовувати його у своєму додатку!
Потім встановіть пакет dotenv (opens in a new tab) у вашому каталозі проєкту, щоб у нас було безпечне місце для зберігання нашого ключа API після того, як ми його отримаємо.
1npm install dotenv --saveДля нашого dapp ми будемо використовувати наш ключ API Websockets, а не наш ключ API HTTP, оскільки це дозволить нам налаштувати слухача, який виявляє, коли змінюється повідомлення, що зберігається в смарт-контракті.
Коли у вас буде ключ API, створіть файл .env у вашому кореневому каталозі та додайте до нього URL-адресу Alchemy Websockets. Після цього ваш файл .env повинен виглядати так:
1REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<ключ>Тепер ми готові налаштувати нашу кінцеву точку Alchemy Web3 у нашому dapp! Давайте повернемося до нашого файлу interact.js, який знаходиться в папці util, і додамо наступний код на початку файлу:
1// interact.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78//export const helloWorldContract;Вище ми спочатку імпортували ключ Alchemy з нашого файлу .env, а потім передали наш alchemyKey до createAlchemyWeb3, щоб встановити нашу кінцеву точку Alchemy Web3.
Коли ця кінцева точка готова, настав час завантажити наш смарт-контракт!
Завантаження вашого смарт-контракту Hello World
Щоб завантажити ваш смарт-контракт Hello World, вам знадобляться його адреса контракту та ABI, обидва з яких можна знайти на Etherscan, якщо ви завершили Частину 3 цього посібника.
Як отримати ABI вашого контракту з Etherscan
Якщо ви пропустили частину 3 цього посібника, ви можете використовувати контракт HelloWorld з адресою 0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab). Його ABI можна знайти тут (opens in a new tab).
ABI контракту необхідний для визначення, яку функцію буде викликати контракт, а також для гарантії, що функція поверне дані в очікуваному форматі. Після того, як ми скопіювали наш ABI контракту, давайте збережемо його як файл JSON з назвою contract-abi.json у вашому каталозі src.
Ваш файл contract-abi.json повинен зберігатися у вашій папці src.
Озброївшись адресою контракту, ABI та кінцевою точкою Alchemy Web3, ми можемо використовувати метод контракту (opens in a new tab), щоб завантажити екземпляр нашого смарт-контракту. Імпортуйте ваш ABI контракту у файл interact.js та додайте адресу вашого контракту.
1// interact.js23const contractABI = require("../contract-abi.json")4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"Тепер ми можемо нарешті розкоментувати нашу змінну helloWorldContract і завантажити смарт-контракт, використовуючи нашу кінцеву точку AlchemyWeb3:
1// interact.js2export const helloWorldContract = new web3.eth.Contract(3 contractABI,4 contractAddress5)Отже, перші 12 рядків вашого файлу interact.js тепер повинні виглядати так:
1// interact.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78const contractABI = require("../contract-abi.json")9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"1011export const helloWorldContract = new web3.eth.Contract(12 contractABI,13 contractAddress14)Показати всеТепер, коли наш контракт завантажений, ми можемо реалізувати нашу функцію loadCurrentMessage!
Реалізація loadCurrentMessage у вашому файлі interact.js
Ця функція надзвичайно проста. Ми зробимо простий асинхронний виклик web3 для читання з нашого контракту. Наша функція поверне повідомлення, що зберігається в смарт-контракті:
Оновіть loadCurrentMessage у вашому файлі interact.js до наступного:
1// interact.js23export const loadCurrentMessage = async () => {4 const message = await helloWorldContract.methods.message().call()5 return message6}Оскільки ми хочемо відобразити цей смарт-контракт у нашому інтерфейсі, давайте оновимо функцію useEffect у нашому компоненті HelloWorld.js до наступного:
1// HelloWorld.js23//викликається лише один раз4useEffect(async () => {5 const message = await loadCurrentMessage()6 setMessage(message)7}, [])Зауважте, ми хочемо, щоб наша функція loadCurrentMessage викликалася лише один раз під час першого рендерингу компонента. Незабаром ми реалізуємо addSmartContractListener, щоб автоматично оновлювати інтерфейс після зміни повідомлення в смарт-контракті.
Перш ніж зануритися в наш слухач, давайте перевіримо, що ми маємо на даний момент! Збережіть ваші файли HelloWorld.js та interact.js, а потім перейдіть на http://localhost:3000/ (opens in a new tab)
Ви помітите, що поточне повідомлення більше не говорить "Немає з'єднання з мережею." Натомість воно відображає повідомлення, що зберігається в смарт-контракті. Круто!
Ваш інтерфейс тепер повинен відображати повідомлення, що зберігається в смарт-контракті
Тепер щодо того слухача...
Реалізувати addSmartContractListener
Якщо ви згадаєте файл HelloWorld.sol, який ми написали в Частині 1 цієї серії посібників (opens in a new tab), ви згадаєте, що існує подія смарт-контракту під назвою UpdatedMessages, яка випромінюється після виклику функції update нашого смарт-контракту (див. рядки 9 та 27):
1// HelloWorld.sol23// Визначає версію Solidity, використовуючи семантичне версіонування.4// Дізнатися більше: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma5pragma solidity ^0.7.3;67// Визначає контракт з назвою `HelloWorld`.8// Контракт — це набір функцій і даних (його стан). Після розгортання контракт знаходиться за певною адресою в блокчейні Ethereum. Дізнатися більше: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html9contract HelloWorld {1011 // Викликається, коли викликається функція оновлення12 // Події смарт-контракту — це спосіб, за допомогою якого ваш контракт повідомляє, що щось сталося в блокчейні, вашому зовнішньому застосунку, який може «слухати» певні події та реагувати на них, коли вони відбуваються.13 event UpdatedMessages(string oldStr, string newStr);1415 // Оголошує змінну стану `message` типу `string`.16 // Змінні стану — це змінні, значення яких постійно зберігаються в сховищі контракту. Ключове слово `public` робить змінні доступними ззовні контракту та створює функцію, яку інші контракти або клієнти можуть викликати для доступу до значення.17 string public message;1819 // Подібно до багатьох класових об'єктно-орієнтованих мов, конструктор — це спеціальна функція, яка виконується лише під час створення контракту.20 // Конструктори використовуються для ініціалізації даних контракту. Дізнатися більше:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors21 constructor(string memory initMessage) {2223 // Приймає строковий аргумент `initMessage` та встановлює значення у змінну сховища контракту `message`).24 message = initMessage;25 }2627 // Публічна функція, яка приймає строковий аргумент і оновлює змінну сховища `message`.28 function update(string memory newMessage) public {29 string memory oldMsg = message;30 message = newMessage;31 emit UpdatedMessages(oldMsg, newMessage);32 }33}Показати всеПодії смарт-контракту — це спосіб, за допомогою якого ваш контракт повідомляє, що щось сталося (тобто, відбулася подія) в блокчейні, вашому фронтенд-застосунку, який може «слухати» певні події та реагувати на них, коли вони відбуваються.
Функція addSmartContractListener буде спеціально слухати подію UpdatedMessages нашого смарт-контракту Hello World і оновлювати наш інтерфейс для відображення нового повідомлення.
Змініть addSmartContractListener на наступне:
1// HelloWorld.js23function addSmartContractListener() {4 helloWorldContract.events.UpdatedMessages({}, (error, data) => {5 if (error) {6 setStatus("😥 " + error.message)7 } else {8 setMessage(data.returnValues[1])9 setNewMessage("")10 setStatus("🎉 Ваше повідомлення оновлено!")11 }12 })13}Показати всеДавайте розберемо, що відбувається, коли слухач виявляє подію:
- Якщо під час випромінювання події виникає помилка, це буде відображено в інтерфейсі через нашу змінну стану
status. - В іншому випадку ми будемо використовувати повернутий об'єкт
data.data.returnValues— це масив, індексований з нуля, де перший елемент масиву зберігає попереднє повідомлення, а другий — оновлене. Загалом, у разі успішної події ми встановимо наш рядокmessageна оновлене повідомлення, очистимо рядокnewMessageі оновимо нашу змінну стануstatus, щоб відобразити, що нове повідомлення було опубліковано в нашому смарт-контракті.
Нарешті, давайте викличемо наш слухач у нашій функції useEffect, щоб він був ініціалізований під час першого рендерингу компонента HelloWorld.js. Загалом, ваша функція useEffect повинна виглядати так:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()7}, [])Тепер, коли ми можемо читати з нашого смарт-контракту, було б чудово з'ясувати, як писати в нього! Однак, щоб писати в наш dapp, ми повинні спочатку підключити до нього гаманець Ethereum.
Отже, далі ми займемося налаштуванням нашого гаманця Ethereum (MetaMask), а потім підключимо його до нашого dapp!
Крок 4: Налаштуйте свій гаманець Ethereum
Щоб щось записати в ланцюг Ethereum, користувачі повинні підписувати транзакції, використовуючи приватні ключі свого віртуального гаманця. Для цього посібника ми будемо використовувати MetaMask (opens in a new tab), віртуальний гаманець у браузері, який використовується для керування адресою вашого облікового запису Ethereum, оскільки він робить підписання транзакцій надзвичайно простим для кінцевого користувача.
Якщо ви хочете дізнатися більше про те, як працюють транзакції в Ethereum, перегляньте цю сторінку від Ethereum Foundation.
Завантажте MetaMask
Ви можете завантажити та створити обліковий запис MetaMask безкоштовно тут (opens in a new tab). Під час створення облікового запису, або якщо у вас вже є обліковий запис, обов’язково переключіться на «тестову мережу Goerli» у верхньому правому куті (щоб ми не мали справу з реальними грошима).
Додайте ефір з крана
Щоб підписати транзакцію в блокчейні Ethereum, нам знадобиться трохи несправжнього Eth. Щоб отримати Eth, ви можете перейти до FaucETH (opens in a new tab) і ввести адресу свого облікового запису Goerli, натиснути «Запросити кошти», потім вибрати «Ethereum Testnet Goerli» у спадному меню і, нарешті, знову натиснути кнопку «Запросити кошти». Незабаром ви маєте побачити Eth у своєму обліковому записі MetaMask!
Перевірте свій баланс
Щоб перевірити наявність балансу, давайте зробимо запит eth_getBalance (opens in a new tab) за допомогою інструмента-композитора від Alchemy (opens in a new tab). Це поверне кількість Eth у нашому гаманці. Після введення вашої адреси облікового запису MetaMask і натисніть кнопку "Відправити запит", ви повинні побачити таку відповідь:
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}ПРИМІТКА: Цей результат у wei, а не в eth. Wei використовується в якості найменшого номіналу ether. Перетворення з wei в eth: 1 eth = 10¹⁸ wei. Отже, якщо ми перетворимо 0xde0b6b3a7640000 у десяткове число, ми отримаємо 1*10¹⁸, що дорівнює 1 eth.
Фух! Наші підроблені гроші усі там! 🤑
Крок 5: Підключіть MetaMask до вашого інтерфейсу
Тепер, коли наш гаманець MetaMask налаштовано, давайте підключимо до нього наш dapp!
Функція connectWallet
У нашому файлі interact.js давайте реалізуємо функцію connectWallet, яку ми потім зможемо викликати в нашому компоненті HelloWorld.js.
Давайте змінимо connectWallet на наступне:
1// interact.js23export const connectWallet = async () => {4 if (window.ethereum) {5 try {6 const addressArray = await window.ethereum.request({7 method: "eth_requestAccounts",8 })9 const obj = {10 status: "👆🏽 Напишіть повідомлення в текстовому полі вище.",11 address: addressArray[0],12 }13 return obj14 } catch (err) {15 return {16 address: "",17 status: "😥 " + err.message,18 }19 }20 } else {21 return {22 address: "",23 status: (24 <span>25 <p>26 {" "}27 🦊 <a target="_blank" href={`https://metamask.io/download`}>28 Ви повинні встановити MetaMask, віртуальний гаманець Ethereum, у своєму29 браузері.30 </a>31 </p>32 </span>33 ),34 }35 }36}Показати всеОтже, що саме робить цей величезний блок коду?
Ну, по-перше, він перевіряє, чи увімкнено window.ethereum у вашому браузері.
window.ethereum — це глобальний API, що впроваджується MetaMask та іншими постачальниками гаманців, який дозволяє веб-сайтам запитувати облікові записи Ethereum користувачів. Якщо схвалено, він може читати дані з блокчейнів, до яких підключений користувач, і пропонувати користувачеві підписувати повідомлення та транзакції. Для отримання додаткової інформації перегляньте документацію MetaMask (opens in a new tab)!
Якщо window.ethereum не присутній, це означає, що MetaMask не встановлено. Це призводить до повернення об'єкта JSON, де повернута address є порожнім рядком, а об'єкт JSX status повідомляє, що користувач повинен встановити MetaMask.
Тепер, якщо window.ethereum присутній, ось тут і починається найцікавіше.
Використовуючи цикл try/catch, ми спробуємо підключитися до MetaMask, викликавши window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Виклик цієї функції відкриє MetaMask у браузері, де користувачеві буде запропоновано підключити свій гаманець до вашого dapp.
- Якщо користувач вирішить підключитися,
method: "eth_requestAccounts"поверне масив, який містить усі адреси облікових записів користувача, підключені до dapp. Загалом, наша функціяconnectWalletповерне об'єкт JSON, який містить першуaddressу цьому масиві (див. рядок 9) та повідомленняstatus, яке пропонує користувачеві написати повідомлення для смарт-контракту. - Якщо користувач відхиляє підключення, то об'єкт JSON міститиме порожній рядок для повернутої
addressта повідомленняstatus, що відображає відхилення підключення користувачем.
Тепер, коли ми написали цю функцію connectWallet, наступним кроком буде її виклик у нашому компоненті HelloWorld.js.
Додайте функцію connectWallet до вашого компонента інтерфейсу HelloWorld.js
Перейдіть до функції connectWalletPressed у HelloWorld.js та оновіть її до наступного:
1// HelloWorld.js23const connectWalletPressed = async () => {4 const walletResponse = await connectWallet()5 setStatus(walletResponse.status)6 setWallet(walletResponse.address)7}Помітили, як більша частина нашої функціональності абстрагована від нашого компонента HelloWorld.js у файлі interact.js? Це для того, щоб ми дотримувалися парадигми M-V-C!
У connectWalletPressed ми просто робимо await-виклик нашої імпортованої функції connectWallet і, використовуючи її відповідь, оновлюємо наші змінні status та walletAddress за допомогою їхніх хуків стану.
Тепер давайте збережемо обидва файли (HelloWorld.js та interact.js) і протестуємо наш інтерфейс.
Відкрийте браузер на сторінці http://localhost:3000/ (opens in a new tab) і натисніть кнопку «Підключити гаманець» у верхньому правому куті сторінки.
Якщо у вас встановлено MetaMask, вам буде запропоновано підключити свій гаманець до вашого dapp. Прийміть запрошення на підключення.
Ви повинні побачити, що кнопка гаманця тепер відображає, що ваша адреса підключена! Такссс 🔥
Далі спробуйте оновити сторінку… це дивно. Наша кнопка гаманця пропонує нам підключити MetaMask, хоча він уже підключений…
Але не бійтеся! Ми легко можемо вирішити цю проблему (зрозуміли каламбур з адресою?) реалізувавши getCurrentWalletConnected, який перевірить, чи адреса вже підключена до нашого dapp, і відповідно оновить наш інтерфейс!
Функція getCurrentWalletConnected
Оновіть вашу функцію getCurrentWalletConnected у файлі interact.js до наступного:
1// interact.js23export const getCurrentWalletConnected = async () => {4 if (window.ethereum) {5 try {6 const addressArray = await window.ethereum.request({7 method: "eth_accounts",8 })9 if (addressArray.length > 0) {10 return {11 address: addressArray[0],12 status: "👆🏽 Напишіть повідомлення в текстовому полі вище.",13 }14 } else {15 return {16 address: "",17 status: "🦊 Підключіться до MetaMask за допомогою кнопки вгорі праворуч.",18 }19 }20 } catch (err) {21 return {22 address: "",23 status: "😥 " + err.message,24 }25 }26 } else {27 return {28 address: "",29 status: (30 <span>31 <p>32 {" "}33 🦊 <a target="_blank" href={`https://metamask.io/download`}>34 Ви повинні встановити MetaMask, віртуальний гаманець Ethereum, у своєму35 браузері.36 </a>37 </p>38 </span>39 ),40 }41 }42}Показати всеЦей код дуже схожий на функцію connectWallet, яку ми щойно написали на попередньому кроці.
Основна відмінність полягає в тому, що замість виклику методу eth_requestAccounts, який відкриває MetaMask для підключення гаманця користувача, тут ми викликаємо метод eth_accounts, який просто повертає масив, що містить адреси MetaMask, які зараз підключені до нашого dapp.
Щоб побачити цю функцію в дії, давайте викличемо її в нашій функції useEffect нашого компонента HelloWorld.js:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)11}, [])Показати всеЗверніть увагу, що ми використовуємо відповідь нашого виклику getCurrentWalletConnected для оновлення наших змінних стану walletAddress та status.
Тепер, коли ви додали цей код, давайте спробуємо оновити вікно нашого браузера.
Супер! Кнопка має показувати, що ви підключені, і відображати попередній перегляд адреси вашого підключеного гаманця — навіть після оновлення!
Реалізувати addWalletListener
Останній крок у налаштуванні гаманця нашого dapp — це реалізація прослуховувача гаманця, щоб наш інтерфейс користувача оновлювався при зміні стану нашого гаманця, наприклад, коли користувач відключається або перемикає облікові записи.
У вашому файлі HelloWorld.js змініть вашу функцію addWalletListener на наступне:
1// HelloWorld.js23function addWalletListener() {4 if (window.ethereum) {5 window.ethereum.on("accountsChanged", (accounts) => {6 if (accounts.length > 0) {7 setWallet(accounts[0])8 setStatus("👆🏽 Напишіть повідомлення в текстовому полі вище.")9 } else {10 setWallet("")11 setStatus("🦊 Підключіться до MetaMask за допомогою кнопки вгорі праворуч.")12 }13 })14 } else {15 setStatus(16 <p>17 {" "}18 🦊 <a target="_blank" href={`https://metamask.io/download`}>19 Ви повинні встановити MetaMask, віртуальний гаманець Ethereum, у своєму браузері.20 </a>21 </p>22 )23 }24}Показати всеЯ впевнений, що на даному етапі вам навіть не потрібна наша допомога, щоб зрозуміти, що тут відбувається, але для повноти картини давайте швидко розберемося:
- По-перше, наша функція перевіряє, чи ввімкнено
window.ethereum(тобто, чи встановлено MetaMask).- Якщо ні, ми просто встановлюємо нашу змінну стану
statusна рядок JSX, який пропонує користувачеві встановити MetaMask. - Якщо він увімкнений, ми налаштовуємо прослуховувач
window.ethereum.on("accountsChanged")у рядку 3, який прослуховує зміни стану в гаманці MetaMask, зокрема, коли користувач підключає додатковий обліковий запис до dapp, перемикає облікові записи або відключає обліковий запис. Якщо підключено хоча б один обліковий запис, змінна стануwalletAddressоновлюється як перший обліковий запис у масивіaccounts, що повертається прослуховувачем. В іншому випадкуwalletAddressвстановлюється як порожній рядок.
- Якщо ні, ми просто встановлюємо нашу змінну стану
І останнє, але не менш важливе, ми повинні викликати її в нашій функції useEffect:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)1112 addWalletListener()13}, [])Показати всеІ це все! Ми успішно завершили програмування всієї функціональності нашого гаманця! Тепер до нашого останнього завдання: оновлення повідомлення, що зберігається в нашому смарт-контракті!
Крок 6: Реалізація функції updateMessage
Добре, друзі, ми на фінішній прямій! У updateMessage вашого файлу interact.js ми зробимо наступне:
- Переконатися, що повідомлення, яке ми хочемо опублікувати в нашому смарт-контракті, є дійсним
- Підписати нашу транзакцію за допомогою MetaMask
- Викликати цю функцію з нашого фронтенд-компонента
HelloWorld.js
Це не займе багато часу; давайте завершимо цей dapp!
Обробка помилок введення
Природно, має сенс мати якусь обробку помилок введення на початку функції.
Ми захочемо, щоб наша функція завершувалася раніше, якщо не встановлено розширення MetaMask, не підключено гаманець (тобто address, що передається, є порожнім рядком), або message є порожнім рядком. Давайте додамо наступну обробку помилок до updateMessage:
1// interact.js23export const updateMessage = async (address, message) => {4 if (!window.ethereum || address === null) {5 return {6 status:7 "💡 Підключіть свій гаманець MetaMask, щоб оновити повідомлення в блокчейні.",8 }9 }1011 if (message.trim() === "") {12 return {13 status: "❌ Ваше повідомлення не може бути порожнім рядком.",14 }15 }16}Показати всеТепер, коли є належна обробка помилок введення, настав час підписати транзакцію через MetaMask!
Підписання нашої транзакції
Якщо ви вже знайомі з традиційними транзакціями web3 Ethereum, код, який ми напишемо далі, буде вам дуже знайомий. Нижче вашого коду обробки помилок введення додайте наступне до updateMessage:
1// interact.js23//налаштування параметрів транзакції4const transactionParameters = {5 to: contractAddress, // Обов'язково, крім випадків публікації контракту.6 from: address, // повинен збігатися з активною адресою користувача.7 data: helloWorldContract.methods.update(message).encodeABI(),8}910//підписання транзакції11try {12 const txHash = await window.ethereum.request({13 method: "eth_sendTransaction",14 params: [transactionParameters],15 })16 return {17 status: (18 <span>19 ✅{" "}20 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>21 Перегляньте статус вашої транзакції на Etherscan!22 </a>23 <br />24 ℹ️ Як тільки транзакція буде підтверджена мережею, повідомлення25 буде оновлено автоматично.26 </span>27 ),28 }29} catch (error) {30 return {31 status: "😥 " + error.message,32 }33}Показати всеДавайте розберемо, що відбувається. Спочатку ми налаштовуємо наші параметри транзакцій, де:
toвказує адресу одержувача (наш смарт-контракт)fromвказує підписанта транзакції, зміннуaddress, яку ми передали в нашу функціюdataмістить виклик методуupdateнашого смарт-контракту Hello World, отримуючи наш рядокmessageяк вхідні дані
Потім ми робимо await виклик, window.ethereum.request, де ми просимо MetaMask підписати транзакцію. Зверніть увагу, що в рядках 11 і 12 ми вказуємо наш метод eth, eth_sendTransaction і передаємо наші transactionParameters.
На цьому етапі MetaMask відкриється в браузері і запропонує користувачеві підписати або відхилити транзакцію.
- Якщо транзакція буде успішною, функція поверне об'єкт JSON, де рядок JSX
statusпропонує користувачеві перевірити Etherscan для отримання додаткової інформації про свою транзакцію. - Якщо транзакція не вдасться, функція поверне об'єкт JSON, де рядок
statusпередає повідомлення про помилку.
Загалом, наша функція updateMessage повинна виглядати так:
1// interact.js23export const updateMessage = async (address, message) => {4 //обробка помилок введення5 if (!window.ethereum || address === null) {6 return {7 status:8 "💡 Підключіть свій гаманець MetaMask, щоб оновити повідомлення в блокчейні.",9 }10 }1112 if (message.trim() === "") {13 return {14 status: "❌ Ваше повідомлення не може бути порожнім рядком.",15 }16 }1718 //налаштування параметрів транзакції19 const transactionParameters = {20 to: contractAddress, // Обов'язково, крім випадків публікації контракту.21 from: address, // повинен збігатися з активною адресою користувача.22 data: helloWorldContract.methods.update(message).encodeABI(),23 }2425 //підписання транзакції26 try {27 const txHash = await window.ethereum.request({28 method: "eth_sendTransaction",29 params: [transactionParameters],30 })31 return {32 status: (33 <span>34 ✅{" "}35 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>36 Перегляньте статус вашої транзакції на Etherscan!37 </a>38 <br />39 ℹ️ Як тільки транзакція буде підтверджена мережею, повідомлення40 буде оновлено автоматично.41 </span>42 ),43 }44 } catch (error) {45 return {46 status: "😥 " + error.message,47 }48 }49}Показати всеІ останнє, але не менш важливе, нам потрібно підключити нашу функцію updateMessage до нашого компонента HelloWorld.js.
Підключення updateMessage до фронтенду HelloWorld.js
Наша функція onUpdatePressed повинна зробити await виклик до імпортованої функції updateMessage і змінити змінну стану status, щоб відобразити, чи наша транзакція пройшла успішно чи ні:
1// HelloWorld.js23const onUpdatePressed = async () => {4 const { status } = await updateMessage(walletAddress, newMessage)5 setStatus(status)6}Це надзвичайно чисто та просто. І вгадайте що... ВАШ DAPP ГОТОВИЙ!!!
Спробуйте натиснути кнопку Оновити!
Створіть свій власний dapp
Вуууу, ви дійшли до кінця посібника! Підсумовуючи, ви навчилися:
- Підключати гаманець MetaMask до вашого dapp проєкту
- Читання даних з вашого смарт-контракту за допомогою Alchemy Web3 (opens in a new tab) API
- Підписувати транзакції Ethereum за допомогою MetaMask
Тепер ви повністю готові застосувати навички з цього посібника для створення власного dapp проєкту! Як завжди, якщо у вас виникнуть запитання, не соромтеся звертатися до нас за допомогою в Discord Alchemy (opens in a new tab). 🧙♂️
Після завершення цього посібника, повідомте нам про свій досвід або якщо у вас є відгуки, позначивши нас у Twitter @alchemyplatform (opens in a new tab)!
Останні оновлення сторінки: 26 лютого 2026 р.





