読了時間:13分
概要
Solanaブロックチェーンは、1秒あたり数千件のトランザクションを、ほぼ無料の取引手数料で処理できる強力なツールです。Web3を初めて利用される方や、これまでEVMベースのブロックチェーン上で開発を行ってきた方にとって、このガイドはSolanaの基本要素を理解し、Solana開発への第一歩を踏み出すのに役立つでしょう。すでにSolana上で開発を行っている方は、このガイドを参考資料として活用し、Solanaの基礎知識をさらに深めてください。
このガイドでは、Solanaの基本的な構成要素、具体的には「アカウント」、「プログラム」、「命令」、「トランザクション」、「RPC」、「サブスクリプション」について解説します。さっそく始めましょう!
要約
- アカウントは、従来のオペレーティングシステムにおけるファイルのようなものです。Solana上のほとんどのものはアカウントであり、ユーザーのウォレット、プログラム、データログ、さらにはシステムプログラムまでもがアカウントとして扱われます。
- プログラム(他のブロックチェーンでは「スマートコントラクト」として知られる)は、「実行可能」なアカウントの一種であり、ステートレスで、ユーザーが入力したパラメータに基づいてのみコードを実行します。プログラムは、自身を更新するのではなく、「所有」する他のアカウントのデータ(または状態)を更新する特定の権限を持っています。
- ユーザーは、RPCを介してSolanaクラスターにトランザクション(プログラム固有の命令の束)に署名して送信し、プログラムに何をすべきかを指示します。
- Solanaは、「Proof of History(PoH)」と呼ばれる独自のコンセンサスメカニズムを採用しており、時間と暗号技術を活用して、ネットワークに送信されるトランザクションにタイムスタンプを付与し、順序付けを行っています。
- プログラムやトランザクションの署名者は、ネットワークの状態を更新するために、オンチェーン上の特定のデータアカウントに対する変更を承認します。
もっと知りたい? 続きを読んでね! 👇
ソラナとは何ですか?
Solanaは、速度とコストに最適化されたオープンソースのブロックチェーンであり、情報と状態をグローバルに同期させることができます。Solanaは、「Proof of History」と呼ばれる独自のプルーフ・オブ・ステーク(PoS)コンセンサス方式を採用しており、これは粒度の細かい検証可能な遅延関数(GVDF)を用いて、ネットワークに送信されたトランザクションに効果的にタイムスタンプを付与し、順序付けを行います。ソラナのホワイトペーパーによると、アナトリー・ヤコヴェンコ氏は、「これは、入力から出力を予測できず、出力を生成するには関数を完全に実行しなければならないように記述された、暗号的に安全な関数を使用している」(3)と述べています。「リーダーは、システム内の他のノードによって効率的に処理され、スループットが最大化されるように、ユーザーメッセージをシーケンス化し、順序付けを行う」(2)。

*この記事では、Solanaのランタイムに関する解説はこれくらいに留め、今後はSolanaとのやり取りや開発方法に焦点を移していきます。Solanaのアーキテクチャや、その設計上の特徴の背景にある理論についてさらに詳しく知りたい方は、以下の優れたリソースをご参照ください:
アカウント
オペレーティングシステムにおけるファイルと同様に、アカウントはシステムの実行、データや状態の保存、およびコードの実行に使用されます。
Solanaには主に3種類のアカウントがあります:
- 情報を保存し、ネットワークの状態を管理するデータアカウント。データアカウントには2つの種類があります:
- システム所有のアカウント(一般的には「ウォレット」として知られている)[例:ユーザーウォレット]
- プログラム由来のアカウント(通称PDA) — 一般的な例としては、特定のトークンに対するユーザーのトークンアカウントが挙げられます。これは、Solana SPLトークンプログラムによって生成されます。[例:ユーザーのトークンアカウント]
- 実行可能コードを保持するプログラムアカウント。これらについては次のセクションで詳しく説明しますが、これらのアカウントはステートレスです(つまり、データはアカウントを通過しますが、アカウント自体は更新されません)。[例:Candy Machine v2 プログラムアカウント]
- ネイティブシステムアカウント(例:システムプログラム、投票、ステーキング、BPFローダー)[例:Solana システムプログラム]
この記事では、#1と#2に焦点を当てます。#3の「ネイティブシステムアカウント」について詳しく知りたい場合は、docs.solana.comをご覧ください。
要するに、Solana上のほぼすべてがアカウントです。最も一般的なアカウントの種類はユーザーのウォレットであり、これはユーザーのSOL残高の状態を管理するデータアカウントです。
各アカウントには、一意のアドレス(公開鍵、いわばファイルパスのようなもの)と、それに関連付けられた一連のメタデータがあります:
- lamports: このアカウントが所有するlamportsの数(64ビットの符号なし整数)*
- 所有者:このアカウントが割り当てられているプログラムの所有者(所有者プログラムの文字列アドレス)。所有者プログラムは、どのアカウントに対して書き込みアクセス権を付与するかを制御します。他のプログラムは別のアカウントのデータを読み取ることはできますが、そのデータを変更しようとすると、トランザクションは失敗します。
- executable:このアカウントが命令を処理できるかどうか(ブール値)。executable の値がtrueのアカウントは、実際にはプログラム(またはスマートコントラクト)です。これらについては次のセクションで説明します。
- data: アカウントに関連付けられた追加データ。生データのバイト配列として格納される。
- rent_epoch: このアカウントが家賃を支払うことになる次のエポック(64ビットの符号なし整数)
* ラムポートは、ソラナのネイティブトークンであるSOLの最小単位です(1 SOLの10億分の1に相当します)。
どのアカウントのメタデータも、getAccountInfo JSON RPC クエリを実行するか、Solana Explorer で検索することで確認できます。Solana Explorer では、任意のアカウントを検索し、そのアカウントの種類や関連付けられているプログラムなどの基本情報を確認できます。以下は、システム所有のデータアカウント(ユーザーのウォレット)の例です:

すべての口座タイプの例を具体的に見てみましょう:

ここではさまざまなことが行われていますが、この図に示されているものはすべて「アカウント」です。左側には、すべてのユーザーウォレットを管理する「System Program」というシステムアカウントがあります。 また、「Solana SPL Token Program」と呼ばれる実行可能なプログラムアカウントもあり、ユーザーはこのアカウントを使ってSPLトークンの鋳造、焼却、転送を行うことができます。最後に、プログラム派生アカウントがあります。この場合、これらはトークンプログラムが所有し、ユーザーのアカウントに関連付けられたトークンアカウントです。1つのユーザーウォレットが複数のトークンアカウントを持つことができる点に注目してください。
Solana Explorerでは、任意のアカウントアドレスを検索してそのメタデータを取得し、そのアカウントがどのようなタイプのものか、またどのプログラムに関連付けられているかを把握できることを覚えておいてください。
ラムポートと賃料エポックアカウントおよびアカウントデータはバリデーターのメモリに保存されており、そこに保持し続けるためには、ラムポート単位の「賃料」(アカウントのラムポートメタデータフィールドに指定されている)を支払う必要があります。アカウントに保存されるデータ量が多いほど、必要な賃料も増えます(注:現在のアカウントの最大サイズは10 MBです)。 JSON RPCメソッド「getMinimumBalanceForRentExemption」を使用して、必要な賃料を計算できます。バリデーターは、すべてのアカウントを定期的にスキャンし、賃料を徴収する責任があります。アカウントのチェックは、rent_epochメタデータフィールドで指定されたエポックでチェックされます。アカウントの残高が0 lamportまで減少した場合、ネットワークはそのアカウントを削除し、データは失われます。本稿執筆時点では、すべての新規アカウントは「賃料免除」ステータスを維持することが義務付けられており、これはアカウント内に2年分の賃料相当額を保有することで付与されます。出典:https://docs.solana.com/developing/programming-model/accounts エポックはSolanaにおける時間の単位であり、現在は約2.5日に相当します。
プログラム
プログラムとは、ソラナが「スマートコントラクト」と呼ぶものであり、情報やリクエストを処理するエンジンです。トークンの転送やキャンディマシンのミントから、「hello world」のログやDeFiのエスクローガバナンスに至るまで、あらゆる処理を担っています。
- 覚えておいてください。Solanaのプログラムは、「実行可能」とマークされたステートレスなアカウントです。「プログラムアカウントに保存される主なデータはコンパイル済みのBPFコードであるため、プログラムはステートレスであるとみなされます。」
- プログラムは「プログラム間呼び出し」を利用して、他のSolanaプログラムを利用したり、それらを基盤として構築したりすることができます。これはしばしば「コンポーザビリティ」と呼ばれます。
- 他の人気チェーン店とは異なり、プログラムはアップグレード可能です。
- ほとんどのプログラムはユーザーによって作成されていますが、Solana Labs は「Solana Program Library」として知られる、数多くのオンチェーンプログラムを開発・維持管理しています。
実質的に、ユーザーはプログラムに対してそのプログラム固有の命令を与え、プログラムはそれらの命令を処理し、その後、他のプログラムを呼び出したり、自身が所有するプログラム由来のアカウント(PDAについては後述)を更新したりすることがあります。
プログラム由来のアドレスアカウント
プログラム由来アドレス(PDA)とは、特定のプログラムによって作成・管理されるアカウントのことです。PDAには以下の特徴があります:
- プログラムが「プログラムアドレス」と呼ばれる特定のアドレスを制御できるようにし、外部のユーザーがそれらのアドレスに対して有効な署名付きトランザクションを生成できないようにする。
- プログラムが、クロスプログラム呼び出しを介して呼び出される命令に含まれるプログラムアドレスに対して、プログラム的に署名できるようにします。Solanaは、プログラムによって定義されたシードとプログラムIDに基づいて、PDAを決定論的に導出します。生成されるプログラムアドレスの詳細については、docs.solana をご覧ください。
これにより、プログラムは、取引や賭け、あるいはDeFiプロトコルを安全に管理するためのエスクロー口座のような、トラストレスなサービスを提供できるようになります。上記の図を参照すると、SPLトークンプログラムは、ユーザーのウォレットと、その特定のトークンのミントアドレスに基づいて、ユーザー用のPDAを生成します。その特定のPDAには、ユーザーが所有するそのトークンの数量に関する状態やデータが格納されます。
命令やトランザクションを使ってプログラムを動作させる仕組みについて、詳しく見ていきましょう。
手順
命令とは、本質的に、プログラムに対して何かを行うよう指示するものです。Solanaでは、命令を「プログラムにおける実行ロジックの最小の連続した単位」と定義しています(出典:docs.solana.com/terminology)。
命令は3つの部分から構成されています:
- 呼び出す予定のプログラムの公開鍵は、
- 読み取りまたは変更を行うアカウントの配列、および
- プログラムの実行に必要な追加データのバイト配列(プログラム固有)
この最後の点は極めて重要です。つまり、指示はプログラムごとに異なります。間違ったプログラムIDを入力したり、プログラムに渡すべきデータの構造が分からなかったりすると、トランザクションは失敗します。
JavaScript では、トランザクション命令は次のような形になります:
new TransactionInstruction({
programId: new PublicKey(PROGRAM_ID),
keys: [
{ pubkey: signerKeypair.publicKey, isSigner: true, isWritable: true },
{ pubkey: pda, isSigner: false, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
],
data: Buffer.from(SOME_ENCODED_DATA),
})
ここで、トランザクション指示に必要なアカウント情報について触れておく価値があります。アカウントの公開鍵、そのアカウントがトランザクションの署名者であるかどうか、およびそのアカウントがトランザクションの一部として書き込まれるかどうかを指定する必要があります(これは、Solanaの高速な実行能力に寄与します)。 上記の例では、ユーザーがウォレットに署名し、データがPDAに書き込まれます。通常、システムプログラムアカウントも(例えば、新しいアカウントを作成する場合など)含める必要があります。最後に、SOME_ENCODED_DATAを渡します。必要なデータの型はプログラム内で定義されています。プログラムは、入力データを正しく処理するために、そのデータをデシリアライズする必要があります。
取引
トランザクションとは、ネットワーク上のプログラムに指示を伝える手段です。トランザクションでは、複数の指示をまとめてパッケージ化することで、複数の操作を同時に処理することができます。 例えば、3つの異なる指示がある場合を考えてみましょう。ウォレットAからウォレットBへ1 $SOLを送金する指示、ウォレットBからウォレットAへ33 $USDCを送金する指示、そして「ウォレットAがYYYY-MM-DDにウォレットBと1 $SOLを33 $USDCと交換した」というメモをトランザクションに記録する指示です。 これらをそれぞれ個別にネットワークに送信することも可能ですが、もし1つの命令が失敗し、他の命令が成功した場合はどうなるでしょうか?ある人はトークンを受け取れたものの、別の人は受け取れないという状況が生じてしまいます。命令を1つのパッケージにまとめることで、すべてのトランザクションが一緒に成功するか、一緒に失敗するかを確実にすることができます。
トランザクションは現在、1,232バイトに制限されています。各トランザクションには、以下の内容を必ず含める必要があります:
- 1つ以上のトランザクション命令からなる配列
- プログラムがやり取りを行うすべてのアカウントアドレスの配列
- 署名のセット:トランザクションに渡すアカウントの一部には、署名も含まれている必要があります。「これらの署名は、アカウント所有者がそのトランザクションを承認したことをオンチェーンプログラムに伝えます。通常、プログラムはこの承認に基づいて、アカウントからの引き落としやデータの変更を許可します」(出典:docs.solana.com)。例えば、クリエイターがNFTの真正性を確認する際にも、これが必要となる場合があります。
- 最新のブロックハッシュ:ブロックハッシュは、基本的にはPoHのタイムスタンプです。Solanaでは、デフォルトで「古い」トランザクションが破棄されます。これは、二重計上を防ぐため、バリデーターがごく直近のバッチに対してのみトランザクションを検証できるようにするため、そしてユーザーが自分のトランザクションが処理されたか(あるいは失敗したか、再試行が必要か)を迅速に確認できるようにするためです。 トランザクションを送信する前に最新のブロックハッシュを取得することで、ネットワークは当該トランザクションの経過時間や(必要に応じて)有効期限がいつになるかを把握できるようになります。
ネットワークがトランザクションを受信すると、トランザクションID(Base-58エンコードされた文字列)を返します。このIDを使用することで、トランザクションの詳細を照会することができます。
では、取引をネットワークに送信する方法について話し合いましょう!
RPC
JSON-RPCは、JSON形式でエンコードされたリモートプロシージャコール(RPC)プロトコルであり、ユーザーがSolanaクラスターに情報を送信し、ネットワークから応答を受け取ることを可能にします。Solanaは現在、3つのネットワーククラスターを運用しています:
- Devnet:アプリケーションやメソッドの機能をテストするための実験場
- テストネット:システムに予定されている最新のアップグレードをすべて対象とした負荷テストを行うための開発環境
- メインネットベータ:本番環境
クライアントは、Solanaノードを経由してJSON-RPCリクエストを送信することで、3つのクラスターのいずれに対しても読み取りまたは書き込みのリクエストを行うことができます。ノードは、ネットワークへのリクエストを処理する「航空管制」のような役割を果たします。クライアントは、Solana Web3Connectionクラスを通じてSolanaノードに接続します:
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'
const SOLANA_CONNECTION = new Connection(HTTP_ENDPOINT)
上記の例では、HTTP_ENDPOINTは、最終的にリクエストを 3 つのクラスターのいずれかにルーティングする Solana ノードを指す HTTP URL です。
リクエストを行うには、ネットワークに接続するためのAPIエンドポイントが必要です。そこでQuicknodeの出番です!パブリックノードを利用することも、独自のインフラストラクチャをデプロイして管理することも可能です。ただし、応答時間を8倍高速化したい場合は、面倒な作業は私たちにお任せください。
Solana上のプロジェクトの50%以上がQuicknodeを選んでいる理由をご確認いただき、こちらからアカウント登録を行ってください。ログインして、アプリのHTTP_ENDPOINTをアカウントダッシュボードに記載されている値に更新するだけで、すぐに利用開始できます!
Solana上のプロジェクトの50%以上がQuicknodeを選んでいる理由をご確認いただき、こちらからアカウント登録を行ってください。ログインして、アプリのHTTP_ENDPOINTをアカウントダッシュボードに記載されている値に更新するだけで、すぐに利用開始できます!

Solanaクラスターへの接続を確立すると、ネットワークに対してさまざまな読み取りや書き込みのメソッドを実行できるようになります。RPCメソッドの完全な一覧とドキュメントは、Quicknode Docsで確認できます。各メソッドにはそれぞれ目的がありますが、このガイドでこれまでに説明してきた内容に関連する主なメソッドは以下の通りです:
- sendTransaction- Solanaネットワークにトランザクションを送信します
- getAccountInfo- 任意のアカウントに関連付けられたメタデータを取得します
- getLatestBlockhash- ネットワークから最新のブロックハッシュを取得します
HTTP呼び出しを使えば、ネットワークに対して何らかの処理(例えば、取引とその指示の処理、口座の現在の状態情報の返却など)を実行するよう指示することができますが、口座の状態に変化があった際にネットワークから通知を受け取りたい場合はどうすればよいでしょうか?
定期購読
サブスクリプションとは、HTTPエンドポイントを使用する代わりにWebSocket(WSS)リクエストを行う、特殊な種類のRPCリクエストです。Solana Web3 JSON RPCには、いくつかのサブスクリプションメソッド(例:アカウントのlamportsやデータの変更を監視する`accountSubscribe`など)が用意されています。これらのイベントに対してコールバック関数を設定してサブスクライブすることで、ネットワークに変化が生じた際に、所望のアクションを実行させることができます。
Connectionクラスのコンストラクタでは、オプションのcommitmentOrConfigを渡すことができます。その中に、wsEndpointを渡すことができます。これは、フルノードのJSON RPC WebSocketエンドポイントへのエンドポイントURLを指定するためのオプションです。Quicknodeエンドポイントを作成している場合、エンドポイントページの右側にこのオプションが表示されているはずです。
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'
const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'
const solanaConnection = new Connection(HTTP_ENDPOINT, {
wsEndpoint: WSS_ENDPOINT,
})
wsEndpoint を指定しなかった場合はどうなるのでしょうか?実は、Solana にはmakeWebsocketUrl という関数が用意されており、この関数によってエンドポイント URL の「https」が 「wss」に、「http」が 「ws」に置き換えられます(出典)。Quicknode のすべての HTTP エンドポイントには、同じ認証トークンを持つ対応する WSS エンドポイントが存在するため、WebSocket クエリ用に別のエンドポイントを使用したい場合を除き、このパラメータを省略しても問題ありません。
WebSocketサブスクリプションの作成についてさらに詳しく知りたい場合は、ガイド「Solana WebSocketサブスクリプションの作成方法」をご覧ください。
すべてをまとめよう
よし! かなりの情報量でしたね。おそらく、これらの基本要素の間に何らかの関連性が見えてきたことと思いますが、すべての要素を結びつける前に、簡単に振り返ってみましょう:
- Solana上のほぼすべてが「アカウント」です。ユーザーのウォレット、プログラム、システムプログラム、プログラムデータなどがこれに含まれます。
- プログラムはステートレスである――プログラムは命令を処理し、他のプログラムを呼び出したり、自身が管理するデータアカウントの状態を変更したりすることができる。
- 命令はプログラムごとに異なり、プログラムに何をすべきかを指示するものです。
- トランザクションは、1つ以上の命令を署名とともに束ね、プログラムが1つ以上のタスクを実行することを許可するものです。
- RPCプロトコルを利用することで、クライアントはQuicknodeのようなノードを介して、Solanaの3つのクラスターのいずれかに読み取り/書き込みリクエストを送信することができます。
- クライアントは、RPCプロバイダーを介してWebSocketリクエストを使用し、ネットワークの変更を購読することができます。
具体的な例を見てみましょう。「GM」というプログラムがあり、ユーザーがこのプログラムに「GM」というメッセージを送信するたびに、その回数を記録するカウンターを持っていると想像してください。

上記の例では、クライアントが複数のトランザクション命令を含むトランザクションを作成します。これらの命令はプログラム固有のものであるため、GMプログラムの想定される構造と一致している必要があります。クライアントは、Connectionクラスを使用して、QuicknodeなどのRPCプロバイダーを経由して、トランザクションに署名し、Solanaクラスターに送信します。 RPCプロバイダーはリクエストをクラスターに転送します。プログラムアカウントは、受信した入力指示を実行・処理します。プログラムアカウントはステートレスであり、それ自体は変更されませんが、GMカウンターの値をインクリメントするためにPDAのデータ変更を承認します。クライアントが作成したサブスクリプションはPDAの変更を監視しており、アカウントが変更されると、ネットワークからクライアントに通知が行われます。
まとめ
ふぅ! 先ほど説明した内容は本当に盛りだくさんでしたね。これらの概念を確実に理解できるよう、何度でも読み返してみてください。
Solanaについてより深く理解できたところで、いよいよ開発を始めましょう!まずは、当社のSolana開発ガイドをいくつか試してみてください。以下にいくつかの例をご紹介します:
- Solana で(バージョン管理された)トランザクションを作成・送信する方法
- TypeScript を使用して Solana で NFT を発行する方法
- ログを使ってSolanaの取引履歴を追跡する方法
これらのコンセプトについて質問がある方や、単に技術談義をしたい方は、Discordや Twitterでぜひご連絡ください。現在取り組んでいるプロジェクトについても教えてくださいね!
皆様からのフィードバックを心よりお待ちしております!❤️
このガイドについてご意見やご質問がございましたら、ぜひお知らせください。皆様からのご連絡を心よりお待ちしております!
参考文献および出典
- https://solanacookbook.com/core-concepts/accounts.html#deep-dive
- https://www.quicknode.com/guides/solana-development/an-introduction-to-the-solana-account-model
- https://medium.com/@anatolyyakovenko
- https://docs.solana.com/developing/programming-model/accounts
- https://docs.solana.com/developing/clients/jsonrpc-api#getaccountinfo
