Как создать смарт контракт
Перейти к содержимому

Как создать смарт контракт

  • автор:

Smart-контракты. Самое полное руководство (2 часть)

Smart Contracts (смарт-контракты) — это программируемые контракты, которые автоматически исполняются, когда выполняются условия, оговоренные в контракте. Smart Contracts представляют собой децентрализованный, безопасный и прозрачный способ совершения сделок между участниками без необходимости доверять друг другу.

Smart Contracts основаны на технологии блокчейн, которая представляет собой распределенную базу данных, которая содержит информацию о всех транзакциях. Smart Contracts работают так: участники сделки заключают контракт, который содержит определенные условия. Затем этот контракт загружается на блокчейн и программирование контракта начинает работу. Когда условия контракта выполняются, Smart Contract автоматически исполняет сделку.

  • Экономия времени и средств: Использование Smart Contracts уменьшает время, необходимое для заключения сделки и избавляет от необходимости оплачивать услуги посредников.
  • Устранение риска мошенничества: Smart Contracts предоставляют безопасный способ совершения сделок без необходимости доверять друг другу.
  • Повышение прозрачности: Smart Contracts обеспечивают прозрачность сделок и их исполнения, что позволяет участникам сделки иметь более четкое представление о процессе.

a) Децентрализованные финансы (DeFi) ��

DeFi Apps (Dapps) — это децентрализованные финансовые приложения, которые предоставляют финансовые услуги без необходимости использовать централизованные биржи и финансовые учреждения. Сеть DeFi приложений стремится создать открытые, прозрачные и доступные финансовые услуги, которые предлагают альтернативу традиционным банковским услугам. Smart Contracts используются для создания децентрализованных финансовых приложений, которые позволяют пользователям торговать, кредитоваться, и инвестировать.

��Условия, которые включаются в смарт-контракты DeFi, могут варьироваться в зависимости от конкретного приложения, но обычно они включают:

  • Условия для предоставления займа и его погашения;
  • Условия для торговли и обмена криптовалют;
  • Условия для инвестирования в финансовые продукты;
  • Условия для создания и управления финансовыми пулами.

b) Микрокредитование ��

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

��Условия, которые включаются в смарт-контракты микрокредитования, могут варьироваться в зависимости от конкретного приложения, но обычно они включают:

  • Условия для предоставления кредита;
  • Условия для определения суммы, сроков и процентной ставки кредита;
  • Условия для возврата кредита;
  • Условия для штрафов и задержек в погашении кредита.

c) Управление недвижимостью ��

Smart contracts также могут быть использованы для управления недвижимостью. Они могут упростить и автоматизировать процесс покупки, продажи и аренды недвижимости. Контракты могут быть созданы, чтобы установить правила, согласно которым сделка может быть выполнена.

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

��Пример использования smart contracts в недвижимости:

Предположим, что Алиса хочет купить квартиру у Боба.

Они могут создать smart contract, который будет содержать следующие условия:

  • Сумма покупки квартиры.
  • Сроки платежей.
  • Условия отмены контракта.
  • Дата и время заключения сделки.
  • Условия передачи собственности.

Когда Алиса выполняет платежи в соответствии с контрактом, Боб автоматически получает оплату, а собственность на квартиру передается Алисе.

d) Управление цепочкой поставок ��

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

��Пример использования smart contracts в управлении цепочкой поставок:

Предположим, что компания А хочет купить товары у компании В.

Они могут создать smart contract, который будет содержать следующие условия:

  • Количество товаров, которые будут поставлены.
  • Цена за каждый товар.
  • Сроки поставки товаров.
  • Условия оплаты.
  • Условия возврата товаров.

Когда компания А выполняет платежи в соответствии с контрактом, компания В автоматически получает оплату, а товары поставляются в соответствии с установленными сроками.

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

➡ ��Наша команда написала курс по основам DeFi. Первый в РФ, СНГ и Европе полный курс по основным возможностям системы децентрализованных финансов.�� ⬅

�� Самый полный. Самый эффективный. Самый практичный. Вся инфа в боте: @defi_systems_bot

�� Позже мы выпустим подробный обзор каждого из них. ��

Solidity — это язык программирования для смарт-контрактов, используемых на платформе Ethereum. Он имеет синтаксис, похожий на язык программирования JavaScript, и позволяет разработчикам создавать сложные смарт-контракты на основе блокчейна Ethereum. Одной из ключевых особенностей Solidity является возможность создания токенов и контрактов для ICO (Initial Coin Offering). Кроме того, Solidity имеет библиотеки, которые облегчают разработку контрактов и позволяют разработчикам создавать расширяемые и устойчивые контракты. Однако, Solidity имеет некоторые сложности в отладке, поскольку не всегда легко предсказать, какой код будет выполнен в определенных условиях, что может приводить к ошибкам и уязвимостям в контрактах.

Rust — это системный язык программирования с открытым исходным кодом, который используется для создания безопасных и эффективных смарт-контрактов на платформе Substrate. Язык Rust известен своей безопасностью, скоростью и параллелизмом, что позволяет разработчикам создавать сложные смарт-контракты на многих уровнях. Он также предоставляет высокий уровень защиты от уязвимостей, таких как переполнение буфера и недействительные указатели, что делает его идеальным языком для создания безопасных смарт-контрактов на платформе Substrate.

Move — это язык программирования с открытым исходным кодом, который разработан специально для смарт-контрактов на блокчейне Diem. Язык Move известен своей безопасностью и эффективностью, так как он позволяет обнаруживать ошибки в коде на этапе компиляции. Это позволяет избежать многих уязвимостей, связанных с смарт-контрактами. Кроме того, язык Move обеспечивает более высокую производительность и скорость, чем другие языки, используемые для написания смарт-контрактов, что делает его идеальным языком для создания эффективных и безопасных смарт-контрактов на блокчейне Diem.

Cadence — это язык программирования, который был разработан специально для смарт-контрактов на платформе Flow. Язык Cadence удобен для написания сложных смарт-контрактов, поскольку он позволяет программистам использовать объектно-ориентированную парадигму программирования, что облегчает процесс разработки. Кроме того, язык Cadence предоставляет высокий уровень безопасности, что помогает избежать многих уязвимостей, связанных с смарт-контрактами. Это делает его идеальным языком для создания безопасных и эффективных смарт-контрактов на платформе Flow.

Vyper — это язык программирования для смарт-контрактов, основанный на Python, который предназначен для использования на платформе Ethereum. Он известен своей безопасностью и простотой, поскольку не поддерживает некоторые функции, которые могут стать причиной уязвимостей в контрактах. Кроме того, Vyper позволяет создавать эффективные контракты, используя ограниченный набор инструкций.

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

Bamboo — это язык программирования для смарт-контрактов, который основан на Rust и предназначен для использования на платформе æternity. Он известен своей производительностью, безопасностью и простотой в использовании. Кроме того, Bamboo позволяет создавать контракты с высокой степенью параллелизма, что обеспечивает более быстрое выполнение и масштабируемость.

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

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

Simplicity — это язык программирования для смарт-контрактов, разработанный компанией Blockstream. Его главной особенностью является упрощенный и формализованный синтаксис, который устраняет большинство сложностей, связанных с написанием и верификацией смарт-контрактов. Это позволяет сократить время разработки и уменьшить вероятность ошибок.

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

Smart contracts представляют собой инновационную технологию, которая может сохранить многих от расследований, судебных и исполнительных процедур.

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

Как написать смарт-контракт для ICO за 5 минут

Всем привет! В этой статье я расскажу вам, как за 5 минут и несколько команд в терминале запустить смарт-контракт сбора денег для своего ICO на Ethereum. Этот очерк потенциально сэкономит вам десятки тысяч американских долларов, так как любой программист — да и не программист тоже — сможет запустить проаудированный и безопасный смарт-контракт (вместо того, чтобы платить $15,000 – $75,000 за разработку). Вкратце, на этот смарт-контракт можно будет отправить денег и получить за это ERC20 токены. Можно сказать, эта статья — сборник всего опыта, который я получил, запуская ICO для своего проекта.

В Интернетах этих ваших и так полно статьей про смарт-контракты, но как только начинаешь писать оный, сталкиваешься с тем, что информация везде повторяется, а туториалов, как запулить свой ERC20 попросту либо нет, либо они устарели что аж донельзя. К слову, чтобы эта статья оставалась актуальной, постараюсь указать потенциальные места, где она может устареть (и как это поправить). Поехали!

Solidity

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

Смарт-контракты

… и все, что вам нужно о них знать. Пропустите эту секцию, если вы не программист. Смарт-контракт — это кусок кода. В принципе, это класс в солидити (ООП, да), у которого есть два типа функций: изменяющие состояние и не изменяющие. Ну и чтобы запускать функции в смарт-контракте просто отправив на него кефир, нужно эту функции пометить payable .

Состояние — это хранилище данных, блокчейн, епта. Контракты могут изменять блокчейн (состояние, хранилище) — но чтобы изменить блокчейн нужно заплатить кефира майнерам. Как они будут делить кефир разбирать не будем в рамках этой статьи. Оплата майнерам за запуск кода, изменяющий состояние, называется Газом (Gas). Если кто-то извне закинет кефира на адрес смарт-контракта с вызовом функции, помеченной payable , но не помеченной Constant , View или Pure , то из отправленной суммы будет вычтено нужное количество кефира для оплаты майнерам. Обычно в ERC20 токенах это функции, которые либо выдают отправителю токенов за кефир, либо переводят токены от одного держателя токенов другому.

А если вы пометите функцию в контракте словами Constant или View (означают одно и то же, разрешают только читать состояние), либо Pure (то же самое, только даже состояние не читает), то на исполнение этой функции даже кефир тратить не нужно будет! Даже больше скажу, эти функции не нужно вызывать транзакциями — ведь любой клиент кефира, теоретически, сможет ее выполнить у себя — и никому больше об этом знать не нужно (в блокчейн ведь ничего не пишется).

А еще есть две важные штуки в солидити: множественное наследование и модификаторы функций. Про них тоже нужно знать.

Первое — просто контракты могут наследоваться одновременно с нескольких классов типа TimedCrowdsale , CappedCrowdsale , MintedCrowdsale , Ownable — при этом функции конструкторов тоже запускаются друг за другом — но это я объясню на примере уже дальше.

Второе — это возможности создавать функции, которые потом будут вставлены в другие функции. Это как простая инкапсуляция, только чуть более гибкая — это буквально шаблон функции. Когда вы создаете модификатор, вы пишете специальный символ _ там, где подразумеваете код функции, использующей этот модификатор. То есть модификаторы — это не просто инкапсулированный функционал, который возвращает значение; это — шаблон функции, когда код из модификатора буквально вставляется в функцию, использующую этот модификатор.

Перейдем к практике.

Готовим окружение

Если вы не знаете, что такое Терминал — почитайте вот эту статью. Если вы на окнах, ставьте себе Терминал через WLS. Если вы уже знакомы с Терминалом, продолжим. Алсо, сразу поставьте себе Node.js — он будет необходим для следующих шагов. Лучше ставить LTS, но, на самом деле, абсолютно без разницы, какую из современных версий ноды ставить.

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

Если вам выплюнуло версию geth — все в ажуре, продолжаем туториал. Если нет — хреново, исправляйте; придется, похоже, заняться любовными ласками с Терминалом и своей операционной системой — но вам не впервой, разберетесь. Как установите geth, запускайте в Терминале команду:

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

Процесс синхронизации у меня занимал от 1 до 4 часов — когда как. Алсо, помимо синхронизации блоков, придется ждать еще и синхронизации состояний — это чаще дольше, чем синхронизация блоков. Также можно использовать команды geth с флагом —light — тогда синхронизация длится от нескольких секунд до минуты и вы все еще можете деплоить контракты.

Ладно, первую утилиту мы поставили — ставим следующую. Нам нужно поставить аналог geth , только совсем уж локальную симуляцию блокчейна — testrpc . Да-да, у нас 3 блокчейна:

  • testrpc — локальная симуляция блокчейна; быстрая, но ненастоящяя и хранится только у вас на машине
  • geth —testnet — уже реальный блокчейн, но тестовая сеть, где можно бесплатно получать кефир и тестить всякие непотребства, денег не потеряете
  • geth — мейннет, главный, реальный блокчейн, настоящий кефир; все по-взрослому, ошибки тут — потери реального кефира

Ставим testrpc , запустив следующую команду:

Ну или встанет сразу с трюфелем, так как теперь testrpc под крылом трюфеля и зовется ganache-cli . Хотя черт его знает, у меня все и с ванильным testrpc сработало. А если работает — не трогай, как учили меня в межгалактической академии. Можно еще его и запустить, чтобы проверить установку, прописав truffle в консоли, но у нас уже синхронизируется тестовый блокчейн — не будем ему мешать.

Ну что, разобрались с блокчейнами? Ноды теперь есть и тестовая даже синхронизируется? Ставим удобную утилиту для работы со смарт-контрактами на кефире — truffle , следующей командой:

Трюфель — это тулза, которая позволяет держать смарт-контракты в разных файлах, импортировать другие файлы, а так же компилирует ваш код смарт-контрактов в один большой байт-код (нечитаемый человеком), автоматически находит у вас локально запущенный geth (тестовый и реальный) или testrpc , деплоит ваш смарт-контракт в эту сеть. Алсо, проверяет ваш код смарт-контракта на ошибки и с недавних пор еще и дебажить помогает завершенные транзакции. Мастхев, короче.

На этом этапе у вас должны быть установлены: testrpc , geth , truffle — если чего-то из этого нет или версия не выплевывается в консоль по запросу, то поправьте это; иначе не получится у вас ничего.

Алсо, я накидал простенький баш-скриптик, который установит все за вас. Вызывается вот так:

— но я его ни разу не тестил еще, так что не уверен в его работоспособности. Однако пулл-реквестам буду только рад.

Фигачим контракт

За вас все уже придумано и написано — это хорошо. Немного головняка будет все равно — но я постараюсь вам его минимизировать. Использовать мы будем уже готовые ERC20 контракты от OpenZeppelin — это сейчас стандарт индустрии, они прошли аудит, да и вообще все их код используют. Спасибо им огромное за вклад в опенсоус.

Сделайте cd в какую-нибудь безопасную папку и после пропишите:

В этой папке и будем работать. Создадим здесь заглушку для нашего смарт-контракта:

Зашибись, четко. У нас теперь есть две очень важные папки, в которые мы и будем лезть: contracts и migrations . Первая — код наших контрактов, вторая — код для truffle, чтобы знать, что делать при деплое контрактов в блокчейн.

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

Отлично, код смарт-контрактов от OpenZeppelin у нас в кармане в папке node_modules/openzeppelin-solidity/contracts . Теперь заходим в главную папку contracts , удаляем там все файлы и добавляем файлы MyToken.sol и MyCrowdsale.sol — естественно, свои контракты вы назовете иначе. Первый будет контрактом на наш ERC20 Токен, а второй — контрактом нашего ICO, который будет принимать кефир и раздавать людям MyToken . Эта статья может устареть, но вы всегда можете глянуть, как OpenZeppelin предлагают вам создавать контракты у них в репозитории. Вот так у нас будет выглядеть MyToken.sol :

Найс — у вас есть смарт-контракт собственного токена (только смените названия в константах)! Можете глянуть, что там за наследование от MintableToken — но там все максимально просто. Это токен, который можно выпускать (от англ. «Mint» — чеканить), и выпускать его имеет право только владелец, так как MintableToken еще и наследуется от Ownable . Алсо, MintableToken еще и наследуется от классов ERC20 токенов, написанных OpenZeppelin, в которых и реализован интерфейс ERC20:

