ローカルでの構築とdevnetでのテストは、Solana決済を始めるための優れた方法です。しかし、Mainnetへのデプロイの準備が整ったら、mainnetの特性を理解する必要があります。Devnetはミスを許容しますが、Mainnetは許容しません。このガイドでは、ユーザーがスムーズな体験を得られるようにするために重要な違いについて説明します。
| Devnet | Mainnet |
|---|---|
| フォーセットから無料のSOL | 手数料のために実際のSOLを取得 |
| ブロックスペースの競争が少ない | 優先手数料が重要 |
| トランザクションが容易に成立 | トランザクション設定が重要 |
| パブリックRPCで十分 | 本番環境用RPCが必要 |
| Devnetのキーペアとミント | 異なるキーとトークンミント—設定を更新してください |
RPCインフラストラクチャ
パブリックエンドポイント
(api.mainnet-beta.solana.com)はレート制限があり、SLAもありません。開発には問題ありませんが、本番環境の決済フローでは失敗します。これは、稼働時間保証のない共有APIを通じて決済プロセッサを実行しようとするようなものです。
本番環境ではパブリックRPCを使用しないでください
信頼性が高く、低レイテンシのアクセスのためにプライベートRPCプロバイダーを使用してください。
RPCプロバイダーを選択する際は、以下を確認してください:
- 信頼性: 稼働時間保証付きのSLA(99.9%以上)
- レイテンシ: ユーザーとの地理的な近接性
- 機能: トランザクション着地機能、インデックス作成、優先手数料API
RPCプロバイダーの完全なリストについては、 RPCインフラストラクチャプロバイダーガイドを参照してください。
冗長RPC設定
他のネットワークサービスプロバイダーと同様に、RPCプロバイダーもダウンタイムやパフォーマンス低下の期間を経験する可能性があります。アプリケーションの回復力を確保するために、複数のRPCプロバイダーを使用するようにアプリケーションを設定する必要があります。
Solana Kitは、RPCトランスポートをカスタマイズするためのライブラリを提供しており、独自の冗長RPCクライアントを構築できます。以下は、冗長RPCクライアントを構築する際の使用例です:
import { RpcTransport } from "@solana/rpc-spec";import { RpcResponse } from "@solana/rpc-spec-types";import { createHttpTransport } from "@solana/rpc-transport-http";// Create a transport for each RPC serverconst transports = [createHttpTransport({ url: "https://mainnet-beta.my-server-1.com" }),createHttpTransport({ url: "https://mainnet-beta.my-server-2.com" }),createHttpTransport({ url: "https://mainnet-beta.my-server-3.com" })];// Create a wrapper transport that distributes requests to themlet nextTransport = 0;async function roundRobinTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<RpcResponse<TResponse>> {const transport = transports[nextTransport];nextTransport = (nextTransport + 1) % transports.length;return await transport(...args);}
独自のルーティングツールを構築したくない場合は、Iron Forgeのようなサードパーティサービスを活用して、ルーティングを処理させることができます。
トランザクションの着地
Devnetでは、トランザクションは比較的容易に着地します。Mainnetでは、ブロックスペースを競い合うことになります。トランザクションがブロックに含まれる可能性を高めるには、トランザクションを適切に組み立てる必要があります。つまり:
- トランザクション送信前に新しいブロックハッシュを含める
- 競争力のある優先手数料を設定した優先手数料インストラクションをトランザクションに含める
- トランザクションに必要な推定コンピュートユニットに基づいたコンピュートユニット制限インストラクションをトランザクションに含める
さらに、Jito Bundlesのような他のツールを検討して、トランザクションがブロックに含まれる可能性を高めることをお勧めします。これらのツールについて詳しく見ていきましょう。
トランザクション送信設定
Mainnetでトランザクションを送信する際は、最適な着地率を得るために以下のパラメータを設定してください:
ブロックハッシュ管理:
confirmedコミットメントで取得getLatestBlockhashから返されるlastValidBlockHeightを保存—これによりトランザクションの有効期限がわかります- ブロックハッシュは約150ブロック(約60〜90秒)後に期限切れになります
送信オプション:
maxRetries: 0— 自動RPCリトライを無効化します。必要に応じてブロックハッシュを更新できるよう、リトライは自分で処理してください。skipPreflight: true— 送信前のシミュレーションをバイパスします。トランザクションを既に検証済みで、最低レイテンシが必要な場合に使用します。開発中はエラーを早期に検出するためfalseのままにしてください。
import { createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc(process.env.RPC_URL!);// 1. Get blockhash with confirmed commitmentconst { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();// 2. Build and sign your transaction with the blockhash// ... (transaction building code)// 3. Send with production settingsconst signature = await rpc.sendTransaction(encodedTransaction, {encoding: "base64",maxRetries: 0n, // Handle retries yourselfskipPreflight: true, // Skip simulation for speed (use false during dev)preflightCommitment: "confirmed"}).send();// 4. Track expiration using lastValidBlockHeightconst { lastValidBlockHeight } = latestBlockhash;// Stop retrying when current block height exceeds lastValidBlockHeight
優先手数料を使用する
すべてのSolanaトランザクションには、SOLで支払われるトランザクション手数料が必要です。トランザクション手数料は、基本手数料と優先手数料の2つの部分に分かれています。基本手数料は、トランザクションを処理するバリデーターへの報酬です。優先手数料は任意の手数料で、現在のリーダーがトランザクションを処理する可能性を高めるためのものです。速達配送のようなものと考えてください。より速く、より確実な配送のために追加料金を支払うのです。
手数料の仕組み:
Total fee = Base fee (5,000 lamports per signature) + Priority feePriority fee = Compute units x Price per unit (micro-lamports per compute unit)
実際のコスト:
- シンプルなUSDC送金: 通常時は約$0.001-0.005
- 混雑時: 約$0.01-0.05
- ピーク時の混雑: さらに高騰する可能性あり
実装例:
@solana-program/compute-budgetパッケージは、トランザクションに対してコンピュートユニット価格(マイクロlamports単位)の命令を簡単に更新または追加するためのヘルパー関数を提供します。
import { updateOrAppendSetComputeUnitPriceInstruction } from "@solana-program/compute-budget";const tx = pipe(createTransactionMessage({ version: 0 }),(m) => setTransactionMessageFeePayerSigner(payer, m),(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),(m) => appendTransactionMessageInstructions([myInstructions], m),(m) => updateOrAppendSetComputeUnitPriceInstruction(1000n as MicroLamports, m));
手数料見積もりの取得: ほとんどのRPCプロバイダーは優先手数料APIを提供しています:
手数料の仕組みの詳細については、トランザクション手数料および次のガイドを参照してください: トランザクションに優先手数料を追加する方法。
コンピュートユニットを最適化する
Solanaにおけるコンピュートは、プログラムが実行する作業量の実質的な尺度です。トランザクションで使用できるコンピュート量には制限があり(現在140万コンピュートユニット)、ブロックごとにアカウントあたりで使用できるコンピュート量にも制限があります(現在1億コンピュートユニット)。
トランザクションを送信する際には、使用されるコンピュート量を見積もり、それに応じてコンピュートユニット制限を設定する必要があります。これは実質的に、トランザクションのために確保すべき総容量の量に対するリクエストです。実際には、トランザクションに必要なコンピュートユニットを適切に見積もることが、トランザクションをブロックに含めるために重要であり(また、優先手数料を管理する上でも重要です)。
Solana JSON RPC
APIには、トランザクションに必要なコンピュートユニットを推定するために使用できるsimulatetransactionメソッドがあり、使用されるコンピュートユニットの推定値が含まれます。
@solana/kitは、トランザクションのリソース制限を推定し、1つのステップでメッセージに設定するヘルパー関数を提供します(内部でsimulatetransactionメソッドを使用)。バージョン1のトランザクションでは、これらのヘルパーはロードされたアカウントのデータサイズ制限も推定します。
import {estimateResourceLimitsFactory,estimateAndSetResourceLimitsFactory} from "@solana/kit";const estimateResourceLimits = estimateResourceLimitsFactory({ rpc });const estimateAndSetResourceLimits = estimateAndSetResourceLimitsFactory(estimateResourceLimits);const txWithResourceLimits = await estimateAndSetResourceLimits(tx);
kitプラグインクライアントを使用してトランザクションをビルドおよび送信する場合、通常このステップは不要です。クライアントが送信時(.sendTransaction())にコンピュートバジェットのinstructionsを自動的に追加します。上記の手動フローは、@solana/kitを使用してトランザクションを直接アセンブルする場合に使用します。
本番環境において、同じ種類のトランザクションを繰り返し実行する場合は、毎回コンピュートユニットを推定するオーバーヘッドを避けるために、そのトランザクションタイプのコンピュート推定値をキャッシュすることを検討してください。
Jitoバンドル
Jitoバンドルは、複数のトランザクションのアトミックな実行を管理するためのツールです。これは、チップを付けて複数のトランザクションをJitoネットワークに送信することで実現されます。チップは、Jitoネットワークがあなたのトランザクションをブロックに含めるよう促すインセンティブとして使用できます。
リソース:
リトライ戦略
トランザクションはさまざまな理由で失敗することがあります。成功/失敗を即座に返す従来の決済APIとは異なり、ブロックチェーンのトランザクションは確認のトラッキングが必要です。
主要な概念:
- ブロックハッシュの有効期限: トランザクションは約150ブロック(約60〜90秒)有効です
- 冪等性: 同じ署名済みトランザクションは常に同じシグネチャを生成するため、再送信は安全です
- 指数バックオフ: 短時間での大量リトライによるネットワークへの過負荷を避けてください
import {createSolanaRpc,createSolanaRpcSubscriptions,sendAndConfirmTransactionFactory,isSolanaError,SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED} from "@solana/kit";const rpc = createSolanaRpc(process.env.RPC_URL!);const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.RPC_WSS_URL!);const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({rpc,rpcSubscriptions});// Send with automatic confirmation tracking and block height monitoringtry {await sendAndConfirmTransaction(signedTransaction, {commitment: "confirmed",// Optional: abort after 75 secondsabortSignal: AbortSignal.timeout(75_000)});} catch (e) {if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {// Blockhash expired—rebuild transaction with fresh blockhash and retryrebuildAndRetryTransaction(); // implement your own logic for rebuilding and retrying the transaction}throw e;}
sendAndConfirmTransactionFactory は、@solana/kit
からの確認ポーリングとブロック高の追跡を自動的に処理します。トランザクションのブロックハッシュが期限切れになると
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED
をスローし、新しいブロックハッシュでトランザクションを再構築する必要があることを通知します。
参考リソース
確認レベルの理解
Solanaは3つの確認レベルを提供しています。従来の金融用語で表すと:
| レベル | Solanaの定義 | 従来の金融における相当 | ユースケース |
|---|---|---|---|
processed | ブロックに含まれているが、未投票 | 承認待ち | リアルタイムUI更新 |
confirmed | スーパーマジョリティが投票済み | 決済済み資金 | ほとんどの支払い |
finalized | ルート済み、不可逆 | 確定済み資金 | 高額取引、コンプライアンス |
各レベルの使い分け:
- UI更新: 即時フィードバックには
processedを表示(「支払いが送信されました」) - ユーザーアカウントへのクレジット:
confirmedを待つ(ほとんどのトランザクションで安全) - 物理的な商品の発送:
finalizedを待つ - 大口出金:
finalizedを待つ - コンプライアンス/監査: 常に
finalizedステータスを記録する
トランザクションステータスの確認方法については、 Solanaとのインタラクションを参照してください。
エラー処理
Solana Kit は isSolanaError()
を通じて型付きエラーを提供します。文字列マッチングの代わりに、特定のエラーコードを使用してください:
import {isSolanaError,SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE,SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND,SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS} from "@solana/kit";function handlePaymentError(error: unknown): {message: string;retryable: boolean;} {// Blockhash expired—rebuild and retryif (isSolanaError(error, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED) ||isSolanaError(error, SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND)) {return { message: "Transaction expired—rebuilding", retryable: true };}// Insufficient SOL for feesif (isSolanaError(error,SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE)) {return { message: "Not enough SOL for fees", retryable: false };}// Insufficient token balanceif (isSolanaError(error, SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS)) {return { message: "Insufficient balance", retryable: false };}// Unknown errorconsole.error("Payment error:", error);return { message: "Payment failed—please retry", retryable: true };}
一般的なエラーコード:
| エラーコード | 原因 | 対処法 |
|---|---|---|
BLOCK_HEIGHT_EXCEEDED | ブロックハッシュの有効期限切れ | 新しいブロックハッシュで再構築 |
BLOCKHASH_NOT_FOUND | ブロックハッシュが見つからない | 新しいブロックハッシュで再構築 |
INSUFFICIENT_FUNDS_FOR_FEE | SOL残高不足 | 手数料支払者に入金するか、手数料の抽象化を使用 |
INSUFFICIENT_FUNDS | トークン残高不足 | ユーザーの残高が不足しています |
ACCOUNT_NOT_FOUND | token account が存在しない | トランザクションにATAを作成 |
ガスレストランザクション
ユーザーはネットワーク手数料のためにSOLを取得するのではなく、ステーブルコインで支払うことを望んでいます。ガスレストランザクションはこの問題を解決します。これは、VenmoユーザーがACH手数料を意識しないのと同様です。完全な実装については、 手数料の抽象化 をご覧ください。
セキュリティ
鍵管理
- フロントエンドコードに秘密鍵を絶対に公開しないでください。 バックエンド署名、ハードウェアウォレット、マルチシグウォレット、または鍵管理サービスを使用してください。
- ホットウォレットとコールドウォレットを分離してください。 ホットウォレットは運用用、コールドウォレットは資産保管用に使用します。
- すべての本番環境の鍵をバックアップしてください。 暗号化されたバックアップを複数の安全な場所に保管してください。鍵を紛失すると、アクセスが永久に失われます。
- 開発ネットと本番ネットで異なる鍵を使用してください。 開発ネットの鍵を本番ネットの鍵として使用しないでください。環境ベースの設定を使用して、各ネットワークに適切な鍵が読み込まれるようにしてください。
署名インフラストラクチャ
本番環境のバックエンド署名には、Keychainを使用してください。これは、単一のインターフェースを通じて複数の鍵管理バックエンドを抽象化する統合署名ライブラリです。対応バックエンド:Memory、Vault、Privy、Turnkey、AWS KMS、Fireblocks、GCP KMS、CDP、Para、Dfns、Crossmint、Openfort、Utila。これにより、インメモリ鍵を使用してローカルで開発し、アプリケーションコードを変更することなく本番環境グレードのバックエンドに切り替えることができます。
どのバックエンドが適しているかわからない場合は、 バックエンドの選択をご覧ください。KeychainはRustと TypeScriptの両方で利用可能です。
RPCセキュリティ
RPCエンドポイントはAPIキーのように扱ってください。抽出や悪用が可能なフロントエンドコードに公開しないでください。クライアントコードにバンドルされないバックエンドプロキシまたは環境変数を使用してください。
モニタリング
本番環境では以下の指標を追跡してください:
| 指標 | 理由 |
|---|---|
| トランザクション成功率 | 早期に問題を検出 |
| 確認レイテンシ | ネットワークの健全性を監視 |
| 優先手数料支出 | コスト管理 |
| RPCエラー率 | プロバイダーの健全性 |
以下の項目にアラートを設定してください:
- トレジャリーからの閾値を超える送金
- 失敗したトランザクション率の急増
- 異常な受取人パターン
- RPCエラー率の増加
大規模なリアルタイムトランザクション監視については、インデックス作成ガイドをご覧ください。
アドレスの検証
すべてのトークンとプログラムは、メインネット上で正確に1つの正しいアドレスを持っています。USDCや他のステーブルコインを模倣した偽装トークンは一般的です。これらは同じ名前とシンボルを持ちますが、異なるミントアドレスを持ちます。アプリケーションは(要件に基づいて)ミントアドレスをハードコードまたは許可リストに登録し、信頼できないソースから動的に受け入れてはいけません。
環境ベースの設定: DevnetとMainnetでは、完全に異なるトークンミントを使用することがよくあります。アプリケーション設定を構成して、環境ごとに正しいアドレスを読み込むようにしてください。Mainnetアドレスをハードコードしてテスト中に交換し忘れたり、さらに悪いことに、Devnetアドレスを本番環境にデプロイしたりしないようにしましょう。
一般的なステーブルコインのミントには以下があります:
| トークン | 発行者 | ミントアドレス |
|---|---|---|
| USDC | Circle | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Tether | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| PYUSD | PayPal | 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo |
| USDG | Paxos | 2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH |
プログラムアドレスも重要です。間違ったプログラムにinstructionsを送信すると、失敗するか、さらに悪い場合には資金の回復不可能な損失につながります。Token Programのアドレスは以下の通りです:
| プログラム | アドレス |
|---|---|
| Token Program | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA |
| Token-2022 Program | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
適切なアドレスをアローリストに登録することで、なりすましトークンからの保護は可能ですが、誤った種類のアカウントへの送信は防げません。受取人アドレスはウォレット、token account、ミント、またはプログラムのいずれかであり、それぞれ異なる処理が必要です。ウォレット以外に送信されたネイティブSOLは送信者の管理下から外れます。ミントやプログラムへの送信では完全に失われ、token accountへの送信はアカウント所有者のみが取り戻せますが、ユーザーへの警告となる失敗トランザクションは発生しません。
送信前に受取人を検証する
送金に署名する前に、すべての受取人アドレスを分類してください。ウォレット、token account、ミント、プログラムを区別するための完全なデシジョンツリーとリファレンスコードについては、アドレスの検証を参照してください。
ローンチ前チェックリスト
- 手数料とrentのためのメインネットSOLを取得済み
- 本番用RPCを設定済み(パブリックエンドポイントは使用しない)
- フォールバックRPCエンドポイントを設定済み
- 動的な価格設定による優先手数料を実装済み
- リトライロジックでブロックハッシュの有効期限を処理済み
- ユースケースに適した確認レベルを設定済み
- すべての一般的なエラーを適切に処理済み
- ガスレスを設定済み(該当する場合)
- メインネットのトークンアドレスを確認済み(devnetのミントではない)
- 送信前に受取人アドレスを検証済み(ウォレット vs token account vs ミント vs プログラム)
- すべての鍵を安全にバックアップ済み
- 鍵管理を確認済み(フロントエンドに鍵を置かない)
- トランザクションの監視とアラートを有効化済み
- 想定トラフィックでの負荷テスト完了
プログラムのデプロイ
支払いインフラの一部としてカスタムSolanaプログラムをデプロイする場合、追加の考慮事項があります。
デプロイ前の準備
- Solana CLIのバージョン: 最新バージョンの Solana CLIを使用していることを確認してください。
- プログラムのkeypair:
メインネット上のプログラムアドレスは、devnetとは異なります(同じkeypairを再利用する場合を除く)。アプリケーション設定内のすべての参照を更新してください。プログラムのkeypairは安全な場所に保管してください(
cargo cleanを実行すると、プログラムのkeypairが削除される可能性があります)。 - アカウントの初期化: プログラムに管理者アカウント、PDA、またはその他のステートアカウントが必要な場合、ユーザーがアプリケーションを操作する前に、これらをメインネット上で作成しておく必要があります。プログラムが必要とするassociated token accountについても同様です。
デプロイのプロセス
- バッファーアカウント: 大規模なプログラムはbuffer
accountsを介してデプロイされます。
solana program deployコマンドはこれを自動的に処理しますが、デプロイはアトミックではないことに注意してください。中断された場合、buffer accountsの回復またはクローズが必要になることがあります。詳細はプログラムのデプロイを参照してください。 - アップグレード権限: プログラムをリリース後にアップグレード可能にするかどうかを決定してください。不変性を確保するには、デプロイ後にアップグレード権限を失効させてください。柔軟性を保持する場合は、アップグレード権限キーを適切に保護してください。
- レント: デプロイ用ウォレットに、すべてのprogram accountのレント免除最低額をカバーするのに十分なSOLがあることを確認してください。
- 検証: プログラムを検証して、Solanaネットワークにデプロイした実行可能プログラムがリポジトリのソースコードと一致していることを確認してください。
プログラムのデプロイに関する完全なガイダンスについては、 プログラムのデプロイを参照してください。
Is this page helpful?