交易

要与 Solana 网络交互,您必须发送一笔交易。您可以将交易视为一个装有多种表单的信封。每个表单都是一条指令,告诉网络该做什么。发送交易就像邮寄信封,以便处理这些表单。

下面的示例展示了两个交易的简化版本。当第一个交易被处理时,它将执行一条指令。当第二个交易被处理时,它将按顺序执行三条指令:首先是指令 1,然后是指令 2,最后是指令 3。

交易是原子性的:如果单条指令失败,整个交易将失败,并且不会发生任何更改。

一个展示两个交易的简化图示一个展示两个交易的简化图示

一个 Transaction 包含以下信息:

  • signatures:一个签名数组
  • message:交易信息,包括要处理的指令列表
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

展示交易两部分的图示展示交易两部分的图示

交易的总大小限制为 1232 字节。此限制包括 signatures 数组和 message 结构体。

此限制来源于 IPv6 最大传输单元 (MTU) 的大小为 1280 字节,减去 48 字节的网络头(40 字节 IPv6 + 8 字节头)。

展示交易格式和大小限制的图示展示交易格式和大小限制的图示

签名

交易的 signatures 数组包含 Signature 结构体。每个 Signature 为 64 字节,通过使用账户的私钥对交易的 Message 进行签名创建。每个 签名账户 的指令都必须提供一个签名。

第一个签名属于支付交易基础费用的账户,并且是交易签名。交易签名可用于在网络上查找交易的详细信息。

消息

交易的 message 是一个 Message 结构体,包含以下信息:

  • header:消息的头部
  • account_keys:一个 账户地址数组,交易指令所需的
  • recent_blockhash:一个 区块哈希,作为交易的时间戳
  • instructions:一个 指令数组

为了节省空间,交易不会单独存储每个账户的权限。相反,账户权限是通过 headeraccount_keys 确定的。

Message
pub struct Message {
/// The message header, identifying signed and read-only `account_keys`.
pub header: MessageHeader,
/// All the account keys used by this transaction.
#[serde(with = "short_vec")]
pub account_keys: Vec<Pubkey>,
/// The id of a recent ledger entry.
pub recent_blockhash: Hash,
/// Programs that will be executed in sequence and committed in
/// one atomic transaction if all succeed.
#[serde(with = "short_vec")]
pub instructions: Vec<CompiledInstruction>,
}

头部

消息的 header 是一个 MessageHeader 结构体,包含以下信息:

  • num_required_signatures:交易所需的总签名数
  • num_readonly_signed_accounts:需要签名的只读账户总数
  • num_readonly_unsigned_accounts:不需要签名的只读账户总数
MessageHeader
pub struct MessageHeader {
/// The number of signatures required for this message to be considered
/// valid. The signers of those signatures must match the first
/// `num_required_signatures` of [`Message::account_keys`].
pub num_required_signatures: u8,
/// The last `num_readonly_signed_accounts` of the signed keys are read-only
/// accounts.
pub num_readonly_signed_accounts: u8,
/// The last `num_readonly_unsigned_accounts` of the unsigned keys are
/// read-only accounts.
pub num_readonly_unsigned_accounts: u8,
}

显示消息头部三个部分的图示显示消息头部三个部分的图示

账户地址

消息的 account_keys 是一个账户地址数组,以 紧凑数组格式发送。数组的前缀表示其长度。数组中的每一项是一个公钥,指向其指令使用的账户。accounts_keys 数组必须完整,并严格按以下顺序排列:

  1. 签名者 + 可写
  2. 签名者 + 只读
  3. 非签名者 + 可写
  4. 非签名者 + 只读

严格的排序允许 account_keys 数组与消息的 header 中的信息结合,以确定每个账户的权限。

显示账户地址数组顺序的图示显示账户地址数组顺序的图示

最近的区块哈希

消息的 recent_blockhash 是一个哈希值,作为交易的时间戳并防止重复交易。区块哈希在 150 个区块后过期。(相当于一分钟——假设每个区块为 400 毫秒。)区块过期后,交易也会过期,无法被处理。

getLatestBlockhash RPC 方法 允许您获取当前的区块哈希以及区块哈希有效的最后区块高度。

指令

消息的 instructions 是一个包含所有待处理指令的数组,采用 紧凑数组格式。数组的前缀表示其长度。数组中的每一项是一个 CompiledInstruction 结构体,包含以下信息:

  1. program_id_index:一个索引,指向 account_keys 数组中的地址。此值表示处理该指令的程序的地址。
  2. accounts:一个索引数组,指向 account_keys 数组中的地址。每个索引指向该指令所需账户的地址。
  3. data:一个字节数组,指定要在程序上调用的指令。它还包括指令所需的任何附加数据。(例如,函数参数)
CompiledInstruction
pub struct CompiledInstruction {
/// Index into the transaction keys array indicating the program account that executes this instruction.
pub program_id_index: u8,
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program.
#[serde(with = "short_vec")]
pub accounts: Vec<u8>,
/// The program input data.
#[serde(with = "short_vec")]
pub data: Vec<u8>,
}

指令的紧凑数组指令的紧凑数组

示例交易结构

以下示例展示了包含单个 SOL 转账指令的交易结构。

import {
createSolanaRpc,
generateKeyPairSigner,
lamports,
createTransactionMessage,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
pipe,
signTransactionMessageWithSigners,
getCompiledTransactionMessageDecoder
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
const rpc = createSolanaRpc("http://localhost:8899");
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Generate sender and recipient keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const LAMPORTS_PER_SOL = 1_000_000_000n;
const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL
// Create a transfer instruction for transferring SOL from sender to recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Decode the messageBytes
const compiledTransactionMessage =
getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);
console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Console
Click to execute the code.

以下代码展示了前面代码片段的输出。 格式因 SDK 而异, 但请注意,每个指令都包含相同的必要信息。

{
"version": 0,
"header": {
"numSignerAccounts": 1,
"numReadonlySignerAccounts": 0,
"numReadonlyNonSignerAccounts": 1
},
"staticAccounts": [
"HoCy8p5xxDDYTYWEbQZasEjVNM5rxvidx8AfyqA4ywBa",
"5T388jBjovy7d8mQ3emHxMDTbUF8b7nWvAnSiP3EAdFL",
"11111111111111111111111111111111"
],
"lifetimeToken": "EGCWPUEXhqHJWYBfDirq3mHZb4qDpATmYqBZMBy9TBC1",
"instructions": [
{
"programAddressIndex": 2,
"accountIndices": [0, 1],
"data": {
"0": 2,
"1": 0,
"2": 0,
"3": 0,
"4": 128,
"5": 150,
"6": 152,
"7": 0,
"8": 0,
"9": 0,
"10": 0,
"11": 0
}
}
]
}

提交交易后,您可以使用交易签名和 getTransaction RPC 方法检索其详细信息。响应的结构将类似于以下代码片段。

您还可以使用 Solana Explorer 查找交易。

Transaction Data
{
"blockTime": 1745196488,
"meta": {
"computeUnitsConsumed": 150,
"err": null,
"fee": 5000,
"innerInstructions": [],
"loadedAddresses": {
"readonly": [],
"writable": []
},
"logMessages": [
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"postBalances": [989995000, 10000000, 1],
"postTokenBalances": [],
"preBalances": [1000000000, 0, 1],
"preTokenBalances": [],
"rewards": [],
"status": {
"Ok": null
}
},
"slot": 13049,
"transaction": {
"message": {
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1,
"numRequiredSignatures": 1
},
"accountKeys": [
"8PLdpLxkuv9Nt8w3XcGXvNa663LXDjSrSNon4EK7QSjQ",
"7GLg7bqgLBv1HVWXKgWAm6YoPf1LoWnyWGABbgk487Ma",
"11111111111111111111111111111111"
],
"recentBlockhash": "7ZCxc2SDhzV2bYgEQqdxTpweYJkpwshVSDtXuY7uPtjf",
"instructions": [
{
"accounts": [0, 1],
"data": "3Bxs4NN8M2Yn4TLb",
"programIdIndex": 2,
"stackHeight": null
}
],
"indexToProgramIds": {}
},
"signatures": [
"3jUKrQp1UGq5ih6FTDUUt2kkqUfoG2o4kY5T1DoVHK2tXXDLdxJSXzuJGY4JPoRivgbi45U2bc7LZfMa6C4R3szX"
]
},
"version": "legacy"
}

Is this page helpful?

Table of Contents

Edit Page

管理者

©️ 2025 Solana 基金会版权所有
取得联系
交易 | Solana