Ага, вот вам и весь ERC20 интерфейс. Сложно? Не думаю. Дает возможность глянуть, сколько было выпущено токенов, проверить баланс адреса и перевести токенов на другой адрес, выплюнув в сеть событие перевода для легких клиентов кефира. И все это вы получаете фор фри в вашем MyToken.sol благодаря работе OpenZeppelin — они молодцы.

А теперь перейдем к главной части нашего ICO — нам же нужно принимать кефиры и раздавать MyToken ! Вот так будет выглядеть ваш MyCrowdsale.sol :

Так-так-так, что тут у нас? Что, пацаны, смарт-контракты? Наша публичная продажа токенов наследует три самых популярных свойства: у нее есть хард-кап, больше которого собрать не получится; софт-кап, не собрав который эфиры возвращаются; время начало и конца продажи токенов. Собственно говоря, а что еще для счастья нужно?

Программисты, обратите внимание, как конструкторы классов множественного наследования выстроены в ряд и получают аргументы из главного конструктора MyCrowdsale . Алсо, мы проверяем, что хардкеп у нас выше софткепа — алес гут! Алсо, не пугайтесь туче параметров в конструкторе MyCrowdsale — мы передадим их на этапе деплоя контракта в трюфеле.

Вот и все — у вас есть готовые контракты вашего собственного ERC20 токена и даже смарт-контракт ICO, который настраивается по вашему желанию и раздает ваши токены за кефир. Алсо, его поддерживают все ERC20 кошельки — ляпота! Перейдем к ручным тестам и деплою.

Миграции

Как я уже говорил ранее, тестировать мы будем последовательно на трех блокчейн-сетях, но процесс тестирования ручками всегда будет один и тот же. Начнем с testrpc , потом перейдем к geth —testnet и дальше в geth . Соу фар мы только писали код, давайте его попробуем скомпилировать. В папке проекта пропишите:

Если все скомпилировалось без проблем — то у вас появится папочка build , в которой будет содержаться кракозябра для трюфеля, чтобы он смог задеплоить в блокчейн байт-код ваших смарт-контрактов. Перед тем, как деплоить смарт-контракты, нам нужно рассказать трюфелю, что вообще нужно делать. Деплой смарт-контрактов в трюфеле называется миграцией — ну, что же, будем придерживаться этой терминологии. Зайдите в migrations/1_initial_migration.js и измените его следующим способом:

Это тот самый файл, который будет использоваться трюфелем для деплоя контрактов. Что же мы тут такого наворотили? Во-первых, мы запросили скомпилированные MyToken и MyCrowdsale . После, мы установили константы со всеми аргументами нашего ICO — установили время начала и конца; сколько токенов будут получать люди за 1 вей кефира (0.000000000000000001 eth = 1 wei; установка decimals указывает, сколько нужно порядков wei, чтобы получить 1 ваш новоиспеченный токен); кошелек, куда придут полученные на продаже кефиры; хард-кеп и софт-кеп. Учтите, что openingTime всегда должен быть после времени текущего блока в блокчейне — иначе ваш смарт-контракт не задеплоится из-за проверки условия в TimedCrowdsale . Я на эти грабли наступал, а провалившиеся транзакции вообще никак не получается дебажить. Меняйте эти константы по своему усмотрению.

Следующий шаг — это именно деплой смарт-контрактов. Тут ничего интересного: у нас есть объект deployer , который деплоит артефакты смарт-контрактов и передает туда аргументы. Заметьте, что сначала деплоится MyToken , и только потом MyCrowdsale — и во второй передается аргументом адрес первого.

Дальше самое интересное — то, о чем не пишут ни в документации, ни в книжках. Когда вы создаете с кошелька MyToken , этот кошелек становится владельцем MyToken по суперклассу Ownable — то же самое происходит и с MyCrowdsale . Если глубоко копнуть в MintableToken , то можно увидеть, что чеканить монеты-то может только Owner ! А кто владелец MyToken ? Правильно: адрес, который его и задеплоил. А кто будет отправлять запросы на чеканку монет? Правильно: смарт-контракт MyCrowdsale . Напомню, что адрес, создавший MyToken и адрес MyCrowdsale — это два разных адреса.

Поэтому у нас добавляется неправославный третий шаг деплоя, где адрес, задеплоивший контракты ( web3.eth.accounts[0] ) вызывает функцию transferOwnership на контракте MyToken , чтобы MyCrowdsale владел MyToken и мог чеканить монеты. А MyCrowdsale все еще под владением web3.eth.accounts[0] — так что все пучком.

Тестирование и деплой

Йес, контракты готовы, миграции написаны, осталось только задеплоить и проверить. Как geth (тестовый и реальный), так и testrpc управляются одинаково через truffle console — так что опишу способ проверки для testrpc и просто расскажу, как включить geth после. И так, запускаем тестовый локальный блокчейн кефира:

Эм… вот и все. У вас локально работает симуляция блокчейна кефира.

Теперь открываем новое окошко Терминала ( testrpc не закрываем — он должен работать) и прописываем в папке проекта:

Эта магическая команда скомпилирует смарт-контракт (то есть не нужно каждый раз писать truffle compile ) и задеплоит его на микро-сервер блокчейна, найденный открытым локально. Стоит отметить, что если testrpc сделает это мгновенно, то тестовый и реальный блокчейны будут гораздо дольше включать транзакцию в следующие блоки. После этого у вас должно выплюнуться нечто подобное в консольку:

Думаю, вы уже поняли, что консолька вам выдала адреса смарт-контрактов MyToken и MyCrowdsale . Все! Смарт-контракт задеплоен в тот блокчейн, микро-сервер которого у вас открыт. Осталось лишь проверить, что токены и вправду раздаются юзерам, которые присылают кефир на смарт-контракт MyCrowdsale . Прописываем в Терминале следующее, чтобы зайти в консоль трюфеля:

Прописываем следующее в теперь уже трюфеле (без комментариев только):

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

Вот и все! Сначала тестите свой контракт на testrpc , потом на geth —testnet , потом деплойте на geth . Вот и запустили вы свое собственное ICO! И не пришлось вам тратить десятки килобаксов на аудит и запуск. Накосячить с тем, что нам предоставили ребята из OpenZeppelin, на самом деле, очень сложно. А когда вы используете truffle — так разработка на солидити вообще в сказку превращается. Ну, кроме случаев, когда транзакции ревертятся еще во время выполнения на смарт-контракте — дебажить их сущий ад. Но дебаггинг смарт-контрактов, воистину, достоин отдельной статьи.

Заключение

Огромное спасибо, что дочитали до конца этой статьи! Если мне удалось сэкономить вам время или деньги, либо если вы узнали что-то новое из этой статьи, то я этому буду очень рад. Буду так же очень признателен, если поделитесь этой статьей со своими друзьями или знакомыми, которые хотят провести ICO — сэкономьте им $75,000 на недо-программистов, которые высасывают деньги из крипто-рынка, как паразиты, копи-пастя одни и те же 25 строк кода.

Удачи в разработке смарт-контрактов! Остались вопросы? Милости прошу в комментарии — с удовольствием на все отвечу и постараюсь помочь с проблемами.

Бонус

А что, если вы хотите изменить логику, по которой считается цена покупки токенов? Конечно, можно изменить правильно rate или использовать один из классов контрактов от OpenZeppelin, но вдруг вы хотите чего-нибудь еще более извращенного? В смарт-контракте можно оверрайтнуть функцию getTokenAmount следующим образом:

В общем, так можно сделать цену токена зависящей от времени покупки — чем дальше в лес, тем дороже токены. Не бойтесь экспериментировать и переписывать некоторые функции смарт-контрактов — это весело!

