TONプロジェクト開発チュートリアル(1):ソースコードの観点からTON Chain上でNFTを作成する方法

著者: @Web3Mario (_mario)

概要:前回のTON技術紹介に引き続き、最近TON公式の開発ドキュメントを詳しく研究しましたが、学習にはいくつかの障壁があるように感じました。現在のドキュメントの内容は内部向けの開発ドキュメントのようで、初心者の開発者にはあまり親しみやすくありません。そこで、自分自身の学習経験に基づいて、TON Chainプロジェクトの開発に関する一連の記事を整理し、TON DApps開発の初心者が素早く入門できるようにすることを試みました。誤りがあれば指摘していただければ幸いです。一緒に学びましょう。

EVMでNFTを開発するとTON ChainでNFTを開発するとはどう違いますか

DApp開発者にとって、FTまたはNFTの発行は通常、最も基本的な要件です。したがって、私もそれを学習の入り口としています。まず、EVM技術スタックでのNFTの開発とTON Chainでの開発の違いを理解しましょう。EVMベースのNFTは、通常、ERC-721の標準を継承することを選択します。NFTとは、分割できない暗号化資産のタイプであり、各資産がユニークであり、特定の特性を持つことを指します。ERC-721は、このタイプの資産に対する一般的な開発パターンです。一般的なERC721コントラクトが実装する必要がある関数と記録する情報を見てみましょう。以下はERC721インターフェースの例です。FTとは異なり、トランスファーインターフェースでは、転送されるtokenIdを入力する必要があります。このtokenIdは、NFTアセットのユニーク性を基本的に表しており、より多くの属性を持つために、通常、各tokenIdにはメタデータが記録されます。このメタデータは外部リンクであり、NFTの他の拡張可能なデータ(例:PFP画像のリンク、特定の属性名など)が保存されています。

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

Solidityやオブジェクト指向の開発に精通している場合、このようなスマートコントラクトを実装することは簡単です。必要なデータ型、例えばいくつかの重要なマッピング関係を定義し、これらのデータを変更するための対応するロジックを開発するだけで、NFTを実現できます。

しかし、TON Chainでは、すべてが少し異なり、その主な原因は2つあります:

  • TONでは、データの格納はCellに基づいて行われ、同じアカウントのCellは有向非巡回グラフを使用して実現されます。そのため、無限に成長する必要がある永続的なデータは制約されます。有向非巡回グラフでは、データの深さがクエリのコストを決定するため、深さが無限に広がるとクエリのコストが高すぎる可能性があり、契約がデッドロックに陥る問題が発生する可能性があります。
  • 高い並行性能を追求するために、TONは直列実行のアーキテクチャを捨て、並行用に専用された開発パラダイムであるアクターモデルを採用して実行環境を再構築しました。これにより、スマートコントラクト間の通信は内部メッセージの送信による非同期呼び出しのみとなります。状態変更や読み取り専用の呼び出しに関してもこの原則に従う必要があります。また、非同期呼び出しが失敗した場合のデータのロールバック処理についても注意深く検討する必要があります。

当然、技術的他の違いについては、前の記事で詳しく説明されているので、この記事ではスマートコントラクトの開発に焦点を当てたいと思いますので、議論は展開しません。上記の2つの設計原則により、TONのスマートコントラクトの開発とEVMとは大きく異なります。始めの議論では、NFTコントラクトではいくつかのマッピング関係、つまりマッピングを定義する必要があります。これには、所有者を管理するownersなどが含まれます。このマッピングは、あるtokenIDに対応するNFTの所有者のアドレスの関係を保存し、NFTの所有権を決定し、転送はこの所有権の変更です。理論上、これは無限のデータ構造になりますので、できるだけ避ける必要があります。したがって、無限のデータ構造の存在を分片の基準として推奨しています。つまり、同様のデータ保存要件がある場合、マスター・スレーブ・コントラクトの范式を使用して、各キーに対応するデータを管理するためにサブ・コントラクトを作成し、グローバルなパラメータをマスター・コントラクトで管理したり、サブ・コントラクト間の内部情報のやり取りをサポートしたりします。

これはTON内のNFTにも同様のアーキテクチャが必要であり、各NFTは独立したサブコントラクトであり、所有者のアドレス、メタデータなどの専用データが保存され、NFT名、シンボル、総供給量などのグローバルデータを管理するメインコントラクトを介しています。

明確なアーキテクチャが確立された後、次は中核機能の要件を解決する必要があります。このマスター/スレーブ契約方式を採用しているため、どの機能がマスターコントラクトによって提供され、どの機能がサブコントラクトによって提供されるかを明確にする必要があります。また、それらの間でどのように内部情報が伝達されるかを明確にし、さらに、実行エラーが発生した場合には、以前のデータをロールバックする方法も考える必要があります。通常、複雑な大規模プロジェクトを開発する前に、クラス図を通じて情報フローを明確にし、内部呼び出しの失敗後のロールバックロジックを慎重に考えることが必要です。もちろん、上記のNFTの開発は簡単ですが、同様の検証を行うことも可能です。

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

TONスマートコントラクトの開発をソースコードから学ぶ

TONは、スマートコントラクトの開発言語として、C言語に似た静的型付けの言語であるFuncを選択しました。次に、ソースコードからTONスマートコントラクトの開発方法を学びましょう。私はTON公式ドキュメントのNFTの例を選択し、紹介します。興味のある方は、自分で調べてみてください。この例では、TON NFTの簡単な実装を行っています。まず、コントラクトの構造を見てみましょう。2つの機能コントラクトと3つの必要なライブラリに分かれています。

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

これらの2つの主な機能契約は、上記の原則に従って設計されており、まず最初にnft-collectionメイン契約のコードを見てみましょう:

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

これは最初の知識ポイントを導入します、TONスマートコントラクトでデータを永続化する方法です。私たちは、Solidityではデータの永続化がEVMによって自動的に処理されることを知っています。通常、スマートコントラクトの状態変数は、実行後に最新の値に基づいて自動的に永続化されます。開発者はこのプロセスを考慮する必要はありません。しかし、Funcではこのようなことはありません。開発者は対応する処理ロジックを自分で実装する必要があります。この状況は、CやC++でGCのプロセスを考慮する必要がある点に似ていますが、他の新しい開発言語では通常、この部分のロジックを自動化して処理します。コードを見てみましょう。まず、必要なライブラリをインポートし、最初の関数load_dataがデータを永続化するために使用されます。そのロジックは、まずget_dataを使用して永続化されたコントラクトストレージセルを取得するというものです。これは標準ライブラリstdlib.fcによって実装されており、一部の関数はシステム関数として使用できます。

この関数の戻り値の型はcellであり、これはTVMのcell型です。以前の紹介で、TONブロックチェーンのすべての永続的データがcellツリーに格納されていることを知りました。各セルには最大で1023ビットの任意のデータと最大で4つの他のセルへの参照があります。セルはスタックベースのTVMでメモリとして使用されます。セルには圧縮されたデータが保存されており、具体的な平文データを取得するには、セルをsliceと呼ばれる型に変換する必要があります。セルはbegin_parse関数を使用してslice型に変換でき、その後、スライスからデータビットと他のセルへの参照をロードしてセル内のデータにアクセスできます。注意すべきは、15行目のコードのこのような呼び出し方法は、func内でのシンタックスシュガーであり、最初の関数の戻り値の2番目の関数を直接呼び出すことができる点です。そして、最後にデータの永続化順序に従って対応するデータを順番にロードします。注意すべきは、このプロセスはSolidityとは異なり、ハッシュマップの呼び出しに基づいていないため、この呼び出しの順序はランダムにできません。

save_data関数では、ロジックは似ていますが、これは逆のプロセスです。これには次の知識点が導入されます。新しいタイプのbuilder、これはcell builderのタイプです。データビットと他のcellへの参照はbuilderに格納され、そしてbuilderは最終的に新しいcellにfinalizedできます。まず、標準関数begin_cellを使用してbuilderを作成し、次に関連する関数を順番に使用して関連する関数を保存します。前述の呼び出し順序とこの保存順序が一致する必要があることに注意してください。最後に、end_cellを使用して新しいcellの構築を完了し、この時点でそのcellはメモリ内で管理されます。最後に、最外層のset_dataを使用してそのcellを永続的に保存できます。

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

次に、ビジネス関連の関数を見てみましょう。まず、新しい契約を契約する方法について説明する必要があります。これは、先ほど紹介したマスターとスレーブのアーキテクチャで頻繁に使用されます。 TONでは、スマートコントラクト間の呼び出しは、内部メッセージを送信することで実現されます。これはsend_raw_messageという名前のメソッドを使用して行われます。最初のパラメータはメッセージのエンコード後のセルであり、2番目のパラメータはトランザクションの実行方法を示すフラグです。 TONでは、異なる内部メッセージの送信方法が設定されており、現在、3つのメッセージモードと3つのメッセージフラグがあります。単一のモードと複数の(おそらくない)フラグを組み合わせて、必要なモードを取得できます。組み合わせは、それらの値を合計して埋め込むことを意味します。以下に、モードとフラグの説明表が示されています:

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

それでは、最初の主要な関数であるdeploy_nft_itemを見てみましょう。その名の通り、これは新しいNFTインスタンスを作成または鋳造するための関数です。一連の操作とメッセージのエンコードを経て、内部契約をsend_raw_messageで送信し、フラグ1を選択し、指定された手数料のみをガス手数料として実行します。前述の説明から、このエンコーディング規則は新しいスマートコントラクトを作成するためのものであることが容易に理解できます。それでは、具体的な実装方法を見てみましょう。

51行目を直接見てみましょう。上記の2つの関数は、メッセージを生成するために必要な情報を生成するための補助関数です。そのため、後で説明します。これは、スマートコントラクトを作成するための内部メッセージのエンコードプロセスであり、中間のいくつかの数字は、実際にはその内部メッセージの要件を説明するために使用されるいくつかの識別子です。ここで、TONは、TL-Bと呼ばれるバイナリ言語を使用して、メッセージの実行方法を記述し、特定の機能を持つ内部メッセージを設定するための異なるフラグを設定しています。最も一般的な2つの使用シナリオは、新しいコントラクトの作成と、既存のコントラクトの関数の呼び出しです。そして、51行目のこの方法は、前者に対応しており、新しいnftアイテムコントラクトを作成することを意味します。これは、55、56、57行で指定されたものによって主に実現されています。まず、55行目のこの大量の数字は、一連のフラグです。注意:store_uintの最初の引数は値であり、2番目はビット数であり、これらは内部メッセージが作成されることを示す後ろの3つのフラグを決定しそのバイナリ値が111(10進数で4 + 2 + 1)であることを示しています。最初の2つは、このメッセージがStateInitデータを添付することを示します。このデータは、新しいコントラクトのソースコードと必要なデータの初期化です。最後のフラグは、内部メッセージが添付されることを示します。つまり、関連するロジックを実行し、必要なパラメーターが必要です。そのため、コードの66行目では、これらの3つのデータが設定されていないため、既存のコントラクトの関数の呼び出しであることがわかります。詳細なエンコードルールは、こちらをご覧ください。

したがって、StateInitのエンコードルールは、49行目のコードに対応しています。calculate_nft_item_state_initを介して計算されます。stateinitデータのエンコードも、いくつかのフラグビットを除いて、TL-Bエンコードルールに従います。コードと初期化データの2つの主要な部分が関与します。データのエンコード順序は、新しい契約に指定された永続化セルの保存順序と一致する必要があります。36行目では、初期化データにはitem_index(ERC721のtokenIdと同様)と、現在の契約アドレス(collection_addressとして返される標準関数my_address)が含まれています。これらのデータの順序は、nft-itemの宣言と一致しています。

次に、TON内では、まだ生成されていないスマートコントラクトのアドレスを事前に計算することができます。これは、Solidityのcreate2関数に似ています。TONでは、新しいアドレスの生成は、workchainの識別子とstateinitのハッシュ値の連結で構成されます。workchainは、以前の説明でTONの無限シャーディングアーキテクチャに応じて指定する必要があることを既に知っています。現在は統一された値です。workchainは、標準関数workchainで取得します。stateinitは、標準関数cell_hashで取得します。したがって、この例に戻ると、calculate_nft_item_addressは新しいコントラクトのアドレスを事前に計算する関数です。そして、その生成値を53行目でメッセージにエンコードし、この内部メッセージの受信アドレスとして使用します。nft_contentは、作成されたコントラクトの初期化呼び出しに対応しています。具体的な実装については、次の記事で説明します。

至于send_royalty_params,则需要はある読み取りリクエストへの内部メッセージの応答である、前述の説明で、TON内部メッセージにはデータを変更する可能性がある操作だけでなく、読み取り操作もこのような方法で実装する必要があることを強調しました。したがって、この契約はそのような操作です。まず、注目すべきは67行目がリクエスト者のコールバック関数に対する応答を示すことであり、次に返されるデータ、つまりリクエストのアイテムインデックスとそれに対応するロイヤルティデータです。

次に次の知識点を紹介します。TONのスマートコントラクトには、recv_internalとrecv_externalという2つの統一されたエントリがあります。前者はすべての内部メッセージの統一された呼び出しエントリであり、後者はすべての外部メッセージの統一された呼び出しエントリです。開発者は、要件に応じて、メッセージで指定された異なるフラグに応答するために、関数内でスイッチのような方法を採用する必要があります。ここでのフラグは、上記の67行のコールバック関数のフラグです。この例に戻りますと、まずメッセージを空であるかどうかチェックし、その後、メッセージの情報を個別に解析します。最初に83行でsender_addressを解析し、このパラメータは後続の権限チェックに使用されます。ここでの~演算子に注意してください。これは別の構文糖です。ここでは詳細には触れません。次に、op操作フラグを解析し、その後、異なるフラグに応じて、対応するリクエストを個別に処理します。これにより、特定のロジックに基づいて、上記の関数がそれぞれ呼び出されます。たとえば、royaltyパラメータのリクエストに応答したり、新しいnftをミントしたり、グローバルインデックスを自動的に増やしたりすることが含まれます。

次に、知識ポイントには108行が対応しており、関数の処理ロジックは名前からもわかると思います。Solidityのrequire関数に似ています。Funcでは、標準関数throw_unlessを使用して例外をスローします。最初の引数はエラーコードで、2番目の引数はチェックビットのブール値です。ビットがfalseの場合は例外をスローし、エラーコードが付属します。そして、この行では、上記で解析したsender_addressがこの契約の永続的なowner_addressと等しいかどうかをequal_slicesで判断し、アクセス権をチェックします。

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

最後には、コード構造をより明確にするために、持続的な情報を取得するための補助関数シリーズを開始しましたが、ここでは詳細には触れません。開発者はこのような構造を参考にして、独自のスマートコントラクトを開発することができます。

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

TONエコシステムのDApps開発は非常に面白いことです。EVMの開発パラダイムとは大きく異なるため、TON ChainでDAppsを開発する方法を紹介する一連の記事を通じて共に学び、この機会を掴んでください。また、Twitterで私と交流し、いくつかの新しい面白いDAppsのアイデアを共有し、一緒に開発することも歓迎します。

TON0.27%
原文表示
このページには第三者のコンテンツが含まれている場合があり、情報提供のみを目的としております(表明・保証をするものではありません)。Gateによる見解の支持や、金融・専門的な助言とみなされるべきものではありません。詳細については免責事項をご覧ください。
  • 報酬
  • コメント
  • リポスト
  • 共有
コメント
0/400
コメントなし
  • ピン