Рубрики
Без рубрики

Руководство по разработке приложений к децентрализованным голосованию Ethereum Decentralized

Тимоти КО Руководство по развитию децентрализованного голосования Ethereum Decentralized Primentithphoto от Andre Francoisafter Весь рынок криптовалюта прошел 700 миллиардов долларов в рыночной кепке, пространство криптовалюта взорвалось за последние пару месяцев. Но это только начало. Как система Blockchain продолжают развиваться и масштабировать, один

Тимоти КО

После того, как весь рынок криптовалюта прошел 700 миллиардов долларов в рыночной кепке, криптовалютное пространство взорвалось за последние пару месяцев. Но это только начало. Поскольку системы Blockchain продолжают развиваться и масштабировать, один отличный способ попадать в это новое пространство и использовать эту технологию, связано с децентрализованными приложениями, иначе известными как Dapps.

Криптокитики , Известен своими заторами на блокчане Ethereum, является отличным примером DAPP, однозначно сочетающей концепции племенных и коллекционных кошек с блокчан. Эта сенсационная игра является только одним творческим примером из практически неограниченного количества возможностей.

Хотя, казалось бы, очень сложные, определенные рамки и инструменты были разработаны для абстрактных взаимодействий с блокчанами и умными контрактами. В этом посте в блоге я пойду один способ создать децентрализованное приложение для голосования на Ethereum. Я буду кратко пойти на Ethereum, но вы, вероятно, должны иметь понимание этого, чтобы использовать это руководство в полной мере. Кроме того, я ожидаю, что вы узнаете JavaScript.

Зачем сделать децентрализованное приложение для голосования?

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

Несмотря на то, что приложение для голосования не может быть отличным приложением для потребителей, я решил использовать его для этого руководства, потому что основные проблемы BlockChain решает – прозрачность, безопасность, доступность, слышимость – это основные проблемы, связанные с текущими демократическими выборами.

Поскольку блокчан является постоянным рекордом транзакций (голосов), которые распределяются, каждый голос может быть прослеженным назад до именно, когда и где это произошло без выявления личности избирателей. Кроме того, прошлые голоса не могут быть изменены, в то время как настоящий нельзя взломать, потому что каждая транзакция проверяется каждым узлом в сети. И любой внешний или внутренний злоумышленник должен иметь контроль 51% узлов для изменения записи.

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

Основные компоненты Ethereum

Я ожидаю, что у вас будет понимание Блокчан и Ethereum Для оставшейся части этого руководства. Здесь Это потрясающее руководство по этому поводу, и я написал краткий обзор основных компонентов, которые я хотел бы, чтобы вы знали.

  1. Умные контракты Выступать в качестве задней части логики и хранения. Договор написан в Солидность , умный контрактный язык и является набор кода и данных, которые находятся на конкретном адресе на блокчане Ethereum. Это очень похоже на класс в объекте ориентированного на программирование, где он включает в себя функции и переменные состояния. Умные контракты, наряду с блокчан, являются основой всех децентрализованных приложений. Они, как и блокчан, неизменные и распределенные и распределенные, что означает модернизацию их, будет боль, если они уже находятся в сети Ethereum. К счастью, здесь некоторые способы сделать это.
  2. Виртуальная машина Ethereum (EVM) обрабатывает внутреннее состояние и вычисление всей сети Ethereum. Подумайте о EVM в качестве массивного децентрализованного компьютера, который содержит «адреса», которые способны выполнять код, изменять данные и взаимодействовать друг с другом.
  3. Web3.js Является ли API JavaScript, который позволяет вам взаимодействовать с блокчан, включая делать транзакции и вызовы на умные контракты. Это API тезирует общение с клиентами Ethereum, что позволяет разработчикам сосредоточиться на содержании их применения. Вы должны иметь экземпляр Web3, в который вспомогал в своем браузере, чтобы сделать это.

Другие инструменты, которые мы будем использовать

  1. Трюфель Это популярное тестирование в рамках развития Ethereum. Он включает в себя блокировку разработки, компиляцию и сценарии миграции для развертывания вашего контракта на блокчан, тестирование контракта и так далее. Это делает развитие проще!
  2. Трюфельные контракты Является ли абстракция на вершине API Web3 JavaScript API, что позволяет вам легко подключиться и взаимодействовать с вашим интеллектуальным контрактом.
  3. Метамаск приносит Ethereum в ваш браузер. Это расширение браузера, которое предоставляет безопасный экземпляр Web3, связанный с вашим адресом Ethereum, что позволяет использовать децентрализованные приложения. Мы не будем использовать метамаск в этом руководстве, но для людей есть способ взаимодействовать с вашим DAPP в производстве. Вместо этого мы внесем наш собственный экземпляр Web3 во время разработки. Для получения дополнительной информации проверить Это ссылка.

