Среда разработки Remix - создайте свой первый Smart Contract - Kauri
Articles
Communities
Collections
Write Article
Create Collection
Import from medium
Loading...
Sign in
POSTED 31 May 2019 11:59

Среда разработки Remix - создайте свой первый Smart Contract

ethereum
remix
solidity
AUTHOR
A
Alex D.

Примечания от переводчика

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

Во избежание неоднозначностей и разночтений, часть устоявшихся терминов оставлена без перевода. Например: JavaScript, Blockchain, Solidity.

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

Среда разработки Remix - создайте свой первый Smart Contract

1. Вступление от автора

Простейшим способом разработать свой smart contract на языке Solidity является использование онлайн-окружения Remix

Оно не требует никаких действий по установке либо настройке, поскольку сделано как сайт, а не как настольное приложение. Просто откройте этот сайт - и вы уже готовы приступить к написанию кода своего контракта.

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

Исходный код, приведенный в данном обучающем материале, можно найти по этой ссылке.


Прежде, чем продолжить, вспомним:

"А что, собственно, будет являться 
конечным результатом нашей работы?"


Итак, идея распределенного приложения такова:

Пользователи нашего распределенного приложения (dApp)
смогут создавать задания
и выплачивать за их выполнение гонорар 
в криптовалюте "эфир" (имеющей буквенный код ETH).
  • Любой пользователь, имеющий учетную запись в блокчейне Ethereum, может создать задание. Задание содержит описание критериев приемки выполненной работы, а также значение суммы гонорара для исполнителя. (под учетной записью понимается пара ключей "public key/pivate key" - прим. переводчика)
  • Любой пользователь может подать заявку на получение гонорара, предоставив доказательства факта выполнения задания надлежащим образом.
  • Создатель задания должен подтвердить приемку работы, дабы получить ее результат. В этом случае вознаграждение будет автоматически выплачено исполнителю созданным нами контрактом.

2. Создание контракта в среде Remix

Откройте страницу среды Remix. В левом верхнем углу вы увидите кнопку с иконкой "+". Нажмите на нее и введите "Bounties.sol" в качестве имени файла в появившееся диалоговое окно.

screenshot : creatng a file in remix

В качестве первой строки нашего контракта на языке Solidity обязательно должен находиться номер версии компилятора.

pragma solidity ^0.5.0;

Благодаря этой инструкции, Solidity будет знать, что нужен компилятор версии 0.5.0 либо новее. Но не более новый, чем 0.6.0, в которой могут быть изменения, ломающие обратную совместимость. Данное ограничение обеспечивается символом ^, стоящим перед номером версии. (Таким образом, Solidity следует правилам semantic versioning - прим. переводчика)

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

contract Bounties 
{
}

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

constructor() public {}

3. Компиляция контракта

На этом этапе мы получили базовый каркас электронного контракта (Smart Contract). Далее - скомпилируем его в Remix IDE.

Ваш файл Bounties.sol сейчас должен выглядеть следующим образом:

pragma solidity ^0.5.0;

contract Bounties 
{
    constructor() public {}
}

В окружении Remix выберите вкладку "Compile" в правом верхнем углу экрана и запустите процесс компиляции нажатием кнопки "Start to Compile" как показано на снимке экрана ниже.

screenshot: compile a smart contract in Remix IDE

В случаче успеха вы должны увидеть название своего контракта "Bounties" на зеленом фоне, как показано на том же снимке экрана.

4. Публикация задания

Пришло время расширить наш каркас контракта некоторой функциональностью. Начнем с публикации задания.

4.1 Объявление переменных для хранения состояния

Что же такое переменные состояния в Solidity? Дело в том, что экземпляр электронного контракта имеет возможность запоминать свое состояние, помещая его в хранилище EVM (Ehtereum Virtual Machine - прим. переводчика). Это состояние описываеться одной либо несколькими переменными, которые могут иметь один из встроенных в язык Solidity типов. Значение, хранящееся в этих переменных, может быть изменено лишь функциями, вызванными во время транзакции.

Исчерпывающий список типов можно найти в документации языка Solidity. А именно, в секции "Types".

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

