Solidity блокчейн программирование: как начать разрабатывать смарт контракты на Ethereum


Понимание основ смарт контрактов

В рамках разработки смарт контракт состоит из трех разделов: баланс, хранения и коды. Баланс представляет, сколько Ethereum имеет умный контракт. Хранилище содержит данные, такие как строки и массивы, которые являются специфическими для каждого приложения. Раздел кода содержит необработанный машинный код, который скомпилирован из того, что мы пишем в Solidity.

В отличие от учетных записей пользователей, учетные записи смарт-контракта не являются внешними по отношению к соответствующим сетям. Другими словами, вы можете использовать свой кошелек в различных сетях, таких как Kovan и Ropsten, но вы не можете сделать это с помощью умного контракта. Умные контракты являются внутренними.

Каждый смарт-контракт имеет источник, который хранится на устройстве автора и экземпляры, которые хранятся в блокчейне. Чтобы создать экземпляр (учетную запись) умного контракта, нам нужно развернуть его в сети. Он очень напоминает отношения между классами и экземплярами в традиционном объектно-ориентированном программировании (ООП) и представляющих его языках (JS, Ruby). Чтобы дать вам более наглядное представление, давайте создадим класс Bike и добавим его экземпляр.

Bike class & instance

Мы напишем определение контракта, которое затем будет запускаться через компилятор, который создаст два файла: байт-код и двоичный интерфейс приложения (ABI). Байт-код — это то, что будет фактически передано в EVM, а ABI — это слой между байт-кодом и обычным кодом JavaScript, который позволяет создавать пользовательский интерфейс (UI).

Переменные

Типы переменных

  • State Variable — переменная состояния, значения которых хранятся в хранилище контракта.
  • Local Variable — переменные, которые существуют до выполнения функции.
  • Global Variable — глобальные переменные, которые позволяют получать информацию о блокчейне.

Типы значений

Boolean

Булевая переменная, которая может хранить в себе только 2 состояния: true и false. Пример:

bool public paused = false;

Применяемые операторы: !, &&, ||, ==, !=

Integers

int / uint — целочисленный тип данных со знаком и без знака. Чаще всего указывается с количеством зарезервированных байт под объявляемую переменную. Например, int / uint является аналогом int256 / uint256 м

int / uint — целочисленный тип данных со знаком и без знака. Чаще всего указывается с количеством зарезервированных битов от 8 до 256 под объявляемую переменную. Например, int / uint является аналогом int256 / uint256.

// переменные int* int8 = От -128 до 127 int16 = От -32 768 до 32 767 int32 = От -2 147 483 648 до 2 147 483 647 int64 = От -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 int128 = От -170141183460469231731687303715884105728 до 170141183460469231731687303715884105727 int256 = От -57896044618658097711785492504343953926634992332820282019728792003956564819968 до 57896044618658097711785492504343953926634992332820282019728792003956564819967 // переменные uint* uint8 = От 0 до 255 uint16 = От 0 до 65 535 uint32 = От 0 до 4 294 967 295 uint64 = От 0 до 18 446 744 073 709 551 615 uint256 = От 0 до 115792089237316195423570985008687907853269984665640564039457584007913129639935

Применяемые операторы: — Сравнение: <=, <, ==, !=, >=, > — Битовые: &, |, ^, ~ — Сдвига: <<, >> — Арифметические: +, —, унарный —, *, /, %, **

Fixed Point Numbers

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

Определяются они ключевыми словами fixedMxN и ufixedMxN соответственно для чисел со знаком и без знака. Где вместо М проставляется количество битов занятых хранимым значением от 8 до 256, а N — количество доступных десятичных знаков от 0 до 80. Причем ufixed и fixed — является аналогом для ufixed128x18 и fixed128x18 соответственно.

Применяемые операторы: — Сравнение: <=, <, ==, !=, >=, > — Арифметические: +, —, унарный —, *, /, %

Address

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

address nameReg = 0xdfc4bccf1aec515932c2d1ae499f92bb4ce04113;

Адрес также может быть объявлен как payable, в таком случае на этот адрес появляется возможность отправлять эфир при помощи двух дополнительных методов, которые он приобретает: transfer и send.

address payable nameReg = 0xdfc4bccf1aec515932c2d1ae499f92bb4ce04113;