Давайте начнем!

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

Таким образом, мы сможем сосредоточить процесс создания и взаимодействия со смарт-контрактами в приложении. Исходный код для всего приложения будет в Этот репозиторий И вам нужно будет установить Node.js и NPM.

Во-первых, давайте установим трюфель во всем мире.

npm install -g truffle

Чтобы использовать команды Truffle, вы должны запустить их в существующем проекте.

git clone https://github.com/tko22/truffle-webpack-boilerplatecd truffle-webpack-boilerplatenpm install

Этот репозиторий – это просто скелет из коробки Trufle, который являются котламитами или приложениями, которые вы можете получить в одной команде – Truffle Unbox [Имя коробки] Отказ Однако коробка Trufle с WebPack не обновляется с последними версиями и включает пример приложения. Таким образом, я создал это репо (Один связан в инструкциях выше).

2. Структура каталогов

Структура вашей каталога должна включать в себя эти:

  • контракты/ – папка, держащая все контракты. Не удалять Миграции .sol.sol.
  • миграции/ – Папка Холдинг Миграционные файлы , что поможет вам развернуть ваши умные контракты в блокчану.
  • SRC/ – Удерживает файлы HTML/CSS и JavaScript для приложения
  • truffle.js – Файл конфигурации Truffle
  • построить/ – Вы не увидите эту папку, пока вы не скомпилируете ваши контракты. Эта папка удерживает артефакты сборки, поэтому не модифицируйте какие-либо из этих файлов! Build Artifacts Опишите функцию и архитектуру вашего договора и дают трюфельные контракты и информацию Web3 о том, как взаимодействовать с вашим интеллектуальным контрактом в блокчане.

1. Напишите свои умные контракты

Достаточно с установкой и введением. Давайте попадаем в код! Во-первых, мы будем писать наш смарт-контракт, который написан в Солидность (другие языки не так популярны). Это может показаться страшным, но это не так.

Для любого приложения Вы хотите, чтобы ваши умные контракты были максимально простыми, даже тупо простыми. Помните, что вы должны заплатить за каждый вычислений/транзакцию, которую вы делаете, и ваши умные контракты будут на блокчане навсегда. Итак, вы действительно хотите, чтобы это было прекрасно – значит, тем более сложным, тем легче сделать ошибку.

Наш контракт будет включать в себя:

  1. Государственные переменные – Переменные, которые содержат значения, которые постоянно хранятся на блокчане. Мы будем использовать переменные состояния для проведения списка и количества избирателей и кандидатов.
  2. Функции – Функции являются исполняемыми элементами интеллектуальных контрактов. Это то, что мы будем позвонить, чтобы взаимодействовать с блокчан, и они имеют разные уровни видимости, внутренне и внешне. Имейте в виду, что всякий раз, когда вы хотите изменить значение/состояние переменной, транзакция должна происходить – стоимость эфира. Вы также можете сделать звонки в блокчан, который не будет стоить никакого эфира, потому что внесенные вами изменения будут уничтожены (подробнее об этом в разделе 3, когда мы на самом деле сделаем транзакции и звонки ).
  3. События – Всякий раз, когда вызывается событие, значение, передаваемое в событие, будет регистрироваться в журнале транзакции. Это позволяет функциям обратного вызова JavaScript или разрешены обещает просмотреть определенное значение, которое вы хотите вернуть после транзакции. Это потому, что каждый раз, когда вы делаете транзакцию, возвращается журнал транзакций. Мы будем использовать событие для регистрации идентификатора вновь созданного кандидата, который мы будем отображаться (проверьте первую пунцу пули 3).
  4. Типы структур – Это очень похоже на структуру C. Структы позволяют удерживать несколько переменных и потрясающие для вещей с несколькими атрибутами. Кандидаты У нас будет только их имя и вечеринка, но вы определенно можете добавить их дополнительные атрибуты.
  5. Сопоставления – Подумайте об этом как хэш-карты или словари, где он имеет пару клавише. Мы будем использовать два сопоставления.

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

Для справки, вот код Smart Contract. Обратите внимание, что этот файл должен быть назван Голосование Но я хотел, чтобы Github Gist пожилал, поэтому я дал ему .js расширение. Как и в остальной части этого руководства, я предоставим комментарии в рамках кода, который объяснит, что он делает, и я объясню большую картину потом, указывая на определенные предостережения и логику.

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

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

