ソースコードからTONスマートコントラクトの開発を学ぶTONはスマートコントラクトの開発言語として、Funcと呼ばれるC言語、静的型付け言語のクラスを設計することを選択しましたので、ソースコードからTONスマートコントラクトを開発する方法を学びましょう。今回は、シンプルなTON NFTの例を実装しています。2つの機能契約と3つの必要なライブラリに分かれている契約の構造を見てみましょう。
2つの主な機能コントラクトは、上記の原則に従って設計されています。まず、主なコントラクトであるnft-collectionのコードを見てみましょう。collectionコード:
TON スマートコントラクトでデータを永続的に保存する方法という、最初の知識を紹介します。通常、スマートコントラクトの状態変数は、実行終了時に最新の値に基づいて自動的に永続化されるため、開発者はこのプロセスについて考える必要はありません。これは、CやC++がGC処理を考慮する必要があるのと少し似ていますが、他の新しい開発言語では通常、この部分のロジックは自動化されています。コードを見てみましょう。まず、必要なライブラリをいくつか紹介し、最初の関数load_dataは永続化されるデータを読み込むために使用されます。ロジックは、最初にget_dataを使用して永続契約ストレージセルを返すことです。これは標準ライブラリstdlib.fcによって実装されていることに注意してください。
この関数の戻り値はセル型で、TVMのセル型である。前回の紹介でわかったように、TONブロックチェーンのすべての永続データはセルツリーに格納されている。各セルは最大1023ビットの任意のデータと、他のセルへの最大4つの参照を持つ。cellはスタックベースのTVMでメモリとして使用される。cellは厳重にエンコードされたデータを保持し、その中の特定の平文データを取得するには、cellをsliceと呼ばれる型に変換する必要がある。cellはbegin_parse関数によってslice型に変換することができる。begin_parse関数でセルをスライス型に変換し、スライスからビットをロードして他のセルを参照することで、セル内のデータを得ることができる。この15行目の呼び出しは、最初の関数の戻り値から2番目の関数を直接呼び出す func の構文上の糖分であることに注意。そして最後には、データが永続化された順にロードされる。この処理は、solidityとは異なり、ハッシュマップに基づいて呼び出されるわけではないので、この呼び出しの順序を台無しにすることはできないことに注意してください。
save_data関数では、逆の処理であることを除けば、ロジックは同様で、次のポイントであるセルビルダーの新しいタイプにつながる。データ・ビットや他のセルへの参照はビルダーに格納することができ、それを新しいセルに確定することができる。まず標準関数 begin_cell を使ってビルダーを作成し、次に store 関連関数を使って関連する関数を格納する。上記の呼び出しの順番は、ここでの格納の順番と一致させる必要があることに注意。最後に、end_cellで新しいセルの構築が完了し、メモリ上で管理される。そして、一番外側のset_dataでセルの永続的な保存が完了する。
次に、ビジネス関連の機能を見ていきましょう。まず、次の知識を紹介する必要があります。これは、先ほど紹介したマスター・スレーブ・アーキテクチャで頻繁に使用されます。TONでは、スマート・コントラクト間の呼び出しは内部メッセージの送信によって行われることがわかっている。これは send_raw_message と呼ばれるメソッドで実行される。 第 1 引数はセルとしてエンコードされたメッセージで、第 2 引数はトランザクションの実行方法の違いを示す識別子ビットであること、そして TON で送信される内部メッセージには異なる実行方法があり、現在 3 つのメッセージ Modes と 3 つのメッセージ Flags があることに注意。単一のモードといくつかのフラグ(あるいはフラグなし)を組み合わせて、希望するモードにすることが可能である。モードとフラグを説明する表を以下に示します。
では、最初のメイン関数であるdeploy_itemを見てみましょう。nft_itemは、その名前が示すように、新しいNFTインスタンスを作成(またはキャスト)するための関数です。いくつかの操作でmsgをエンコードした後、send_raw_messageを介して内部コントラクトを送信します。このエンコーディング・ルールが、新しいスマート・コントラクトの作成方法に対応するものであることに気づくのは簡単だ。では、その方法を見てみよう。
51行目を直接見てみましょう。 上の2つの関数は、メッセージに必要な情報を生成するヘルパー関数なので、後で見てみましょう。 これは、スマートコントラクトを作成するために使われる内部メッセージをエンコードする処理で、真ん中の数字のいくつかは、実際には内部メッセージの必要性を示す識別子ビットです。TONは、メッセージの実行を記述するためにTL-Bと呼ばれるバイナリ言語を選択し、内部メッセージのいくつかの特定の機能を実現するために異なるマーキングビットの設定に応じて、最も簡単に2つのシナリオ、新しいコントラクトの作成とコントラクト関数の呼び出しの展開を考える。51行目のアプローチは前者、つまり55、56、57行目で指定されている新しいnft項目コントラクトの作成に対応する。まず、55行目の一連の数値は、識別子のビット列です。store_uintへの最初の入力は数値であり、2番目はビット長であることに注意してください。これは、内部メッセージが識別子の最後の3ビットによって作成されたコントラクトであることを決定するとともに、対応する2進数値111(10進数では4+2+1)であり、その最初の2ビットは、新しいコントラクトのソースコードであるStateInitデータと、コントラクトの初期化を伴うメッセージであることを示しています。最初の2ビットは、メッセージがStateInitデータを伴うことを示す。StateInitデータは、新しいコントラクトのソースコードであり、初期化に必要なデータである。最後の2ビットは、メッセージがStateInitデータを伴うことを示している。これは、新しいコントラクトのソースコードであり、初期化に必要なデータである。つまり、66行目のコードは3ビットのデータをセットしているのではなく、デプロイされたコントラクトへの関数コールを示していることがわかります。具体的なコーディングルールはこちらをご覧ください。
したがって、StateInitのエンコーディング規則は、calculate_nft_item_state_initによって計算されたコードの49行目に対応し、stateinitデータのエンコーディングも、確立されたTL-Bエンコーディング規則に従っていることに注意してください。初期化data.dataは、新しい契約によって指定された永続セルと同じ順序でエンコードされる必要がある。36行目を見てわかるように、初期化データはitem_indexで、これはERC721のtokenIdに似ており、標準関数my_addressによって返される現在のコントラクトのアドレスはcollection_addressであり、このデータの順序はnft-itemの宣言と一致している。
次に知っておくべき点は、TONでは、生成されていないスマートコントラクトはすべて、生成後のアドレスを事前に計算できるということです。これは、Solidityのcreate2関数に似ており、新しいアドレスの生成は、ワークチェーン識別子ビットとstateinitのハッシュ値という2つの部分から構成されます。前者は、前回紹介したように、TON無限スライシングアーキテクチャのために指定する必要があり、現在は一律の値です。これは標準関数workchainから得られる。後者は標準関数cell_hashから得られる。つまり、例に戻ると、calculate_nft_item_addressは新しいコントラクトのアドレスを事前に計算する関数である。結果の値は 53 行目のメッセージにエンコードされ、内部メッセージの送信先アドレスとなる。そして、nft_contentは、作成されたコントラクトの初期化呼び出しに対応します。
send_royalty_paramsに関しては、読み取り専用リクエストに対応する内部メッセージである必要があります。 前回の紹介で、TONの内部メッセージはデータを変更する可能性のある操作だけでなく、このように実装する必要がある読み取り専用操作も含むことを意図的に強調しましたので、コントラクトはそのような操作です。要求は、要求者のマークのコールバック関数の後に行われるべきであり、それぞれ、戻りデータは、項目インデックスの要求だけでなく、対応するロイヤリティデータであることに注意してください。
次に、知識の次のポイントを紹介しましょう、TONのスマートコントラクトは、2つだけrecv_internalとrecv_externalと呼ばれる統一されたエントリ、前者はすべての内部メッセージのために、後者はすべての外部メッセージの統一された呼び出しのために、開発者は、エントリの統一された呼び出しのニーズに応じて関数にする必要があり、開発者は、内部の関数を使用する必要があります。開発者は、上記67行目のコールバック関数のタグである、メッセージで指定された異なるタグビットに基づくスイッチのようなアプローチを使用して、必要に応じて関数内で異なるリクエストに応答する必要があります。まず、83行目でメッセージを解析してsender_addressを取得します。これは、その後のパーミッションチェックに使用されます。演算子は別の構文糖に属していることに注意。次に、opオペレーションのタグビットを解析し、異なるタグビットに従って リクエストを処理する。つまり、上記の関数は何らかのロジックに従って呼び出される。たとえば、ロイヤリティパラメータの要求に応じて、または新しいnftをキャストし、自己インクリメントグローバルindex.
知識の次のポイントは108行に対応し、関数のロジックによって命名される必要があります知ることができる、とrequire関数内のSolidityは、例外をスローする標準関数throw_ unlessを通じてFuncに似ている、例外をスローするFuncの最初のエントリです。unlessは例外をスローするFuncの標準関数で、最初のパラメーターはエラーコード、2番目はビット単位のブール値チェックで、もしfalseならエラーコードで例外をスローします。この行では、equal_slicesを使用して、上記で解析されたsender_addressがコントラクトの永続ストアのowner_addressと等しいかどうかを判断し、権限判定を行っています。
最後に、コードの構造を明確にするために、永続性情報を取得するための一連の補助関数がアイドル状態になり始めました。開発者は、独自のスマート・コントラクトを開発するために、この構造を参照することができる。
DApp開発のTONエコシステムは実に興味深いもので、EVMとは全く異なるパラダイムです。TONチェーンでDAppsを開発する方法に関する記事