Учебное пособие по разработке проекта TON (часть 1): Как создать NFT на цепочке TON с точки зрения исходного кода

Автор оригинала: @Web3 Mario (_mario)

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

Какие различия между разработкой NFT на EVM и разработкой NFT на TON Chain

Для разработчиков DApp выпуск FT или NFT обычно является самой основной потребностью. Поэтому я начинаю с этого в качестве точки входа в изучение. Во-первых, давайте разберем разницу между разработкой NFT на стеке технологий EVM и на TON Chain. NFT, основанный на EVM, обычно выбирает стандарт ERC-721. NFT - это тип неделимого шифрованного актива, каждый из которых обладает уникальностью и имеет некоторые эксклюзивные свойства. ERC-721 представляет собой общую разработочную парадигму для этого типа активов. Давайте посмотрим, какие функции должны быть реализованы в обычном контракте ERC 721 и какая информация должна быть записана. На рисунке ниже представлен интерфейс ERC 721. В отличие от FT, в интерфейсе передачи средств необходимо указать tokenId, который будет передан, а не количество. Этот tokenId является базовым проявлением уникальности NFT-актива. Конечно, для хранения дополнительных атрибутов обычно записывается метаданные для каждого tokenId. Эти метаданные представляют собой внешнюю ссылку, которая содержит другие расширенные данные NFT, такие как ссылка на изображение PFP или некоторые названия атрибутов.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

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

Однако в TON Chain все это становится несколько иным, и это обусловлено двумя основными причинами:

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

Конечно, о других технических различиях было подробно рассказано в предыдущей статье, в этой статье мы надеемся сосредоточиться на разработке смарт-контрактов, поэтому не будем раскрывать эту тему. Вышеупомянутые два принципа дизайна делают разработку смарт-контрактов в TON очень отличной от EVM. В начале изложения мы узнаем, что в контракте NFT нужно определить некоторые отображения, то есть отображения, чтобы сохранить связанные с NFT данные. Самым важным из них является владельцы, это отображение хранит отображение адресов всех владельцев NFT, соответствующих данному tokenID, что определяет владение NFT, а передача является изменением этого владения. Поскольку это теоретически структура данных без границ, ее следует избегать всячески. Поэтому официально рекомендуется использовать наличие структуры данных без границ в качестве критерия для шардинга. То есть, когда есть аналогичная потребность в хранении данных, это следует заменять паттерном главного и подчиненного контрактов, управлять каждым ключом данных путем создания дочернего контракта. И главный контракт управляет глобальными параметрами или помогает обрабатывать внутреннюю информационную взаимодействие между дочерними контрактами.

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

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

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Изучение разработки TON смарт-контрактов из исходного кода

TON выбрал язык программирования Func, который является статическим типом языка, похожим на C, в качестве языка разработки смарт-контрактов. Теперь давайте изучим, как разрабатывать смарт-контракты TON, начиная с исходного кода. Я выбрал пример NFT из официальной документации TON для демонстрации. Если вас интересует, вы можете самостоятельно посмотреть. В этом примере реализован простой пример TON NFT. Давайте посмотрим на структуру контракта, который состоит из двух функциональных контрактов и трех необходимых библиотек.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Эти два основных функциональных контракта были разработаны в соответствии с вышеуказанными принципами, давайте сначала посмотрим на код основного контракта nft-collection:

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Это вводит первый элемент знаний: как сохранять данные в постоянном хранилище в TON смарт-контрактах. Мы знаем, что в Solidity хранение данных в постоянном хранилище автоматически обрабатывается EVM в зависимости от типа параметра, в общем случае переменные состояния смарт-контракта будут автоматически сохранены в постоянном хранилище по итогам выполнения на основе последнего значения, и разработчикам не нужно беспокоиться об этом процессе. Однако в Func это не так, разработчикам приходится самостоятельно реализовывать соответствующую обработку, что немного похоже на процесс GC в C и C++, но в других новых языках программирования эту часть логики обычно автоматизирована. Давайте посмотрим на код, сначала импортируем необходимые библиотеки, затем видим первую функцию load_data для чтения сохраненных данных, ее логика состоит в том, чтобы сначала через get_data вернуть сохраненную ячейку хранения контракта, обратите внимание, что это реализовано в стандартной библиотеке stdlib.fc, и в общем случае некоторые из этих функций можно рассматривать как системные функции для использования.

Тип возвращаемого значения этой функции - является типом cell в TVM. Как мы уже узнали ранее, все постоянные данные в блокчейне TON хранятся в дереве cell. Каждая ячейка может содержать до 1023 битов произвольных данных и до четырех ссылок на другие ячейки. В TVM, основанном на стеке, cell используется в качестве памяти. Внутри ячейки хранятся данные, закодированные в сжатом виде, чтобы получить конкретные данные в открытом тексте, необходимо преобразовать ячейку в тип, называемый slice. Ячейку можно преобразовать в тип slice с помощью функции begin_parse, а затем можно получить данные ячейки, загружая биты данных и ссылки на другие ячейки из slice. Обратите внимание, что вызов этого вида на 15-й строке - это синтаксический сахар для вызова второй функции, возвращаемой первой функцией. И в конце загружаются соответствующие данные в порядке их постоянства. Обратите внимание, что этот процесс отличается от Solidity и не основан на вызовах hashmap, поэтому порядок вызовов должен быть правильным.

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

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Далее давайте посмотрим на связанные с бизнесом функции, сначала нужно ввести одну концепцию, а именно, как создать новый смарт-контракт через другой смарт-контракт, что часто используется в только что описанной мастер-системе. Мы знаем, что в TON вызовы между смарт-контрактами реализуются путем отправки внутренних сообщений. Это достигается через функцию с именем send_raw_message, где первый параметр - это закодированная ячейка сообщения, а второй параметр - флаг, используемый для указания различных способов выполнения транзакции, установленных в TON. В настоящее время существует 3 режима сообщений и 3 флага сообщений в TON. Можно комбинировать отдельный режим с несколькими (возможно, ни одним) флагами, чтобы получить нужный режим. Комбинация означает просто сложение их значений и использование суммы.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Итак, давайте посмотрим на первую основную функцию, deploy_nft_item, как следует из названия, это функция для создания или, скажем, минтирования нового экземпляра NFT, после кодирования сообщения через msg, отправляем внутренний контракт через send_raw_message, выбрав флаг 1 в качестве бита отправки, и используем только указанную в кодировке комиссию в качестве газовой платы за этот вызов. Из предыдущего объяснения мы легко понимаем, что эти правила кодирования должны соответствовать способу создания нового смарт-контракта. Давайте посмотрим, как это конкретно реализуется.

Давайте посмотрим на строку 51 прямо сейчас. Два вышеуказанных метода - это вспомогательные методы для генерации информации, необходимой для создания сообщения. Поэтому давайте посмотрим на них позже. Это процесс кодирования внутреннего сообщения для создания смарт-контракта. Некоторые из цифр посередине фактически являются флагами, которые указывают на требования этого внутреннего сообщения. Здесь введем следующую тему. TON выбрал двоичный язык с названием TL-B для описания способа выполнения сообщения и реализации некоторых специфических функций внутреннего сообщения путем установки различных флагов. Самые очевидные два сценария использования - создание нового контракта и вызов функций уже развернутого контракта. А такой подход, как на 51 строке, соответствует первому сценарию, создание нового контракта nft item, и это основано на строках 55, 56, 57. Во-первых, длинная последовательность цифр на строке 55 представляет собой ряд флагов, обратите внимание, что первый аргумент store_uint - это число, а второй - длина в битах, где определяются последние три флага этого внутреннего сообщения, и соответствующее двоичное значение равно 111 (в десятичной системе это 4 + 2 + 1), где первые два указывают, что это сообщение будет сопровождаться данными StateInit, которые являются исходным кодом нового контракта, а также данными для инициализации. Последний флаг указывает на наличие вложенного сообщения, то есть на выполнение связанной логики и необходимых параметров. Поэтому вы увидите, что в строке 66 код не устанавливает эти три бита, что означает вызов функции уже развернутого контракта. Правила кодирования можно найти здесь.

Тогда правила кодирования StateInit соответствуют 49-й строке кода, вычисляются с помощью calculate_nft_item_state_init. Обратите внимание, что кодирование данных stateinit также следует определенным правилам кодирования TL-B. помимо некоторых флагов, главным образом затрагиваются две части: новый код контракта и инициализирующие данные. Порядок кодирования данных должен быть согласован с порядком хранения постоянной ячейки, указанной в новом контракте. На 36-й строке видно, что инициализационные данные содержат item_index, то есть что-то похожее на tokenId в ERC 721, а также текущий адрес контракта, который возвращается стандартной функцией my_address, то есть collection_address. Порядок этих данных соответствует объявлению в nft-item.

Далее один из ключевых моментов заключается в том, что в TON все негенерированные смарт-контракты можно заранее вычислить их адрес после генерации, что аналогично функции create 2 в Solidity. Генерация нового адреса в TON состоит из двух частей: workchain и конкатенация значения stateinit hash, причем первый, как мы уже знаем из предыдущего раздела, должен быть указан для соответствия бесконечной архитектуре TON и в настоящее время имеет единое значение, получаемое из стандартной функции workchain. Второе значение получается из стандартной функции cell_hash. Таким образом, возвращаясь к данному примеру, функция calculate_nft_item_address представляет собой предварительное вычисление адреса нового контракта. Значение, сгенерированное на 53-й строке, кодируется в сообщении в качестве адреса получателя этого внутреннего сообщения. А nft_content соответствует вызову инициализации созданного контракта, конкретная реализация которого будет представлена в следующей статье.

Что касается send_royalty_params, оно должно быть ответом на внутреннее сообщение по запросу только для чтения. В предыдущем объяснении мы особо подчеркнули, что в TON внутренние сообщения не только содержат операции, которые могут изменять данные, но и операции только для чтения также должны быть реализованы таким образом. Поэтому этот контракт представляет собой такую операцию. Важно отметить, что на строке 67 устанавливается метка для обратного вызова функции обратного вызова для запроса. Запишите это, это будут возвращенные данные, а именно индекс запрашиваемого элемента и соответствующие данные о роялти.

Далее давайте введем следующую концепцию: в TON умные контракты имеют только два универсальных входа, названных recv_internal и recv_external. Первый - это универсальная точка вызова для всех внутренних сообщений, а второй - для всех внешних сообщений. Разработчикам необходимо внутри функции отвечать на различные запросы, используя аналогичный переключатель switch в зависимости от различных флагов, указанных в сообщении, который является маркером обратного вызова на 67 строке. Вернемся к этому примеру. Сначала проверим наличие пустых мест в сообщении, и, если все в порядке, разберем информацию в сообщении. Сначала на 83 строке разберем sender_address, который будет использоваться для проверки прав доступа в последующем. Обратите внимание на оператор ~, который является синтаксическим сахаром. Давайте пока не будем углубляться в это. Затем разберем маркер операции op и, в зависимости от разных флагов, обработаем соответствующие запросы. Один из них - ответ на запрос royalty-параметра или создание нового NFT и увеличение глобального индекса.

Следующая точка знания соответствует 108 строкам, и, вероятно, вы можете понять логику обработки этой функции по ее имени. Аналогично функции require в Solidity, в Func используется стандартная функция throw_unless для генерации исключений. Первый параметр-это код ошибки, второй-логическое значение проверки бита. Если бит равен false, генерируется исключение с этим кодом ошибки. В этой строке используется equal_slices, чтобы проверить, равен ли sender_address, разобранный выше, owner_address, хранимый в этом контракте, для проверки разрешений.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

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

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Разработка DApp в экосистеме TON - это действительно интересное занятие, которое сильно отличается от парадигмы разработки EVM. Поэтому я собираюсь рассказать вам, как разрабатывать DApp на TON Chain, через ряд статей. Давайте вместе учиться и использовать эту возможность. Также приветствую общение с вами на Twitter, чтобы обмениваться новыми интересными идеями для dapp и разрабатывать их вместе.

TON2,93%
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • комментарий
  • Репост
  • Поделиться
комментарий
0/400
Нет комментариев
  • Закрепить