Автономность и безопасность. Как работают смарт-контракты

Смарт-контракт — компьютерная программа, которая выполняет соглашения, заключенные между двумя и более сторонами, в результате которых, при выполнении тех или иных условий происходят определенные действия. То есть, когда задействуется ранее запрограммированное условие, смарт-контракт автоматический выполняет соответствующее соглашение. Если рассматривать обычный контракт и смарт-контракт, то можно точно сказать, что они оба являются соглашениями, в которых две или более стороны соглашаются соблюдать ряд условий. Их фундаментальные элементы одинаковы: добровольное согласие всех сторон, объект договора (товара или услуги) и единая цель. Тем не менее, оба отличаются по трем факторам: способ написания, его юридические последствия и концепт соблюдения.

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

Термин «умные контракты» был придуман ученым-программистом Ником Сабо, вероятно, в 1993 году, чтобы объяснить цель внедрения в разработку протоколов того, что он назвал «высокоразвитой» ступенью договорного права и связанных с ними бизнес-практик в электронной коммерции. Сабо, вдохновленный такими исследователями, как Дэвид Чаум, также полагал, что разработка умных-контрактов с помощью выполнения криптографических протоколов и других механизмов цифровой безопасности могут стать значительным улучшением по сравнению с традиционными юридическими контрактами.

Сабо использовал слово «умный» в кавычках и заявил о том, что искусственный интеллект задействован не будет. Он приводил классический пример смарт-контракта: это вендинговый автомат. Если условия «контракта» устраивают покупателя (т. е. он опускает деньги в автомат), то машина автоматически соблюдает условия неписаного соглашения и предоставляет покупку.

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

Как работают смарт-контракты?

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

Платформа Ethereum заменяет более ограниченный язык BTC (язык сценариев, насчитывающий около ста) языком, который позволяет разработчикам задавать собственные сценарии. Ethereum позволяет разработчикам программировать свои собственные смарт-контракты. Язык является «полным по Тьюрингу», что означает, что он поддерживает более широкий инструментарий вычислительных инструкций.

Платформа Ethereum использовалась для распространения децентрализованных приложений (DApps). Вместо множества приложений, управляемых многими протоколами, Ethereum позволяет управлять всеми приложениями по одному протоколу.

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

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

Используя смарт-контракты, больше нет необходимости прибегать к помощи третьей стороны, например, адвоката или нотариуса, который, помимо возможных ошибок, влечет за собой значительные расходы. Блокчейн способен защитить информацию в зашифрованной сети, к которой можно обращаться из любой точки мира, поэтому скорость и безопасность очевидны. Самыми главными преимуществами контрактов являются:

Автономность

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

Снижение затрат

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

Скорость

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

Безопасность

Основывая контракты на блокчейне Ethereum, они не могут быть потеряны. Все является неизменным. Ничто и никто не может заставить его исчезнуть, и у вас всегда есть к ним доступ.

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

Недостатки смарт-контрактов

С другой стороны, программы такого типа также имеют определенные недостатки. Главный состоит преимущественно в использовании технологий, которые он задействует: интернет-вещей и блокчейн.

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

Как создать смарт-контракт?

Контракт разработан на определенном языке (Solidity, Serpent или Mutan), скомпилирован в EVM и, за некоторыми исключениями, соответствует стандартному интерфейсу ERC-20. Solidity является языком, который основан на JS, Python и C++.

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

В документе «Введение юриста в смарт-контракты» для смарт-контрактов описываются два «уровня»: платформа смарт-контрактов (SCP), которая представляет собой инфраструктуру, позволяющую создавать их и управлять ими на блокчейне, и система управления смарт-контрактами (SCMS). Этот протокол добавляется к этой инфраструктуре, чтобы пользователю было проще взаимодействовать с контрактами. То есть, он является графическим интерфейсом.

Контракт выполняется в сети, внутри виртуальной машины Ethereum (EVM). Это исполнение не является бесплатным, но оплачивается в форме «газа», небольшого количества эфирной валюты, внесенного одним или несколькими участниками контракта. Важной деталью является то, что исполнение децентрализовано и производится во всех узлах сети. Это одна из причин, по которой EVM работает так медленно и может обрабатывать только около 15 транзакций в секунду.

Как происходит процесс оформления смарт-контрактов

Инфраструктура интеллектуальных контрактов может быть реализована с помощью реплицированных записей, а выполнение контрактов может осуществляться с использованием древа Меркле, который работает через криптографические хеш-функции и репликацию задачи византийских генералов (BFT), которая является обобщенной задачей двух генералов. Каждый узел в одноранговой сети действует как реестр доверия или гарантии, выполняя изменения держателей контракта и автоматически проверяя правила, наложенные транзакцией, а также проверяя ту же работу других узлов. Криптовалюты типа BTC реализовали особые случаи для тех записей или узлов, где собственностью и ядром транзакции являются деньги. BTC и многие его производные содержат более обобщенные механизмы для прав собственности и исполнения контрактов. Ведение кода — очень важная и часть протокола BTC, основанная на репликации конечного автомата.

Как можно использовать смарт-контракты?

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

Автоматизация платежей: контракт может быть запрограммирован, чтобы гарантировать, что требуемая сумма поступит в указанное время указанным лицам или организациям.

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

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

Интеллектуальная собственность: можно встроить смарт-контракт в любой объект, который контролируется цифровыми средствами. Именно здесь рождается «умная собственность», которая может быть ассимилирована с объектами IoT, подключенными к сети. Они могут варьироваться от дома до автомобилей. Таким образом, например, аренда этих свойств может быть автоматизирована.

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

Финансовые услуги: Криптовалюты, очевидно, открывают широкий спектр различных вариантов использования для умных контрактов, которые иначе были бы невозможны. Например, системы, подобные используемой BurstCoin, могут проводить аукционы, которые автоматически проверяют самую высокую цену в заданный срок и автоматически переводят неактивные средства.

Больше новостей о криптовалютах вы найдете в нашем телеграм-канале РБК-Крипто.

TON Hello World part 2: Step by step guide for writing your first smart contract

A smart contract is simply a computer program running on TON Blockchain — or more exactly its TVM (TON Virtual Machine). The contract is made of code (compiled TVM instructions) and data (persistent state) that are stored in some address on TON Blockchain.

In the world of blockchain, code is law, meaning that instead of lawyers and papers, computer instructions define in absolute terms the rules of interaction between the different users of the contract. Before engaging with any smart contract as a user, you’re expected to review its code and thus understand its terms of agreement. Accordingly, we’ll make an effort to make our contract as easy to read as possible, so its users could understand what they’re getting into.

Dapps — decentralized applications

Smart contracts are a key part of decentralized apps — a special type of application invented in the blockchain era, that does not depend on any single entity to run it. Unlike the app Uber, for example, which depends on the company Uber Inc to run it — a decentralized Uber would allow riders and drivers to interact directly (order, pay for and fulfill rides) without any intermediary like Uber Inc. Dapps are also unstoppable — if we don’t depend on anyone specific to run them, nobody can take them down.

Dapps on TON Blockchain are usually made of 2 main projects:

Smart contracts in the FunC programming language that are deployed on-chain — these act as the backend server of the app, with a database for persistent storage.

Web frontend for interacting with the dapp from a web browser — this acts as the frontend or client, normally with special support for Telegram messenger in the form of a Telegram Web App.

Throughout this series of tutorials, we will build a full dapp together and see detailed implementations of both projects.

Step 1: Define our first smart contract

So what are we going to build? Our smart contract will be quite simple:

Its main feature is to hold a counter. The counter will start at some number, and allow users to send increment transactions to the contract, which will in turn increase the counter value by 1. The contract will also have a getter function that will allow any user to query the current value of the counter.

In later tutorials we will make this contract a little more advanced and allow TON coins that are deposited in it to be withdrawn by a special admin role. This admin will also be able to transfer ownership to a different admin and more.

