Хотите погрузиться в разработку Web3? Вот введение и краткий пример того, как разработчики полного стека могут начать работу.

Прошлым летом мы с женой смотрели программу Стартап на Netflix. В сериале, состоящем из трех сезонов, рассказывается о проблемах, с которыми сталкиваются персонажи при создании GenCoin, новой (и вымышленной) цифровой валюты. Когда я смотрел шоу, я задавался вопросом, как изменилась бы моя жизнь, если бы я был членом команды инженеров, занимающихся внедрением GenCoin.

Хотя такие продукты, как GenCoin, были созданы с целью предоставить новый способ выполнения финансовых транзакций, лежащие в их основе концепции более эффективны, чем просто задачи, связанные с валютой. На самом деле, распределенный характер того, как проектируется блокчейн, напрямую связан с сердцем Web3.

Прежде чем идти дальше, давайте вспомним три основные эпохи Интернета:

  • Web1 — статические веб-страницы (1991–2004 гг.)
  • Web2 — веб как платформа (с 2004 г.)
  • Web3 — децентрализованный дизайн, включает технологии блокчейна (с 2009 года, но за последние несколько лет действительно набрал обороты)

Web3 представляет собой альтернативу реальности Web2, где контроль централизован в руках нескольких поставщиков технологий, таких как Google, Apple и Amazon. Web3 создает хранилище данных без разрешений, в котором ни один человек или корпорация не контролирует и не владеет данными, но при этом гарантируется, что эти данные верны. Данные хранятся в общедоступных реестрах сети блокчейн. Таким образом, вместо одного объекта, владеющего данными, несколько узлов (компьютеров, на которых запущена цепочка блоков) хранят данные и приходят к консенсусу относительно того, действительны данные или нет.

Протокол для создания такого хранилища данных (который начался с Биткойна и продолжился такими протоколами, как Ethereum и другими) является основой web3 и открывает широкий спектр новых вариантов использования, таких как:

  • Идентификация личности контролируется пользователем, а не корпорацией (например, MetaMask)
  • Финансовая система без разрешений (биткойн и другие цифровые валюты, которые позволяют получать кредиты без разрешения, валюты, инвестиции и т. д.)
  • Доказуемое цифровое право собственности на цифровые предметы, такие как музыка, искусство и т. д., с помощью утилиты NFT.
  • Разовое формирование групп с аналогичной целью через децентрализованные автономные организации (ДАО), такие как Конституция ДАО или социальная ДАО Друзья с пользой
  • Игры играй, чтобы заработать (p2e), в которых пользователи могут зарабатывать на жизнь, играя в игру (например, Axie Infinity)

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

Насколько близок к этому идеалу веб3 или может приблизиться — и хорошо это или плохо — это глубокий разговор со многими сильными (и сильно предвзятыми) мнениями. Я не буду вдаваться в это здесь. Еще следует упомянуть, что web3 не заменит web2, точно так же, как web2 не заменит web1. Все три будут иметь свое место в будущем.

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

Переход от полного стека к стеку Web3

Термин Full-Stack Developer набрал обороты примерно в 2015 году, предлагая преимущество, заключающееся в том, что один инженер-программист может внести свой вклад в любой уровень стека программного обеспечения. В результате, если была зарегистрирована функция или ошибка, связанная с уровнем служб, тот же разработчик, который только что завершил задачу, связанную с клиентом, мог получить тикет и работать продуктивно.

Для тех, кто хочет прочитать мои мысли о разработке с полным стеком (написанные в разгар повального увлечения этим модным словом), щелкните ссылку ниже:

Полная разработка стека… действительно возможна?

Вопреки мнению разработчиков с полным стеком, теперь внимание должно сместиться на размышления о том, как выглядит стек web3 и как инженеры-программисты будут внедрять функции и функции в веб-разработку следующего поколения.

Основы Web3

Детальное погружение в web3 потребовало бы длинной серии статей. Мы собираемся сохранить его на высоком уровне и предоставить обзор с некоторыми ссылками на дополнительные ресурсы. Мы рассмотрим типичные первые шаги: создание смарт-контракта, а затем Dapp для взаимодействия с этим смарт-контрактом.

