Все, что вы когда-либо хотели знать о процессе с пошаговым руководством
Вы, вероятно, нажали на это руководство, потому что окунулись в захватывающий мир web3, просмотрели некоторые вводные ресурсы о том, что такое web3, и решили сделать следующий шаг, став разработчиком блокчейна. В этом руководстве мы рассмотрим создание полноценного децентрализованного приложения в сети Ethereum с нуля.
Обзор
Для приложения, которое мы будем создавать, мы хотим, чтобы пользователи, которые регистрируются в dApp, могли просматривать список учебных ресурсов, голосовать за ресурсы, которые им больше всего нравятся, и добавлять свои собственные ресурсы в dApp. Мы начнем руководство с настройки нашей среды разработки с помощью Hardhat, которая позволяет нам легко создавать смарт-контракты на Ethereum.
После настройки нашей среды мы создадим надежный смарт-контракт, проверим, что все методы работают в нашем терминале, развернем контракт и подключим его к нашему интерфейсу React. Вот как выглядит наша готовая демонстрация:
Настройка нашей среды разработки
Давайте настроим нашу разработку с помощью Hardhat. Воспользуемся инструкцией из официальной документации каски для настройки нашего проекта.
Установка каски
Сначала перейдите в каталог, в котором вы хотите создать свой проект, создайте пустой каталог.
mkdir resourcedapp cd resourcedapp
Убедитесь, что вы находитесь в каталоге resourcedapp
, затем инициализируйте пустой каталог с помощью команды npm init
и следуйте инструкциям на терминале. Теперь давайте установим Hardhat и другие зависимости плагинов Hardhat, которые будут полезны, когда мы будем следовать в этом руководстве.
npm install --save-dev hardhat @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
Установка всех компонентов может занять некоторое время из-за некоторых зависимостей Ethereum Javascript.
Как только все будет установлено, в нашем терминале запустите команду:
npx hardhat
Вот что мы получаем в нашем терминале после запуска команды:
Выберите Create a basic sample project
, оставьте корень конфигурации каски по умолчанию как есть, затем выберите yes
для «Вы хотите добавить .gitignore», и мы увидим, что наш проект был успешно создан. давайте взглянем на структуру нашего проекта в следующем разделе.
Структура проекта
У нас есть все готово для создания нашего контракта, но перед этим давайте изучим нашу файловую структуру.
Кодирование нашего смарт-контракта
В предыдущем разделе мы рассмотрели файловую структуру, а теперь пришло время приступить к написанию кода нашего контракта. Первое, что мы делаем, это удаляем файл greeter.sol
в папке contracts
и создаем новый файл с именем Resource.sol
, так как именно здесь мы будем писать наши функции смарт-контракта. Прежде чем перейти к коду, давайте посмотрим на функции, которые мы хотим реализовать в смарт-контракте:
- Добавить новый ресурс. Для этого нам понадобится функция
addResource
, которая при вызове создаст новый ресурс и добавит его в наш массив ресурсов. - Список всех наших ресурсов. Для этого нам понадобится функция
getResources
для вывода списка всех наших ресурсов. - Проголосовать за ресурс — нам нужна функция
voteResource
, которую мы передадим в конкретный ресурсid
и проголосуем за него. - Получить конкретный ресурс — нам нужна функция
getResource
, которая возвращает конкретный ресурс.
Solidity — это объектно-ориентированный язык, и если вы знакомы с Javascript или Python, у вас не возникнет особых проблем с программированием на нем.
Я вставлю весь код контракта в файл Resource.sol
, который мы создали ранее, и разобью его по частям, чтобы вы видели, что делает контракт.
//Resource.sol //SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; // Solidity files have to start with this pragma. contract ResourceShare { struct Resource { uint8 id; address creator; string title; string url; string description; uint8 total_votes; } mapping(uint256 => Resource) public resources; event savingsEvent(uint256 indexed _resourceId); uint8 public numResource; constructor() { numResource = 0; addResource( "freecodecamp", "https://www.freecodecamp.org/", "Learn to code" ); } function addResource( string memory title, string memory url, string memory description ) public { Resource storage resource = resources[numResource]; resource.creator = msg.sender; resource.total_votes = 0; resources[numResource] = Resource( numResource, resource.creator, title, url, description, resource.total_votes ); numResource++; } //return a particular resource function getResource(uint256 resourceId) public view returns (Resource memory) { return resources[resourceId]; } //return the array of resources function getResources() public view returns (Resource[] memory) { Resource\[] memory id = new Resource[\](numResource); for (uint256 i = 0; i < numResource; i++) { Resource storage resource = resources[i]; id[i] = resource; } return id; } // Vote a resource function voteResource(uint256 resourceId) public { Resource storage resource = resources[resourceId]; resource.total_votes++; } }
Структура контракта
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; // Solidity files have to start with this pragma. contract ResourceShare { }
Контракты Solidity обычно начинаются с указания того, в какой версии Solidity написан наш контракт. Ключевое слово pragma
указывает, какую версию Solidity мы используем, в нашем случае это версия 0.8.0
. Затем мы назовем наш контракт ResourceShare
с фигурной скобкой. Все, что мы будем делать, должно находиться в фигурных скобках.
Структура ресурса struct
по солидности аналогична классам в других языках. Он используется для определения пользовательских типов объекта и атрибутов, которые он может иметь.
struct Resource { address creator; string title; string url; string description; uint8 total_votes; }
В нашем случае у нас есть ресурс struct
со следующими атрибутами:
- id — уникальный идентификатор для определенного общего ресурса
- создатель — создатель общего ресурса
- title — название нашего ресурса
- url — URL-адрес ресурса, которым мы делимся
- description — описание ресурса
- total_votes — общее количество голосов, полученных ресурсом
Теперь, когда мы определили наш Resource struct
, давайте сопоставим каждый из наших ресурсов и закодируем функциональные возможности для контракта.
Добавить ресурс
// Resource.sol mapping(uint256 => Resource) public resources; uint8 public numResource; constructor() { numResource = 0; addResource( "freecodecamp", "https://www.freecodecamp.org/", "Learn to code" ); } function addResource( string memory title, string memory url, string memory description ) public { Resource storage resource = resources[numResource]; //create new instance of our resource object. resource.creator = msg.sender; resource.total_votes = 0; resources[numResource] = Resource( numResource, resource.creator, title, url, description, resource.total_votes ); numResource++; }
Первая строка кода в приведенном выше примере представляет собой отображение в твердости, которое похоже на объект в Javascript, используемый для хранения данных в формате пары ключ-значение, или на хэш-карту на каком-либо другом языке.
mapping(uint256 => Resource) public resources;
В этой строке мы сопоставляем встроенный тип Solidity uint256
с нашим Resource struct
и устанавливаем общедоступный массив ресурсов. Мы создаем переменную numResource
, которую мы будем использовать для ссылки на id
ресурса, который у нас есть в нашем массиве ресурсов.
Примечание 1: В Solidity использование ключевого слова public
означает, что указанные данные могут быть вызваны кем угодно или любым контрактом.
Примечание 2. Массив resources
может быть заполнен различными ресурсами образа Resource struct
, который мы определили.
Затем мы создаем новый экземпляр нашего объекта ресурса в нашем хранилище контрактов Solidity, используя ключевое слово storage
. Это позволит нам напрямую изменить состояние контракта. Далее мы устанавливаем значение того, что будет иметь конкретный ресурс, которым мы хотим поделиться. Мы установили, что создателем ресурса всегда будет msg.sender
, то есть лицо, вызывающее функции в нашем контракте, или лицо, связанное с нашим контрактом, а total_votes
изначально равно нулю. Затем мы устанавливаем все значения ресурса, которым мы хотим поделиться, включая аргументы, которые будет предоставлять пользователь, создающий ресурс, и увеличиваем numResource
каждый раз при вызове функции.
В коде мы видим функцию-конструктор. Функция конструктора вызывается каждый раз, когда мы запускаем наш контракт.
Получить все ресурсы
В предыдущем разделе мы написали функцию для добавления нового ресурса. Давайте рассмотрим, как получить тот или иной ресурс, а затем все ресурсы в этом разделе.
//Resource.sol //return a particular resource function getResource(uint256 resourceId) public view returns (Resource memory) { return resources[resourceId]; } //return the array of resources function getResources() public view returns (Resource[] memory) { Resource\[] memory id = new Resource[\](numResource); for (uint256 i = 0; i < numResource; i++) { Resource storage resource = resources[i]; id[i] = resource; } return id; }
В приведенном выше коде первая функция getResource
принимает resourceId
в качестве параметра, используемого для нацеливания на конкретный ресурс, а затем возвращает его. Синтаксис view
указывает, что мы не вносим никаких изменений в состояние контракта, а только просматриваем или запрашиваем данные о состоянии. Синтаксис returns
сообщает нам тип данных, которые мы возвращаем, в нашем случае это тип нашего Resource struct
. Вторая функция getResources
перебирает все ресурсы в массиве и возвращает их.
Голосование за ресурс Мы обеспечим функцию голосования, чтобы пользователь также мог голосовать за определенный ресурс, который ему нравится.
//Resource.sol // Vote a resource function voteResource(uint256 resourceId) public { Resource storage resource = resources[resourceId]; resource.total_votes++; }
Эта функция просто нацелена на определенный ресурс в хранилище контрактов и увеличивает общее количество голосов каждый раз, когда вызывается функция voteResource
.
Развертывание нашего контракта
Мы закончили программировать наш контракт, пришло время его развернуть! В этом разделе мы развернем контракт дважды. Сначала мы создадим локальный узел на нашей машине и локально развернем контракт, а позже мы развернем контракт в режиме реального времени в полигональной тестовой сети, где любой человек в мире сможет просмотреть контракт и транзакции, которые в нем происходят. Теперь давайте начнем.
Локальное развертывание нашего контракта
Чтобы развернуть наш контракт локально в локальной сети узлов, предоставленной hardhat
, давайте запустим узел, выполнив команду:
npx hardhat node
После запуска этой команды Hardhat предоставляет нам 20 учетных записей, каждая со своим идентификатором учетной записи кошелька и закрытым ключом. Мы будем использовать их для подключения к MetaMask позже. Каждая учетная запись финансируется фальшивыми 100ETH, которые мы можем использовать для выполнения операций и оплаты газа в создаваемом нами децентрализованном приложении. Вот так выглядит наш терминал:
Как видно из изображения, порт, на котором работает наша локальная сеть: https://127.0.0.1:8545/. Перейдите к своему hardhat.config.js
и добавьте эту сетевую конфигурацию.
//hardhat.config.js module.exports = { <------> solidity: "0.8.4", networks: { hardhat: { chainId: 31337 } } };
В нашей папке сценария переименуйте файл sample-script.js
в deploy.js
, так как именно здесь мы напишем сценарий для развертывания нашего смарт-контракта. Измените файл deploy.js
, чтобы он выглядел следующим образом:
// deploy.js const hre = require("hardhat"); async function main() { // If this script is run directly using `node` you may want to call compile // manually to make sure everything is compiled // await hre.run('compile'); // We get the contract to deploy const ResourceShare = await hre.ethers.getContractFactory("ResourceShare"); const resourceshare = await ResourceShare.deploy(); await resourceshare.deployed(); console.log("resourceshare deployed to:", resourceshare.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); });
Теперь, чтобы окончательно развернуть наш контракт, мы запускаем команду.
npx hardhat run --network localhost scripts/deploy.js
Когда мы запускаем указанную выше команду, Hardhat автоматически создает папку артефактов в корне нашего каталога. Папка артефакта очень важна, если мы хотим взаимодействовать со смарт-контрактом, который мы только что развернули, и подключиться к нашему внешнему интерфейсу. Вот что мы получаем в нашем терминале после успешного развертывания:
мы также можем увидеть на вкладке терминала, где мы начали наш узел:
Выше мы можем видеть различную информацию о нашем развернутом контракте, включая адрес контракта, учетную запись из 20 учетных записей, из которых был развернут наш контракт, и плату за газ, использованную для развертывания нашего контракта.
Теперь, когда у нас есть успешное развертывание, давайте установим и подключим наш кошелек metamask к нашей учетной записи, чтобы мы могли взаимодействовать с интерфейсным приложением, которое мы скоро создадим.
Я бы хотел, чтобы мы сосредоточились на самых важных вещах, поэтому я свяжу вас с руководством по установке и настройке вашего кошелька метамаски, чтобы мы могли сосредоточиться на важных вещах, связанных с подключением нашего контракта к метамаске. Теперь, когда у вас установлен кошелек метамаски, давайте подключимся к нашей локальной сети Hardhat и импортируем учетную запись, из которой мы развернули наш контракт. Следуй этим шагам:
- Щелкните значок метамаски на вкладке расширения и войдите в метамаску. После входа вы будете автоматически перенаправлены в свою учетную запись основной сети Ethereum.
- Щелкните раскрывающийся список сети и нажмите кнопку «Добавить сеть».
Заполните поля следующей информацией и сохраните.
Убедитесь, что вы находитесь в только что добавленной сети Hardhat, затем щелкните круглый значок в правом верхнем углу.
Перейдите на вкладку терминала, где вы запустили узел Hardhat и получили 20 учетных записей. Найдите первую учетную запись, из которой был развернут ваш контракт, а затем скопируйте и вставьте ее в поле, где вас попросят вставить закрытый ключ.
Нажмите «Импорт», и вы увидите баланс импортированной учетной записи.
Баланс больше не составляет 10000 Eth, как мы видели на нашем терминале, потому что мы использовали немного Eth для оплаты платы за газ для развертывания контракта.
Теперь, когда у нас все настроено, пришло время создать наш интерфейс и интегрировать его с нашим контрактом, чтобы пользователь мог выполнять операции из нашего пользовательского интерфейса.
Создание нашего интерфейса
В последнем разделе мы рассмотрели, как настроить наш кошелек метамаски и импортировать нашу учетную запись. Настало время создать наш интерфейс и взаимодействовать с контрактом.
Мы будем использовать create-react-app
для создания внешнего интерфейса в корневой папке нашего приложения.
npx create-react-app frontend
Теперь, когда мы настроили наше внешнее приложение, давайте перейдем к нашему файлу App.js
в папке src
и начнем писать код!. Прежде чем перейти к коду, я хотел бы обсудить, как наш интерфейс подключается к нашему смарт-контракту, и общую структуру, которой будет следовать наш интерфейс.
При создании децентрализованного приложения есть полезные библиотеки, которые позволяют запрашивать у блокчейна некоторые данные или вносить изменения в состояние контракта в блокчейне. Примерами таких библиотек являются наиболее популярные web3.js и ethers.js. Без этих библиотек взаимодействие с нашим пользовательским интерфейсом будет очень болезненным. В нашем случае мы используем библиотеку ethers.js
, которую мы уже установили при запуске этого руководства, и поэтому мы будем импортировать ее в наш файл app.js
.
Внешняя структура
Мы создаем dApp для совместного использования ресурсов, поэтому наш интерфейс React будет состоять из двух компонентов:
ListResources
— это компонент определенного ресурса, которым мы делимся в нашем приложении. Он будет содержать всю информацию о ресурсе, которым мы делимся.CreateResource
— Этот компонент содержит форму, поэтому мы можем добавить новый ресурс в наше приложение.
Эти два компонента будут импортированы в наш файл App.js
.
Давайте посмотрим, как выглядит наш App.js
:
// App.js import './App.css' import ResourceArtifact from '../src/artifact/contracts/Resource.sol/ResourceShare.json' import { ethers } from 'ethers' import ListResources from './components/ListResources' import CreateResource from './components/CreateResource' import { useEffect, useState } from 'react' const resourceAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3' function App() { const [resourceData, setResourceData] = useState([]) const [toggleModal, setToggleModal] = useState(false) const [contract, setContract] = useState() async function requestAccount() { await window.ethereum.request({ method: 'eth_requestAccounts' }) } function addResource() { setToggleModal(!toggleModal) } const provider = new ethers.providers.Web3Provider(window.ethereum) const signer = provider.getSigner() async function _intializeContract(init) { // We first initialize ethers by creating a provider using window.ethereum // When, we initialize the contract using that provider and the token's // artifact. You can do this same thing with your contracts. const contract = new ethers.Contract( resourceAddress, ResourceArtifact.abi, init, ) return contract } useEffect(() => { // in this case, we only care to query the contract when signed in if (typeof window.ethereum !== 'undefined') { (async function getResourcesCount() { await requestAccount() const contract = await _intializeContract(signer) const resourcedata = await contract.getResources() const resources = [...resourcedata] setContract(contract) setResourceData(resources) })() } }, []) return ( <> <header> <button onClick={addResource}>Add a resource</button> </header> <main> <CreateResource toggleModal={toggleModal} contract={contract} /> <section className="resources"> {resourceData.map((resource, id) => { return ( <div key={id}> <ListResources resource={resource} contract={contract} /> </div> ) })} </section> </main> </> ) } export default App
давайте проанализируем наш файл App.js
по крупицам, начиная с первых 8 строк:
import './App.css' import ResourceArtifact from '../src/artifact/contracts/Resource.sol/ResourceShare.json' import { ethers } from 'ethers' import ListResources from './components/ListResources' import CreateResource from './components/CreateResource' import { useEffect, useState } from 'react' const resourceAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3'
Выше мы импортировали библиотеку ethers.js, о которой говорили ранее, наши компоненты, наш файл артефакта контракта и адрес нашего контракта.
Чтобы подключиться к нашему контракту и взаимодействовать с ним, необходимо иметь адрес контракта и файл ABI, который можно найти в нашей папке артефактов. Файл ABI содержит важную информацию о нашем контракте, например методы, которые нужно вызывать в нашем контракте.
const [resourceData, setResourceData] = useState([]) const [toggleModal, setToggleModal] = useState(false) const [contract, setContract] = useState() async function requestAccount() { await window.ethereum.request({ method: 'eth_requestAccounts' }) }
Вот наше состояние приложения.
- В состоянии
resourceData
будет храниться массив ресурсов, содержащий все ресурсы. - Состояние
toggleModal
для удержания состояния, чтобы решить, открыто наше модальное окно или нет. - Состояние
contract
, чтобы мы могли хранить контракт и передавать его как реквизит, чтобы мы могли вызывать методы контракта из других компонентов.
Затем у нас есть функция requestAccount
, которая вызывает метод eth_requestAccounts
для объекта окна ethereum. При вызове этого метода пользователю предлагается войти в свой кошелек метамаски в браузере.
const provider = new ethers.providers.Web3Provider(window.ethereum) const signer = provider.getSigner() async function _intializeContract(init) { // We first initialize ethers by creating a provider using window.ethereum // When, we initialize the contract using that provider and the token's // artifact. You can do this same thing with your contracts. const contract = new ethers.Contract( resourceAddress, ResourceArtifact.abi, init, ) return contract } useEffect(() => { // in this case, we only care to query the contract when signed in if (typeof window.ethereum !== 'undefined') { (async function getResources() { await requestAccount() const contract = await _intializeContract(signer) const resourcedata = await contract.getResources() const resources = [...resourcedata] setContract(contract) setResourceData(resources) })() } }, [])
В приведенном выше коде у нас есть функция *_intializeContract*
, которая инициализирует наш контракт с библиотекой ethers.js. Для инициализации контракта нам нужно создать переменную контракта, которая использует метод Contract
из библиотеки Ethers, который принимает три параметра:
- Адрес.
- Артефакт контракта
ResourceShare
ABI - Учетная запись подписывающей стороны, которую мы получаем, используя
window.ethereum
в качестве нашего провайдера.
Потом возвращаем договор. Позже мы увидим, как будет использоваться функция _intializeContract. В нашем хуке React useEffect
мы вызываем функцию getResources
в IIFE, которая вызывает функцию requestAccount()
, предлагающую пользователю войти в систему с помощью своей метамаски, затем мы инициализируем наш контракт в переменной contract
и вызываем метод getResources
в нашем контракте, и установите наши состояния contract
и resourceData
.
return ( <> <header> <button onClick={addResource}>Add a resource</button> </header> <main> <CreateResource toggleModal={toggleModal} contract={contract} /> <section className="resources"> {resourceData.map((resource, id) => { return ( <div key={id}> <ListResources resource={resource} contract={contract} /> </div> ) })} </section> </main> </> )
Здесь мы возвращаем некоторую разметку того, что мы будем отображать в нашем пользовательском интерфейсе. В нашем заголовке у нас есть только кнопка «Добавить ресурс», при нажатии на которую вызывается функция addResource
, которая переключает наше модальное окно и открывает форму для создания и добавления нового ресурса. В теге main
у нас есть компонент CreateResource
, затем мы перебираем наш массив resourceData
и передаем объект ресурса в наш компонент ListResources
в качестве реквизита. Мы также передаем контракт в качестве реквизита в двух компонентах, чтобы мы могли вызывать методы контракта в двух компонентах.
Список компонентов ресурса
Мы перебрали состояние массива ресурсов в нашем компоненте App.js
и предоставили объектную поддержку нашему компоненту ListResources
. Давайте посмотрим, как выглядит наш ListResources
:
function ListResources({ resource, contract }) { async function vote(id) { await contract.voteResource(id) } return ( <div className="project"> <h2>{resource.title}</h2>{' '} <span className="creator">{resource.creator}</span> <h3>description:</h3> <p>{resource.description}</p> <h4>Link:<a href={resource.url}>{resource.url}</a></h4> <h4>Votes: {resource.total_votes}</h4> <button onClick={(id) => vote(resource.id)} > Vote </button> </div> ) } export default ListResources
В этом компоненте мы просто принимаем свойства resource
и contract
, список отображает все данные в нашем объекте ресурса. Когда мы нажимаем кнопку, мы вызываем функцию vote
, которая принимает идентификатор ресурса и вызывает метод voteResource
в нашем контракте, увеличивая счетчик total_votes
на 1.
Создать компоненты ресурсов
Наши компоненты создания ресурсов представляют собой модальную форму с полями ввода для добавления нового ресурса.
import React, { useState } from 'react' function CreateResource({ toggleModal, contract }) { const [title, setTitle] = useState('') const [description, setDescription] = useState('') const [url, setUrl] = useState('') const handleSubmit = (event) => { event.preventDefault() contract.addResource(title, url, description) } return ( <div> {toggleModal === true && ( <div className="addresource"> <form onSubmit={handleSubmit}> <label> Enter resource title: <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} /> </label> <label> Enter resource url: <input type="text" value={url} onChange={(e) => setUrl(e.target.value)} /> </label> <label> Enter resource description: <input type="text" value={description} onChange={(e) => setDescription(e.target.value)} /> </label> <input type="submit" className="submit" /> </form> </div> )} </div> ) } export default CreateResource
Когда состояние toggleModal
равно true
, этот компонент отображается условно. У нас есть состояние для ресурса title
, description
и url
, и мы устанавливаем состояние на входное значение при каждом изменении нашего ввода. Функция handleSubmit
вызывается при отправке формы, а наш метод addResource
вызывается из контракта.
Повтор сеанса с открытым исходным кодом
Отладка веб-приложения в рабочей среде может быть сложной и трудоемкой. OpenReplay — это альтернатива FullStory, LogRocket и Hotjar с открытым исходным кодом. Это позволяет вам отслеживать и воспроизводить все, что делают ваши пользователи, и показывает, как ваше приложение ведет себя при каждой проблеме. Это похоже на то, как если бы инспектор вашего браузера был открыт, когда вы смотрите через плечо вашего пользователя. OpenReplay — единственная доступная в настоящее время альтернатива с открытым исходным кодом.
Удачной отладки, для современных фронтенд-команд — Начните бесплатно отслеживать свое веб-приложение.
Развертывание нашего контракта в тестовой сети
В последнем разделе мы рассмотрели, как подключить наш интерфейс к нашему смарт-контракту. В этом разделе мы рассмотрим, как развернуть наш смарт-контракт в тестовой сети, чтобы все могли ее увидеть. Сеть, в которой мы будем развертывать, — это тестовая сеть Ropsten Ethereum, которая позволяет нам развернуть наш контракт и протестировать его перед развертыванием в основной сети Ethereum.
Сначала нам нужно щелкнуть значок расширения метамаски, затем щелкнуть раскрывающийся список сети и выбрать файл Ropsten test network
. Нам нужен способ взаимодействия с тестовой сетью Ropsten без запуска локального узла Ethereum на нашей машине, и есть довольно много сервисов, которые позволяют нам это сделать, один из них — Infura. Чтобы использовать узел Ethereum сети Ropsten, предоставленный Infura, нам необходимо создать учетную запись и войти в систему. После входа в систему нажмите Создать новый проект.
После нажатия вам будет предложено модальное окно с запросом имени проекта.
Мы можем назвать наш проект как угодно и нажать кнопку «Создать». На панели инструментов нашего проекта мы можем увидеть детали нашего проекта и конечные точки, которые мы будем использовать.
Щелкните раскрывающийся список конечных точек и выберите сеть Ropsten. Теперь у нас есть идентификатор нашего проекта и конечная точка Ropsten, которая нам понадобится для подключения к сети Ropsten. Давайте настроим Hardhat для развертывания в сети. В нашем hardhat.config.js
теперь у нас есть:
module.exports = { paths: { artifacts: './frontend/src/artifact', }, solidity: "0.8.4", networks: { hardhat: { chainId: 31337 }, ropsten: { url: "https://ropsten.infura.io/v3/ca9b0c1b342b44658282a52daaf1be25", accounts: [`0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`] } }
Мы добавили новую конфигурацию в наши сети. Для конфигурации сети Ropsten требуется URL
, который является нашей конечной точкой ropsten, полученной от Infura, и account
, который является закрытым ключом учетной записи кошелька Ropsten, которую мы выбрали в метамаске.
Чтобы получить закрытый ключ учетной записи, нажмите на сведения об учетной записи, затем на «экспортировать закрытый ключ».
Введите свой пароль метамаски и нажмите «Подтвердить», затем вы можете скопировать закрытый ключ и вставить его в конфигурацию своей каски. Просто убедитесь, что вы добавили к нему префикс 0x
, как показано в нашей конфигурации Ropsten.
В реальном случае использования, когда вы будете выполнять развертывание в основной сети Ethereum, убедитесь, что закрытые ключи являются закрытыми и хранятся там, где у вас есть доступ только для предотвращения потери средств. При желании вы можете сохранить его в файле
env
.
Теперь мы готовы развернуть контракт в сети Ropsten. Все, что нам нужно сделать сейчас, это пополнить нашу тестовую сетевую учетную запись Ropsten и запустить команду развертывания Hardhat. Чтобы пополнить нашу учетную запись, нам нужно вставить нашу учетную запись кошелька тестовой сети Ropsten на сайт крана Ropsten, который отправляет нам бесплатный эфир.
Подождите немного, и вы увидите отправленный вам бесплатный эфир
Теперь мы можем запустить команду развертывания:
npx hardhat run --network ropsten scripts/deploy.js
и наш контракт теперь развернут в сети Ropsten, и вы можете проверить его на Etherscan, сайте, который позволяет вам изучить контракт, который вы только что развернули.
Чтобы изучить наш контракт на Etherscan, скопируйте учетную запись, на которую развернут контракт, с терминала и вставьте его на Etherscan.
Нажмите на значок поиска, и вы увидите информацию о контракте, который мы развернули.
Ура!. Мы завершили обучение, и вы можете найти исходный код здесь.
Заключение
Наконец, мы закончили с этим уроком. Мы узнали, как создать dApp от начала до конца, подключить смарт-контракт и развернуть его в тестовой сети, изучая такие инструменты, как Infura и Etherscan. Я надеюсь, что это руководство поможет вам приступить к созданию dApps на блокчейне Ethereum.
Первоначально опубликовано на https://blog.openreplay.com 8 марта 2022 г.