Step 2: Set up your local machine

Before we can start writing code, we need to install certain developer tools on our computer.

For convenience, our development environment will rely on several clever scripts for testing, compiling and deploying our code. The most convenient language for these scripts is JavaScript, executed by an engine called Node.js. The installation instructions are here. We will need a fairly recent version of node like v18. You can verify your nodejs version by running node -v in terminal.

You will also need a decent IDE with FunC and TypeScript support. I recommend Visual Studio Code — it’s free and open source. Also install the FunC Plugin to add syntax highlighting for the FunC language.

Step 3: Set up the project

Let’s open a terminal in the project directory where you want to place your project. When you run the following command, be sure to choose «Counter» as the contract name and select «an empty contract (FunC)» from the list of templates. This will help prevent any issues in the future.

This will create a new project with a preconfigured structure, which includes a workflow for developing, testing, and deploying smart contracts using the Blueprint SDK. The project structure will initially consist of four directories: contracts , wrappers , tests , and scripts . Later on, we will also create a build directory.

And finally, execute the following command to open a folder, allowing us to proceed with the tutorial:

Step 4: Structuring our smart contract

Much like everything else in life, smart contracts in FunC are divided into 3 sections. These sections are: storage, messages and getters.

The storage section deals with our contract’s persistent data. Our contract will have to store data between calls from different users, for example the value of our counter variable. To write this data to state storage, we will need a write/encode function and to read this data back from state storage, we will need a read/decode function.

The messages section deals with messages sent to our contract. The main form of interaction with contracts on TON Blockchain is by sending them messages. We mentioned before that our contract will need to support a variety of actions like increment, deposit, withdraw and transfer ownership. All of these operations are performed by users as transactions. These operations are not read-only because they change something in the contract’s persistent state.

The getters section deals with read-only interactions that don’t change state. For example, we would want to allow users to query the value of our counter, so we can implement a getter for that. We’ve also mentioned that the contract has a special owner, so what about a getter to query that. Since our contract can hold money (TON coins), another useful getter could be to query the current balance.

Step 5: Implement the Counter contract

We’re about to write our first lines in FunC! Our first task would be to implement the counter feature of our contract.

The FunC programming language is very similar to the C language. It has strict types, which is a good idea, since compilation errors will help us spot contract mistakes early on. The language was designed specifically for TON Blockchain, so you will not find a lot of documentation beyond the official FunC docs.

Before the first section, please remember to keep the following line of code at the beginning of the file to import the standard library, as its absence can cause issues later on.

Storage

Let’s start with the first section, storage, and implement two utility functions (which we will use later) for reading and writing variables to the contract’s persistent state — load_data() and save_data() . The primary variable will be the counter value. We must persist this value to storage because we need to remember it between calls. The appropriate type for our counter variable is int . Notice in the docs that the int TVM runtime type is always 257 bit long (256 bit signed) so it can hold huge huge numbers — I’m pretty sure the universe has less than 2^256 atoms in it, so you’ll never have a number so large that you can’t fit in it. Storing the full 257 bits in blockchain storage is somewhat wasteful because the contract pays rent proportionally to the total amount of data it keeps. To optimize costs, let’s keep in persistent storage just the lowest 64 bits — capping our counter’s maximum value at 2^64 which should be enough:

The standard library functions get_data() and set_data() are documented here and load/store the storage cell. We will cover cells in detail in future posts of this series. Cells are read from using the slice type (an array of bits) and written to using the builder type. The various methods that you see are all taken from the standard library. Also notice two interesting function modifiers that appear in the declarations — inline and impure.

Messages

Let’s continue to the next section, messages, and implement the main message handler of our contract — recv_internal() . This is the primary entry point of our contract. It runs whenever a message is sent as a transaction to the contract by another contract or by a user’s wallet contract:

Messages sent between contracts are called internal messages. TON also supports external messages through the handler recv_external() , but as a dapp developer you’re never expected to use them. External messages are used for very specific cases, mainly when implementing wallet contracts, that you would normally never have to write by yourself. You can safely ignore them.

Internal messages received by the contract may be empty. This is what happens for example when somebody sends TON coins to the contract from their wallet. This is useful for funding the contract so it can pay fees. In order to be able to receive those incoming transfers we will have to return successfully when an empty message arrives.

If an incoming message is not empty, the first thing to do is read its operation type. By convention, internal messages are encoded with a 32 bit unsigned int in the beginning that acts as operation type (op for short). We are free to assign any serial numbers we want to our different ops. In this case, we’ve assigned the number 1 to the increment action, which is handled by writing back to persistent state the current value counter plus 1.

Getters

Our last section, as you recall, is getters. Let’s implement a simple getter that will allow users to query the counter value:

We can choose what input arguments the getter takes as input and what output it returns as result. Also notice the function modifier appearing in the declaration — method_id. It is customary to place method_id on all getters.

That’s it. We completed our 3 sections and the first version of our contract is ready. To get the complete code, simply concat the 3 snippets above and replace the existing code in contracts/counter.fc . This will be the FunC ( .fc file extension) source file of our contract. The resulting source file should look like this.

Step 6: Build the counter contract

Right now, the contract is just FunC source code. To get it to run on-chain, we need to convert it to TVM bytecode.

In TON, we don’t compile FunC directly to bytecode, but instead go through another programming language called Fift. Just like FunC, Fift is another language that was designed specifically for TON Blockchain. It’s a low level language that is very close to TVM opcodes. For us regular mortals, Fift is not very useful, so unless you’re planning on some extra advanced things, I believe you can safely ignore it for now.

Since we’re using func-js for building, it would be a good idea to create a directory where we can store the build result. To do this, open the terminal and run the following command:

The func-js package contains everything we need to compile our contract to bytecode. To use it, open terminal in the project directory and run the following:

The build should succeed, with the output of this command being a new file — counter.cell . This is a binary file that finally contains the TVM bytecode in cell format that is ready to be deployed on-chain. This will actually be the only file we need for deployment moving forward (we won’t need the FunC source file).

Step 7: Prepare init data for deploying on-chain

Now that our contract has been compiled to bytecode, we can finally see it in action running on-chain. The act of uploading the bytecode to the blockchain is called deployment. The deployment result would be an address where the contract resides. This address will allow us to communicate with this specific contract instance later on and send it transactions.

There are two variations of TON Blockchain we can deploy to — mainnet and testnet. We covered both in the previous tutorial. Personally, I almost never deploy to testnet. There are far better ways to gain confidence that my code is working as expected. The primary of which is writing a dedicated test suite. We will cover this in detail in one of the next tutorials. For now, let’s assume the code is working perfectly and no further debugging is required.

Init arguments

The new address of our deployed contract in TON depends on only two things — the deployed bytecode (initial code) and the initial contract storage (initial data). You can say that the address is some derivation of the hash of both. If two different developers were to deploy the exact same code with the exact same initialization data, they would collide.

The bytecode part is easy, we have that ready as a cell in the file counter.cell that we compiled in step 6. Now what about the initial contract storage? As you recall, the format of our persistent storage data was decided when we implemented the function save_data() of our contract FunC source. Our storage layout was very simple — just one unsigned int of 64 bit holding the counter value. Therefore, to initialize our contract, we would need to generate a data cell holding some arbitrary initial uint64 value — for example the number 1 .

Interface class

The recommended way to interact with contracts is to create a small TypeScript class that will implement the interaction interface with the contract. We’re using the project structure created by Blueprint, but we’re still working on low-level aspects. Use the following code in wrappers/Counter.ts to create the initial data cell for deployment:

Notice a few interesting things about this TypeScript code. First, it depends on the package ton-core instead of ton, which contains a small subset of base types and is therefore slower to change — an important feature when building a stable interface for our contract. Second, the code that creates the data cell mimics the FunC API and is almost identical to our save_data() FunC function. Third, we can see the derivation of the contract address from the code cell and data cell using the function contractAddress .

