Моя ОС - Mac OS High Sierra версии 10.13.3 (17D47)

Источник: https://coursetro.com/posts/code/99/Interacting-with-a-Smart-Contract-through-Web3.js-(Tutorial)

Установка и запуск Ethereum TestRPC

Ganache - это клиент Node.js Ethereum для тестирования и разработки смарт-контрактов. Поскольку он основан на Node.js, для его установки нам потребуется установить Node.js вместе с NPM (Node Package Manager).

Откройте командную строку или консоль и выполните следующие 2 команды:

> node -v
> npm -v

(моя версия узла - v9.7.1, а моя версия npm - 5.6.0)

Если какая-либо из этих команд не распознается, посетите Nodejs.org и загрузите соответствующий установщик. Запустите его через все параметры по умолчанию.

По завершении закройте и перезагрузите консоль, а затем повторно запустите приведенные выше команды. Теперь они должны предоставить вам номера версий.

Затем давайте используем NPM для установки Ganache:

> npm install -g ganache-cli

(Моя версия ganache-cli - Ganache CLI v6.1.0 (ganache-core: 2.1.0))

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

> ganache-cli

Это предоставляет вам 10 различных учетных записей и закрытые ключи, а также локальный сервер на localhost: 8545.

Установка Web3.js

Web3.js - это официальный API-интерфейс Ethereum Javascript. Вы используете его для взаимодействия со своими смарт-контрактами Ethereum.

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

> mkdir coursetro-eth
> cd coursetro-eth

Затем запустите команду npm init, чтобы создать файл package.json, в котором будут храниться зависимости проекта:

> npm init

Нажмите Enter во всех подсказках. Затем выполните следующую команду, чтобы установить web3.js:

> npm install ethereum/web3.js --save

Изменение окружения в Remix

Переключитесь на Remix IDE, щелкните вкладку Выполнить, а затем измените раскрывающееся меню Среда с Javascript VM на Web3 Provider.

Нажмите ОК и укажите адрес localhost testrpc (по умолчанию это http: // localhost: 8545).

Это означает, что вместо развертывания и тестирования в виртуальной машине Javascript мы теперь используем клиент Ganache на вашем компьютере.

Если вы не следили за этим со времени предыдущего урока, вставьте этот контракт в новый файл Solidity под названием «Coursetro.sol»:

pragma solidity ^0.4.18;
contract Coursetro {
    
   string fName;
   uint age;
   
   function setInstructor(string _fName, uint _age) public {
       fName = _fName;
       age = _age;
   }
   
   function getInstructor() public constant returns (string, uint) {
       return (fName, age);
   }
    
}

Нажмите Создать. Вскоре нам понадобится адрес этого контракта, поэтому оставьте это окно открытым.

Создание UI

Откройте предпочтительный редактор кода (я использую Visual Studio Code) с папкой проекта, которую мы создали. Здесь вы заметите папку node_modules, в которую входит web3, который мы установили ранее через npm.

Давайте создадим index.html в папке проекта.

Мы не собираемся создавать что-то слишком сложное с точки зрения пользовательского интерфейса, но у нас будет ограниченный CSS и пользовательский интерфейс, который состоит из места, которое извлекает имя и возраст инструктора из getInstructor () и форму с двумя полями ввода для имени и возраста, которые будут установлены через jQuery из двух текстовых полей ввода.

Для начала вставьте следующее содержимое в пустой файл index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="main.css">
    <script src="node_modules/web3/dist/web3.min.js"></script>
</head>
<body>
    <div class="container">
        <h1>Coursetro Instructor</h1>
        <h2 id="instructor"></h2>
        <label for="name" class="col-lg-2 control-label">Instructor Name</label>
        <input id="name" type="text">
        <label for="name" class="col-lg-2 control-label">Instructor Age</label>
        <input id="age" type="text">
        <button id="button">Update Instructor</button>

    </div>
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
    <script>
       // Our future code here..
    </script>
</body>
</html>

Как видите, мы ссылаемся на файл main.css, поэтому очень быстро создайте этот файл и вставьте в следующие наборы правил:

body {
    background-color:#F0F0F0;
    padding: 2em;
    font-family: 'Raleway','Source Sans Pro', 'Arial';
}
.container {
    width: 50%;
    margin: 0 auto;
}
label {
    display:block;
    margin-bottom:10px;
}
input {
    padding:10px;
    width: 50%;
    margin-bottom: 1em;
}
button {
    margin: 2em 0;
    padding: 1em 4em;
    display:block;
}
#instructor {
    padding:1em;
    background-color:#fff;
    margin: 1em 0;
}

Сохрани это.

Использование Web3.js для подключения и взаимодействия со смарт-контрактом

Возвращаясь к index.html, внизу файла у нас есть пустой тег ‹script›. Здесь мы напишем необходимый код для работы с нашим смарт-контрактом.

Обычно я бы никогда не использовал jQuery (я большой поклонник Angular), но это упрощает задачу.

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

<script>
        if (typeof web3 !== 'undefined') {
            web3 = new Web3(web3.currentProvider);
        } else {
            // set the provider you want from Web3.providers
            web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
        }
    </script>

Этот код взят прямо со страницы Web3.js Github.

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

Вам может быть интересно, как можно определить web3? Что ж, если вы используете расширение Chrome MetaMask (которое мы будем использовать позже в этом курсе) или браузер Ethereum, например Mist, поставщик автоматически введен.

Затем мы должны указать учетную запись ethereum по умолчанию для использования с помощью метода web3.eth.defaultAccount:

<script>
        // Previous if/else statement removed for brevity
        web3.eth.defaultAccount = web3.eth.accounts[0];
    </script>

Помните, когда мы запускали консольную команду ganache? Он предоставил нам 10 аккаунтов. Мы просто выбираем первую учетную запись для использования.

Затем нам нужно использовать метод web3.eth.contract () для инициализации (или создания) контракта на адресе. Он принимает один параметр, который называется ABI (двоичный интерфейс приложения).

Этот ABI позволяет вам вызывать функции и получать данные из вашего смарт-контракта.

Если вы вернетесь к Remix IDE, перейдите на вкладку Компиляция и нажмите Подробнее. Прокрутите вниз до раздела Интерфейс - ABI и щелкните значок копии, как показано ниже:

Вернувшись к index.html вставьте следующий код:

<script>
        // Previous if/else statement removed for brevity
        web3.eth.defaultAccount = web3.eth.accounts[0];
        var CoursetroContract = web3.eth.contract(PASTE ABI HERE!);
    </script>

Большой. Теперь, когда у нас есть интерфейс для взаимодействия с нашим контрактом через переменную CoursetroContract, последнее, что нужно сделать, - это определить фактический адрес контракта.

Ранее мы использовали Remix для создания контракта, и у него есть связанный адрес.

Вернитесь в Remix и щелкните вкладку Выполнить, а затем щелкните значок копии рядом с контрактом, который мы создали ранее, в правом столбце.

Вернитесь в index.html и добавьте следующую строку:

<script>
        // Previous if/else statement removed for brevity
        web3.eth.defaultAccount = web3.eth.accounts[0];
        var CoursetroContract = web3.eth.contract(YOUR ABI);
        var Coursetro = CoursetroContract.at('PASTE CONTRACT ADDRESS HERE');
        console.log(Coursetro);
    </script>

Большой. Давайте сохраним это, а затем (в Visual Studio Code) вы можете щелкнуть правой кнопкой мыши index.html и отобразить в проводнике. Дважды щелкните index.html, чтобы запустить его в браузере.

CTRL-SHIFT-I (i) покажет консоль. Вы увидите что-то похожее на следующее:

Обратите внимание на наши 2 функции! getInstructor и setInstructor.

Если вы хотите, в окне консоли в инспекторе вы можете ввести:

> Coursetro.setInstructor('Brutis', 44) // Hit Enter
"0x894..."                           // This is the response (address)
> Coursetro.getInstructor()          // Hit Enter
(2) ["brutis", e]                    // An array containing our data

Потрясающие! Но давайте воспользуемся jQuery для выполнения этих вызовов на основе нашей формы:

<script>
        // Previous code removed for brevity
        Coursetro.getInstructor(function(error, result){
            if(!error)
                {
                    $("#instructor").html(result[0]+' ('+result[1]+' years old)');
                    console.log(result);
                }
            else
                console.error(error);
        });
        $("#button").click(function() {
            Coursetro.setInstructor($("#name").val(), $("#age").val());
        });
    </script>

Мы просто вызываем .getInstructor и передаем ошибку и результат через функцию обратного вызова. Если ошибки нет, мы устанавливаем html элемента h2 с идентификатором #instructor в возвращаемый массив результатов (0 = имя, 1 = возраст).

Затем при нажатии мы вызываем .setInstructor для значений имени и возраста из полей ввода в форме.

Сохраните, обновите и попробуйте!

Вот весь файл index.html (включая данные моего примера)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
<link rel="stylesheet" type="text/css" href="main.css">
<script src="node_modules/web3/dist/web3.min.js"></script>
</head>
<body>
    <div class="container">
<h1>Coursetro Instructor</h1>
<h2 id="instructor"></h2>
<label for="name" class="col-lg-2 control-label">Instructor Name</label>
        <input id="name" type="text">
<label for="name" class="col-lg-2 control-label">Instructor Age</label>
        <input id="age" type="text">
<button id="button">Update Instructor</button>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script>
if (typeof web3 !== 'undefined') {
            web3 = new Web3(web3.currentProvider);
        } else {
            // set the provider you want from Web3.providers
            web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));   
        }
  
  web3.eth.defaultAccount = web3.eth.accounts[0];
  var CoursetroContract = web3.eth.contract([ { "constant": false, "inputs": [ { "name": "_fName", "type": "string" }, { "name": "_age", "type": "uint256" } ], "name": "setInstructor", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getInstructor", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" } ]);
var Coursetro = CoursetroContract.at('0x7c74fa5e63b9599550131fc921c1f27482604236');
  //console.log(Coursetro);
  
  Coursetro.getInstructor(function(error, result){
            if(!error)
                {
                    $("#instructor").html(result[0]+' ('+result[1]+' years old)');
                    console.log(result);
                }
            else
                console.error(error);
        });
$("#button").click(function() {
            Coursetro.setInstructor($("#name").val(), $("#age").val());
        });
</script>
</body>
</html>