Языки смарт-контрактов
Отличная особенность Эфириума заключается в том, что смарт-контракты можно программировать с использованием относительно удобных для разработчиков языков. Если у вас есть опыт работы с Python или любым языком с фигурными скобками (opens in a new tab), вы сможете найти язык со знакомым синтаксисом.
Два наиболее активных и поддерживаемых языка:
- Solidity
- Vyper
Remix IDE предоставляет комплексную среду разработки для создания и тестирования контрактов как на Solidity, так и на Vyper. Попробуйте браузерную Remix IDE (opens in a new tab), чтобы начать программировать.
Более опытные разработчики также могут захотеть использовать Yul, промежуточный язык для виртуальной машины Эфириума (EVM), или Yul+, расширение для Yul.
Если вам любопытно и вы хотите помочь в тестировании новых языков, которые все еще находятся в стадии активной разработки, вы можете поэкспериментировать с Fe — новым языком смарт-контрактов, который в настоящее время находится на ранней стадии развития.
Предварительные требования
Предыдущий опыт работы с языками программирования, особенно с JavaScript или Python, может помочь вам разобраться в различиях языков смарт-контрактов. Мы также рекомендуем вам понять концепцию смарт-контрактов, прежде чем слишком глубоко погружаться в сравнение языков. Введение в смарт-контракты.
Solidity
- Объектно-ориентированный язык высокого уровня для реализации смарт-контрактов.
- Язык с фигурными скобками, на который наибольшее влияние оказал C++.
- Статически типизированный (тип переменной известен во время компиляции).
- Поддерживает:
- Наследование (вы можете расширять другие контракты).
- Библиотеки (вы можете создавать переиспользуемый код, который можно вызывать из разных контрактов — подобно статическим функциям в статическом классе в других объектно-ориентированных языках программирования).
- Сложные пользовательские типы.
Важные ссылки
- Документация (opens in a new tab)
- Портал языка Solidity (opens in a new tab)
- Solidity на примерах (opens in a new tab)
- GitHub (opens in a new tab)
- Чат Solidity в Gitter (opens in a new tab), связанный с чатом Solidity в Matrix (opens in a new tab)
- Шпаргалка (opens in a new tab)
- Блог Solidity (opens in a new tab)
- Твиттер Solidity (opens in a new tab)
Пример контракта
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.7.0;
contract Coin {
// Ключевое слово "public" делает переменные
// доступными из других контрактов
address public minter;
mapping (address => uint) public balances;
// События позволяют клиентам реагировать на конкретные
// изменения контракта, которые вы объявляете
event Sent(address from, address to, uint amount);
// Код конструктора выполняется только тогда, когда контракт
// создается
constructor() {
minter = msg.sender;
}
// Отправляет определенное количество новых созданных монет на адрес
// Может быть вызвано только создателем контракта
function mint(address receiver, uint amount) public {
require(msg.sender == minter);
require(amount < 1e60);
balances[receiver] += amount;
}
// Отправляет определенное количество существующих монет
// от любого вызывающего на адрес
function send(address receiver, uint amount) public {
require(amount <= balances[msg.sender], "Insufficient balance.");
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Sent(msg.sender, receiver, amount);
}
}
Этот пример должен дать вам представление о том, как выглядит синтаксис контракта на Solidity. Для более подробного описания функций и переменных ознакомьтесь с документацией (opens in a new tab).
Vyper
- Язык программирования в стиле Python
- Строгая типизация
- Небольшой и понятный код компилятора
- Эффективная генерация байт-кода
- Намеренно имеет меньше функций, чем Solidity, с целью сделать контракты более безопасными и простыми для аудита. Vyper не поддерживает:
- Модификаторы
- Наследование
- Встроенный ассемблер
- Перегрузку функций
- Перегрузку операторов
- Рекурсивные вызовы
- Бесконечные циклы
- Двоичные числа с фиксированной запятой
Для получения дополнительной информации прочитайте обоснование создания Vyper (opens in a new tab).
Важные ссылки
- Документация (opens in a new tab)
- Vyper на примерах (opens in a new tab)
- Больше примеров на Vyper (opens in a new tab)
- GitHub (opens in a new tab)
- Чат сообщества Vyper в Дискорде (opens in a new tab)
- Шпаргалка (opens in a new tab)
- Фреймворки и инструменты разработки смарт-контрактов для Vyper
- VyperPunk — научитесь защищать и взламывать смарт-контракты на Vyper (opens in a new tab)
- Vyper Hub для разработки (opens in a new tab)
- Лучшие примеры смарт-контрактов на Vyper (opens in a new tab)
- Подборка полезных ресурсов Awesome Vyper (opens in a new tab)
Пример
# Открытый аукцион
# Параметры аукциона
# Бенефициар получает деньги от участника, предложившего наибольшую цену
beneficiary: public(address)
auctionStart: public(uint256)
auctionEnd: public(uint256)
# Текущее состояние аукциона
highestBidder: public(address)
highestBid: public(uint256)
# Устанавливается в true в конце, запрещает любые изменения
ended: public(bool)
# Отслеживание возвращенных ставок, чтобы мы могли следовать шаблону вывода средств
pendingReturns: public(HashMap[address, uint256])
# Создает простой аукцион со временем торгов `_bidding_time`
# секунд от имени
# адреса бенефициара `_beneficiary`.
@external
def __init__(_beneficiary: address, _bidding_time: uint256):
self.beneficiary = _beneficiary
self.auctionStart = block.timestamp
self.auctionEnd = self.auctionStart + _bidding_time
# Сделать ставку на аукционе со значением, отправленным
# вместе с этой транзакцией.
# Значение будет возвращено только в том случае, если
# аукцион не выигран.
@external
@payable
def bid():
# Проверить, закончился ли период торгов.
assert block.timestamp < self.auctionEnd
# Проверить, достаточно ли высока ставка
assert msg.value > self.highestBid
# Отследить возврат для предыдущего участника с наибольшей ставкой
self.pendingReturns[self.highestBidder] += self.highestBid
# Отследить новую наибольшую ставку
self.highestBidder = msg.sender
self.highestBid = msg.value
# Вывести ранее возвращенную ставку. Шаблон вывода средств
# используется здесь, чтобы избежать проблем с безопасностью. Если бы возвраты напрямую
# отправлялись как часть bid(), вредоносный контракт для ставок мог бы заблокировать
# эти возвраты и тем самым заблокировать поступление новых более высоких ставок.
@external
def withdraw():
pending_amount: uint256 = self.pendingReturns[msg.sender]
self.pendingReturns[msg.sender] = 0
send(msg.sender, pending_amount)
# Завершить аукцион и отправить наибольшую ставку
# бенефициару.
@external
def endAuction():
# Хорошим правилом является структурирование функций, которые взаимодействуют
# с другими контрактами (т.е. они вызывают функции или отправляют эфир)
# на три фазы:
# 1. проверка условий
# 2. выполнение действий (потенциально изменяющих условия)
# 3. взаимодействие с другими контрактами
# Если эти фазы перепутаны, другой контракт может вызвать
# обратно текущий контракт и изменить состояние или привести к тому, что
# эффекты (выплата эфира) будут выполнены несколько раз.
# Если функции, вызываемые внутренне, включают взаимодействие с внешними
# контрактами, они также должны рассматриваться как взаимодействие с
# внешними контрактами.
# 1. Условия
# Проверить, достигнуто ли время окончания аукциона
assert block.timestamp >= self.auctionEnd
# Проверить, была ли уже вызвана эта функция
assert not self.ended
# 2. Эффекты
self.ended = True
# 3. Взаимодействие
send(self.beneficiary, self.highestBid)
Этот пример должен дать вам представление о том, как выглядит синтаксис контракта на Vyper. Для более подробного описания функций и переменных ознакомьтесь с документацией (opens in a new tab).
Yul и Yul+
Если вы новичок в Эфириуме и еще не программировали на языках смарт-контрактов, мы рекомендуем начать с Solidity или Vyper. Изучайте Yul или Yul+ только после того, как ознакомитесь с передовыми методами обеспечения безопасности смарт-контрактов и спецификой работы с EVM.
Yul
- Промежуточный язык для Эфириума.
- Поддерживает EVM и Ewasm (opens in a new tab) (WebAssembly, адаптированный для Эфириума) и разработан как удобный общий знаменатель для обеих платформ.
- Хорошая цель для этапов высокоуровневой оптимизации, которая может принести равную пользу платформам EVM и Ewasm.
Yul+
- Низкоуровневое, высокоэффективное расширение для Yul.
- Изначально разработано для контракта оптимистичного роллапа.
- Yul+ можно рассматривать как экспериментальное предложение по обновлению Yul, добавляющее в него новые функции.
Важные ссылки
- Документация Yul (opens in a new tab)
- Документация Yul+ (opens in a new tab)
- Вводная статья о Yul+ (opens in a new tab)
Пример контракта
Следующий простой пример реализует функцию возведения в степень. Его можно скомпилировать с помощью solc --strict-assembly --bin input.yul. Пример должен быть сохранен в файле input.yul.
{
function power(base, exponent) -> result
{
switch exponent
case 0 { result := 1 }
case 1 { result := base }
default
{
result := power(mul(base, base), div(exponent, 2))
if mod(exponent, 2) { result := mul(base, result) }
}
}
let res := power(calldataload(0), calldataload(32))
mstore(0, res)
return(0, 32)
}
Если у вас уже есть большой опыт работы со смарт-контрактами, полную реализацию ERC-20 на Yul можно найти здесь (opens in a new tab).
Fe
- Статически типизированный язык для виртуальной машины Эфириума (EVM).
- Вдохновлен Python и Rust.
- Стремится быть простым в изучении — даже для разработчиков, которые только знакомятся с экосистемой Эфириума.
- Разработка Fe все еще находится на ранней стадии, альфа-версия языка была выпущена в январе 2021 года.
Важные ссылки
- GitHub (opens in a new tab)
- Анонс Fe (opens in a new tab)
- Дорожная карта Fe на 2021 год (opens in a new tab)
- Чат Fe в Дискорде (opens in a new tab)
- Твиттер Fe (opens in a new tab)
Пример контракта
Ниже приведен простой контракт, реализованный на Fe.
type BookMsg = bytes[100]
contract GuestBook:
pub guest_book: map<address, BookMsg>
event Signed:
book_msg: BookMsg
pub def sign(book_msg: BookMsg):
self.guest_book[msg.sender] = book_msg
emit Signed(book_msg=book_msg)
pub def get_msg(addr: address) -> BookMsg:
return self.guest_book[addr].to_mem()
Как выбрать
Как и в случае с любым другим языком программирования, в основном речь идет о выборе правильного инструмента для конкретной задачи, а также о личных предпочтениях.
Вот несколько моментов, которые следует учитывать, если вы еще не пробовали ни один из этих языков:
Чем хорош Solidity?
- Если вы новичок, существует множество руководств и обучающих инструментов. Подробнее об этом читайте в разделе Обучение через программирование.
- Доступны хорошие инструменты для разработчиков.
- У Solidity большое сообщество разработчиков, а это значит, что вы, скорее всего, довольно быстро найдете ответы на свои вопросы.
Чем хорош Vyper?
- Отличный способ начать для разработчиков на Python, которые хотят писать смарт-контракты.
- Vyper имеет меньшее количество функций, что делает его отличным выбором для быстрого прототипирования идей.
- Vyper стремится быть простым для аудита и максимально удобочитаемым для человека.
Чем хороши Yul и Yul+?
- Упрощенный и функциональный низкоуровневый язык.
- Позволяет стать намного ближе к чистой EVM, что может помочь оптимизировать использование газа вашими контрактами.
Сравнение языков
Для сравнения базового синтаксиса, жизненного цикла контракта, интерфейсов, операторов, структур данных, функций, потока управления и многого другого ознакомьтесь с этой шпаргалкой от Auditless (opens in a new tab).