The actual deployment involves sending the first message that will cause our contract to be deployed. We can piggyback any message that is directed towards our contract. This can even be the increment message with op #1, but we will do something simpler. We will just send some TON coins to our contract (an empty message) and piggyback that. Let’s make this part of our interface. Add the function sendDeploy() to wrappers/Counter.ts — this function will send the deployment message:

In every deployment we need to send some TON coins to our contract so that its balance is not zero. Contracts need to continually pay rent fees otherwise they risk being deleted. According to the docs, storage fees are about 4 TON per MB per year. Since our contract stores less than 1 KB, a balance of 0.01 TON should be enough for more than 2 years. In any case you can always check this in an explorer and send more TON to the contract if it runs low.

The resulting source file should look like this.

Step 8: Deploy the contract on-chain

Communicating with the live network for the deployment will require an RPC service provider — similar to Infura on Ethereum. These providers run TON Blockchain nodes and allow us to communicate with them over HTTP. TON Access is an awesome service that will provide us with unthrottled API access for free. It’s also decentralized, which is the preferred way to access the network.

Install it by opening terminal in the project directory and running:

The deployment is going to cost gas and should be done through a wallet that will fund it. I’m assuming that you have some familiarity with TON wallets and how they’re derived from 24 word secret mnemonics. If not, be sure to follow the previous tutorial in this series.

As you recall from the previous tutorial, TON wallets can come in multiple versions. The code below relies on «wallet v4 r2», if your wallet is different, either switch Tonkeeper through «Settings» to this version, or modify the code below to use your version. Also remember to use a wallet works with the correct network you’ve chosen — testnet or mainnet.

Replace the current code in scripts/deployCounter.ts with a script that will use the interface class we have just written:

Before running this code, make sure you have enough TON in your wallet for the gas payments and the TON sent to the contract during the deploy.

Another thing to watch out for is collisions between different users of this tutorial. As you recall, if the code and initial data of two contracts are identical, they will have the same address. If all followers of this tutorial would choose initial counter value of 1 — then all of them would collide and only the first would actually deploy the contract. To make sure this doesn’t happen, the code above initializes the counter value to the current number of milliseconds since the epoch (something like 1674253934361). This guarantees that your contract for deployment is unique.

To deploy a contract using our script, run the following command in the terminal and follow the on-screen instructions:

If you have network connectivity issues and get errors like backend nodes unhealthy or timeouts, please join the Telegram support chat for TON access to get assistance. A common mistake is trying to use a wallet contract that isn’t deployed or funded. This can happen if you’re setting the wrong wallet version. As explained in the previous tutorial, check your wallet address in an explorer and if your wallet has a different version from «wallet v4 r2» you will need to modify slightly the code above. Let’s say for example that your version is «wallet v3 r2», then replace WalletContractV4 with WalletContractV3R2 .

The script will print the newly deployed contract address — mine is EQBYLTm4nsvoqJRvs_L-IGNKwWs5RKe19HBK_lFadf19FUfb . You can open your address in an explorer to verify that the deploy went smoothly. This is what it should look like:

Write down your deployed contract address. We’re going to use it in the next step.

Step 9: Call a getter on the deployed contract

There are two ways to interact with a smart contract — calling a getter to read data from it or sending a message that can potentially change its state (write). We should support these interactions in the contract interface class that we created in step 7.

Anyone who wants to access the contract from TypeScript would simply use this interface class. This is excellent for separation of responsibilities within your team. The developer of the contract can provide this class to the developer of the client to abstract away implementation details such as how messages should be encoded in the binary level. Let’s start with the getter.

Interface class

Add the following to wrappers/Counter.ts :

Notice that methods in the interface class that call getters must start with the word get . This prefix is a requirement of the ton TypeScript library. The resulting source file should look like this.

Executing the call

Calling a getter is free and does not cost gas. The reason is that this call is read-only, so it does not require consensus by the validators and is not stored in a block on-chain for all eternity like transaction are.

Let’s create a new script called getCounter.ts in the scripts folder and use our shiny interface class to make the call. We’re going to emulate a different developer interacting with our contract and since the contract is already deployed, they are likely to access it by address. Be sure to replace my deployed contract address with yours in the code below:

As always, run the script using the terminal and follow the instructions displayed on the screen. Make sure to choose «getCounter» from the list of available scripts.

Make a note of the current counter value. After we send the increment message in the next step we would like to confirm that this value indeed increases by 1.

Another interesting thing to remember is that getters are only accessible off-chain, for example from a JavaScript client making a call through an RPC service provider. In particular, this means that contracts cannot call getters on other contracts.

Step 10: Send a transaction to the deployed contract

Unlike getters that are read-only, messages can write and change contract state in storage. In our contract implementation we handled messages in recv_internal() and assigned op #1 = increment. Sending messages costs gas and requires payment in TON coin. The reason is that this operation is not read-only, so it requires waiting for consensus by the validators and is stored as a transaction in a block on-chain for all eternity. We will send less TON coin this time since this action is much cheaper than the deployment.

Interface class

Add the following to wrappers/Counter.ts :

As you recall, the increment message is an internal message that is encoded by convention with a 32 bit unsigned int in the beginning to describe the op and a 64 bit unsigned int after to describe the query id. The query id is relevant for messages that expect a response message to be sent back (the request and the response share the same query id).

Notice that methods in the interface class that send messages must start with the word send , another prefix requirement of the ton library. The resulting source file should look like this.

Executing the send

The messages can be sent from any TON wallet, not necessarily the deployer wallet. Create a new script sendIncrement.ts in the scripts folder and use your wallet to fund the send:

As always, run the script using the terminal and follow the instructions displayed on the screen. Make sure to choose «sendIncrement» from the list of available scripts.

Notice that the message will take a few seconds to be processed by validators and will only change contract state after it has been processed. The normal wait time is a block or two, since validators need to produce a new block that contains our sent transaction. The op that was sent above is #1 = increment, which means that after processing, the counter value will increase by 1. Verify this by re-running the script from step 9 to print the new counter value.

Messages can be sent to our contract by other contracts. This means a different contract can increment our counter. This allows the TON ecosystem to create composable apps and protocols that build on top of each other and interact in unforeseen ways.

Reward

Congratulations on successfully completing this tutorial! Before we conclude, let’s take a quick look at the exciting reward awaiting you from the «TON Masters» collection:

QR-code

Ready to claim your reward? Simply scan the QR code below or click here:

Conclusion

For your convenience, all the code in this tutorial is available in executable form here.

In this tutorial we handled the build and deploy processes manually, mostly so we can understand what happens under the hood. When creating a new contract project, you can have these processes managed automatically by an awesome dev tool called Blueprint. To create a new contract project with Blueprint, run in terminal and follow the on-screen instructions:

If you found a mistake in this tutorial, please submit a PR and help us fix it. This tutorial platform is fully open source and available on https://github.com/ton-community/tutorials.

TON Hello World part 2: Step by step guide for writing your first smart contract

by Tal Kol

A smart contract is simply a computer program running on TON Blockchain — or more exactly its TVM (TON Virtual Machine). The contract is made of code (compiled TVM instructions) and data (persistent state) that are stored in some address on TON Blockchain.

In the world of blockchain, code is law, meaning that instead of lawyers and papers, computer instructions define in absolute terms the rules of interaction between the different users of the contract. Before engaging with any smart contract as a user, you’re expected to review its code and thus understand its terms of agreement. Accordingly, we’ll make an effort to make our contract as easy to read as possible, so its users could understand what they’re getting into.

Dapps — decentralized applications

Smart contracts are a key part of decentralized apps — a special type of application invented in the blockchain era, that does not depend on any single entity to run it. Unlike the app Uber, for example, which depends on the company Uber Inc to run it — a decentralized Uber would allow riders and drivers to interact directly (order, pay for and fulfill rides) without any intermediary like Uber Inc. Dapps are also unstoppable — if we don’t depend on anyone specific to run them, nobody can take them down.