// множество "состояния задачи"
//
enum BountyStatus 
{ 
    CREATED  , // "создано" - новая задача, ожидающая иполнителя
    ACCEPTED , // "принято к исполнению" - исполнитель найден и утвержден
    CANCELLED  // "отменена" - работодатель больше не нуждается в данной услуге
}


Далее объявим структуру, для хранения данных о задаче.

// структура "заказ" (работа за вознаграждение)
//
struct Bounty 
{
    // учетная запись ethereum работодателя
    //
    address issuer;

    // срок выполнения.
    // исполнитель не получит вознаграждение, если нарушит его.
    //
    uint deadline;

    // описание задания
    //
    string data;

    // состояние задачи
    // тип enum был только что объявлен нами выше
    //
    BountyStatus status;

    // сумма вознаграждения в WEI
    // WEI - минимальная неделимая дробная часть валюты "эфир"
    // 10^18 WEI == 1 ETH 
    //
    uint amount;
}


Что такое структура? Структура - это конструкция языка, позволяющая описывать собственные типы данных. По сути, это - набор переменных, которые могут принадлежать как типам, встроенным в язык Solidity, так и являться другими структурами. Набор переменных объединяют в структуру с целью их группирования и более понятной организации при разработке кода контракта.

Давайте-ка добавим в наш контракт переменную-массив, хранящую данные обо всех созданных контрактах.

Bounty[] public bounties;

4.2 Функция "создать задание"

Ранее мы подготовили переменные для работы с состоянием. Теперь же - напишем функции для взаимодействия с нашим smart contract.

// создать задание
//
function issueBounty(
    string memory _data,        // описание работ
    uint64        _deadline     // срок исполнения 

) public   // функция может быть вызвана любым пользователем или другим контрактом в сети Ethereum
  payable  // при вызове этой функции контракт может получать эфир
           // полученный эфир будет храниться "на балансе" контракта

    hasValue()  // проверяем, что при вызове контракту был отправлен эфир в ненулевом объеме.
                // реализацию этого modifier запрограммируем позже
    validateDeadline(_deadline) 
                // проверяем, что крайний срок не исчерпан на момент задания
                // реализацию этого modifier запрограммируем позже
returns (uint)  // возвращает целое число - порядковый номер последнего добавленного задания
{
    bounties.push(
        Bounty(
            msg.sender, // адрес отправителя эфира, вызвавшего функцию. 
                        // он же создатель нового задания.
                        //
            _deadline,  // срок исполнения. передан как параметр.
            _data,      // описание работ. передан как параметр.
            BountyStatus.CREATED, // статус "задача создана", исполнитель еще не назначен
            msg.value   // размер награды. 
                        // до выполнения задания сам эфир будет храниться в контракте.
                        // в структуру попадает только его количество
                        // измеряемое в WEI
        )
    );

    // отнимаем единицу, поскольку 
    // нумерация элементов массива начинается с нуля
    return (bounties.length - 1);
}

Функция issueBounty() принимает следующие параметры:

  • _data - требования к исполнителю и описание работы. Имеет тип string memory
  • _deadline - время и дата крайнего срока исполнения задания с точностью до секунды в формате unix timestamp

Начиная с версии 0.5.0 языка Solidity, нужно обязательно указывать явно способ хранения (memory либо storage) для всех переменных, имеющих тип

  • строка - string
  • массив - array
  • ассоциативный массив - mapping

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

Реализация строк в языке Solidity базируется на байтовом массиве (что в случае длинных строк может быть достаточно затратно по сравнению с хранением данных более простых типов таких как uint64 - прим. переводчика). Следовательно, для параметра _data мы должны указать тип хранения. Наш выбор падет на memory, поскольку нам не нужно хранить эти данные после окончания работы функции issueBounty(), а также транзакции, в рамках которой эта функция будет выполняться.

Язык Solidity требует объявления типа возвращаемого значения (или нескольких типов в случае, если возвращается больше одного значения). Мы указали returns(uint) - беззнаковое целое. В него будет записываться индекс последнего, только что добавленного, задания.

Функция объявлена с областью видимости public. Дополнительную информацию о видимости функций в Solidity вы найдете по данной ссылке

Для того, чтоб контракт при вызове функции issueBounty() мог получать средства в криптовалюте "эфир" - ETH, необходимо добавить ключевое слово payable в описание указанной функции. Без данного ключевого слова контракт будет отвергать все попытки послать ему эфир при вызове функции issueBounty().

Тело функции состоит всего лишь из двух инструкций

bounties.push(Bounty(msg.sender, _deadline, _data,
BountyStatus.CREATED, msg.value));

Сперва мы формируем экземпляр структуры объявленного нами ранее типа struct Bounty со статусом BountyStatus.CREATED и помещаем ее в наш массив bounties.

В языке solidity каждая функция имеет скрытый параметр msg, в котором содержится контекст выполнения - то есть, разная полезная информация о транзакции. Нас интересуют такие поля как

  • msg.sender - поскольку там хранится адрес пользователя или контракта, вызвавшего нашу функцию (имеет тип address)
  • msg.value - содержит количество перечисленных средств. Измеряется в WEI - минимально допустимой, неделимой дробной части эфира. (1 ETH = 1000000000000000000 wei = 10^18 wei)

Таким образом, мы зададим msg.sender в качесиве поля issuer и msg.value в качестве поля amount соответственно. (поля принадлежат объявленной нами ранее структуре struct Bounty).

return (bounties.length - 1);

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

5. Проверка входных данных с помощью Modifiers

Modifier - функция, объявленная особым образом, которую можно "прикрепить" средствами языка Solidity к другой, "основной" функции. Логика modifier может быть исполнена как перед, так и после выполнения основной функции.

Широко распространена практика 
запуска modifier перед основной функцией
с целью проверки её входных параметров

5.1 Проверка крайнего срока выполнения (при создании задания)

Добавим функцию-модификатор modifier validateDeadline(_deadline) , дабы с его помощью удостовериться, что переданная конечная дата находится в будущем. У пользователей не должно быть возможности создавать задания, срок исполнения которых истек, так и не успев начаться.

modifier validateDeadline(uint _newDeadline)
{
    require(_newDeadline > now);
    _;
}

Для объявления такой специальной функции необходимо использовать ключевое слово modifier. Модификаторы могут иметь свои входные параметры - так же, как и "обычные" функции языка Solidity.

Расположение инструкции _; - самая важная часть логики модификатора. В этом месте будет исполнен код "основной" ("модифицируемой") функции.

Таким образом, modifier validateDeadline() сперва выполнит проверку require(_newDeadline > now);, а затем - основную функцию.

Использованная для проверки функция require(), принимает логическое условие в качестве параметра. Если условие ложно - то выполнение будет остановлено, транзакция - отменена, а оставшийся газ - возвращен отправителю (он же msg.sender).

Обычно require() используется для проверки входных данных до выполнения основной логики функции.

В общем случае, для обработки нештатных ситуаций и остановки работы контракта могут применяться такие функции как:

  • assert()
  • require()
  • revert()

Подробную информацию об этих функциях и обработке ошибок можно найти в документации solidity

Таким образом, логику модификатора modifier validateDeadline() следует трактовать следующим образом :

Если deadline > now - продолжаем исполнять основную функцию. Иначе - отменяем транзакцию и возвращаем еще не использованный остаток газа его вледельцу.

5.2 Модификатор hasValue()

Реализуем логику modifier hasValue(), дабы с его помощью удостовериться, что количество адресованного контракту эфира не равно нулю.

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

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

Абсолютно так же, как в modifier validateDeadline(), мы воспользуемся функцией require() чтоб удостовериться в выполнении условия количество отправленного эфира больше нуля.

modifier hasValue() 
{
    require(msg.value > 0);
    _;
}

Кстати, ранее использованный нами payable - это тоже модификатор, но встроенный в язык Solidity. Он гарантирует, что эфир (а также, газ) отправляется контракту при вызове функции, которая требует финансирования.

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

6. Событие-уведомление "задание создано"

