Навчальний посібник з розробки проекту TON (Частина 1): Як створити NFT на платформі TON Chain з погляду вихідного коду

Автор: @Web3Mario (_mario)

Резюме: Продовжуючи попередню статтю про впровадження технології TON, в останній час я глибше вивчав офіційну документацію TON і відчував деяку складність у процесі навчання. Наразі, вміст документації, здається, більше схожий на внутрішню документацію для розробників, що може бути не дуже зручним для новачків у розробці. Тому, на основі свого власного навчального досвіду, я постараюся підготувати серію статей щодо розробки проекту TON Chain, сподіваюся, що це допоможе всім швидко засвоїти навички розробки TON DApp. Я також вітаю будь-які виправлення у випадку помилок у моєму тексті. Розпочнемо навчання разом.

Які різниці між розробкою NFT в EVM та розробкою NFT на TON Chain

Випуск FT або NFT зазвичай є найбільш базовою потребою для розробників DApp. Тому я використовую це як вихідну точку для вивчення. Спочатку давайте розберемося в різниці між розробкою NFT в технологічному стеку EVM та на ланцюгу TON. Зазвичай NFT, заснований на EVM, буде вибирати стандарт ERC-721 для успадкування. Так званий NFT вказує на тип незмінного шифрованого активу, причому кожен актив має унікальність, тобто має певні власні властивості. І ERC-721 є загальним парадигмою для цього типу активу. Давайте подивимося, які функції потрібно реалізувати в звичайному контракті ERC721 та яку інформацію потрібно записати. На цьому малюнку показаний інтерфейс ERC721. Можна побачити, що, на відміну від FT, у інтерфейсі передачі потрібно ввести ідентифікатор tokenId, а не кількість. Цей tokenId також є найбільш базовим вираженням унікальності NFT-активу, і, звичайно, для підтримки більшої кількості властивостей, зазвичай для кожного tokenId записується метадані, які є зовнішнім посиланням і містять інші розширені дані про цей NFT, такі як посилання на зображення PFP, певні назви властивостей тощо.

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

Для розробників, які знайомі з Solidity або об’єктно-орієнтованою розробкою, реалізація такого смарт-контракту - це легка справа, лише потрібно визначити необхідні типи даних у контракті, такі як деякі ключові відображення mapping, та розробити відповідну логіку зміни цих даних з урахуванням потрібних функцій, щоб створити NFT.

Проте в TON Chain все це стає трохи іншим, і це має дві різні основні причини:

  • У TON зберігання даних здійснюється на основі Cell, а Cell одного облікового запису реалізується за допомогою спрямованого ациклічного графа. Це призводить до того, що дані, які потрібно постійно зростати безмежно, не можуть, оскільки для спрямованого ациклічного графа глибина даних визначає вартість запиту, і після нескінченного розширення глибини може виникнути занадто велика вартість запиту, що може призвести до проблеми блокування контракту.
  • Для досягнення високої продуктивності з високою паралелізацією, TON відмовився від послідовної архітектури виконання і перейшов до розробки з використанням моделі акторів, яка спеціально створена для паралельного виконання. Це призводить до того, що між розумними контрактами можна взаємодіяти тільки за допомогою асинхронних викликів так званих внутрішніх повідомлень. Зверніть увагу, що потрібно дотримуватися цього принципу як для типів зміни стану, так і для типів, які тільки читають. Крім цього, потрібно ретельно розглянути, як вирішити проблему відкату даних, якщо асинхронний виклик не вдасться.

Звичайно, щодо інших технічних відмінностей було докладно розглянуто в минулій статті, тому ця стаття спрямована на розробку розумного контракту і не розглядається детально. Ці дві принципи проектування роблять розробку розумних контрактів у TON значно відмінною від EVM. У вступі ми знаємо, що у контракті NFT потрібно визначити деякі відображення, щоб зберігати дані, пов’язані з NFT. Найважливішим з них є власники, це відображення зберігає відображення адрес власників для певного tokenID, що визначає власність NFT, а переказ - це зміна цієї власності. Оскільки в теорії це може бути структура даних без меж, потрібно його уникати. Тому офіційно рекомендується використовувати наявність структури даних без меж як критерій розділення. Тобто, коли є потреба у зберіганні подібних даних, то замість цього використовується парадигма головних та підлеглих контрактів, при цьому кожен ключ управляється шляхом створення дочірнього контракту. І головний контракт керує глобальними параметрами або допомагає у взаємодії внутрішньо між дочірніми контрактами.

Це також означає, що в TON NFT також потрібно використовувати подібну архітектуру для проектування, кожен NFT є окремим дочірнім контрактом, який зберігає такі власні дані, як адреса власника, метадані та інші ексклюзивні дані, і керує загальними даними за допомогою головного контракту, наприклад, назва NFT, символ, загальний обсяг постачання тощо.