Dapps on TON Blockchain are usually made of 2 main projects:

Smart contracts in the FunC programming language that are deployed on-chain — these act as the backend server of the app, with a database for persistent storage.

Web frontend for interacting with the dapp from a web browser — this acts as the frontend or client, normally with special support for Telegram messenger in the form of a Telegram Web App.

Throughout this series of tutorials, we will build a full dapp together and see detailed implementations of both projects.

Step 1: Define our first smart contract

So what are we going to build? Our smart contract will be quite simple:

Its main feature is to hold a counter. The counter will start at some number, and allow users to send increment transactions to the contract, which will in turn increase the counter value by 1. The contract will also have a getter function that will allow any user to query the current value of the counter.

In later tutorials we will make this contract a little more advanced and allow TON coins that are deposited in it to be withdrawn by a special admin role. This admin will also be able to transfer ownership to a different admin and more.

Step 2: Set up your local machine

Before we can start writing code, we need to install certain developer tools on our computer.

For convenience, our development environment will rely on several clever scripts for testing, compiling and deploying our code. The most convenient language for these scripts is JavaScript, executed by an engine called Node.js. The installation instructions are here. We will need a fairly recent version of node like v18. You can verify your nodejs version by running node -v in terminal.

You will also need a decent IDE with FunC and TypeScript support. I recommend Visual Studio Code — it’s free and open source. Also install the FunC Plugin to add syntax highlighting for the FunC language.

Step 3: Set up the project

Let’s open a terminal in the project directory where you want to place your project. When you run the following command, be sure to choose «Counter» as the contract name and select «an empty contract (FunC)» from the list of templates. This will help prevent any issues in the future.

This will create a new project with a preconfigured structure, which includes a workflow for developing, testing, and deploying smart contracts using the Blueprint SDK. The project structure will initially consist of four directories: contracts , wrappers , tests , and scripts . Later on, we will also create a build directory.

And finally, execute the following command to open a folder, allowing us to proceed with the tutorial:

Step 4: Structuring our smart contract

Much like everything else in life, smart contracts in FunC are divided into 3 sections. These sections are: storage, messages and getters.

The storage section deals with our contract’s persistent data. Our contract will have to store data between calls from different users, for example the value of our counter variable. To write this data to state storage, we will need a write/encode function and to read this data back from state storage, we will need a read/decode function.

The messages section deals with messages sent to our contract. The main form of interaction with contracts on TON Blockchain is by sending them messages. We mentioned before that our contract will need to support a variety of actions like increment, deposit, withdraw and transfer ownership. All of these operations are performed by users as transactions. These operations are not read-only because they change something in the contract’s persistent state.

The getters section deals with read-only interactions that don’t change state. For example, we would want to allow users to query the value of our counter, so we can implement a getter for that. We’ve also mentioned that the contract has a special owner, so what about a getter to query that. Since our contract can hold money (TON coins), another useful getter could be to query the current balance.

Step 5: Implement the Counter contract

We’re about to write our first lines in FunC! Our first task would be to implement the counter feature of our contract.

The FunC programming language is very similar to the C language. It has strict types, which is a good idea, since compilation errors will help us spot contract mistakes early on. The language was designed specifically for TON Blockchain, so you will not find a lot of documentation beyond the official FunC docs.

Before the first section, please remember to keep the following line of code at the beginning of the file to import the standard library, as its absence can cause issues later on.

Storage

Let’s start with the first section, storage, and implement two utility functions (which we will use later) for reading and writing variables to the contract’s persistent state — load_data() and save_data() . The primary variable will be the counter value. We must persist this value to storage because we need to remember it between calls. The appropriate type for our counter variable is int . Notice in the docs that the int TVM runtime type is always 257 bit long (256 bit signed) so it can hold huge huge numbers — I’m pretty sure the universe has less than 2^256 atoms in it, so you’ll never have a number so large that you can’t fit in it. Storing the full 257 bits in blockchain storage is somewhat wasteful because the contract pays rent proportionally to the total amount of data it keeps. To optimize costs, let’s keep in persistent storage just the lowest 64 bits — capping our counter’s maximum value at 2^64 which should be enough:

The standard library functions get_data() and set_data() are documented here and load/store the storage cell. We will cover cells in detail in future posts of this series. Cells are read from using the slice type (an array of bits) and written to using the builder type. The various methods that you see are all taken from the standard library. Also notice two interesting function modifiers that appear in the declarations — inline and impure.

Messages

Let’s continue to the next section, messages, and implement the main message handler of our contract — recv_internal() . This is the primary entry point of our contract. It runs whenever a message is sent as a transaction to the contract by another contract or by a user’s wallet contract:

Messages sent between contracts are called internal messages. TON also supports external messages through the handler recv_external() , but as a dapp developer you’re never expected to use them. External messages are used for very specific cases, mainly when implementing wallet contracts, that you would normally never have to write by yourself. You can safely ignore them.

Internal messages received by the contract may be empty. This is what happens for example when somebody sends TON coins to the contract from their wallet. This is useful for funding the contract so it can pay fees. In order to be able to receive those incoming transfers we will have to return successfully when an empty message arrives.

If an incoming message is not empty, the first thing to do is read its operation type. By convention, internal messages are encoded with a 32 bit unsigned int in the beginning that acts as operation type (op for short). We are free to assign any serial numbers we want to our different ops. In this case, we’ve assigned the number 1 to the increment action, which is handled by writing back to persistent state the current value counter plus 1.

Getters

Our last section, as you recall, is getters. Let’s implement a simple getter that will allow users to query the counter value:

We can choose what input arguments the getter takes as input and what output it returns as result. Also notice the function modifier appearing in the declaration — method_id. It is customary to place method_id on all getters.

That’s it. We completed our 3 sections and the first version of our contract is ready. To get the complete code, simply concat the 3 snippets above and replace the existing code in contracts/counter.fc . This will be the FunC ( .fc file extension) source file of our contract. The resulting source file should look like this.

Step 6: Build the counter contract

Right now, the contract is just FunC source code. To get it to run on-chain, we need to convert it to TVM bytecode.

In TON, we don’t compile FunC directly to bytecode, but instead go through another programming language called Fift. Just like FunC, Fift is another language that was designed specifically for TON Blockchain. It’s a low level language that is very close to TVM opcodes. For us regular mortals, Fift is not very useful, so unless you’re planning on some extra advanced things, I believe you can safely ignore it for now.

Since we’re using func-js for building, it would be a good idea to create a directory where we can store the build result. To do this, open the terminal and run the following command:

The func-js package contains everything we need to compile our contract to bytecode. To use it, open terminal in the project directory and run the following:

The build should succeed, with the output of this command being a new file — counter.cell . This is a binary file that finally contains the TVM bytecode in cell format that is ready to be deployed on-chain. This will actually be the only file we need for deployment moving forward (we won’t need the FunC source file).

Step 7: Prepare init data for deploying on-chain

Now that our contract has been compiled to bytecode, we can finally see it in action running on-chain. The act of uploading the bytecode to the blockchain is called deployment. The deployment result would be an address where the contract resides. This address will allow us to communicate with this specific contract instance later on and send it transactions.

There are two variations of TON Blockchain we can deploy to — mainnet and testnet. We covered both in the previous tutorial. Personally, I almost never deploy to testnet. There are far better ways to gain confidence that my code is working as expected. The primary of which is writing a dedicated test suite. We will cover this in detail in one of the next tutorials. For now, let’s assume the code is working perfectly and no further debugging is required.

Init arguments

The new address of our deployed contract in TON depends on only two things — the deployed bytecode (initial code) and the initial contract storage (initial data). You can say that the address is some derivation of the hash of both. If two different developers were to deploy the exact same code with the exact same initialization data, they would collide.

