Transactions

Pour interagir avec le réseau Solana, vous devez envoyer une transaction. Vous pouvez considérer une transaction comme une enveloppe contenant plusieurs formulaires. Chaque formulaire est une instruction qui indique au réseau quoi faire. Envoyer la transaction revient à envoyer l'enveloppe par la poste pour que les formulaires puissent être traités.

L'exemple ci-dessous montre une version simplifiée de deux transactions. Lorsque la première transaction est traitée, elle exécutera une seule instruction. Lorsque la seconde transaction est traitée, elle exécutera trois instructions dans un ordre séquentiel : d'abord l'instruction 1, suivie de l'instruction 2, puis de l'instruction 3.

Les transactions sont atomiques : si une seule instruction échoue, toute la transaction échouera et aucun changement n'aura lieu.

Un diagramme simplifié montrant deux transactionsUn diagramme simplifié montrant deux transactions

Une Transaction comprend les informations suivantes :

  • signatures : Un tableau de signatures
  • message : Informations de transaction, y compris la liste des instructions à traiter
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

Diagramme montrant les deux parties d'une transactionDiagramme montrant les deux parties d'une transaction

Les transactions ont une limite de taille totale de 1232 octets. Cette limite inclut à la fois le tableau signatures et la structure message.

Cette limite provient de la taille maximale d'unité de transmission (MTU) IPv6 de 1280 octets, moins 48 octets pour les en-têtes réseau (40 octets IPv6 + 8 octets d'en-tête).

Diagramme montrant le format de transaction et les limites de tailleDiagramme montrant le format de transaction et les limites de taille

Signatures

Le tableau signatures de la transaction contient des structures Signature. Chaque Signature fait 64 octets et est créée en signant le Message de la transaction avec la clé privée du compte. Une signature doit être fournie pour chaque compte signataire inclus dans l'une des instructions de la transaction.

La première signature appartient au compte qui paiera les frais de base de la transaction et constitue la signature de la transaction. La signature de la transaction peut être utilisée pour rechercher les détails de la transaction sur le réseau.

Message

Le message de la transaction est une structure Message qui contient les informations suivantes :

  • header : L'en-tête du message
  • account_keys : Un tableau d'adresses de comptes requises par les instructions de la transaction
  • recent_blockhash : Un blockhash qui sert d'horodatage pour la transaction
  • instructions : Un tableau d'instructions

Pour économiser de l'espace, la transaction ne stocke pas les permissions pour chaque compte individuellement. Au lieu de cela, les permissions des comptes sont déterminées en utilisant le header et le 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>,
}

En-tête

Le header du message est une structure MessageHeader. Il contient les informations suivantes :

  • num_required_signatures : Le nombre total de signatures requises par la transaction
  • num_readonly_signed_accounts : Le nombre total de comptes en lecture seule qui nécessitent des signatures
  • num_readonly_unsigned_accounts : Le nombre total de comptes en lecture seule qui ne nécessitent pas de signatures
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,
}

Diagramme montrant les trois parties de l'en-tête du messageDiagramme montrant les trois parties de l'en-tête du message

Adresses des comptes

Le account_keys du message est un tableau d'adresses de comptes, envoyé au format de tableau compact. Le préfixe du tableau indique sa longueur. Chaque élément du tableau est une clé publique, qui pointe vers un compte utilisé par ses instructions. Le tableau accounts_keys doit être complet et strictement ordonné comme suit :

  1. Signataire + Modifiable
  2. Signataire + Lecture seule
  3. Non signataire + Modifiable
  4. Non signataire + Lecture seule

L'ordre strict permet de combiner le tableau account_keys avec les informations dans le header du message pour déterminer les permissions pour chaque compte.

Diagramme montrant l'ordre du tableau d'adresses de comptesDiagramme montrant l'ordre du tableau d'adresses de comptes

Blockhash récent

Le recent_blockhash du message est une valeur de hachage qui agit comme un horodatage de transaction et empêche les transactions en double. Un blockhash expire après 150 blocs. (Équivalent à une minute — en supposant que chaque bloc dure 400ms.) Après l'expiration du bloc, la transaction est expirée et ne peut plus être traitée.

La méthode RPC getLatestBlockhash vous permet d'obtenir le blockhash actuel et la dernière hauteur de bloc à laquelle le blockhash sera valide.

Instructions

Le instructions du message est un tableau de toutes les instructions à traiter, envoyé au format de tableau compact. Le préfixe du tableau indique sa longueur. Chaque élément du tableau est une structure CompiledInstruction et comprend les informations suivantes :

  1. program_id_index : Un index pointant vers une adresse dans le tableau account_keys. Cette valeur indique l'adresse du programme qui traite l'instruction.
  2. accounts : Un tableau d'indices pointant vers des adresses dans le tableau account_keys. Chaque index pointe vers l'adresse d'un compte requis pour cette instruction.
  3. data : Un tableau d'octets spécifiant quelle instruction invoquer sur le programme. Il inclut également toutes les données supplémentaires requises par l'instruction. (Par exemple, les arguments de fonction)
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>,
}

Tableau compact d'instructionsTableau compact d'instructions

Structure d'une transaction d'exemple

L'exemple suivant montre la structure d'une transaction qui contient une seule instruction de transfert de 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.

Le code ci-dessous montre la sortie des extraits de code précédents. Le format diffère entre les SDK, mais notez que chaque instruction contient les mêmes informations requises.

{
"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
}
}
]
}

Après avoir soumis une transaction, vous pouvez récupérer ses détails en utilisant la signature de la transaction et la méthode RPC getTransaction. La réponse aura une structure similaire à l'extrait suivant.

Vous pouvez également trouver la transaction en utilisant 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 des matières

Modifier la page

Géré par

© 2025 Fondation Solana.
Tous droits réservés.
Restez connecté
Transactions | Solana