
アイデアをSolanaプログラム(スマートコントラクト)にする方法
Solanaプログラムを設計してみましょう。Solanaエコシステムには、Solanaプログラミングモデルを理解するための記事や、初心者がSolanaとAnchorを学ぶのに最適な小規模デモプログラムの作成方法を紹介する記事が数多くあります。しかし、実際のプログラムでは、より複雑な点を考慮する必要があります。
この記事では、実際に作成したいプログラムを取り上げ、以下の内容を説明します。
- データをSolanaアカウントとして定義する
- データ間の関係を構築する
- プログラム内にトークンを保管する
- インデックスを使用して読み取りを高速化する
- シャーディングを使用して書き込みを高速化する
この例の目的は、皆さん自身のアイデアをSolanaプログラムとアカウントとしてモデル化する方法を理解できるようにすることです。
注:この記事は、Anchorの基礎を理解していることを前提としています。
Solanaプログラムの例 — 予測市場の構築
今回設計するプログラムは、Polymarket、Hedgehog、Drift BETに似た予測市場です。予測市場に馴染みがない方のために説明すると、予測市場ではイベントのさまざまな結果に賭けることができます。たとえば、スーパーボウルでどのチームが勝つか、「監督賞」のオスカーを誰が受賞するか、政府が特定の時刻までに発表を行うかどうかなど、明確な結果を持つあらゆる現実世界のイベントが対象になります。当たった結果に賭けた人は、賭け金のプールから配当を受け取ります。
予測市場のアーキテクチャ
以下は、予測市場の中核となるアーキテクチャと、各要素の関係です。
- 複数のイベントがあります。
- 各イベントには複数の結果があります。最終的に、そのうち1つが勝利結果として確定します。
- ユーザーは各結果に複数の賭けを行います。ユーザーが賭けると、その資金がイベントの賞金プールに追加されます。
イベントが「解決」されたとき(つまり、勝利結果が判明したとき)は、次のようになります。
- 勝利結果に賭けたユーザーは、配当を請求できます
- 勝者は、運営側の取り分を差し引いた賞金プールの一部を受け取ります
- 各勝者が受け取る賞金プールの割合は、勝利結果に賭けられた金額に占める、その勝者の賭け金の割合に基づきます。
1. アプリをSolanaアカウントとして設計する
このプログラムでリレーショナルデータベースを使用する場合、以下を考慮します。
- 類似するデータ項目をテーブルの行として保存する
- 主キーを使用して各データを一意に識別する
- テーブルの列で、各データ項目に想定される属性と型を定義する
Solanaでは、これらの概念はおおむね次のように対応します。
- 類似するデータ項目は、同じアカウントタイプを使用して保存されます
- アドレスを使用して各データを一意に識別します
- struct(キーとデータ型)で各項目の属性を定義します
同じデータを、従来のデータベーステーブルとSolanaアカウントの両方で表すと次のようになります。
2. データ間の関係をマッピングする
Solanaでは、データ項目間の関係の扱いが大きく異なります。従来のデータベースではリレーションシップ(つまり、テーブル間の論理的なつながり)を使用します。Solanaでは、1対多の関係をアドレスのベクターとして扱い、各アドレスには該当項目のアカウントが格納されます。
たとえば、イベントには、結果のアドレスを格納したベクターである「結果」があります。Anchorではこれを**Vec<Pubkey>**と表しますが、PDAアドレス(今回の結果など)は、厳密には公開鍵ではありません。
3. トークンを保管する
従来のデータベースとは異なり、Solanaプログラムでは残高の数値だけでなく、実際の資金もアカウント内に保管できます。
この例では、イベントの賞金プール用にトークンアカウントが必要です。イベントのPDAが賞金プールのアカウントを所有します。ユーザーが賭けると、このアカウントにトークンを送ります。さらに重要なのは、ユーザーが配当を請求するときに、プログラムがイベントアカウントとしてトランザクションに署名し、賞金プールからトークンを移動することです。
4. 読み取り処理を削減する
ユーザーができるだけ高い応答性を感じられるようにする必要があります。また、開発者としては、不要なアカウント読み取りに余計な費用を払うことを避けたいものです。累計値とインデックスを使用すれば、プログラムの応答性と効率性をどちらも高められます。
累計値を計算する
ユーザーが配当を請求するとき、各結果に賭けられた正確な金額を把握する必要があります。予測市場では、賞金プール × 賭け金額 ÷ 勝利結果への賭け金総額という式に基づいて、勝者への支払額を決定します。
現時点では、各賭けの金額はその賭けのアカウントにのみ保存されています。特定の結果に賭けられた総額を取得するには、すべての賭けアカウントを読み取り、金額を合計する必要があります。
代わりに、各結果にtotal_amountフィールドを追加し、ユーザーが賭けるたびに増加させましょう。そうすれば、ユーザーが勝ったときに、その結果へのすべての賭けを読み取らなくても支払額を簡単に算出できます。
インデックスを使用する
特定のユーザーアカウントから行われたすべての賭けも検索する必要があります。getProgramAccounts()を使用してすべての賭けアカウントを取得し、賭け手がそのユーザーのアドレスに設定されているものを絞り込むこともできます。Heliusの高速なgetProgramAccounts()を使えば、他のRPCプロバイダーよりもはるかに高速ですが、インデックスも一般的な選択肢です。
そこで、各ユーザーの賭けを保存するインデックスを作成しましょう。ユーザーが新たに賭けるとき、この項目が存在しなければ作成し、そのユーザーの賭け一覧に追加します。
さらに各イベントにタグを追加し、「sports」「politics」「europe」「usa」「politics」などのタグが付いたイベントを簡単に検索できるようにします。そのためのインデックスも作成します。
これで、イベントタグのアカウントを参照するだけで、対象となるすべてのイベントを簡単に取得できます。
5. 書き込み競合を減らす
各イベントには、そのイベントへの賭けを保管するトークンアカウントが1つあることを思い出してください。ユーザーが新しい賭けを追加するたびに、トークンがこのアカウントへ移動します。つまり、このアカウントへの書き込みが発生します。
Solanaが高速なのは、処理を並列化しているためです。しかし、1つのアカウントの残高更新は並列化できません。各アカウントの残高は常に1つに確定していなければならないため、順番に処理する必要があります。
新しいイベントが発表され、多数の賭けが一斉に入ると、イベントのトークンアカウントに対して多くの書き込みが同時に発生し、プログラムのトランザクションが遅く感じられる可能性があります。これは書き込み競合と呼ばれ、複数のトランザクションがアカウントへのアクセスを奪い合っている状態です。
このような場合に並列処理を可能にする方法の1つが、シャーディングです。リソースをシャードと呼ばれる複数の部分に分割し、並列にアクセスできるようにします。
シャーディングの仕組み
入金は、賭け手の公開鍵の最終バイトの値に基づき、shard_num()マクロを使用して個別のwin_poolシャードに送られます。これにより、入金処理の高速性が保たれます。
その後、管理者用の命令ハンドラーでこれらを1つの賞金プールアカウントに統合し、勝者に支払うための流動性を同じアカウント内に確保できます。
人気イベントの結果が確定し、すべての勝者が同時に配当を請求した場合の書き込み競合も考慮する必要があります。賞金プールからも、できるだけ速く資金を移動しなければなりません。
ここで最適なのは、請求プロセス自体をなくすことです。イベントの結果が確定した直後から資金を順番に送れば、ユーザーは遅い請求処理を行う必要がありません。配当はすでにアカウントへ入金されています。
しかし、ユーザーが大勢で賭けたり配当を請求したりする状況を心配するには、まず実際のユーザーが必要です。Solanaプログラムを計画しているものの、まだ公開していないのであれば、存在しない大量のユーザートラフィックに対応するための最適化は不要です。公開時に必要でないなら、このような大規模な最適化がもたらす複雑さを考慮すべきです。ただし、プログラムの人気が高まったときには実装が必要になる可能性も認識しておきましょう。
まとめ
この記事では、Solana上で実用的なアプリケーションを作成する方法を詳しく説明しました。これで、Solanaアカウントの定義、データ間の関係構築、トークンの保管、インデックスとシャーディングによるパフォーマンス最適化について、実践的な知識が身につきました。
追加の質問があれば、気軽にXの@heliusへご連絡いただくか、Helius Discordに参加してください。今後、この予測市場の例をさらに発展させていく予定です。最新情報を受け取るために、ぜひこれらのアカウントをフォローしてください。
新たに身につけたスキルを活用して、アイデアをSolanaプログラムへと変え、Solanaエコシステムで実現しましょう。コーディングを楽しんでください! この記事をレビューしてくださったIchigoさんと、使用したアカウントシャーディング手法を教えてくださったr0breさんに感謝します。
その他のリソース
関連記事
Heliusを購読
Solana開発の最新情報や新しい記事の公開通知を受け取れます