The bytecode part is easy, we have that ready as a cell in the file counter.cell that we compiled in step 6. Now what about the initial contract storage? As you recall, the format of our persistent storage data was decided when we implemented the function save_data() of our contract FunC source. Our storage layout was very simple — just one unsigned int of 64 bit holding the counter value. Therefore, to initialize our contract, we would need to generate a data cell holding some arbitrary initial uint64 value — for example the number 1 .

Interface class

The recommended way to interact with contracts is to create a small TypeScript class that will implement the interaction interface with the contract. We’re using the project structure created by Blueprint, but we’re still working on low-level aspects. Use the following code in wrappers/Counter.ts to create the initial data cell for deployment:

Notice a few interesting things about this TypeScript code. First, it depends on the package ton-core instead of ton, which contains a small subset of base types and is therefore slower to change — an important feature when building a stable interface for our contract. Second, the code that creates the data cell mimics the FunC API and is almost identical to our save_data() FunC function. Third, we can see the derivation of the contract address from the code cell and data cell using the function contractAddress .

The actual deployment involves sending the first message that will cause our contract to be deployed. We can piggyback any message that is directed towards our contract. This can even be the increment message with op #1, but we will do something simpler. We will just send some TON coins to our contract (an empty message) and piggyback that. Let’s make this part of our interface. Add the function sendDeploy() to wrappers/Counter.ts — this function will send the deployment message:

In every deployment we need to send some TON coins to our contract so that its balance is not zero. Contracts need to continually pay rent fees otherwise they risk being deleted. According to the docs, storage fees are about 4 TON per MB per year. Since our contract stores less than 1 KB, a balance of 0.01 TON should be enough for more than 2 years. In any case you can always check this in an explorer and send more TON to the contract if it runs low.

The resulting source file should look like this.

Step 8: Deploy the contract on-chain

Communicating with the live network for the deployment will require an RPC service provider — similar to Infura on Ethereum. These providers run TON Blockchain nodes and allow us to communicate with them over HTTP. TON Access is an awesome service that will provide us with unthrottled API access for free. It’s also decentralized, which is the preferred way to access the network.

Install it by opening terminal in the project directory and running:

The deployment is going to cost gas and should be done through a wallet that will fund it. I’m assuming that you have some familiarity with TON wallets and how they’re derived from 24 word secret mnemonics. If not, be sure to follow the previous tutorial in this series.

As you recall from the previous tutorial, TON wallets can come in multiple versions. The code below relies on «wallet v4 r2», if your wallet is different, either switch Tonkeeper through «Settings» to this version, or modify the code below to use your version. Also remember to use a wallet works with the correct network you’ve chosen — testnet or mainnet.

Replace the current code in scripts/deployCounter.ts with a script that will use the interface class we have just written:

Before running this code, make sure you have enough TON in your wallet for the gas payments and the TON sent to the contract during the deploy.

Another thing to watch out for is collisions between different users of this tutorial. As you recall, if the code and initial data of two contracts are identical, they will have the same address. If all followers of this tutorial would choose initial counter value of 1 — then all of them would collide and only the first would actually deploy the contract. To make sure this doesn’t happen, the code above initializes the counter value to the current number of milliseconds since the epoch (something like 1674253934361). This guarantees that your contract for deployment is unique.

To deploy a contract using our script, run the following command in the terminal and follow the on-screen instructions:

If you have network connectivity issues and get errors like backend nodes unhealthy or timeouts, please join the Telegram support chat for TON access to get assistance. A common mistake is trying to use a wallet contract that isn’t deployed or funded. This can happen if you’re setting the wrong wallet version. As explained in the previous tutorial, check your wallet address in an explorer and if your wallet has a different version from «wallet v4 r2» you will need to modify slightly the code above. Let’s say for example that your version is «wallet v3 r2», then replace WalletContractV4 with WalletContractV3R2 .

The script will print the newly deployed contract address — mine is EQBYLTm4nsvoqJRvs_L-IGNKwWs5RKe19HBK_lFadf19FUfb . You can open your address in an explorer to verify that the deploy went smoothly. This is what it should look like:

Write down your deployed contract address. We’re going to use it in the next step.

Step 9: Call a getter on the deployed contract

There are two ways to interact with a smart contract — calling a getter to read data from it or sending a message that can potentially change its state (write). We should support these interactions in the contract interface class that we created in step 7.

Anyone who wants to access the contract from TypeScript would simply use this interface class. This is excellent for separation of responsibilities within your team. The developer of the contract can provide this class to the developer of the client to abstract away implementation details such as how messages should be encoded in the binary level. Let’s start with the getter.

Interface class

Add the following to wrappers/Counter.ts :

Notice that methods in the interface class that call getters must start with the word get . This prefix is a requirement of the ton TypeScript library. The resulting source file should look like this.

Executing the call

Calling a getter is free and does not cost gas. The reason is that this call is read-only, so it does not require consensus by the validators and is not stored in a block on-chain for all eternity like transaction are.

Let’s create a new script called getCounter.ts in the scripts folder and use our shiny interface class to make the call. We’re going to emulate a different developer interacting with our contract and since the contract is already deployed, they are likely to access it by address. Be sure to replace my deployed contract address with yours in the code below:

As always, run the script using the terminal and follow the instructions displayed on the screen. Make sure to choose «getCounter» from the list of available scripts.

Make a note of the current counter value. After we send the increment message in the next step we would like to confirm that this value indeed increases by 1.

Another interesting thing to remember is that getters are only accessible off-chain, for example from a JavaScript client making a call through an RPC service provider. In particular, this means that contracts cannot call getters on other contracts.

Step 10: Send a transaction to the deployed contract

Unlike getters that are read-only, messages can write and change contract state in storage. In our contract implementation we handled messages in recv_internal() and assigned op #1 = increment. Sending messages costs gas and requires payment in TON coin. The reason is that this operation is not read-only, so it requires waiting for consensus by the validators and is stored as a transaction in a block on-chain for all eternity. We will send less TON coin this time since this action is much cheaper than the deployment.

Interface class

Add the following to wrappers/Counter.ts :

As you recall, the increment message is an internal message that is encoded by convention with a 32 bit unsigned int in the beginning to describe the op and a 64 bit unsigned int after to describe the query id. The query id is relevant for messages that expect a response message to be sent back (the request and the response share the same query id).

Notice that methods in the interface class that send messages must start with the word send , another prefix requirement of the ton library. The resulting source file should look like this.

Executing the send

The messages can be sent from any TON wallet, not necessarily the deployer wallet. Create a new script sendIncrement.ts in the scripts folder and use your wallet to fund the send:

As always, run the script using the terminal and follow the instructions displayed on the screen. Make sure to choose «sendIncrement» from the list of available scripts.

Notice that the message will take a few seconds to be processed by validators and will only change contract state after it has been processed. The normal wait time is a block or two, since validators need to produce a new block that contains our sent transaction. The op that was sent above is #1 = increment, which means that after processing, the counter value will increase by 1. Verify this by re-running the script from step 9 to print the new counter value.

Messages can be sent to our contract by other contracts. This means a different contract can increment our counter. This allows the TON ecosystem to create composable apps and protocols that build on top of each other and interact in unforeseen ways.

Reward

Congratulations on successfully completing this tutorial! Before we conclude, let’s take a quick look at the exciting reward awaiting you from the «TON Masters» collection:

QR-code

Ready to claim your reward? Simply scan the QR code below or click here:

Conclusion

For your convenience, all the code in this tutorial is available in executable form here.

In this tutorial we handled the build and deploy processes manually, mostly so we can understand what happens under the hood. When creating a new contract project, you can have these processes managed automatically by an awesome dev tool called Blueprint. To create a new contract project with Blueprint, run in terminal and follow the on-screen instructions:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *