solana-tx-building について
solana-tx-buildingは、Solanaトランザクションの構築、シミュレーション、検査を行う開発者向けスキルです。命令形式、バージョン付きトランザクション、計算予算、手数料、デコードを扱い、スクリプトは実際の署名や送信を行いません。
主な機能
- 01トランザクション命令の構築
- 02バージョン付き形式とALTの利用
- 03計算上限と優先手数料の設定
- 04送信せずにトランザクションをシミュレーション
- 05オンチェーントランザクションの取得とデコード
活用シーン
- 01複数アカウントを含むSolanaトランザクションの準備
- 02シミュレーションによる動作テスト
- 03確定済みトランザクションの内容確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agiprolabs/ claude-trading-skills --skill solana-tx-buildingインストール後、Claude Code で/solana-tx-buildingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/common_instructions.mdtransaction_anatomy.mdscripts/build_transfer.pydecode_transaction.py詳細
Solana トランザクションの構築
このスキルでは、Solana トランザクションをプログラムで構築、シミュレーション、検査する方法を説明します。生の命令エンコードから、バージョン付きトランザクション形式、コンピュートバジェット管理、優先手数料、アドレスルックアップテーブルまで、Solana トランザクションの全体的な構造を扱います。
安全性:このスキルはトランザクションの構築と分析のみを対象とします。このスキルのスクリプトが実際のトランザクションに署名したり、送信したりすることは決してありません。送信前には必ずシミュレーションしてください。自動署名は絶対に行わないでください。
トランザクションの構造
Solana トランザクションは次の要素で構成されます。
- 署名:1 つ以上の Ed25519 署名(各 64 バイト)
- メッセージ:次の内容を含むシリアライズ可能なペイロード
- ヘッダー:必須署名者、読み取り専用署名者、読み取り専用の非署名者の数
- アカウントキー:命令が参照するすべての公開鍵の配列
- 最新ブロックハッシュ:リプレイ保護のための 32 バイトのハッシュ(約 60~90 秒で期限切れ)
- 命令:プログラム呼び出しの配列
トランザクションサイズの上限
シリアライズされたトランザクション全体の上限は 1232 バイトです。この制限により、含められる命令やアカウントの数が限られます。上限内に収める方法は次のとおりです。
- アドレスルックアップテーブル(ALT)を使用するバージョン付きトランザクションを使う
- 命令ごとのアカウント数を最小限にする
- サポートされている場合は、関連する操作を 1 つの命令にまとめる
- 複雑な操作を複数のトランザクションに分割する
命令の形式
各命令には 3 つのフィールドがあります。
Instruction {
program_id_index: u8, // Index into the account keys array
accounts: [u8], // Indices into account keys array
data: [u8], // Opaque byte array interpreted by the program
}
アカウントメタデータ
命令で参照される各アカウントには、メタデータがあります。
AccountMeta {
pubkey: Pubkey, // 32-byte public key
is_signer: bool, // Must sign the transaction
is_writable: bool, // Will be written to by this instruction
}
次の 4 つの組み合わせによって、アカウントの役割が決まります。
| is_signer | is_writable | 役割 |
|---|---|---|
| true | true | 手数料支払者、転送を実行するトークン所有者 |
| true | false | マルチシグの共同署名者、読み取り専用の権限者 |
| false | true | 宛先アカウント、書き込み対象の PDA |
| false | false | プログラム ID、sysvar、クロック |
レガシートランザクションとバージョン付きトランザクション
レガシートランザクション
従来の形式です。すべてのアカウントをアカウントキー配列に含める必要があります。1232 バイトの制限では、命令データのサイズにもよりますが、およそ 20~35 個のアカウントを含められます。
バージョン付きトランザクション(v0)
**アドレスルックアップテーブル(ALT)**をサポートするために導入されました。v0 トランザクションには次のものが含まれます。
- バージョンプレフィックスの 1 バイト(v0 の場合は
0x80) - レガシーと同じメッセージ構造
- 追加の
address_table_lookups配列
ALT を使うと、完全な 32 バイトの公開鍵を含める代わりに、オンチェーンテーブル内のコンパクトなインデックスでアカウントを参照できます。これにより、トランザクションが参照できるアカウント数が大幅に増えます。
AddressTableLookup {
account_key: Pubkey, // The ALT account address
writable_indexes: [u8], // Indices for writable accounts
readonly_indexes: [u8], // Indices for read-only accounts
}
v0 を使う場合:参照するアカウントが約 20 個を超えるトランザクション、複数ホップのルートを使う Jupiter スワップ、複雑な DeFi の操作。
コンピュートバジェット
各トランザクションには、消費できるコンピュートユニット(CU)の数と支払う優先手数料を決めるコンピュートバジェットがあります。
コンピュートバジェット命令
Compute Budget Program(ComputeBudget111111111111111111111111111111)の主な命令は 2 つあります。
1. コンピュートユニット上限の設定
Instruction data: [0x02, <units as u32 LE>]
このトランザクションが消費できる CU の最大数を設定します。デフォルトは命令ごとに 200,000(トランザクションごとの最大値は 1,400,000)です。必要量より低く設定するとトランザクションは失敗します。高く設定するとバジェットを無駄にしますが、コストは増えません(支払うのは実際の消費量ではなく、要求した量に対してです)。
2. コンピュートユニット価格の設定
Instruction data: [0x03, <micro_lamports as u64 LE>]
CU あたりの価格をマイクロラムポートで設定します。これが優先手数料の仕組みです。優先手数料の合計は次のとおりです。
priority_fee = compute_unit_limit * compute_unit_price / 1_000_000
優先手数料の見積もり
適切な優先手数料を見積もるには、次の手順を実行します。
- トランザクションが使用するアカウントを指定して、RPC メソッド
getRecentPrioritizationFeesを呼び出す - 最近のスロットにおける手数料の中央値または 75 パーセンタイルを確認する
- 混雑時には手数料が急増するため、状況を監視して動的に調整する
import httpx
def get_priority_fees(rpc_url: str, accounts: list[str]) -> list[dict]:
"""Fetch recent prioritization fees for given accounts."""
resp = httpx.post(rpc_url, json={
"jsonrpc": "2.0",
"id": 1,
"method": "getRecentPrioritizationFees",
"params": [accounts]
})
return resp.json()["result"]
一般的なトランザクションパターン
1. SOL の転送
最もシンプルなトランザクションである、System Program の転送です。
# System Program transfer instruction data layout:
# [2, 0, 0, 0] (u32 LE = instruction index 2 = Transfer)
# + amount as u64 LE (lamports)
import struct
def build_sol_transfer_data(lamports: int) -> bytes:
"""Build instruction data for a SOL transfer."""
return struct.pack("<I", 2) + struct.pack("<Q", lamports)
必要なアカウント:
- 送信者(署名者、書き込み可能)
- 受取人(書き込み可能)
2. SPL トークンの転送
SPL トークンを転送するには、Token Program が必要です。
# Token Program transfer instruction:
# [3] (instruction index 3 = Transfer)
# + amount as u64 LE
def build_token_transfer_data(amount: int) -> bytes:
"""Build instruction data for an SPL token transfer."""
return bytes([3]) + struct.pack("<Q", amount)
必要なアカウント:
- 送信元トークンアカウント(書き込み可能)
- 送信先トークンアカウント(書き込み可能)
- オーナー/デリゲート(署名者)
3. 関連付けトークンアカウント(ATA)の作成
トークンを転送する前に、受取人が関連付けトークンアカウントを持っている必要があります。
# ATA Program: instruction index 0 = Create
# No instruction data needed (empty bytes)
ATA_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
必要なアカウント(順番どおり):
- 支払者(署名者、書き込み可能) — レントを支払います
- 関連付けトークンアカウント(書き込み可能) — 作成する ATA です
- ウォレットアドレス — 新しい ATA のオーナーです
- トークンミント
- System Program
- Token Program
4. Jupiter スワップ
Jupiter は、あらかじめ構築されたインストラクションを返す /swap-instructions エンドポイントを提供しています。詳細は jupiter-api スキルを参照してください。
一般的なフロー:
/quoteから見積もりを取得します/swap-instructionsからスワップインストラクションを取得します- セットアップインストラクション、スワップインストラクション、クリーンアップインストラクションを使ってトランザクションを構築します
- コンピュートバジェットのインストラクションを追加します
- シミュレーションを行い、その後署名して送信します
シミュレーション
送信前には必ずシミュレーションしてください。simulateTransaction RPC メソッドを使用します。
def simulate_transaction(rpc_url: str, tx_base64: str) -> dict:
"""Simulate a transaction without submitting it.
Args:
rpc_url: Solana RPC endpoint URL.
tx_base64: Base64-encoded serialized transaction.
Returns:
Simulation result with logs and compute units consumed.
"""
resp = httpx.post(rpc_url, json={
"jsonrpc": "2.0",
"id": 1,
"method": "simulateTransaction",
"params": [
tx_base64,
{"encoding": "base64", "replaceRecentBlockhash": True}
]
})
result = resp.json()["result"]
if result["value"]["err"]:
print(f"Simulation failed: {result['value']['err']}")
for log in result["value"].get("logs", []):
print(f" {log}")
else:
cu = result["value"].get("unitsConsumed", 0)
print(f"Simulation OK — {cu} compute units consumed")
return result
replaceRecentBlockhash: True フラグを使うと、ブロックハッシュの有効期限が切れていてもシミュレーションできます。時間に追われずにトランザクションの構築をテストする場合に便利です。
エラー処理
よくあるトランザクションエラーとその原因:
| エラー | 原因 | 対処法 |
|---|---|---|
BlockhashNotFound | ブロックハッシュの有効期限切れ(約60~90秒) | 新しいブロックハッシュを取得して再構築します |
InsufficientFunds | 手数料と転送額に対して SOL が不足しています | 構築前に残高を確認します |
AccountNotFound | トークンアカウントが存在しません | 先に ATA を作成します |
ProgramFailedToComplete | コンピュートバジェットを超過しました | コンピュートユニットの上限を増やします |
TransactionTooLarge | 1232 バイトを超えています | ALT を使用するか、複数のトランザクションに分割します |
InvalidAccountData | インストラクションに誤ったアカウントが渡されました | アカウントの導出を確認します |
SignatureVerificationFailed | 署名者が不足しているか、誤っています | すべての is_signer アカウントが署名していることを確認します |
再試行戦略
import time
def send_with_retry(
rpc_url: str,
build_fn,
max_retries: int = 3,
base_delay: float = 0.5
) -> dict:
"""Build and send a transaction with blockhash refresh on expiry.
Args:
rpc_url: Solana RPC endpoint.
build_fn: Callable that takes a blockhash and returns a signed tx.
max_retries: Maximum retry attempts.
base_delay: Base delay between retries in seconds.
Returns:
Send result from RPC.
"""
for attempt in range(max_retries):
blockhash = get_latest_blockhash(rpc_url)
tx = build_fn(blockhash)
result = send_transaction(rpc_url, tx)
if "error" not in result:
return result
err = result["error"]
if "BlockhashNotFound" in str(err):
time.sleep(base_delay * (attempt + 1))
continue
raise RuntimeError(f"Transaction failed: {err}")
raise RuntimeError("Max retries exceeded")
トランザクションのデコード
チェーン上の既存のトランザクションをデコードするには:
def decode_transaction(rpc_url: str, signature: str) -> dict:
"""Fetch and return a parsed transaction.
Args:
rpc_url: Solana RPC endpoint.
signature: Transaction signature (base58).
Returns:
Parsed transaction data.
"""
resp = httpx.post(rpc_url, json={
"jsonrpc": "2.0",
"id": 1,
"method": "getTransaction",
"params": [
signature,
{"encoding": "jsonParsed", "maxSupportedTransactionVersion": 0}
]
})
return resp.json()["result"]
他のスキルとの連携
solana-rpc:トランザクションの送信と照会に使う RPC 接続レイヤーを提供しますjupiter-api:このスキルがトランザクションに組み込むスワップインストラクションを提供しますdex-execution:このスキルで構築したトランザクションを使用して、実行フロー全体を調整しますmev-analysis:送信前に、構築したトランザクションの MEV リスクを評価しますhelius-api:トランザクションの高度な解析と、Webhook による確認状況の追跡を提供します
安全性チェックリスト
メインネットにトランザクションを送信する前に、以下を確認してください。
- まずシミュレーションする —
sendTransactionの前に必ずsimulateTransactionを呼び出します - アカウントを検証する — すべてのアカウントアドレスが正しいことを確認します(特にトークン転送の場合)
- 残高を確認する — 手数料と転送額に対して十分な SOL があることを確認します
- コンピュートバジェットを確認する — シミュレーションに基づいて適切な CU 上限を設定します
- 優先手数料を確認する — 現在のネットワーク手数料を確認し、払いすぎないようにします
- 自動署名しない — 署名の前に、ユーザーに明示的な確認を求めます
- テストには devnet を使う — メインネットを使う前に devnet で構築とテストを行います
- すべてを記録する — トランザクション署名、シミュレーション結果、エラーを記録します
ファイル
参考資料
references/transaction_anatomy.md— メッセージ形式、バージョン付きトランザクション、コンピュートバジェット、ブロックハッシュ管理references/common_instructions.md— System、Token、ATA、Compute Budget、Memo、Jupiter プログラムのインストラクション構造
スクリプト
scripts/build_transfer.py— SOL 転送トランザクションを構築してシミュレーションします(デモモード、署名は行いません)scripts/decode_transaction.py— オンチェーントランザクションを取得し、プログラムを特定してデコードします