Мы также отслеживаем количество избирателей и кандидатов, которые помогут нам их индексировать. Кроме того, не забывайте о событии в строке 8, которое будет регистрировать удостоверение личности кандидата, когда он добавлен. Это событие будет использоваться нашим интерфейсом, поскольку нам нужно отслеживать удостоверение личности кандидата, чтобы проголосовать за кандидата.

  1. Я знаю, вопреки тому, что я сказал ранее в том, чтобы сделать контракты Super Simple, я сделал этот договор немного более сложным по сравнению с тем, что делает это приложение на самом деле. Тем не менее, я сделал это так, чтобы вам было намного проще сделать редактирование и добавлять функции к этому приложению позже (больше на этом в конце). Если вы хотите сделать еще проще простую голосование, умный контракт может работать менее чем на 15 строк кода.
  2. Обратите внимание, что государственные переменные NumCandidates и Numvoters не объявлены публичными. По по умолчанию Эти переменные имеют видимость внутренний , что означает, что они могут быть напрямую доступны только по текущему договору или полученным контрактам (не беспокойтесь об этом, мы его не будем использовать).
  3. Мы используем 32байт для струн вместо использования Строка тип. Наше EVM имеет размер слов 32 байта Так что «оптимизировано» для решения данных в кусках 32 байта. (Компиляторы, такие как солидность, должны делать больше работы и генерировать более байт-код, когда данные не в количестве 32 байт, что эффективно приводит к более высокой стоимости газа.)
  4. Когда пользователь голосует, новый Избиратель Структура создается и добавляется к отображению. Чтобы подсчитать количество голосов, имеет определенный кандидат, вы должны пройти все избирателей и подсчитать количество голосов. Кандидаты работают на одном и том же поведении. Таким образом, эти сопоставления будут проводить историю всех кандидатов и избирателей.

2. Значение Web3 и контракты

С нашим умным контрактом завершено, нам теперь нужно запустить нашу тестовую блокировку и развернуть этот договор на блокчане. Нам также понадобится способ поговорить с ним, который будет через Web3.js.

Прежде чем мы начнем наш тестовый блокчан, мы должны создать файл под названием 2_deploy_contracts.js Внутри папки /контракты Это говорит о том, чтобы включить ваш голосование умный контракт, когда вы мигрируете.

Чтобы начать разработку Ethereum Blockchain, перейдите в командную строку и запустите:

truffle develop

Это будет жить в вашей командной строке. Поскольку прочность является скомпилированным языком, мы должны сначала скомпилировать его к Bytecode для EVM для выполнения.

compile

Вы должны увидеть построить/ Папка в вашем каталоге сейчас. Эта папка удерживает артефакты сборки, которые имеют решающее значение для внутренней работы трюфеля, поэтому не трогайте их!

Далее мы должны мигрировать договор. Миграции Это трюфельный скрипт, который помогает вам изменить состояние контракта вашего приложения по мере развития. Помните, что ваш контракт развернут на определенный адрес на блокчане, поэтому всякий раз, когда вы вносите изменения, ваш контракт будет расположен по другому адресу. Миграции помогут вам сделать это, а также помочь вам переместить данные вокруг.

migrate

Поздравляю! Ваш интеллектуальный договор сейчас находится на блокчане навсегда. Ну не совсем…. потому что Трюфель развивается обновляет каждый раз, когда вы останавливаете это.

Если вы хотели бы иметь постоянный блокчан, рассмотрим Ганаш , который также разработан трюфелем. Если вы используете Ganache, вам не нужно позвонить Трюфель развивается Отказ Вместо этого вы будете работать Trufle Compile и трюфель мигрировать Отказ Чтобы понять, что действительно требует, чтобы развернуть контракт без трюфеля, проверьте этот Блог пост Отказ

Как только мы развернули Smart Contract к Blockchain, нам придется настроить экземпляр Web3.0 с JavaScript в браузере всякий раз, когда приложение запускается. Таким образом, следующий кусок кода будет размещен в нижней части js/app.js Отказ Обратите внимание, что мы используем Web3.0 версии 0.20.1.

Вам не нужно слишком беспокоиться, если вы не понимаете этот код. Просто знайте, что это будет запущено, когда приложение запускается и проверит, будет ли уже есть экземпляр Web3 (MetAmask) в вашем браузере. Если нет, мы просто создам тот, который разговаривает с localhost: 9545 , который является блокчан трюфельского развития.

Если вы используете Ganache, вы должны изменить порт для 7545 Отказ Как только экземпляр создан, мы позвоним Начать Функция (я определим его в следующем разделе).

