Транзакции

Чтобы взаимодействовать с сетью 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 больше 9000 байт, большинство интернет-маршрутизаторов используют стандартное значение MTU в 1500 байт (стандарт Ethernet). Чтобы гарантировать, что транзакции помещаются в один пакет без фрагментации, Solana использует 1280 байт (минимальное значение MTU, требуемое для IPv6) минус 48 байт для сетевых заголовков (40 байт для IPv6 + 8 байт для заголовка фрагментации/UDP), что в итоге дает ограничение размера транзакции в 1232 байта.

Диаграмма, показывающая формат транзакции и ограничения по размеруДиаграмма, показывающая формат транзакции и ограничения по размеру

Подписи

Массив signatures транзакции содержит структуры Signature. Каждая Signature занимает 64 байта и создаётся путём подписи Message транзакции приватным ключом аккаунта. Для каждого аккаунта-подписанта, участвующего в любой из инструкций транзакции, должна быть предоставлена подпись.

Первая подпись принадлежит аккаунту, который оплатит базовую комиссию транзакции, и является подписью транзакции. Подпись транзакции можно использовать для поиска деталей транзакции в сети.

Сообщение

Структура message транзакции — это Message, которая содержит следующую информацию:

  • header: Заголовок header сообщения
  • account_keys: Массив адресов аккаунтов, необходимых для инструкций транзакции
  • recent_blockhash: blockhash, который выступает в роли временной метки для транзакции
  • instructions: Массив инструкций

Чтобы сэкономить место, транзакция не хранит права доступа для каждого аккаунта отдельно. Вместо этого права определяются с помощью header и account_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 для определения прав доступа для каждого аккаунта.

Диаграмма, показывающая порядок массива адресов аккаунтовДиаграмма, показывающая порядок массива адресов аккаунтов

Недавний blockhash

Поле recent_blockhash сообщения — это хеш-значение, которое выступает в роли метки времени транзакции и предотвращает дублирование транзакций. Blockhash истекает через 150 блоков. (Это эквивалентно одной минуте — при условии, что каждый блок занимает 400 мс.) После истечения срока действия блока транзакция становится недействительной и не может быть обработана.

Метод RPC getLatestBlockhash позволяет получить текущий blockhash и последний номер блока, на котором blockhash будет действителен.

Инструкции

Поле сообщения 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
}
}
]
}

После отправки транзакции вы можете получить её детали, используя подпись транзакции и метод RPC getTransaction. Ответ будет иметь структуру, похожую на следующий фрагмент.

Вы также можете найти транзакцию с помощью 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?

Содержание

Редактировать страницу

Управляется

© 2026 Фонд Solana.
Все права защищены.
Подключиться