关于 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 交易由以下部分组成:
- 签名:一个或多个 Ed25519 签名(每个 64 字节)
- 消息:可序列化的载荷,包含:
- 标头:所需签名者、只读签名者、只读非签名者的数量
- 账户密钥:指令引用的所有公钥数组
- 近期区块哈希:用于防重放的 32 字节哈希(约 60-90 秒后过期)
- 指令:程序调用数组
交易大小限制
整个序列化交易的硬性限制为 1232 字节。这限制了可包含的指令和账户数量。以下策略有助于控制在限制范围内:
- 使用带有地址查找表(ALTs)的版本化交易
- 尽量减少每条指令的账户数量
- 在支持的情况下,将相关操作合并到单条指令中
- 将复杂操作拆分到多个交易中
指令格式
每条指令包含三个字段:
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
}
这四种组合决定了账户的角色:
| is_signer | is_writable | 角色 |
|---|---|---|
| true | true | 费用支付者、执行转账的代币所有者 |
| true | false | 多签共同签名者、只读权限方 |
| false | true | 目标账户、正在写入的 PDA |
| false | false | 程序 ID、系统变量、时钟 |
传统交易与版本化交易
传统交易
原始格式。所有账户都必须列在账户密钥数组中。在 1232 字节的限制下,根据指令数据大小,通常可容纳约 20-35 个账户。
版本化交易(v0)
此格式的引入是为了支持地址查找表(ALTs)。v0 交易包含:
- 版本前缀字节(v0 为
0x80) - 与传统交易相同的消息结构
- 额外的
address_table_lookups数组
ALTs 让你可以通过链上表中的紧凑索引引用账户,而不必包含完整的 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 交互。
计算预算
每笔交易都有一个计算预算,用于确定可消耗的计算单元(CUs)数量以及需要支付的优先费。
计算预算指令
来自计算预算程序(ComputeBudget111111111111111111111111111111)的两条关键指令:
1. 设置计算单元上限
Instruction data: [0x02, <units as u32 LE>]
设置此交易可消耗的最大 CU 数量。默认值为每条指令 200,000(每笔交易最多 1,400,000)。设置得低于所需值会导致交易失败。设置得更高会浪费预算,但不会增加费用(你只需为申请的数量付费,而非实际消耗的数量)。
2. 设置计算单元价格
Instruction data: [0x03, <micro_lamports as u64 LE>]
设置每 CU 的价格,单位为微 lamport。这是优先费机制。优先费总额为:
priority_fee = compute_unit_limit * compute_unit_price / 1_000_000
优先费估算
估算合适的优先费:
- 调用
getRecentPrioritizationFeesRPC 方法,并传入交易涉及的账户 - 查看近期 slot 的费用中位数或第 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 Token 转账
转移 SPL Token 需要 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)
所需账户:
- 源 Token 账户(可写)
- 目标 Token 账户(可写)
- 所有者/代理(签名者)
3. 创建关联 Token 账户(ATA)
转移 Token 前,接收方必须拥有一个关联 Token 账户。
# ATA Program: instruction index 0 = Create
# No instruction data needed (empty bytes)
ATA_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
所需账户(按顺序):
- 付款方(签名者,可写)— 支付租金
- 关联 Token 账户(可写)— 要创建的 ATA
- 钱包地址 — 新 ATA 的所有者
- Token mint
- 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 | Token 账户不存在 | 先创建 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 - 验证账户 — 确认所有账户地址正确(尤其是 Token 转账)
- 检查余额 — 确保有足够的 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— 获取并解码链上交易,并识别程序