3. Добавить функциональность

Последнее, что нам нужно будет сделать, это написать интерфейс для приложения. Это включает в себя основы для любого веб-приложения – HTML, CSS и JavaScript (мы уже немного написали JavaScript с созданием экземпляра Web3). Во-первых, давайте создадим наш файл HTML.

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

Есть три важных элемента div, хотя, с идентификаторами: коробка кандидата , msg. и Коробка голосов , который проведет флажки для каждого кандидата, сообщение и количество голосов, соответственно. Мы также импортируем jQuery, bootstrap и app.js Отказ

Теперь нам просто нужно взаимодействовать с договором и внедрять функции для голосования и подсчета количества голосов для каждого кандидата. JQuery будет манипулировать DOM, и мы будем использовать обещания, поскольку мы делаем транзакции или звонки в блокчану. Ниже приведен код для app.js Отказ

Обратите внимание, что код I, предоставленный на предыдущем шаге для создания экземпляра Web3, также здесь, также здесь. Во-первых, мы импортируем необходимые библиотеки и материал WebPack, включая Web3 и Трюфельные контракты Отказ Мы будем использовать трюфельные контракты, которые построен на вершине Web3 для взаимодействия с блокчан.

Чтобы использовать его, мы будем захватывать артефакты сборки, которые были автоматически построены, когда мы составили голосование Smart Contract и использовать их для создания контракта трюфель. Наконец, мы настроили функции в глобальной переменной окно Для запуска приложения, голосование за кандидата и нахождение количества голосов.

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

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

Вызов – это операция по чтению, имитирующая транзакцию, но сбросив изменение состояния. Таким образом, это не будет стоить эфира. Это отлично подходит для вызова функций Gotter (проверьте четыре функции Getter, которые мы уже писали в нашем Smart Contract).

Чтобы сделать транзакцию с трюфельными контрактами, вы пишете instance.funcentname (param1, param2) , с экземпляр Как экземпляр, который был возвращен развернут Функция (проверка строки 36 для примера). Эта транзакция вернет обещание с данными транзакции в качестве возвращаемого значения. Таким образом, если вы вернете значение в вашей функции интеллектуального контракта, но вы выполняете транзакцию с этой же функцией, она не будет возвращать это значение.

Вот почему у нас есть событие, которое будет регистрировать все, что вы хотите, чтобы он написал в данные транзакции, которые будут возвращены. В случае линий 36-37 мы делаем транзакцию, чтобы добавить кандидата. Когда мы решим обещание, у нас есть данные транзакции в Результат Отказ

Чтобы получить кандидат что мы вошли с событием Добавлена медицинское обслуживание () (Проверьте Умный контракт, чтобы увидеть его 0), мы должны пройти журналы и получить так: Результат. Logs [0] .args.canditative .

Чтобы понять, что происходит, используйте инструменты разработчика Chrome, чтобы распечатать Результат и просматривать его структуру Результат Отказ

Чтобы позвонить, вы напишите instance.funcientname.call (param1, param2) . Однако, если функция имеет ключевое слово Вид Затем то трюфельные контракты автоматически создают звонок и, таким образом, вам не нужно добавлять .call Отказ

Вот почему наши функции Getter имеют Посмотреть ключевое слово. В отличие от совершения транзакции, возвращенное обещание вызова будет иметь возвратную стоимость того, что возвращается функцией Smart Contract.

Теперь я кратко объясню 3 функции Но это должно быть очень знакомым, если вы создали приложения, полученные/изменяющие данные из хранилища данных и манипулируют Соответственно. Подумайте о BlockChain в качестве вашей базы данных, и Truffr Contons в качестве API для получения данных из вашей базы данных.

Приложение .start ()

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

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

Со всем настроенным, мы теперь будем отображать флажки для каждого кандидата для пользователя, чтобы проголосовать. Для этого мы должны создать экземпляр договора и получить информацию кандидата. Если нет кандидатов, мы создадим их. Для того, чтобы пользователь мог проголосовать за кандидата, мы должны предоставить удостоверение личности этого определенного кандидата. Таким образом, мы сделаем каждый элемент флажки иметь ID (Атрибут элемента HTML) идентификатора кандидата. Кроме того, мы добавим количество кандидатов в глобальную переменную Нумкидидирует , который мы будем использовать в App.findnumofvotes () Отказ JQuery используется для добавления каждого флажка и его кандидата в .Помощь-коробка Отказ

App.vote ()

Эта функция будет голосовать за определенный кандидат, основанный на том, какой флажок нажат и его ID атрибут.

Один, мы проверим, будет ли пользователь ввести свой userID, который является их идентификацией. Если они этого не сделали, мы показываем сообщение, сообщающее им это сделать.

Два, мы проверим, голосует ли пользователь для кандидата, проверяя, есть ли хотя бы один флажок, который щелкнул. Если ни один из флажков не нажал, мы также отобразим сообщение, сообщающее им голосовать за кандидата. Если нажатие одного из флажков, мы схватим ID Атрибут этого флажка, который также является идентификатором связанного кандидата, и использовать его для голосования для кандидата.

После завершения транзакции мы разрешим возвращенное обещание и показать «проголосоемое» сообщение.

App.findnumofvotes ()

Эта последняя функция найдет количество голосов для каждого кандидата и отображения их. Мы пройдемся через кандидаты и позвонили два функциямного контракта, getCandity ?| и TotalVotes Отказ Мы будем разрешать эти обещания и создать HTML-элемент для этого определенного кандидата.

Теперь начните приложение, и вы увидите его на http://localhost: 8080/ Действительно

npm run dev

Ресурсы

Я знаю, это много … у вас может быть, эта статья открыта некоторое время, когда вы медленно разрабатываете это приложение и действительно понимаю, что происходит. Но это обучение! Пожалуйста, добавьте это руководство со всей документацией из Ethereum, Truffr и что я предоставил ниже. Я пытался ударить многих ключевых точек в этой статье, но это просто краткий обзор, и эти ресурсы будут много помогать.

Заключение

Строительные приложения на Ethereum довольно похоже на регулярное применение, вызывающее бэкэндскую службу. Самая трудная часть – написание надежного и полного умного контракта. Я надеюсь, что это руководство помогло вам понять основное знание децентрализованных приложений и этюренсую и поможет вам начать свой интерес к развитию их.

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

  • Показать вечеринку каждого кандидата. Мы уже получаем партию кандидата, когда мы бежим getCandidate (ID) .
  • Проверьте, если идентификатор введенный пользователем уникален.
  • Задайте и храните больше информации о пользователе такие как их дата рождения и домашний адрес.
  • Добавить опцию, чтобы увидеть, проголосовал ли человек с определенным идентификатором или нет. Вы создадим новую форму для ввода в ID ID, который вы будете искать этого определенного пользователя в BlockChain.
  • Напишите новую функцию Smart Contract, которая подсчитывает голоса для обоих кандидатов одновременно. В настоящее время мы должны сделать два отдельных призыва к двум кандидатам, требуя договора на петлю через всех пользователей дважды.
  • Разрешить новые кандидаты добавить. Это означает добавление новой формы для добавления кандидатов, но также немного меняется на том, как мы проявляем и голосуют за кандидаты в интерфейсе.
  • Требуют, чтобы пользователи имеют адрес Ethereum для голосования. Моя логика для не включая пользовательские адреса состоит в том, что избиратели не будут иметь Ethereum для участия в этом процессе голосования. Тем не менее, многие DAPPS потребуют пользователей иметь адрес Ethereum.

Кроме того, вот несколько советов, которые могут предотвратить некоторые контрольные блоки:

  • Двухместный и тройной проверьте свои умные контрактные функции, когда происходит что-то странное. Я потратил пару часов на ошибку, чтобы выяснить, что вернул неправильное значение в одной из моих функций.
  • Проверьте, правильно ли ваш URL-адрес и порт при подключении к вашей блоке разработки. Помните: 7545 для Трюфель развивается и 9545 для ганах. Это по умолчанию, Поэтому, если вы не можете подключиться к своей блокаде, вы могли бы изменить их.
  • Я не прошел по этому поводу, потому что это руководство было бы слишком долго, и я, вероятно, сделаю еще один пост на это – но вы должны Тест Ваши контракты! Это поможет много.
  • Если вы не знакомы с Обещания Пройдите, как они работают и как их использовать. Трюфельные контракты используют обещания, а бета-бета для Web3 также будет поддерживать обещания. Они могут, если вы делаете их неправильно, испортить много данных, которые вы получаете.

Приветствия для работы в отношении децентрализованного и безопасного Интернета – Web 3.0!

Я надеюсь, что вам понравилось, читая это руководство столько, сколько мне нравится писать! Если у вас есть мысли и комментарии, не стесняйтесь оставить комментарий ниже или напишите мне на TK2@illinois.edu или чирикать я (я недавно создал один)! Не стесняйтесь использовать свой код и поделиться этим со своими друзьями!

Оригинал: “https://www.freecodecamp.org/news/developing-an-ethereum-decentralized-voting-application-a99de24992d9/”