Если вы планируете объявить переменную с типом address на который хотите переводить эфир, то сделайте эту переменную с типом address payable.

Применяемые операторы: <=, <, ==, !=, >=, >

Тип контракта

Каждый контракт имеет свой тип. Контракты могут быть преобразованы в тип адреса и обратно. Причем, преобразование контракта в тип address payable можно только в том случае, если сам контракт имеет функцию «получения и возврата» (receive or payable fallback function).

Если вы объявите локальную переменную контракта, то можете вызывать функции этого контракта. Также вы можете создавать экземпляры контракта при помощи слова new, это значит что контракт будет создаваться заново. Передав переменную контракта C в функцию type(C), вы сможете увидеть информацию о контракте.

Массивы

Массивы могут иметь статический или динамический размер. Массив Т фиксированного размера k записывается как T[k], а динамический массив записывается как T[]. Например, массив из 5 динамических массивов записывается как uint[][5]. Обратите внимание, что это обратная нотация по сравнению с большинством других языков (в них было бы наоборот).

Элементами массива могут быть переменные любого типа, включая mapping и struct. Если пометить массивы как public то Solidity создаст для них геттер (функцию получения из него элементов). Числовой индекс будет являться обязательным параметром для этого геттера. Доступ к несуществующему элементу массива вызывает ошибку. При помощи методов .push() и .push(value) вы можете добавить новый элемент в конец массива, причем вызов метода .push() добавит пустой элемент и возвратит ссылку на него.

Массивы типа bytes и string.

Массивы с типом bytes и string представляют из себя специальные массивы. Например, bytes похож на byte[], но плотно упакован в памяти и функции обратного вызова, а строка равна байтам, но не разрешает доступа к длине или индексу.

Вы можете использовать bytes вместо byte[], т.к. это дешевле, поскольку byte[] добавляет 31 байт между элементами. Используйте bytes для необработанных байтовых данных произвольной длинны (UTF-8). А если вы можете ограничит длину произвольным количеством байтов от 1 до 32 то используйте типы bytes1 .. bytes32, так как это намного дешевле. Если вы хотите объединить несколько переменных типа bytes, то используйте встроенную функцию bytes.concat.

bytes s = «Storage»; function f(bytes calldata c, string memory m, bytes16 b) public view { bytes memory a = bytes.concat(s, c, c[:2], «Literal», bytes(m), b); assert((s.length + c.length + 2 + 7 + bytes(m).length + 16) == a.length); }

Выделение массивов Memory

Динамические массивы memory можно создать при помощи оператора new. В отличие от storage массивов, у них нельзя изменить размер, поэтому вы должны заранее рассчитать под них требуемый размер.

uint[] memory a = new uint[](7); bytes memory b = new bytes(len);

Методы массива
  • length — позволяет понять количество элементов в массиве,
  • push() — позволяет добавить новый пустой элемент в конец массива, возвращает ссылку на добавленный элемент,
  • push(x) — позволять добавить новый элемент «х» в конец массива, ничего не возвращает,
  • pop — удаляет последний элемент массива.
Срезы массивов

Это представление непрерывной части массива начиная и заканчивая определенным его индексом.

Вызывается это как x[start:end], где start это начальный индекс, а end — конечный, которым заканчивается представление части массива. Причем, указание start и end носит опциональный характер, т.е. если не указывать start то это будет считаться значением 0, а если не указывать end, то это будет считаться последним элементом массива.

Строки

В переменных типа string вы можете хранить строковые значения, которые заключаются в одинарные или двойные кавычки. В них также есть поддержка escape символов, таких как \n, \xNN и т.д.

string public constant name = «This is my string value;

Solidity не имеет возможности манипулирования строками, но есть сторонние строковые библиотеки. Например, вы можете сравнить две строки по их хэшу: keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)) либо объединить две строки при помощи bytes.concat(bytes(s1), bytes(s2)).

Unicode литералы

Обычные строки задаются в кодировке ASCII, но если в строке вы планируете использовать UTF-8, то нужно перед двойными или одинарными кавычками указать ключевое слово unicode.

string memory a = unicode»Hello

Рейтинг
( 2 оценки, среднее 5 из 5 )
Понравилась статья? Поделиться с друзьями: