В этом уроке мы напишем ваш первый смарт-контракт в тестовой сети The Open Network на языке FUNC, задеплоим его в тестовую сеть с помощью toncli, а также протестируем его с помощью сообщения на языке Fift. Для прохождения данного урока вам необходимо установить интерфейс для командной строки toncli Смарт-контракт, который мы будем делать, должен обладать следующей функциональностью: хранить в своих данных целое число total - 64-битное число без знака; при получении внутреннего входящего сообщения контракт, должен взять 32-битное целое число без знака из тела сообщения, добавить его к total и сохранить в данных контракта; В смарт-контракте должен быть предусмотрен метод get total позволяющий вернуть значение total Если тело входящего сообщения меньше 32 бит, то контракт должен выдать исключение В консоли выполните следующие команды: Toncli создал простой проект кошелька, в нем вы можете увидеть 4 папки: build; func; fift; test; На данном этапе нас интересуют папки func и fift, в которых мы будем писать код на FunС и Fift соответственно. Высокоуровневый язык FunC используется для программирования смарт-контрактов на TON. Программы FunC компилируются в Fift ассемблерный код, который генерирует соответствующий байт-код для виртуальной машины TON(TVM). Далее этот байт-код (на самом деле дерево ячеек, как и любые другие данные в TON Blockchain) может быть использован для создания смарт-контракта в блокчейне или может быть запущен на локальном экземпляре TVM(Ton Virtual Machine). Подробнее с FunC можно ознакомиться здесь Зайдите в папку func: И откройте файл code.func, на своем экране вы уведите смарт-контракт кошелька, удалите весь код и мы готовы начать писать наш первый смарт контракт. У смарт-контрактов в сети TON есть два внешних метода к которым можно обращаться. Первый, recv_external() эта функция выполняется когда запрос к контракту происходит из внешнего мира, то есть не из TON, например когда мы сами формируем сообщение и отправляем его через lite-client. Второй, recv_internal() эта функция выполняется когда внутри самого TON, например когда какой-либо контракт обращается к нашему. Под наши условия подходит recv_internal() В файле code.func пропишем: ;;  две точки с запятой синтаксис однострочного комментария Мы передаем в функцию слайс in_msg_body и используем ключевое слово impure impure — ключевое слово, которое указывает на то, что функция изменяет данные смарт-контракта. А вот чтобы понять, что такое слайс, поговорим про типы в смарт-контрактах сети TON В нашем просто смарт контракте мы будем использовать всего лишь четыре типа: Cell(ячейка) - Ячейка TVM, состоящая из 1023 бит данных и до 4 ссылки на другие ячейки Slice - Частичное представление ячейки TVM, используемой для разбора данных из ячейки Builder - Частично построенная ячейка, содержащая до 1023 бит данных и до четырех ссылок; может использоваться для создания новых ячеек Integrer - знаковое 257-разрядное целое число Подробнее о типах в FunC: кратко здесь развернуто здесь в разделе 2.1 Чтобы преобразовать полученный слайс в Integer  добавим следующий код: int n = in_msg_body~load_uint(32); Функция recv_internal() теперь выглядит так: load_uint функция из стандартной библиотеки FunC  она загружает целое число n-бит без знака из слайса. Чтобы добавить полученную переменную к total и сохранить значение в смарт-контракте, рассмотрим как реализован функционал хранения постоянных данных/хранилища в TON. Примечание: не путайте с TON Storage, хранилище в предыдущем предложении удобная аналогия. Виртуальная машина TVM является стековой, соответственно хорошей практикой хранения данных в контракте будет использовать определенный регистр, а не хранить данный "сверху" стека. Для хранения постоянных данных отведен регистр с4, тип данных Ячейка. Подробнее с регистрами можно ознакомиться с4  в пункте 1.3 Для того чтобы "достать" данные из с4 нам понадобятся две функции из стандартной библиотеки FunC  . А именно: get_data   - берет ячейку из c4 регистра. begin_parse -   ячейку преобразует в slice Передадим это значение в слайс ds slice ds = get_data().begin_parse(); А также преобразуем этот слайс в Integer 64-бит для суммирования в соответствии с нашей задачей. (С помощью уже знакомой нам функции load_uint) int total = ds~load_uint(64); Теперь наша функция будет выглядеть так: Для суммирования будем использовать бинарную операцию суммирования +  и присвоение = Для того чтобы сохранить постоянное значение, нам необходимо выполнить три действия: создать Builder для будущей ячейки записать в нее значение из Builder создать Cell (ячейку) Записать получившуюся ячейку в регистр Делать это мы будем опять же с помощью функций стандартной библиотеки FunC  set_data(begin_cell().store_uint(total, 64).end_cell()); begin_cell() - создаст Builder для будущей ячейки store_uint()- запишет значение total end_cell()- создать Cell (ячейку) set_data() - запишет ячейку в регистр с4 Итог: Все что осталось сделать в нашей internal функции это добавить вызов исключения, если получена переменная не 32-битная. Для этого будем использовать встроенные исключения. Исключения могут быть вызваны условными примитивами throw_if и throw_unless и безусловным throw . Воспользуемся throw_if и передадим любой код ошибки. Для того, чтобы взять битность используем slice_bits(). throw_if(35,in_msg_body.slice_bits() < 32); Вставим в начало функции: Любая функция в FunC  соответствует следующему паттерну: [<forall declarator>] <return_type><function_name(<comma_separated_function_args>) <specifiers> Напишем функцию get_total() возвращающую Integer и имеющую спецификацию method_id (об этом чуть позже) Спецификация method_id позволяет вызывать GET функцию по названию из lite-client or ton-explorer. Грубо говоря все фукнции в том имеют численный идентификатор, get методы нумеруются по crc16 хэшам их названий. Для того, что функция возвращала total хранящееся в контракте, нам надо взять данные из регистра, что мы уже делали: Для деплоя в тестовую сеть будем использовать интерфейса для командной строки toncli toncli deploy -n testnet Необходимо получить их с тестового крана, бот для этого @testgiver_ton_bot Адрес кошелька вы можете увидеть прямо в консоли, после команды деплой, toncli отобразит его справа от строки INFO: Found existing deploy-wallet Чтобы проверить пришли ли TON на ваш кошелек в тестовой сети, можете использовать вот этот explorer: https://testnet.tonscan.org/ Важно: Речь идет только о тестовой сети Для вызова recv_internal() необходимо послать сообщение внутри сети TON. С помощью toncli send Напишем небольшой скрипт на Fift, который будет отправлять 32-битное сообщение в наш контракт. Для этого создадим в папке fift файл try.fif и напишем в нем следующий код: "Asm.fif" include - необходим для компиляции сообщения в байт код Теперь рассмотрим сообщение: <b b> - создают Builder ячейки, подробнее в пункте 5.2 5 32 u - кладем 32-битное unsigned integer 11  // number - однострочный комментарий В командной строке: toncli send -n testnet -a 0.03 --address "адрес вашего контракта" --body ./fift/try.fif Теперь протестируем GET функцию:  toncli get get_total Должно получиться следующее: Код и остальные уроки можно найти  тут.