Після уточнення архітектури наступним кроком буде вирішення потреб у основних функціях. Оскільки використовується підхід головний-підлеглий договір, потрібно чітко визначити, які функції виконує головний договір, а які - підлеглий, і через яку внутрішню інформацію вони спілкуються один з одним, а також які дії виконувати у випадку помилки виконання, наприклад, як відкотити попередні дані. Зазвичай перед розробкою складних великих проектів потрібно скласти діаграму класів та чітко визначити потік інформації між ними, а також уважно обдумати логіку відкочування після невдачі внутрішнього виклику. Звісно, хоча розробка NFT, згаданого вище, є простою, але можна провести схожу перевірку.

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

Навчитися розробляти розумний контракт TON з вихідного коду

TON обрав розробку мови програмування для розумних контрактів, яка є схожою на мову С, зі статичним типом даних, яку назвали Func. Тепер давайте вивчимо розробку розумних контрактів 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, зазвичай деякі з її функцій можна вважати системними функціями, які можна використовувати.

Повернутий тип цієї функції - це комірка, це тип комірки в TVM. Ви вже знаєте з попереднього вступу, що всі стійкі дані в ланцюжку блоків TON зберігаються в дереві комірок. Кожна комірка може містити до 1023 бітів довільних даних та до чотирьох посилань на інші комірки. Комірка використовується як пам’ять в TVM на основі стеку. Комірка містить закодовані дані, і для отримання конкретних даних потрібно перетворити комірку в тип, який називається срез. Комірку можна перетворити у срез за допомогою функції begin_parse, а потім завантажити дані біта та посилання на інші комірки зі срезу, щоб отримати дані з комірки. Зверніть увагу, що виклик на 15 рядку є синтаксичним цукром у функції, що дозволяє безпосередньо викликати другу функцію, яка є результатом першої функції. Тоді відбувається послідовне завантаження відповідних даних згідно з порядком персистентності. Зверніть увагу, що цей процес відрізняється від Solidity і не ґрунтується на виклику хеш-карти, тому цей порядок виклику не можна порушувати.

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

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

Наступним кроком буде розгляд функцій, пов’язаних з бізнесом, спочатку потрібно ввести один пункт, як створити новий контракт через контракт, що буде часто використовуватися в раніше введеній майстерній структурі. Ми знаємо, що в TON виклик між смарт-контрактами здійснюється шляхом внутрішнього повідомлення. Це здійснюється за допомогою send_raw_message, зауважте, що перший параметр - це кодована повідомлення клітинка, другий параметр - це прапорець, який вказує на відмінність виконання транзакції, в TON встановлені різні способи відправлення внутрішніх повідомлень, наразі існує 3 режими повідомлень та 3 прапорці. Можна поєднати один режим з декількома (можливо, ні) прапорцями, щоб отримати потрібний режим. Комбінація просто означає додавання їхніх значень та заповнення отриманою сумою. Нижче наведено таблицю з описом Режимів та Прапорців:

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

Отже, давайте розглянемо першу основну функцію, deploy_nft_item, як ім’я вказує, це функція для створення або, можна сказати, мінтингу нового екземпляру NFT, після кодування msg і відправлення внутрішнього контракту за допомогою send_raw_message та вибору позначки відправки зі значенням 1, тільки з указаної комісії у кодуванні як gas fee. Після попереднього опису нам дуже легко зрозуміти, що це правило кодування повинно відповідати способу створення нового смартконтракту. Тож давайте подивимось, як саме це здійснюється.

Давайте просто подивимось на 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 у ERC721, а також поточну адресу контракту, повернену стандартною функцією my_address, тобто collection_address, порядок цих даних відповідає оголошенню в nft-item.

Наступним етапом є можливість попереднього обчислення адреси всіх незгенерованих розумних контрактів у TON, що подібно до функції create2 в Solidity. У TON генерація нової адреси складається з двох частин: об’єднанням значення workchain та хешу stateinit. Перше вже було описано в попередньому вступі і воно потрібне для підтримки безкінечної архітектури розподілення 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 рядку, ймовірно, кожен з вас може зрозуміти логіку обробки цієї функції через назву. Функція Func схожа на функцію require в Solidity, Func використовує стандартну функцію throw_unless для викидання виключень. Перший параметр - це код помилки, другий - це булеве значення перевірки біта. Якщо біт дорівнює false, викидається виключення з вказаним кодом помилки. У цьому рядку за допомогою equal_slices перевіряється, чи дорівнює адреса відправника, яка була розпарсена раніше, адресі owner_address, яка зберігається в цьому контракті, для перевірки прав доступу.

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

На завершення, для того щоб зробити структуру коду більш зрозумілою, починаємо створювати низку допоміжних функцій для отримання персистентної інформації. Тут не будемо розглядати це детально, розробники можуть використовувати цю структуру для розробки своїх власних розумних контрактів.

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

TON екосистема розробки DApp насправді цікава справа, вона суттєво відрізняється від парадигми розробки EVM, тому я буду представляти, як розробляти DApp в мережі TON, через серію статей. Разом з вами вивчати цю можливість. Також запрошую всіх вас спілкуватися зі мною в Twitter, обмінюватися новими цікавими ідеями для dapp і розробляти їх разом.

TON-0,31%
Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • Прокоментувати
  • Репост
  • Поділіться
Прокоментувати
Додати коментар
Додати коментар
Немає коментарів
  • Закріпити