Смарт-контракт — это фрагмент кода, развернутый на блокчейне (в нашем случае ниже — на Эфириуме). Этот код неизменен и не имеет разрешений. После того, как вы развернете его, он будет существовать в блокчейне, его нельзя будет изменить, и любой может получить его.

Дапп (децентрализованное приложение) — это то, как мы будем взаимодействовать с этим смарт-контрактом из нашего пользовательского интерфейса (обычно это веб-страница или приложение). Децентрализованное приложение использует открытый характер смарт-контрактов на серверной части. Его также можно хранить в децентрализованном файловом хранилище, таком как IPFS (InterPlanetary File Storage), чтобы исключить вероятность простоя. Атаки DDOS также чрезвычайно сложны в исполнении, потому что вам придется атаковать каждый узел, на котором хранится сайт.

И наоборот, безопасность и тестирование гораздо важнее. Перед развертыванием необходимо устранить недостатки и уязвимости в коде.

Давайте рассмотрим это более подробно.

Стек Web3

Для инженеров-программистов, стремящихся погрузиться в веб-конец пула, существует сочетание проверенных технологий и технологий, ориентированных на конкретные цели. Один очень популярный стек включает в себя следующие компоненты:

  • NPM — менеджер пакетов узлов, очень популярный среди разработчиков web2.
  • The Truffle Framework — инструменты разработки, ориентированные на web3
  • Ganache — позволяет запустить приватный блокчейн на локальной машине
  • MetaMask — пользовательский интерфейс блокчейна/шлюз к Эфириуму (децентрализованный блокчейн с открытым исходным кодом)
  • Solidity — ведущий язык программирования смарт-контрактов
  • HTML/CSS/JavaScript — клиентский слой
  • Web3.js — библиотека Ethereum API для взаимодействия с сетью Ethereum
  • Infura — сервис Ethereum API, предоставляющий доступ к сети Ethereum

Обзор децентрализованного приложения Ethereum

Предположим, ассоциация домовладельцев собирается провести периодические выборы, на которых жители района голосуют за ряд решений. Поскольку по соседству проживает группа ИТ-специалистов, стремящихся стать пионерами веб-3, они решают создать Ethereum Dapp к выборам.

Почему это может быть хорошим примером?

Использование Dapp для хранения голосов дает бесспорные результаты. При правильной настройке возможности фальсификации или подделки голосов не существует, поскольку данные хранятся публично в блокчейне, а не на частном сервере отдельной компании. Любой может получить результаты голосования без разрешения, взаимодействуя со смарт-контрактом.

Создание смарт-контракта

Первым шагом является создание нашего смарт-контракта для использования с приложением, в котором используются следующие компоненты стека web3:

  • Инфура
  • НПМ
  • Трюфельная структура
  • Ганаш
  • Солидность

Создание смарт-контракта следует схеме, аналогичной приведенной ниже иллюстрации:

Этот поток был полностью детализирован командой ConsenSys:

Подключение разработчиков Ethereum

Создание Dapp с использованием React

Имея Smart Contact, инженеры web3 могут сосредоточиться на создании приложения, которое будет использоваться на выборах ассоциации. Из списка выше остались следующие компоненты стека web3:

  • НПМ
  • Метамаск
  • HTML/CSS/JavaScript/React
  • Web3.js

В этом примере мы собираемся использовать фреймворк React, который будет использовать следующий поток:

Теперь давайте построим что-нибудь с нуля.

Мое первое децентрализованное приложение Ethereum

После создания бесплатного аккаунта в Инфуре я создал новый проект под названием jvc-homeowners-ballot:

Новый проект содержит следующие детали, на которые я обращусь позже:

Начало работы с трюфелем

На моем локальном компьютере я создал соответствующую папку с именем jvc-homeowners-ballot, а затем инициализировал Truffle с помощью следующей команды CLI:

truffle init

В результате инициализации получается следующая структура каталогов:

├── contracts
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── test
└── truffle-config.js

Далее была добавлена ​​зависимость поставщика кошелька на основе Truffle:

npm install --save @truffle/hdwallet-provider

Чтобы создать локальную сеть разработки, Ganache CLI был запущен с помощью следующей команды:

ganache

CLI ответил следующей информацией, и Ganache теперь работает на порту № 8545 моей локальной машины:

ganache v7.0.1 (@ganache/cli: 0.1.2, @ganache/core: 0.1.2)
Starting RPC server
Available Accounts
==================
(0) 0x2B475e4fd7F600fF1eBC7B9457a5b58469b9EDDb (1000 ETH)
(1) 0x5D4BB40f6fAc40371eF1C9B90E78F82F6df33977 (1000 ETH)
(2) 0xFaab2689Dbf8b7354DaA7A4239bF7dE2D97e3A22 (1000 ETH)
(3) 0x8940fcaa55D5580Ac82b790F08500741326836e0 (1000 ETH)
(4) 0x4c7a1b7EB717F98Fb0c430eB763c3BB9212F49ad (1000 ETH)
(5) 0x22dFCd5df8d4B19a42cB14E87219fea7bcA7C92D (1000 ETH)
(6) 0x56882f79ecBc2D68947C6936D4571f547890D07c (1000 ETH)
(7) 0xD257AFd8958c6616bf1e61f99B2c65dfd9fEE95A (1000 ETH)
(8) 0x4Bb2EE0866578465E3a2d3eCCC41Ea2313372B20 (1000 ETH)
(9) 0xdf267AeFeAfE4b7053ca10c3d661a8CB24E98236 (1000 ETH)
Private Keys
==================
(0) 0x5d58d27b0f294e3222bbd99a3a1f07a441ea4873de6c3a2b7c40b73186eb616d
(1) 0xb9e52d6cfb2c074fa6a6578b946e3d00ea2a332bb356d0b3198ccf909a97fdc8
(2) 0xc52292ce17633fe2724771e81b3b4015374d2a2ea478891dab74f2028184edeb
(3) 0xbc7b0b4581592e48ffb4f6420228fd6b3f954ac8cfef778c2a81188415274275
(4) 0xc63310ccdd9b8c2da6d80c886bef4077359bb97e435fb4fe83fcbec529a536fc
(5) 0x90bc16b1520b66a02835530020e43048198195239ac9880b940d7b2a48b0b32c
(6) 0x4fb227297dafb879e148d44cf4872611819412cdd1620ad028ec7c189a53e973
(7) 0xf0d4dbe2f9970991ccc94a137cfa7cf284c09d0838db0ce25e76c9ab9f4316d9
(8) 0x495fbc6a16ade5647d82c6ad12821667f95d8b3c376dc290ef86c0d926f50fea
(9) 0x434f5618a3343c5e3b0b4dbeaf3f41c62777d91c3314b83f74e194be6c09416b
HD Wallet
==================
Mnemonic:      immense salmon nominee toy jungle main lion universe seminar output oppose hungry
Base HD Path:  m/44'/60'/0'/0/{account_index}
Default Gas Price
==================
2000000000
BlockGas Limit
==================
30000000
Call Gas Limit
==================
50000000
Chain Id
==================
1337
RPC Listening on 127.0.0.1:8545

В папке моего проекта файл truffle-config.js был обновлен, чтобы активировать следующие строки:

development: {
      host: "127.0.0.1",     // Localhost (default: none)
      port: 8545,            // Standard Ethereum port (default: none)
      network_id: "*",       // Any network (default: none)
    },

Теперь консоль Truffle можно запустить в новом окне терминала:

truffle console

… что приводит к простой консоли:

truffle(development)>

Консоль можно использовать для создания кошелька:

const HDWalletProvider = require('@truffle/hdwallet-provider');

Это должно привести к ответу undefined. Это нормально.

Далее нам нужна мнемоническая фраза из 12 слов, поэтому я использовал сайт Mnemonic Code Converter для ее создания.

Затем я использовал эту фразу из 12 слов для обновления консоли Truffle:

const mnemonic = '12 words here';
const wallet = new HDWalletProvider(mnemonic, "http://localhost:8545");

Оба они также привели к ответу undefined, но консоль кошелька показала, что команды действительно работали, как показано ниже:

truffle(development)> wallet
HDWalletProvider {
  walletHdpath: "m/44'/60'/0'/0/",
  wallets: {
...
 },
  addresses: [
    '0xa54b012b406c01dd99a6b18ef8b55a15681449af',
    '0x6d507a70924ea3393ae1667fa88801650b9964ad',
    '0x1237e0a8522a17e29044cde69b7b10b112544b0b',
    '0x80b4adb18698cd47257be881684fff1e14836b4b',
    '0x09867536371e43317081bed18203df4ca5f0490d',
    '0x89f1eeb95b7a659d4748621c8bdbabc33ac47bbb',
    '0x54ceb6f0d722dcb33152c953d5758a08045f254d',
    '0x25d2a8716792b98bf9cce5781b712f00cf33227e',
    '0x37b6364fb97028830bfeb0cb8d2b14e95e2efa05',
    '0xe9f56031cb6208ddefcd3cdd5a1a41f7f3400af5'
  ],
...

Добавление средств ETH для тестирования

Теперь нам нужно получить немного тестовых средств для нашего Dapp и мы будем использовать Ropsten Ethereum Faucet, чтобы добавить средства в мой существующий кошелек MetaMask, созданный ConsenSys. Имейте в виду, что вы можете создать несколько учетных записей в MetaMask, где по крайней мере одна учетная запись предназначена для разработки и тестирования. Это снижает риск случайной потери реальных средств. Кроме того, никогда никому не сообщайте свою исходную фразу и никогда не загружайте свой закрытый ключ… куда угодно!

Чтобы добавить несколько тестовых средств, мне нужно было только указать адрес моей учетной записи:

Используя сайт Ropsten Etherscan, мы можем подтвердить успешное завершение транзакции:

Заключительные этапы подготовки

Зависимость dotenv была добавлена ​​в проект с помощью следующей команды:

npm install --save dotenv

Затем в корне проекта был создан новый файл с именем .env, содержащий следующие две строки:

INFURA_API_KEY=INSERT YOUR API KEY HERE (no quotations)
MNEMONIC="12 words here"

INFURA_API_KEY — это идентификатор проекта, который был присвоен при создании проекта jvc-homeowners-ballot.

Важное примечание. Убедитесь, что файл .env включен в файл .gitignore, чтобы эта секретная информация не была доступна другим лицам, имеющим доступ к репозиторию.

Последним подготовительным этапом является обновление файла truffle-config.js. Во-первых, нам нужно добавить следующие строки вверху файла:

require("dotenv").config();
const HDWalletProvider = require("@truffle/hdwallet-provider");

Затем нам нужно добавить следующую сеть, которая будет использовать зависимость dotenv, добавленную выше:

ropsten: {
     provider: () =>
       new HDWalletProvider(
         process.env.MNEMONIC,
         `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}`
       ),
     network_id: 3, // Ropsten's id
     gas: 5500000, // Ropsten has a lower block limit than mainnet
     confirmations: 2, // # of confs to wait between deployments. (default: 0)
     timeoutBlocks: 200, // # of blocks before a deployment times out  (minimum/default: 50)
     skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
    },

Настройка смарт-контракта

Имея Infura и Truffle и немного тестовых средств на нашем счете, пришло время сосредоточиться на нашем смарт-контракте.

Для выбора товарищества собственников жилья мы будем использовать следующий контракт, который называется JvcHomeownerBallot.sol и находится в папке контрактов проекта:

// SPDX-License-Identifier: UNLICENSED  (it is common practice to include an open source license or declare it unlicensed)
pragma solidity ^0.8.7;  // tells the compiler which version to use
contract Homeowners {
// store the addresses of voters on the blockchain in these 2 arrays
    address[] votedYes;
    address[] votedNo;
function voteYes() public {
        votedYes.push(msg.sender);
    }
function voteNo() public {
        votedNo.push(msg.sender);
    }
function getYesVotes() public view returns (uint) {
        return votedYes.length;
    }
function getNoVotes() public view returns (uint) {
        return votedNo.length;
    }
}

Для этого примера наш контракт будет довольно простым, где избиратели могут выбрать «Да» или «Нет» для любого вопроса в бюллетене.

На этом этапе папка контрактов выглядит так, как показано ниже:

.
├── JvcHomeownersBallot.sol
└── Migrations.sol

Имея контракт, нам нужно установить способ его развертывания. Здесь находится папка миграции. Следующее содержимое было добавлено в файл 2_deploy_contracts.js внутри папки migrations:

const JvcHomeownersBallot = artifacts.require("JvcHomeownersBallot.sol");
module.exports = function(deployer) {
 deployer.deploy(JvcHomeownersBallot);
};

Теперь мы можем выполнить миграцию контракта с помощью следующей команды:

truffle migrate --network ropsten

Ключевое слово migrate дает следующий ответ:

Compiling your contracts...
===========================
> Compiling ./contracts/JvcHomeownersBallot.sol
> Artifacts written to /Users/john.vester/projects/jvc/consensys/jvc-homeowners-ballot/build/contracts
> Compiled successfully using:
   - solc: 0.8.11+commit.d7f03943.Emscripten.clang
Network up to date.
truffle(development)> truffle migrate --network ropsten
Compiling your contracts...
===========================
> Compiling ./contracts/JvcHomeownersBallot.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /Users/john.vester/projects/jvc/consensys/jvc-homeowners-ballot/build/contracts
> Compiled successfully using:
   - solc: 0.8.11+commit.d7f03943.Emscripten.clang
Starting migrations...
======================
> Network name:    'ropsten'
> Network id:      3
> Block gas limit: 8000000 (0x7a1200)
1_initial_migration.js
======================
Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x5f227f26a31a3667a689be2d7fa6121a21153eb219873f6fc9aecede221b3b82
   > Blocks: 5            Seconds: 168
   > contract address:    0x9e6008B354ba4b9f91ce7b8D95DBC6130324024f
   > block number:        11879583
   > block timestamp:     1643257600
   > account:             0xa54b012B406C01dd99A6B18eF8b55A15681449Af
   > balance:             1.573649230299520359
   > gas used:            250142 (0x3d11e)
   > gas price:           2.506517682 gwei
   > value sent:          0 ETH
   > total cost:          0.000626985346010844 ETH
Pausing for 2 confirmations...
   ------------------------------
   > confirmation number: 1 (block: 11879584)
   > confirmation number: 2 (block: 11879585)
> Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:     0.000626985346010844 ETH
2_deploy_contracts.js
=====================
Deploying 'JvcHomeownersBallot'
   -------------------------------
   > transaction hash:    0x1bf86b0eddf625366f65a996e633db589cfcef1a4d6a4d6c92a5c1f4e63c767f
   > Blocks: 0            Seconds: 16
   > contract address:    0xdeCef6474c95E5ef3EFD313f617Ccb126236910e
   > block number:        11879590
   > block timestamp:     1643257803
   > account:             0xa54b012B406C01dd99A6B18eF8b55A15681449Af
   > balance:             1.573133154908720216
   > gas used:            159895 (0x27097)
   > gas price:           2.507502486 gwei
   > value sent:          0 ETH
   > total cost:          0.00040093710999897 ETH
Pausing for 2 confirmations...
   ------------------------------
   > confirmation number: 1 (block: 11879591)
   > confirmation number: 2 (block: 11879592)
> Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:     0.00040093710999897 ETH
Summary
=======
> Total deployments:   2
> Final cost:          0.001027922456009814 ETH
- Blocks: 0            Seconds: 0
- Saving migration to chain.
- Blocks: 0            Seconds: 0
- Saving migration to chain.

На данный момент мы развернули смарт-контракт JvcHomeownersBallot в сети Ropsten. Смарт-контракт можно проверить, используя следующий URL-адрес и указав адрес контракта в журналах «Развертывание JvcHomeownersBallot»:

https://ropsten.etherscan.io/

Или… в этом случае:

https://ropsten.etherscan.io/address/0xdeCef6474c95E5ef3EFD313f617Ccb126236910e

Создание децентрализованного приложения с помощью React

Для предыдущих шагов я использовал папку с именем jvc-homeowners-ballot. На этом же уровне я создам приложение React под названием jvc-homeowners-ballot-client, используя интерфейс командной строки React:

npx create-react-app jvc-homeowners-ballot-client

Затем я изменил каталоги на только что созданную папку и выполнил следующее, чтобы установить зависимость web3 в приложение React:

cd jvc-homeowners-ballot-client
npm install web3

Когда основное приложение React готово, необходимо установить двоичный интерфейс контрактного приложения (ABI), чтобы позволить нашему Dapp взаимодействовать с контрактами в экосистеме Ethereum. Основываясь на содержимом файла смарт-контракта JvcHomeownerBallot.sol, я перешел к папке build/contracts, открыл файл JvcHomeownersBallet.json и использовал значения свойства «abi» для константы jvcHomeOwnersBallot файла abi.js, как показано ниже:

export const jvcHomeownersBallot = [
  {
    "inputs": [],
    "name": "voteYes",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "voteNo",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getYesVotes",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [],
    "name": "getNoVotes",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  }
];

Этот файл был помещен во вновь созданную папку abi внутри папки src приложения React.

Теперь файл React Apps.js необходимо обновить. Давайте сначала начнем с верхней части файла, которую необходимо настроить, как показано ниже:

import React, { useState } from "react";
import { jvcHomeownersBallot } from "./abi/abi";
import Web3 from "web3";
import "./App.css";
const web3 = new Web3(Web3.givenProvider);
const contractAddress = "0xdeCef6474c95E5ef3EFD313f617Ccb126236910e";
const storageContract = new web3.eth.Contract(jvcHomeownersBallot, contractAddress);

contactAddress можно найти несколькими способами. В этом случае я использовал результаты команды CLI truffle — migrate. Другой вариант — использовать сайт Etherscan.

Стандартная разработка React

На этом этапе стандартная разработка React может вступить во владение. Готовый файл App.js будет выглядеть так:

import React, { useState } from "react";
import { jvcHomeownersBallot } from "./abi/abi";
import Web3 from "web3";
import Nav from "./components/Nav.js";
import "./App.css";
import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import {CircularProgress, Grid, Typography} from "@material-ui/core";
const useStyles = makeStyles((theme) => ({
 root: {
   "& > *": {
     margin: theme.spacing(1),
   },
 },
}));
const web3 = new Web3(Web3.givenProvider);
const contractAddress = "0xdeCef6474c95E5ef3EFD313f617Ccb126236910e";
const storageContract = new web3.eth.Contract(jvcHomeownersBallot, contractAddress);
function App() {
 const classes = useStyles();
 const [voteSubmitted, setVoteSubmitted] = useState("");
 const [yesVotes, setYesVotes] = useState(0);
 const [noVotes, setNoVotes] = useState(0);
 const [waiting, setWaiting] = useState(false);
const getVotes = async () => {
     const postYes = await storageContract.methods.getYesVotes().call();
     setYesVotes(postYes);
const postNo = await storageContract.methods.getNoVotes().call();
     setNoVotes(postNo);
 };
const voteYes = async () => {
     setWaiting(true);
const accounts = await window.ethereum.enable();
     const account = accounts[0];
     const gas = (await storageContract.methods.voteYes().estimateGas()) * 1.5;
     const post = await storageContract.methods.voteYes().send({
         from: account,
         gas,
     });
setVoteSubmitted(post.from);
     setWaiting(false);
 };
const voteNo = async () => {
     setWaiting(true);
const accounts = await window.ethereum.enable();
     const account = accounts[0];
     const gas = (await storageContract.methods.voteNo().estimateGas() * 1.5);
     const post = await storageContract.methods.voteNo().send({
         from: account,
         gas,
     });
setVoteSubmitted(post.from);
     setWaiting(false);
 };
return (
   <div className={classes.root}>
     <Nav ></Nav>
     <div className="main">
       <div className="card">
         <Typography variant="h3" gutterBottom>
             JVC Homeowners Ballot
         </Typography>
<Typography gutterBottom>
             How do you wish to vote?
         </Typography>
<span className="buttonSpan">
           <Button
             id="yesButton"
             className="button"
             variant="contained"
             color="primary"
             type="button"
             onClick={voteYes}>Vote Yes</Button>
           <div className="divider"></div>
           <Button
             id="noButton"
             className="button"
             color="secondary"
             variant="contained"
             type="button"
             onClick={voteNo}>Vote No</Button>
           <div className="divider"></div>
         </span>
{waiting && (
           <div>
               <CircularProgress ></CircularProgress>
               <Typography gutterBottom>
                   Submitting Vote ... please wait
               </Typography>
           </div>
         )}
{!waiting && voteSubmitted && (
           <Typography gutterBottom>
               Vote Submitted: {voteSubmitted}
           </Typography>
         )}
<span className="buttonSpan">
            <Button
                id="getVotesButton"
                className="button"
                color="default"
                variant="contained"
                type="button"
                onClick={getVotes}>Get Votes</Button>
         </span>
{(yesVotes > 0 || noVotes > 0) && (
           <div>
           <Typography variant="h5" gutterBottom>
               Current Results
           </Typography>
<Grid container spacing={1}>
               <Grid item xs={6}>
                   <div className="resultsAnswer resultsHeader">Vote</div>
               </Grid>
               <Grid item xs={6}>
                   <div className="resultsValue resultsHeader"># of Votes</div>
               </Grid>
               <Grid item xs={6}>
                   <div className="resultsAnswer">Yes</div>
               </Grid>
               <Grid item xs={6}>
                   <div className="resultsValue">{yesVotes}</div>
               </Grid>
               <Grid item xs={6}>
                   <div className="resultsAnswer">No</div>
               </Grid>
               <Grid item xs={6}>
                   <div className="resultsValue">{noVotes}</div>
               </Grid>
             </Grid>
           </div>
         )}
       </div>
     </div>
   </div>
 );
}
export default App;

Децентрализованное приложение в действии

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

yarn start

После компиляции и проверки приложение появится на экране, как показано ниже:

На данный момент доступны три варианта:

  • VOTE YES — подает голос за
  • VOTE NO — голосование против
  • ПОЛУЧИТЬ ГОЛОСОВАНИЕ — включает новый раздел Dapp, показывающий общее количество голосов «за» и «против».

После того, как я проголосовал ЗА в первый раз, я создал следующее видео, чтобы проголосовать НЕТ, а затем использовать кнопку ПОЛУЧИТЬ ГОЛОСОВАНИЕ:

Это видео также можно найти на YouTube.

Что мы узнали

При установленном смарт-контракте оставшиеся задачи пионера web3 не сильно отличаются с точки зрения клиента:

  • Существующие клиентские фреймворки JavaScript, ставшие популярными благодаря проектам web2, можно продолжать использовать.
  • NPM также используется для включения зависимостей, позволяющих разрабатывать web3.
  • Библиотеки web3, Truffle и MetaMask позволяют приложению взаимодействовать с данными подобно тому, как приложения web2 взаимодействуют с традиционным хранилищем данных.
  • Бизнес-правила и дизайн UI/UX по-прежнему соответствуют критериям приемлемости функций и функций, необходимых владельцу продукта.

Чем веб-приложения отличаются от своих предшественников, так это тем, что не имеет значения, кто ищет информацию, хранящуюся в смарт-контракте в блокчейне. Ответ всегда один и тот же, предоставляя единственный источник правды при запросах информации.

В простом случае использования выборов ассоциации домовладельцев, независимо от того, сколько раз будут запрашиваться бюллетени, результаты всегда будут одинаковыми, даже если для доступа к тем же данным будет написано второе приложение Dapp.

Заключение

С прошлого года я пытаюсь жить в соответствии со следующим заявлением о миссии, которое, как мне кажется, применимо к любому ИТ-специалисту:

«Сосредоточьте свое время на предоставлении функций/функций, повышающих ценность вашей интеллектуальной собственности. Используйте платформы, продукты и услуги для всего остального».

– Дж. Вестер

Web3 Dapps, безусловно, придерживаются моей личной миссии на нескольких уровнях:

  • Существует единственный источник правды при создании Dapps на блокчейне, предоставляющий надежные данные для потребителей информации.
  • Бремя знания того, «кто» участвует в транзакции, больше не является обязательным требованием, что позволяет Dapps сосредоточиться исключительно на бизнес-правилах приложения.
  • Из-за своей распределенной природы контроль возвращается потребителю и выходит из-под контроля немногих избранных.

Разработчики с полным стеком, стремящиеся стать пионерами веб3, имеют набор инструментов без изрядной кривой обучения. Фреймворки и библиотеки могут помочь инженерам-программистам исследовать и проектировать веб-разработки следующего поколения.

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

Хорошего дня!