В разработке на Solidity считается хорошим тоном сгенерировать событие при изменении состояния контракта. События позволяют клиентским приложениям подписываться на них и реагировать на произошедшие изменения. Например, web site может добавить на свою страницу новое задание, получив такой event от blockchain при выполнении написанной нами ранее функции issueBounty(). Разумеется, мы можем обновить страницу просто дождавшись окончания своей транзакции. Благодаря событиям-уведомлениям (в англоязычных материалах - events) мы можем показать пользователю обновленную страницу даже если инициатором транзакции был другой пользователь. Это возможно благодаря децентрализации, идеи которой заложены в технологии блокчейна Ethereum. Технически, клиентское приложение узнает об изменениях, произошедших в blockchain, именно благодаря механизму events (о котором идет речь в данном разделе).

Еще одним примером использования events является обновление списка входящих и исходящих транзакций для некоторого контракта на сервисе etherscan. Как можно увидеть, транзакции были произведены разными пользователями. Что еще раз иллюстрирует идею описанную в предыдущем абзаце.

Более детально о событиях как конструкции языка Solidity можно прочитать в документации.

Поскольку в функции issueBounty() нашего контракта Bounties.sol состояние меняется, мы сгенерируем событие BountyIssued. Для этого объявим его :

event BountyIssued(
    uint    bounty_id, // индекс задания в массиве
    address issuer   , // создатель задания, который внес в контракт средства для вознаграждения
    uint    amount   , // сумма гонорара
    string  data     ); // описание требований к заданию

Таким образом, событие BountyIssued передает клиентским приложениям следующую информацию о свежесозданном задании с вознаграждением:

  • bountyId: - идентификатор нового задания
  • issuer: - учетная запись пользователя, создавшего задание и оплатившего его
  • amount: - сумма вознаграждения за исполнения задания (количество WEI)
  • data: - строка с описанием требований к исполнению заданию

Добавим же генерацию события в функцию issueBounty()

bounties.push(
    Bounty(
        msg.sender, 
        _deadline, 
        _data, 
        BountyStatus.CREATED, 
        msg.value));

emit BountyIssued(            // добавленное событие
        bounties.length - 1,  // индекс нового задания в качестве идентификатора
        msg.sender,           // пользователь, финансирующий задание
        msg.value,            // размер гонорара
        _data);               // описание задания

return (bounties.length - 1);

После описанного выше добавления функции issueBounty наш контракт Bounties.sol будет выглядеть примерно вот так :

pragma solidity ^0.5.0;
/**
* @title Bounties
* @author Joshua Cassidy- <joshua.cassidy@consensys.net>
* @dev Простой электронный контракт
* который позволяет любому пользователю
* оплатить криптовалютой "эфир"
* задание с четко определенными критериями приёмки
* награду может получить любой пользователь
* предоставивший доказательства исполнения задачи
*/

contract Bounties 
{

/*
* Множества (перечислимые типы)
*/
enum BountyStatus 
{ 
    CREATED  , // "создано" - новая задача, ожидающая иполнителя
    ACCEPTED , // "принято к исполнению" - исполнитель найден и утвержден
    CANCELLED  // "отменена" - работодатель больше не нуждается в данной услуге
}

/*
* Состояние, хранилище
*/
Bounty[] public bounties;

/*
* Структуры
*/
struct Bounty 
{
    // учетная запись ethereum работодателя
    //
    address issuer;

    // срок выполнения.
    // исполнитель не получит вознаграждение, если нарушит его.
    //
    uint deadline;

    // описание задания
    //
    string data;

    // состояние задачи
    // тип enum был только что объявлен нами выше
    //
    BountyStatus status;

    // сумма вознаграждения в WEI
    // WEI - минимальная неделимая дробная часть валюты "эфир"
    // 10^18 WEI == 1 ETH 
    //
    uint amount;
}


/**
* @dev Конструктор
*/
constructor() public 
{
}


/**
* @dev issueBounty(): создает новое задание и выделяет на него средства
* @param _deadline the unix timestamp after which fulfillments will no longer be accepted
* @param _data the requirements of the bounty
*/
function issueBounty(
    string memory _data, // описание работ
    uint64 _deadline     // срок исполнения 

) public   // функция может быть вызвана 
           // любым пользователем 
           // или другим контрактом в сети Ethereum
           // ---
  payable  // при вызове этой функции контракт может получать эфир
           // полученный эфир будет храниться "на балансе" контракта
    hasValue()  // проверяем, что при вызове 
                // контракту был отправлен эфир
                // в ненулевом объеме.
    validateDeadline(_deadline) // проверяем, что крайний срок
                                // не исчерпан на момент создания нового задания
returns (uint)  // возвращает целое число - 
                // порядковый номер последнего добавленного задания
{
 bounties.push(
        Bounty(
            msg.sender, // адрес отправителя эфира, вызвавшего функцию. 
                        // он же создатель нового задания.
                        // ---
            _deadline,  // срок исполнения. передан как параметр.
            _data,      // описание работ. передан как параметр.
            BountyStatus.CREATED, // статус "задача создана", исполнитель еще не назначен
            msg.value   // размер награды. 
                        // до выполнения задания сам эфир будет храниться в контракте.
                        // в структуру попадает только его количество
                        // измеряемое в WEI
        )
    );

    emit BountyIssued(            // добавленное событие
        bounties.length - 1,  // индекс нового задания в качестве идентификатора
        msg.sender,           // пользователь, финансирующий задание
        msg.value,            // размер гонорара
        _data);               // описание задания

    // отнимаем единицу, поскольку 
    // нумерация элементов массива начинается с нуля
    return (bounties.length - 1);
}

/**
* Модификаторы
* Проверка входных параметров
*/
modifier hasValue() 
{
    require(msg.value > 0); // проверяем, что контракт получил достаточно эфира
    _; // исполняем основную функцию
}

modifier validateDeadline(uint _newDeadline) 
{
    require(_newDeadline > now); // проверяем, что введенная дата не в прошлом. 
                                 // чтоб контракт не "забивался" бесполезными данными.
    _; // исполняем основную функцию
}

/**
* События-сообщения
*/
event BountyIssued(
        uint    bounty_id, 
        address issuer   , 
        uint    amount   , 
        string  data     );
}

7. Публикация контракта и взаимодействие с ним средствами Remix

И вот, наш контракт написан. А значит, мы, наконец, можем поместить его в локальный тестовый мини-блокчейн внутри Remix IDE в целях тестирования функции issueBounty().

Первым делом, скомпилируем наш контракт Bounties.sol и убедимся в отсутствии ошибок. Для этого нужно выбрать вкладку "Compile" в правом верхнем углу экрана и запустить процесс компиляции нажатием на кнопку "Start to Compile".

screenshot: remix IDE compilation result with static warnings

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

Больше информации о статическом анализе в среде Remix можно найти по ссылке.

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

Для этого выберем вкладку "Run" в правом верхнем углу экрана. В выпадающем списке "Environment" следует выбрать вариант "JavaScript VM" как показано на снимке экрана ниже.

screenshot: ethereum environment drop-down

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

Узнать больше об исполнении транзакций внутри Remix IDE можно из документации

На вкладке "Run" окружения Remix, выбрав окружение "JavaScript VM", нажмите на кнопку "Deploy" как показано на снимке экрана ниже.

screenshot: "deploy" button in the "run" tab

Это действие выполнит транзакцию по публикации контракта в блокчейн, работающий локально в браузре. Подобные транзакции будут описаны подробней в последующих статьях.

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

screenshot: results of the constructor transaction

Иконка "зеленый кружок с галочкой" означает, что транзакция прошла успешно.


Теперь во вкладке "Run" окружения Remix можно выбрать наш контракт "Bounties", чтоб вызвать функцию issueBounty(). Для этого следует найти нужную функцию в секции "Deployed Contracts".

На данный момент секция содержит следующие функции:

  • issueBounty - розовый цвет этой кнопки означает, что ее нажатие и вызов соответствующей функции создаст новую транзакцию.
  • bounties - синий цвет этой кнопки означает простой вызов функции без транзакции и изменения состояния контракта.

screenshot: buttons to invoke contract functions

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

В качестве параметра string _data укажем "некоторые требования" (кавычки необходимы для типа string). В свою очередь, в качестве uint64 _deadline запишем значение 1691452800 (без кавычек, поскольку uint64 - целочисленный тип), что соответствует дате 8 августа 2023 года

Поскольку функция issueBounty помечена ключевым словом payable, мы должны убедиться, что параметр msg.value задан. Ввести нужное значение количество эфира можно в верхней части вкладки "Run" в среде Remix.

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

  • Environment: как уже изложено выше, позволяет выбрать блокчейн, с которым предстоит работать (в котором находится наш опубликованный контракт).

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

  • Gas Limit: позволяет задать верхний предел количества газа, который можно использовать для выполнения кода в рамках данной транзакции.

  • Value: количество эфира, перечисляемого на счет контракта. Для удобства ввода суммы можно выбрать единицу измерения: Wei, Gwei, Finney, Ether Стоит отметить, что внутри контракта это значение доступно из переменной msg.value и там оно будет всегда измеряться в Wei и не зависит от настроек Remix.

Итак, давайте зададим в поле "Value" какое-нибудь значение больше нуля. В рамках даного упражнения введем 1 ETH.

Теперь можно нажать кнопку "issueBounty" в секции "Deployed Contracts" вкладки "Run" и таким образом запустить транзакцию, которая вызовет функцию issueBounty() контракта Bounties.

В консоли будет отображен результат работы этой транзакции.

screenshot: issueBounty function invoked

Иконка "зеленый кружок с галочкой" свидетельствует об успехе транзакции.

Раскодированный вывод содержит значение, которое вернула вызванная функция. В данном случае это ноль 0.

Мы ожидаем получить индекс нового экземпляра структуры типа Bounty в массиве, хранящем состояние контракта. Мы можем проверить это, вызвав функцию bounties в секции Deployed Contracts. Введем ноль 0 в качестве аргумента и нажмем синюю кнопку с названием bounties.

screenshot: the result of bounties getter invocation

На этом снимке экрана мы можем увидеть, что данные, которые мы ввели ранее при вызове issueBounty, успешно записаны в хранилище smart contract. Поскольку именно их мы только что извлекли вызовом функции bounties из одноименного поля-массива.

8. Попробуйте сами!

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

  • fulfilBounty(uint _bountyId, string _data) - эта функция должна сохранить запись о выполнении задачи с заданным идентификатором-индексом. msg.sender следует трактовать как учетную запись исполнителя.

  • acceptFulfilment(uint _bountyId, uint _fulfilmentId) - вызвав эту функцию, создатель контракта подтверждает приемку работы и соглашается, что она была выполнена надлежащим образом. При этом вознаграждение выплачивается исполнителю при условии, что существует заявка от исполнителя.

  • function cancelBounty(uint _bountyId) - эта функция должна отменить заказ на работу при условии отсутствия факта приёмки этого заказа. В этом случае средства возвращаются создателю задания.

Примечание автора: для реализации функции acceptFulfilment нужно будет использовать функцию address.transfer(uint amount), которая производит пересылку эфира из контракта на адрес исполнителя.

Документация по функции address.transfer находится по ссылке.

Полную версию контракта Bounties.sol можно посмотреть в справочных целях на github

9. Что дальше?

Понравился этот обущающий материал? Появились вопросы или предложения по его содержанию?

Тогда откройте статью-оригинал и напишите там свои комментарии на английском языке. Автор будет рад пообщаться с вами.

Нашли ошибку в содержании статьи?

Тогда откройте статью-оригинал и отредактируйте ее, нажав кнопку Update Article в меню по правую сторону экрана.

Нашли ошибку перевода или опечатку?

Тогда отредактируйте этот перевод, нажав кнопку Update Article в меню по правую сторону экрана.

Нашли ошибку в коде?

Отправьте pull request на github

Outline
  • 1. Вступление от автора

  • 2. Создание контракта в среде Remix

  • 3. Компиляция контракта

  • 4. Публикация задания

  • 5. Проверка входных данных с помощью Modifiers

  • 5.1 Проверка крайнего срока выполнения (при создании задания)

  • 7. Публикация контракта и взаимодействие с ним средствами Remix

  • 9. Что дальше?

AUTHOR
A
Alex D.