Перейти к основному контенту
Change page

Анатомия смарт-контрактов

Смарт-контракт — это программа, которая выполняется по определенному адресу в Эфириуме. Они состоят из данных и функций, которые могут выполняться при получении транзакции. Вот обзор того, из чего состоит смарт-контракт.

Предварительные требования

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

Данные

Любые данные контракта должны быть привязаны к определенному месту: либо к storage, либо к memory. Изменение хранилища в смарт-контракте обходится дорого, поэтому вам нужно тщательно продумать, где должны находиться ваши данные.

Хранилище (Storage)

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

// Пример на Solidity
contract SimpleStorage {
    uint storedData; // Переменная состояния
    // ...
}
# Пример на Vyper
storedData: int128

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

Тип address может содержать адрес Эфириума, который равен 20 байтам или 160 битам. Он возвращается в шестнадцатеричном формате с префиксом 0x.

Другие типы включают:

  • логический тип (boolean)
  • целые числа (integer)
  • числа с фиксированной запятой (fixed point numbers)
  • массивы байтов фиксированного размера (fixed-size byte arrays)
  • массивы байтов динамического размера (dynamically sized byte arrays)
  • рациональные и целочисленные литералы (rational and integer literals)
  • строковые литералы (string literals)
  • шестнадцатеричные литералы (hexadecimal literals)
  • перечисления (enums)

Для получения дополнительных объяснений загляните в документацию:

Память (Memory)

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

Узнайте больше о том, как виртуальная машина Эфириума (EVM) хранит данные (хранилище, память и стек), в документации Solidity (opens in a new tab).

Переменные среды

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

Примеры:

СвойствоПеременная состоянияОписание
block.timestampuint256Временная метка эпохи текущего блока
msg.senderaddressОтправитель сообщения (текущий вызов)

Функции

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

Существует два типа вызовов функций:

  • internal — они не создают вызов EVM
    • Внутренние функции и переменные состояния могут быть доступны только изнутри (т. е. из текущего контракта или контрактов, производных от него)
  • external — они создают вызов EVM
    • Внешние функции являются частью интерфейса контракта, что означает, что они могут быть вызваны из других контрактов и через транзакции. Внешняя функция f не может быть вызвана изнутри (т. е. f() не работает, но this.f() работает).

Они также могут быть public или private

  • Функции public могут быть вызваны изнутри контракта или извне через сообщения
  • Функции private видны только для контракта, в котором они определены, и не видны в производных контрактах

Как функции, так и переменные состояния могут быть сделаны публичными (public) или приватными (private)

Вот функция для обновления переменной состояния в контракте:

// Пример на Solidity
function update_name(string value) public {
    dapp_name = value;
}
  • Параметр value типа string передается в функцию: update_name
  • Она объявлена как public, что означает, что любой может получить к ней доступ
  • Она не объявлена как view, поэтому она может изменять состояние контракта

Функции просмотра (View)

Эти функции обещают не изменять состояние данных контракта. Типичными примерами являются функции-геттеры (getter) — вы можете использовать их, например, для получения баланса пользователя.

// Пример на Solidity
function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerPizzaCount[_owner];
}
dappName: public(string)

@view
@public
def readName() -> string:
  return dappName

Что считается изменением состояния:

  1. Запись в переменные состояния.
  2. Генерация событий (opens in a new tab).
  3. Создание других контрактов (opens in a new tab).
  4. Использование selfdestruct.
  5. Отправка эфира через вызовы.
  6. Вызов любой функции, не помеченной как view или pure.
  7. Использование низкоуровневых вызовов.
  8. Использование встроенного ассемблера, содержащего определенные коды операций (opcodes).

Функции-конструкторы

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

# Пример на Vyper

@external
def __init__(_beneficiary: address, _bidding_time: uint256):
    self.beneficiary = _beneficiary
    self.auctionStart = block.timestamp
    self.auctionEnd = self.auctionStart + _bidding_time

Встроенные функции

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

  • address.send() — Solidity
  • send(address) — Vyper

Они позволяют контрактам отправлять ETH на другие аккаунты.

Написание функций

Вашей функции нужны:

  • переменная параметра и тип (если она принимает параметры)
  • объявление internal/external
  • объявление pure/view/payable
  • тип возвращаемого значения (если она возвращает значение)

Полный контракт может выглядеть примерно так. Здесь функция constructor предоставляет начальное значение для переменной dapp_name.

События и логи

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

Примеры с аннотациями

Вот несколько примеров, написанных на Solidity. Если вы хотите поэкспериментировать с кодом, вы можете взаимодействовать с ними в Remix (opens in a new tab).

Hello world

Токен

Уникальный цифровой актив

Дополнительная литература

Ознакомьтесь с документацией Solidity и Vyper для более полного обзора смарт-контрактов: