Solanaの基本
Solanaトランザクションの基礎
Last updated September 21, 2026
構造から確認まで、Solanaトランザクションの仕組みを包括的に解説します。
このガイドで学ぶこと
- Solanaトランザクションの構造
- トランザクションに署名して送信する方法
- トランザクションの確認とファイナリティ
- バージョン付きトランザクションとlegacyトランザクションの違い
- 一般的なトランザクションエラーとその意味
前提条件
トランザクションの構造
Solanaトランザクションは複数のコンポーネントで構成されます。
┌─────────────────────────────────────────────────────────────┐
│ Transaction │
├─────────────────────────────────────────────────────────────┤
│ Signatures: [sig1, sig2, ...] │
│ │
│ Message: │
│ ├── Header │
│ │ ├── num_required_signatures │
│ │ ├── num_readonly_signed_accounts │
│ │ └── num_readonly_unsigned_accounts │
│ │ │
│ ├── Account Keys: [pubkey1, pubkey2, ...] │
│ │ │
│ ├── Recent Blockhash │
│ │ │
│ └── Instructions: [ │
│ { program_id_index, accounts, data }, │
│ { program_id_index, accounts, data }, │
│ ] │
└─────────────────────────────────────────────────────────────┘
主要コンポーネント
| コンポーネント | 説明 |
|---|---|
| Signatures | 必要な署名者によるEd25519署名 |
| Recent Blockhash | 最近のブロックハッシュ(約60〜90秒間有効) |
| Instructions | 実行する処理 |
| Account Keys | トランザクションに関与するすべてのアカウント |
命令
命令はトランザクション内で実際に実行される処理です。各命令は次の項目を指定します。
- Program ID - 実行するプログラム
- Accounts - プログラムが必要とするアカウント
- Data - プログラムに渡すシリアライズ済み引数
Instruction:
├── program_id: 11111111111111111111111111111111 (System Program)
├── accounts:
│ ├── sender (signer, writable)
│ └── recipient (not signer, writable)
└── data: [encoded transfer amount]
複数の命令
トランザクションには、アトミックに実行される複数の命令を含めることができます。
import { transactionBuilder } from '@metaplex-foundation/umi'
// All instructions succeed or all fail (atomic)
const builder = transactionBuilder()
.add(createAccountInstruction)
.add(initializeMintInstruction)
.add(mintTokensInstruction)
await builder.sendAndConfirm(umi)
このアトミック性により、いずれかの命令が失敗するとトランザクション全体がロールバックされます。
Recent Blockhash
すべてのトランザクションには、次の役割を持つrecent blockhashが必要です。
- トランザクションが最近作成されたことを証明する
- リプレイ攻撃を防ぐ
- 約60〜90秒後(約150スロット後)に期限切れになる
// UMI handles blockhash automatically when sending transactions.
// To fetch it manually:
const { blockhash, lastValidBlockHeight } = await umi.rpc.getLatestBlockhash()
ブロックハッシュの期限切れ
ブロックハッシュの期限が切れる前にトランザクションが確認されなければ、そのトランザクションは破棄されます。時間のかかる処理では、送信前に新しいブロックハッシュを取得してください。
トランザクションへの署名
トランザクションには、isSignerと指定されたすべてのアカウントによる署名が必要です。
// UMI signs automatically with the identity signer when sending.
// For additional signers, pass them during the build:
const tx = await myBuilder
.setBlockhash(await umi.rpc.getLatestBlockhash())
.buildAndSign(umi)
// For partial signing (multi-sig workflows), you can sign
// and serialize the transaction, then pass it to another party:
import { base64 } from '@metaplex-foundation/umi/serializers'
const serialized = umi.transactions.serialize(tx)
const encoded = base64.deserialize(serialized)[0]
// ... send encoded string to another party for additional signing ...
トランザクションの送信
基本的な送信
// Send and wait for confirmation (recommended)
const result = await myBuilder.sendAndConfirm(umi)
// Or just send without waiting
const signature = await myBuilder.send(umi)
オプションを指定した送信
const result = await myBuilder.sendAndConfirm(umi, {
send: { skipPreflight: false },
confirm: { commitment: 'confirmed' },
})
トランザクションの確認
Solanaには、トランザクションのファイナリティを示す複数のcommitment levelがあります。
| Commitment | 説明 | ユースケース |
|---|---|---|
processed | リーダーがトランザクションを受信済み | リアルタイム更新 |
confirmed | スーパーマジョリティによる投票済み | ほとんどのアプリケーション |
finalized | 31ブロック以上経過し、不可逆 | 金融処理 |
確認状況を調べる
// sendAndConfirm waits for confirmation automatically.
// To check a signature status manually:
const result = await umi.rpc.getSignatureStatuses([signature])
実際のCommitment指定
// For most operations, 'confirmed' is the right default
const result = await myBuilder.sendAndConfirm(umi, {
confirm: { commitment: 'confirmed' },
})
// For financial operations where you need full finality
const result = await myBuilder.sendAndConfirm(umi, {
confirm: { commitment: 'finalized' },
})
バージョン付きトランザクション
Solanaは3つのトランザクション形式をサポートしています。
Legacyトランザクション
Legacyトランザクションは、Address Lookup Tablesを使用しないSolanaの元来のトランザクション形式です。
- 元来の形式
- 最大35アカウント
- より単純な構造
V0トランザクション
V0トランザクションは、より多くのアカウントを必要とするトランザクション向けにAddress Lookup Tableをサポートします。
- Address Lookup Tables(ALT)をサポート
- 最大256アカウントを参照可能
- 複雑なDeFi処理で必要
V1トランザクション
V1トランザクションではトランザクションのサイズ上限が増え、コンピュート設定がメッセージ内に格納されます。
- 最大4,096バイトのトランザクションをサポート
- コンピュートバジェット設定をトランザクションメッセージ内に格納
- Address Lookup Tablesはサポートしない
// Umi uses V0 transactions by default. Opt in to V1 explicitly.
const result = await myBuilder
.useV1()
.sendAndConfirm(umi)
// To use legacy transactions instead
const result = await myBuilder
.useLegacyVersion()
.sendAndConfirm(umi)
// With Address Lookup Tables
import { createLut } from '@metaplex-foundation/mpl-toolbox'
const [lutBuilder, lut] = createLut(umi, {
recentSlot: await umi.rpc.getSlot({ commitment: 'finalized' }),
addresses: [addressA, addressB, addressC],
})
await lutBuilder.sendAndConfirm(umi)
// Address Lookup Tables require V0.
await myBuilder
.useV0()
.setAddressLookupTables([lut])
.sendAndConfirm(umi)
トランザクションバージョンの選び方
- ウォレットがトランザクションバージョン
1をサポートし、トランザクションが1,232バイトを超える場合はV1を使用します。 - トランザクションにAddress Lookup Tableが必要な場合はV0を使用します。
- 互換性のために元来の形式が必要な場合にのみlegacyトランザクションを使用します。
後方互換性のため、UmiのデフォルトはV0です。アプリケーション全体のデフォルトを変更する前に、V0からV1トランザクションへの移行を参照してください。
トランザクションのサイズ制限
Solanaトランザクションには厳格なサイズ制限があります。
| 制限 | 値 |
|---|---|
| LegacyおよびV0トランザクションのサイズ | 1,232バイト |
| V1トランザクションのサイズ | 4,096バイト |
| Address Lookup Tables | V0のみ |
| 命令の最大数 | サイズによる制限 |
サイズ制限への対処
トランザクションが大きすぎる場合は、次の方法で対処します。
- V1を使用する - Address Lookup Tableが不要な場合、サイズ上限を4,096バイトに増やす
- V0でAddress Lookup Tablesを使用する - アカウント参照を圧縮する
- 複数のトランザクションに分割する - 順番に実行する
- 命令データを最適化する - シリアライズされるデータを最小化する
シミュレーション
送信前にトランザクションをシミュレーションしてエラーを検出します。
// Build the transaction without sending
const tx = await myBuilder
.setBlockhash(await umi.rpc.getLatestBlockhash())
.buildAndSign(umi)
// Simulate it
const simulation = await umi.rpc.simulateTransaction(tx, {
commitment: 'confirmed',
})
console.log('Simulation result:', simulation)
シミュレーションには次の利点があります。
- 料金を支払う前にエラーを検出できる
- コンピュートユニットを見積もれる
- プログラムロジックをデバッグできる
一般的なトランザクションエラー
"Blockhash not found"
原因:確認前にブロックハッシュの期限が切れています。
解決方法:
- 新しいブロックハッシュで再試行します(UMIは送信のたびに新しいブロックハッシュを自動取得します)
- ネットワーク混雑時には、ブロックハッシュに
'finalized'commitmentを使用します - アプリケーションに再試行ロジックを実装します
"Insufficient funds"
原因:アカウントにトランザクション料金とrentを支払うための十分なSOLがありません。
解決方法:fee payerに十分な残高があることを確認します。
solana balance
solana airdrop 1 # On devnet
"Transaction simulation failed"
原因:プログラムロジックのエラーです。
解決方法:Solana Explorerの使用を参照してExplorerでシミュレーションログを確認するか、送信前にトランザクションをシミュレーションしてエラー出力を調べます。
"Account not found"
原因:トランザクション内のアカウントが存在しません。
解決方法:先にアカウントを作成するか、アドレスを確認します。
"Invalid account owner"
原因:アカウントが想定とは異なるプログラムによって所有されています。
解決方法:アカウントの所有者が呼び出し先のプログラムと一致することを確認します。
実践例:完全なフロー
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
import { transferSol } from '@metaplex-foundation/mpl-toolbox'
import { signerIdentity, generateSigner, sol, publicKey } from '@metaplex-foundation/umi'
import { base58 } from '@metaplex-foundation/umi/serializers'
// 1. Create UMI instance
const umi = createUmi('https://api.devnet.solana.com')
// 2. Set up signer (your wallet)
const signer = generateSigner(umi)
umi.use(signerIdentity(signer))
// 3. Build and send the transaction
const result = await transferSol(umi, {
source: umi.identity,
destination: publicKey('RecipientAddressHere...'),
amount: sol(1),
}).sendAndConfirm(umi)
// 4. Get the transaction signature
const signature = base58.deserialize(result.signature)[0]
console.log('Transaction confirmed:', signature)
console.log(`Explorer: https://explorer.solana.com/tx/${signature}?cluster=devnet`)
次のステップ
- コンピュートユニットと優先料金 - トランザクション実行を最適化する
- devnetとtestnetの使用 - トランザクションをテストする
- トランザクションエラーの診断 - 失敗したトランザクションをデバッグする
FAQ
トランザクションを確認できる時間はどのくらいですか?
トランザクションのブロックハッシュは約60〜90秒間(約150スロット)有効です。その時間を過ぎても確認されなかったトランザクションは破棄されます。
トランザクションをキャンセルできますか?
いいえ。一度送信したトランザクションはキャンセルできません。ただし、まだ確認されていない場合は、durable nonceを使用して同じnonceの新しいトランザクションを送信し、実質的に「置き換える」ことができます。
"processed"と"confirmed"の違いは何ですか?
"Processed"は、バリデーターがトランザクションを受信したことを意味します。"Confirmed"は、トランザクションを含むブロックに対してスーパーマジョリティ(66%以上)のバリデーターが投票したことを意味します。重要な処理には必ず"confirmed"または"finalized"を使用してください。
シミュレーションが成功した後にトランザクションが失敗したのはなぜですか?
シミュレーションと実行の間に状態が変化することがあります。別のトランザクションがアカウントを変更した可能性があります。これはNFTのミントなど、競合が発生する状況でよく起こります。
