Как создать DeFi приложение "Купи мне кофе", вторая неделя
Ну что, не будем долго затягивать и преступим ко второму челленджу: созданию DeFi аппки Buy Me A Coffee, скажу в начале лишь что технология блокчейн удивительна, потому что она дает нам возможность программировать деньги с помощью кода и программного обеспечения. С помощью нескольких строк кода можно создавать всевозможные приложения и протоколы, которые могут создать новые возможности для людей по всему миру.
Buy Me A Coffee - это популярный веб-сайт, который создатели, артисты и другие люди используют для создания целевой страницы, на которую любой желающий может отправить некоторую сумму денег в качестве благодарности за свои услуги. Однако для того, чтобы воспользоваться им, у вас должен быть банковский счет и кредитная карта. Не у всех это есть!
Преимущество децентрализованных приложений, построенных на основе блокчейна, заключается в том, что любой человек со всего мира может получить доступ к приложению, используя только кошелек Ethereum, который любой может настроить бесплатно менее чем за 1 минуту. Давайте посмотрим, как мы можем использовать это в наших интересах!
В этом уроке вы узнаете, как разработать и внедрить децентрализованный смарт-контракт "Купи мне кофе", который позволяет посетителям отправлять вам (тестовые) ETH в качестве чаевых и оставлять приятные сообщения, используя Alchemy, Hardhat, Ethers.js , и Ethereum Goerli.
К концу этого урока вы научитесь
- Используйте среду разработки Hardhat для создания, тестирования и развертывания нашего смарт-контракта.
- Подключите свой кошелек MetaMask к тестовой сети Goerli с помощью конечной точки Alchemy rpc.
- Получите бесплатный ETH Goerli от goerlifaucet.com .
- Использование Ethers.js для взаимодействия с вашим развернутым смарт-контрактом.
- Создайте веб-сайт с интерфейсом для вашего децентрализованного приложения с помощью Replit.
Необходимые навыки и компоненты
Чтобы подготовиться к остальной части этого урока, вам необходимо иметь:
Следующее не обязательно, но чрезвычайно полезно:
- некоторое знакомство с командной строкой
- некоторое знакомство с JavaScript
Теперь давайте начнем создавать наш смарт-контракт!
Кодим смарт-контракт BuyMeACoffee.sol
Ссылка на Github: https://github.com/alchemyplatform/RTW3-Week2-BuyMeACoffee-Contracts
Если вы раньше использовали такие инструменты, как Open Zeppelin Wizard и Remix, то вы уже готовы к использованию Hardhat.
Hardhat похож, потому что это среда разработки и инструмент программирования, но он немного более настраиваемый и запускается из интерфейса командной строки вашего собственного компьютера, а не из приложения браузера.
Мы будем использовать Hardhat, чтобы:
Откройте свой терминал и создайте новый каталог. Поехали!
mkdir BuyMeACoffee-contracts cd BuyMeACoffee-contracts
Внутри этого каталога мы хотим запустить новый проект npm
(настройки по умолчанию вполне подойдут):
npm init -y
Эта команда должна создать файл package.json, который выглядит следующим образом:
~/BuyMeACoffee-contracts > npm init -y { "name": "buymeacoffee-contracts", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
Теперь мы создаем стандартный проект для примера:
npx hardhat
Нажмите Enter
чтобы принять все настройки по умолчанию и установить зависимости проекта (hardhat
, @nomiclabs/hardhat-waffle
, ethereum-waffle
, chai
, @nomiclabs/hardhat-ethers
, ethers
).
В случае, если что-то пойдет не так с установкой зависимостей, попробуйте переустановить, выполнив эту команду:
npm install --save-dev hardhat@^2.9.3 @nomiclabs/hardhat-waffle@^2.0.0 ethereum-waffle@^3.0.0 chai@^4.2.0 @nomiclabs/hardhat-ethers@^2.0.0 ethers@^5.0.0
Теперь ваш каталог проекта должен выглядеть примерно так (я использую дерево для визуализации):
~/BuyMeACoffee-contracts > tree -C -L 1 . ├── README.md ├── contracts ├── hardhat.config.js ├── node_modules ├── package-lock.json ├── package.json ├── scripts └── test
Тут важны следующие файлы и папки:
contracts
- папка содержащая ваши смарт-контрактыscripts
- папка, в которой хранятся ваши скрипты hardhat javscripthardhat.config.js
- конфигурационный файл с настройками для версии solidity и развертывания
Теперь используйте любой редактор кода, чтобы открыть папку проекта! Как вы заметили я использую Visual Studio Code.
Вы можете заметить ряд файлов, автоматически сгенерированных с помощью инструмента Hardhat JavaScript project. Мы заменим их все, начиная с контракта Lock.sol.
//SPDX-License-Identifier: Unlicense // contracts/BuyMeACoffee.sol pragma solidity ^0.8.0; // Switch this to your own contract address once deployed, for bookkeeping! // Example Contract Address on Goerli: 0xDBa03676a2fBb6711CB652beF5B7416A53c1421D contract BuyMeACoffee { // Event to emit when a Memo is created. event NewMemo( address indexed from, uint256 timestamp, string name, string message ); // Memo struct. struct Memo { address from; uint256 timestamp; string name; string message; } // Address of contract deployer. Marked payable so that // we can withdraw to this address later. address payable owner; // List of all memos received from coffee purchases. Memo[] memos; constructor() { // Store the address of the deployer as a payable address. // When we withdraw funds, we'll withdraw here. owner = payable(msg.sender); } /** * @dev fetches all stored memos */ function getMemos() public view returns (Memo[] memory) { return memos; } /** * @dev buy a coffee for owner (sends an ETH tip and leaves a memo) * @param _name name of the coffee purchaser * @param _message a nice message from the purchaser */ function buyCoffee(string memory _name, string memory _message) public payable { // Must accept more than 0 ETH for a coffee. require(msg.value > 0, "can't buy coffee for free!"); // Add the memo to storage! memos.push(Memo( msg.sender, block.timestamp, _name, _message )); // Emit a NewMemo event with details about the memo. emit NewMemo( msg.sender, block.timestamp, _name, _message ); } /** * @dev send the entire balance stored in this contract to the owner */ function withdrawTips() public { require(owner.send(address(this).balance)); } }
Потратьте некоторое время, чтобы прочитать комментарии к контракту и посмотреть, сможете ли вы понять, что происходит!
Перечислю здесь основные моменты:
- Когда мы развертываем контракт,
constructor
сохраняет адрес кошелька, который был ответственен за развертывание, внутри переменнойowner
в качествеpayable
адреса. Это полезно для последующего использования, когда мы захотим отозвать любые чаевые, собранные по контракту. - Функция
buyCoffee
- самая важная функция в контракте. Она принимает две строки:_name
, и_message
, а также принимает эфир из-за модификатораpayable
. Она также использует входные данные_name
, и_message
для создания структурыMemo
, которая хранится в блокчейне. - Когда пользователи вызывают функцию
buyCoffee
, они должны отправить какой-либ запрос из-за инструкцииrequire(msg.value > 0)
. Затем эфир удерживается на балансе контракта до тех пор, пока он не будет снят. - Массив
memos
содержит все структурыMemo
, созданные в результате покупок кофе. NewMemo
журнала заметок генерируются каждый раз при покупке кофе. Это позволяет нам отслеживать новые покупки кофе с нашего веб-сайта.withdrawTips
- это функция, которую может вызвать любой желающий, но она будет отправлять деньги только первоначальному деплойеру контракта.address(this).balance
получает эфир, хранящийся в контрактеowner.send(...)
это синтаксис для создания транзакции отправки с помощью etherrequire(...)
стейтмент, который оборачивает все, существует для того, чтобы гарантировать, что в случае возникновения каких-либо проблем транзакция будет отменена и ничего не будет потеряно- вот что мы получаем
require(owner.send(address(this).balance))
Вооружившись этим кодом смарт-контракта, мы можем написать скрипт для проверки нашей логики!
Создаем buy-coffee.js скрипт для теста нашего контракта
В папке scripts
должен быть образец скрипта deploy.js
. Давайте переименуем этот файл в buy-coffee.js
и вставим следующий код:
// scripts/buy-coffee.js const hre = require("hardhat"); // Returns the Ether balance of a given address. async function getBalance(address) { const balanceBigInt = await hre.waffle.provider.getBalance(address); return hre.ethers.utils.formatEther(balanceBigInt); } // Logs the Ether balances for a list of addresses. async function printBalances(addresses) { let idx = 0; for (const address of addresses) { console.log(`Address ${idx} balance: `, await getBalance(address)); idx ++; } } // Logs the memos stored on-chain from coffee purchases. async function printMemos(memos) { for (const memo of memos) { const timestamp = memo.timestamp; const tipper = memo.name; const tipperAddress = memo.from; const message = memo.message; console.log(`At ${timestamp}, ${tipper} (${tipperAddress}) said: "${message}"`); } } async function main() { // Get the example accounts we'll be working with. const [owner, tipper, tipper2, tipper3] = await hre.ethers.getSigners(); // We get the contract to deploy. const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee"); const buyMeACoffee = await BuyMeACoffee.deploy(); // Deploy the contract. await buyMeACoffee.deployed(); console.log("BuyMeACoffee deployed to:", buyMeACoffee.address); // Check balances before the coffee purchase. const addresses = [owner.address, tipper.address, buyMeACoffee.address]; console.log("== start =="); await printBalances(addresses); // Buy the owner a few coffees. const tip = {value: hre.ethers.utils.parseEther("1")}; await buyMeACoffee.connect(tipper).buyCoffee("Carolina", "You're the best!", tip); await buyMeACoffee.connect(tipper2).buyCoffee("Vitto", "Amazing teacher", tip); await buyMeACoffee.connect(tipper3).buyCoffee("Kay", "I love my Proof of Knowledge", tip); // Check balances after the coffee purchase. console.log("== bought coffee =="); await printBalances(addresses); // Withdraw. await buyMeACoffee.connect(owner).withdrawTips(); // Check balances after withdrawal. console.log("== withdrawTips =="); await printBalances(addresses); // Check out the memos. console.log("== memos =="); const memos = await buyMeACoffee.getMemos(); printMemos(memos); } // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
На этом этапе каталог нашего проекта должен выглядеть примерно так:
Не стесняйтесь потратить несколько минут на чтение кода скрипта. Вверху есть некоторые служебные функции, определенные для удобства выполнения таких действий, как получение остатков на кошельке и их отображения.
Основная логика скрипта находится внутри функции main()
. Закоментированный код показывает ход выполнения скрипта:
- Получаем примеры учетных записей, с которыми мы будем работать.
- Получаем контракт на развертывание.
- Развертываем контракт.
- Проверяем баланс перед покупкой кофе.
- Покупаем хозяину несколько чашек кофе.
- Проверяем баланс после покупки кофе.
- Выводим.
- Проверяем баланс после вывода.
- Проверяем заметки.
Этот скрипт тестирует все функции, которые мы реализовали в нашем смарт-контракте!
Вы также можете заметить, что мы делаем интересные вызовы, такие как:
hre.waffle.provider.getBalance
В этих строках кода мы используем преимущества среды разработки Hardhat (hre) вместе с плагинами Ethers и Waffle SDK для доступа к функциям, которые позволяют считывать остатки на счетах блокчейн-кошельков, развертывать контракты и форматировать значения криптовалюты Ether.
Мы не будем слишком подробно останавливаться на этом коде в этом руководстве, но вы можете узнать о них больше, просмотрев документацию Hardhat и Ethers.js .
Хватит разговоров. А теперь для развлечения давайте запустим сценарий:
npx hardhat run scripts/buy-coffee.js
Вы должны увидеть вывод в своем терминале что-то вроде этого:
~/roadtoweb3/BuyMeACoffee-contracts ❯ npx hardhat run scripts/buy-coffee.js 9m 7s 02:56:16 BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 == start == Address 0 balance: 9999.998754619375 Address 1 balance: 10000.0 Address 2 balance: 0.0 == bought coffee == Address 0 balance: 9999.998754619375 Address 1 balance: 9998.999752893990255063 Address 2 balance: 3.0 == withdrawTips == Address 0 balance: 10002.998708719732606388 Address 1 balance: 9998.999752893990255063 Address 2 balance: 0.0 == memos == At 1657991838, Carolina (0x70997970C51812dc3A010C7d01b50e0d17dc79C8) said: "You're the best!" At 1657991839, Vitto (0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC) said: "Amazing teacher" At 1657991840, Kay (0x90F79bf6EB2c4f870365E785982E1f101E93b906) said: "I love my Proof of Knowledge"
В начале скрипта (сразу после развертывания контракта) обратите внимание, что адрес 0
имеет 9999.99877086625
ETH. Это связано с тем, что он начинался с 10k ETH в качестве одного из предварительно заполненных адресов hardhat, но для развертывания в локальном блокчейне пришлось потратить небольшую сумму.
На втором шаге == bought coffee ==
Address 1 покупает один кофе. Два других кошелька, которые не показаны, ТАКЖЕ покупают кофе. В общей сложности было куплено 3 кофе на общую сумму чаевых 3.0
ETH. Вы можете видеть, что Address 2 (который представляет адрес контракта) удерживает 3.0
ETH.
После вызова функции withdrawTips()
в == withdrawTips ==
контракт возвращается к 0 ETH, а исходный деплойер, он же Address 0, теперь заработал немного денег и содержит 10002.998724967892122376
ETH.
Ну как, весело?!?! Можете ли вы представить себе чаевые, которые вы сможете заработать?? Я могу.
Теперь давайте внедрим сценарий изолированного развертывания, чтобы упростить реальное развертывание, а также подготовимся к развертыванию в тестовой сети Goerli!
Развертываем свой BuyMeACoffe.sol смарт-контракт в сети Ethereum Goerly с Alchemy и MetaMack
Let's create a new file scripts/deploy.js
that will be super simple, just for deploying our contract to any network we choose later (we'll choose Goerli later if you haven't noticed).
The deploy.js
file should look like this:
Давайте создадим новый файл scripts/deploy.js
, для развертывания нашего контракта в любой сети, которую мы выберем (мы выберем Goerli, если вы не заметили).
В deploy.js
файл должен выглядеть следующим образом:
// scripts/deploy.js const hre = require("hardhat"); async function main() { // We get the contract to deploy. const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee"); const buyMeACoffee = await BuyMeACoffee.deploy(); await buyMeACoffee.deployed(); console.log("BuyMeACoffee deployed to:", buyMeACoffee.address); } // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
Чтобы пересмотреть структуру проекта, теперь у нас есть один смарт-контракт и два скрипта hardhat:
Теперь, когда скрипт deploy.js
написан и сохранен, если вы выполните следующую команду в терминале:
npx hardhat run scripts/deploy.js
Вы увидите вывод одной единственной строки:
BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Что интересно, так это то, что если вы будете запускать скрипт снова и снова, вы будете видеть один и тот же точный адрес развертывания каждый раз:
Почему так? Это связано с тем, что при запуске скрипта сеть по умолчанию, которую использует Hardhat - это локальная сеть разработки, расположенная прямо на вашем компьютере. Это быстро и детерминировано, и отлично подходит для быстрой проверки на работоспособность.
Однако для развертывания в тестовой сети, которая работает через Интернет с узлами по всему миру, нам нужно изменить наш конфигурационный файл Hardhat, чтобы предоставить себе такую возможность.
Здесь мы познакомимся с файлом hardhat.config.json
.
Небольшое предостережение, прежде чем мы погрузимся тему:
КОНФИГУРАЦИЯ - ЭТО СЛОЖНО! ХРАНИТЕ СВОИ СЕКРЕТЫ В БЕЗОПАСНОСТИ!
Есть множество мелких деталей, которые могут пойти не так, и все постоянно меняется. Самая опасная вещь - это секретные значения, например, ваш закрытый ключ Netmask и ваш URL-адрес Alchemy.
Если у вас что-то не работает, попробуйте найти ответ в Ethereum StackExchange, Alchemy Discord или найдите свои ошибки в Google.
И никогда не делитесь своими секретами! Ваши ключи, ваши монеты!
Когда вы откроете свой hardhat.config.js
файл, вы увидите некоторый стандартный код конфига. Удалите его и вставьте следующий код:
// hardhat.config.js require("@nomiclabs/hardhat-ethers"); require("@nomiclabs/hardhat-waffle"); require("dotenv").config() // You need to export an object to set up your config // Go to https://hardhat.org/config/ to learn more const GOERLI_URL = process.env.GOERLI_URL; const PRIVATE_KEY = process.env.PRIVATE_KEY; /** * @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.4", networks: { goerli: { url: GOERLI_URL, accounts: [PRIVATE_KEY] } } };
Рассмотрим подробнее что тут происходит:
- Импортируя
hardhat-ethers
,hardhat-waffle
иdotenv
в верхней части файла конфигурации, весь наш проект Hardhat получит доступ к этим зависимостям. - Я знаю, что мы еще не внедрили
dotenv
, это важный инструмент, о котором мы немного поговорим. process.env.GOERLI_URL
иprocess.env.PRIVATE_KEY
- это то, как мы можем получить доступ к переменным среды для использования в вашем конфигурационном файле, не раскрывая секретные значения.- Inside the
modules.exports
, we are using solidity compiler version0.8.4
. Different compiler versions support different features and syntax sets, so it's important to match this version with thepragma
declaration at the top of ourBuyMeACoffee.sol
smart contract. - Внутри
modules.exports
мы используем компилятор solidity версии0.8.9
. Разные версии компилятора поддерживают разные функции и наборы синтаксиса, поэтому важно сопоставить эту версию с объявлениемpragma
в верхней части нашегоBuyMeACoffee.sol
смарт-контракта. - Если вы вернетесь к этому файлу, вы можете перепроверить инструкцию
pragma solidity ^0.8.0;
. В этом случае, даже если цифры не совпадают точно, это нормально, потому что символ karat^
означает, что будет работать любая версия, которая больше или равна0.8.0
. - Также в файле
modules.exports
мы определяем настройку сетей, которая содержит одну конфигурацию тестовой сети дляgoerli
.
Теперь, прежде чем мы сможем приступить к развертыванию, нам нужно убедиться, что у нас установлен последний инструмент - модуль dotenv
. Как следует из названия, dotenv
помогает нам подключить файл .env
к остальной части нашего проекта. Давайте займемся этим.
npm install dotenv
Создадим .env
файл в корне нашего проекта:
touch .env
Заполните файл .env
переменными, которые нам нужны:
GOERLI_URL=https://eth-goerli.alchemyapi.io/v2/<your api key> GOERLI_API_KEY=<your api key> PRIVATE_KEY=<your metamask api key>
Вы заметите, что я не выдал ни одного из своих собственных секретов. Ага. Безопасность превыше всего. Однако вы вполне можете поместить этот файл, если у вас также есть .gitignore
, который гарантирует, что вы случайно не передадите файл в систему управления версиями. Убедитесь, что файл .env
указан в вашем файле .gitignore
node_modules .env coverage coverage.json typechain #Hardhat files cache artifacts
Кроме того, чтобы получить то, что нам нужно для переменных среды, вы можете использовать следующие ресурсы:
GOERLI_URL
- зарегистрируйте учетную запись на Alchemy, создайте приложение Ethereum -> Goerli и используйте HTTP-URLGOERLI_API_KEY
- из того же приложения Alchemy Ethereum Gerli вы можете получить последнюю часть URL-адреса, и это будет ваш API KEYPRIVATE_KEY
- следуйте этим инструкциям от MetaMask, чтобы экспортировать свой закрытый ключ.
Теперь, когда dotenv
установлен и ваш файл .env
заполнен, мы ПОЧТИ готовы к развертыванию в Goerli testnet!
Последнее, что нам нужно сделать, это убедиться, что у вас есть немного Goerli ETH. Это тестовый ETH, который позволяет вам практиковаться в выполнении действий в тестовой сети Goerli, которая является своего рода тренировочной зоной для создания приложений Ethereum. Таким образом, вам не придется тратить реальные деньги на основную сеть Ethereum.
Перейти к https://www.goerlifaucet.com и войдите в свою учетную запись Alchemy, чтобы получить бесплатный тестовый эфир.
Теперь мы можем приступать к развертыванию!
Запустите сценарий развертывания, на этот раз добавив специальный флаг для использования сети Goerli:
npx hardhat run scripts/deploy.js --network goerli
Если вы столкнетесь здесь с какими-либо ошибками, например с Error HH8
, то я настоятельно рекомендую поискать решения в Google и StackOverflow или Ethereum Stackexchange. Часто приходится сталкиваться с такими проблемами, когда что-то в вашем hardhat.config.js
, .env
, или ваш модуль dotenv
настроен неправильно.
Если все пойдет хорошо, через несколько секунд вы сможете увидеть свой адрес контракта, зарегистрированный в консоли:
~/roadtoweb3/BuyMeACoffee-contracts ❯ npx hardhat run scripts/deploy.js --network goerli 43s 13:50:42 BuyMeACoffee deployed to: 0xa0a094FeF68Eb89dB349825413E485D9B16AB9cd
Поздравляю! 🎉 Теперь у тебя есть контракт, развернутый в тестовой сети Goerli. Вы можете просмотреть его в проводнике блокчейна Goerli etherscan, вставив свой адрес здесь: https://goerli.etherscan.io/
Прежде чем мы перейдем к части руководства по интерфейсу веб-сайтау (dapp), давайте подготовим еще один скрипт, который мы захотим использовать позже - withdraw.js
.
Пишем скрипт вывода средств
Позже, когда мы опубликуем наш веб-сайт, нам понадобится способ собрать все коментарии, которые оставляют нам наши друзья и поклонники. Мы можем написать еще один сценарий hardhat, чтобы сделать именно это!
Создаем скрипт scripts/withdraw.js
// scripts/withdraw.js const hre = require("hardhat"); const abi = require("../artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json"); async function getBalance(provider, address) { const balanceBigInt = await provider.getBalance(address); return hre.ethers.utils.formatEther(balanceBigInt); } async function main() { // Get the contract that has been deployed to Goerli. const contractAddress="0xDBa03676a2fBb6711CB652beF5B7416A53c1421D"; const contractABI = abi.abi; // Get the node connection and wallet connection. const provider = new hre.ethers.providers.AlchemyProvider("goerli", process.env.GOERLI_API_KEY); // Ensure that signer is the SAME address as the original contract deployer, // or else this script will fail with an error. const signer = new hre.ethers.Wallet(process.env.PRIVATE_KEY, provider); // Instantiate connected contract. const buyMeACoffee = new hre.ethers.Contract(contractAddress, contractABI, signer); // Check starting balances. console.log("current balance of owner: ", await getBalance(provider, signer.address), "ETH"); const contractBalance = await getBalance(provider, buyMeACoffee.address); console.log("current balance of contract: ", await getBalance(provider, buyMeACoffee.address), "ETH"); // Withdraw funds if there are funds to withdraw. if (contractBalance !== "0.0") { console.log("withdrawing funds..") const withdrawTxn = await buyMeACoffee.withdrawTips(); await withdrawTxn.wait(); } else { console.log("no funds to withdraw!"); } // Check ending balance. console.log("current balance of owner: ", await getBalance(provider, signer.address), "ETH"); } // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
Структура нашего проекта на данном этапе должна выглядеть следующим образом:
Сейчас у нас есть 1 смарт-контракт и 3 скрипта hardhat.
Самая важная часть этого скрипта - это когда мы вызываем функцию withdrawTips()
, чтобы снять деньги с баланса нашего контракта и отправить их на кошелек владельца:
// Withdraw funds if there are funds to withdraw. if (contractBalance !== "0.0") { console.log("withdrawing funds..") const withdrawTxn = await buyMeACoffee.withdrawTips(); await withdrawTxn.wait(); }
Если в контракте нет средств, мы избегаем попыток вывести средства, чтобы не тратить плату за газ без необходимости.
Когда вы запустите скрипт, вы увидите вывод, подобный этому:
~/roadtoweb3/BuyMeACoffee-contracts ❯ npx hardhat run scripts/withdraw.js 10s 19:21:05 current balance of owner: 0.048750102785301158 ETH current balance of contract: 0.0 ETH no funds to withdraw! current balance of owner: 0.048750102785301158 ETH
Обратите внимание, что на этот раз мы не добавили флаг --network goerli
, и это потому, что наш скрипт жестко кодирует конфигурацию сети непосредственно внутри логики:
const provider = new hre.ethers.providers.AlchemyProvider( "goerli", process.env.GOERLI_API_KEY );
Отлично, теперь у нас есть способ получить чаевые из контракта! Давайте перейдем к визуальной части dapp этого проекта, чтобы мы могли поделиться нашей страницей чаевых со всеми нашими друзьями :)
Создаем интерфейс dApp BuyMeACoffee с Replit и Ethers.js
Для фронтенд части веб-сайта, чтобы все было просто и понятно, мы собираемся использовать удивительный инструмент для быстрого запуска демонстрационных проектов, называемый Replit IDE.
Посетите пример проекта из официальной документации здесь и форкните его, чтобы создать свою собственную копию для изменения: https://replit.com/@thatguyintech/BuyMeACoffee-Solidity-DeFi-Tipping-app
Вы также можете просмотреть полный код веб-сайта здесь: https://github.com/alchemyplatform/RTW3-Week2-BuyMeACoffee-Website
После форка Repl'а вы должны перейти на страницу IDE, где вы можете:
- Смотрите код Next.js веб-приложения
- Получите доступ к консоли, оболочке терминала и предварительному просмотру README.md файла
- Просмотр версии вашего приложения с горячей перезагрузкой
Это должно выглядеть примерно так:
Эта часть урока будет быстрой и увлекательной - мы собираемся обновить пару переменных, чтобы они были подключены к смарт-контракту, который мы развернули в предыдущих частях проекта, и чтобы он отображал ваше собственное имя на веб-сайте!
Давайте сначала все подключим и запустим, а потом я объясню вам, что происходит в каждой части.
Вот изменения, которые нам нужно внести:
- Обновите
contractAddress
в/index.js
- Обновите строку имени на ваше в
pages/index.js
- Убедитесь, что ABI контракта соответствует вашему контракту в
utils/BuyMeACoffee.json
Обновляем contractAddress в pages/index.js
Вы можете видеть, что переменная contractAddress уже заполнена адресом. Это пример контракта, который я развернул, который вы можете использовать, но если вы это сделаете ... все деньги, отправленные на ваш сайт, будут отправлены на мой адрес :)
Вы можете исправить это, вставив свой адрес, указанный при развертывании приложения BuyMeACoffee.sol
ранее.
Обновляем имя, на то которое вы хотите в pages/index.js
Прямо сейчас на сайте повсюду написано имя Albert. Найдите все места, где используется Albert
, и замените его своим именем / никнеймом / ENS доменом или как вы хотели бы, чтобы люди называли вас.
Нажмите cmd + F
или ctrl + F
чтобы найти строкуAlbert
для замены.
Убеждаемся, что ABI совпадает с utils/BuyMeACoffee.json
Это также важно проверить, особенно когда вы будите вносить изменения в свой смарт-контракт позже (после этого урока).
ABI - это двоичный интерфейс приложения, который представляет собой просто причудливый способ сообщить нашему коду интерфейса, какие функции доступны для вызова в смарт-контракте. ABI генерируется внутри файла json при компиляции смарт-контракта. Вы можете найти его в папке смарт-контракта по пути:
artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json
Всякий раз, когда вы меняете код своего смарт-контракта и повторно развертываете его, ваш API также меняется. Скопируйте это и вставьте в файл Replit: utils/BuyMeACoffee.json
Теперь, если приложение еще не запущено, вы можете перейти в командную оболочку и использовать npm run dev
для запуска локального сервера для проверки внесенных изменений. Веб-сайт должен загрузиться через несколько секунд:
Самое замечательное в Replit то, что после того, как вы запустили веб-сайт, вы можете вернуться в свой профиль, найти ссылку на проект Replit и отправить ее друзьям, чтобы они посетили вашу страницу чаевых.
Теперь давайте совершим экскурсию по веб-сайту и коду. Вы уже можете видеть на скриншоте выше, что при первом посещении приложения оно проверит, установлен ли у вас MetaMask и подключен ли ваш кошелек к сайту. При первом посещении вы не будете подключены, поэтому появится кнопка с просьбой подключить ваш кошелек Connect your wallet
.
После того, как вы нажмете Connect your wallet
, появится окно MetaMask, в котором вас спросят, хотите ли вы подтвердить подключение, подписав сообщение. Подписание этого сообщения не требует каких-либо сборов или затрат за газ.
Once the signature is complete, the website will acknowledge your connection and you will be able to see the coffee form, as well as any of the previous memos left behind by other visitors.
Как только сообщение будет подписано, веб-сайт подтвердит ваше подключение, и вы сможете увидеть форму для кофе, а также любые предыдущие заметки, оставленные другими посетителями.
Ухх! Вот и все! Вот и весь проект. Потратьте секунду, чтобы похлопать себя по спине и поразмыслить о путешествии, в котором вы только что побывали!
Подведем итог
- Мы использовали Hardhat и Ethers.js для кодинга, тестирования и развертывания пользовательского смарт-контракта solidity.
- Мы развернули смарт-контракт в общей тестовой сети, используя Alchemy и Metal Mask.
- Мы внедрили скрипт вывода средств, чтобы позволить нам принять плоды нашего труда.
- Мы подключили интерфейс веб-сайта, созданный с помощью Next.js, чтобы он мог реагировать и взаимодействовать со смарт-контрактом с помощью Ethers.js для загрузки контракт ABI.
Домашнее задание
Ладно, теперь время для самой лучшей части. Я оставлю вам несколько задач, которые вы можете попробовать самостоятельно, чтобы убедиться, что вы полностью понимаете то, чему здесь научились! (Для получения некоторых рекомендаций посмотрите видео на YouTube здесь).
- Разрешите вашему смарт-контракту обновлять адрес вывода средств.
- Разрешите вашему смарт-контракту покупать большой кофе за 0,003 ETH и создайте кнопку на интерфейсе веб-сайта, которая показывает кнопку "Buy Large Coffee for 0.003ETH".
- Как только вы закончите со своим заданием, напишите об этом в Твиттере, отметив @AlchemyPlatform в Twitter и используя хэштег #roadtoweb3!
Заполняем форму
Чтобы получить наш заветный еженедельный PoK (Proof of Knowledge), заполните эту форму, включая адрес развернутого смарт-контракта: https://alchemyapi.typeform.com/roadtoweektwo
Примерно через неделю вам отправят PoK, забираем на https://mintkudos.xyz/, вот так он выглядит!
Этот непередаваемый NFT доказывает, что вы официально завершили вторую неделю курса Alchemy Road to Web3 Bootcamp. Вы успешно написали, спроектировали и развернули децентрализованное приложение с использованием: Solidity, Hardhat, Ethers.js, Alchemy. Поздравляем!
Ну вот и вторая неделя нашей поездки закончена, подписывайтесть на обновления чтобы не пропустить гайд для следующей недели и залетайте в чатик если есть вопросы !👇 Увидимся ❤️