İşlemler ve talimatlar

Solana'da kullanıcılar, ağ ile etkileşim kurmak için işlemler gönderirler. İşlemler, işlenecek operasyonları belirten bir veya daha fazla talimat içerir. Talimatların yürütme mantığı, Solana ağına dağıtılan programlarda saklanır ve her program kendi talimat setini tanımlar.

Aşağıda Solana işlem işleme hakkında önemli detaylar bulunmaktadır:

  • Bir işlem birden fazla talimat içeriyorsa, talimatlar işleme eklenen sırayla yürütülür.
  • İşlemler "atomiktir" - tüm talimatlar başarıyla işlenmeli, aksi takdirde tüm işlem başarısız olur ve hiçbir değişiklik gerçekleşmez.

Bir işlem, esasen bir veya daha fazla talimatı işleme koymak için bir istektir. Bir işlemi, formlar içeren bir zarf olarak düşünebilirsiniz. Her form, ağa ne yapması gerektiğini söyleyen bir talimattır. İşlemi göndermek, formların işlenmesi için zarfı postalamak gibidir.

Basitleştirilmiş İşlemBasitleştirilmiş İşlem

Önemli Noktalar

  • Solana işlemleri, ağdaki programları çağıran talimatlar içerir.
  • İşlemler atomiktir - herhangi bir talimat başarısız olursa, tüm işlem başarısız olur ve hiçbir değişiklik gerçekleşmez.
  • Bir işlemdeki talimatlar sıralı olarak yürütülür.
  • İşlem boyutu sınırı 1232 bayttır.
  • Her talimat üç parça bilgi gerektirir:
    1. Çağrılacak programın adresi
    2. Talimatın okuduğu veya yazdığı hesaplar
    3. Talimat tarafından gereken herhangi bir ek veri (örn. fonksiyon argümanları)

SOL Transfer Örneği

Aşağıdaki diyagram, bir göndericiden bir alıcıya SOL transferi için tek bir talimat içeren bir işlemi temsil eder.

Solana'da, "cüzdanlar" System Program tarafından sahip olunan hesaplardır. Yalnızca program sahibi bir hesabın verilerini değiştirebilir, bu nedenle SOL transferi yapmak System Program'ı çağıran bir işlem göndermeyi gerektirir.

SOL TransferSOL Transfer

Gönderici hesabın, System Program'ın lamport bakiyesini düşürebilmesi için işlemi imzalaması (is_signer) gerekir. Gönderici ve alıcı hesapların lamport bakiyeleri değiştiği için yazılabilir (is_writable) olmaları gerekir.

İşlemi gönderdikten sonra, System Program transfer talimatını işler. Ardından System Program, hem gönderici hem de alıcı hesapların lamport bakiyelerini günceller.

SOL Transfer İşlemiSOL Transfer İşlemi

Aşağıdaki örnekler, bir hesaptan diğerine SOL aktaran bir işlemin nasıl gönderileceğini göstermektedir. System Program'ın transfer talimatı kaynak kodunu buradan inceleyebilirsiniz.

import {
airdropFactory,
appendTransactionMessageInstructions,
createSolanaRpc,
createSolanaRpcSubscriptions,
createTransactionMessage,
generateKeyPairSigner,
getSignatureFromTransaction,
lamports,
pipe,
sendAndConfirmTransactionFactory,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
// Create a connection to cluster
const rpc = createSolanaRpc("http://localhost:8899");
const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");
// Generate sender and recipient keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
const LAMPORTS_PER_SOL = 1_000_000_000n;
const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL
// Fund sender with airdrop
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: sender.address,
lamports: lamports(LAMPORTS_PER_SOL), // 1 SOL
commitment: "confirmed"
});
// Check balance before transfer
const { value: preBalance1 } = await rpc.getBalance(sender.address).send();
const { value: preBalance2 } = await rpc.getBalance(recipient.address).send();
// Create a transfer instruction for transferring SOL from sender to recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount // 0.01 SOL in lamports
});
// Add the transfer instruction to a new transaction
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
// Send the transaction to the network
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedTransaction,
{ commitment: "confirmed" }
);
const transactionSignature = getSignatureFromTransaction(signedTransaction);
// Check balance after transfer
const { value: postBalance1 } = await rpc.getBalance(sender.address).send();
const { value: postBalance2 } = await rpc.getBalance(recipient.address).send();
console.log(
"Sender prebalance:",
Number(preBalance1) / Number(LAMPORTS_PER_SOL)
);
console.log(
"Recipient prebalance:",
Number(preBalance2) / Number(LAMPORTS_PER_SOL)
);
console.log(
"Sender postbalance:",
Number(postBalance1) / Number(LAMPORTS_PER_SOL)
);
console.log(
"Recipient postbalance:",
Number(postBalance2) / Number(LAMPORTS_PER_SOL)
);
console.log("Transaction Signature:", transactionSignature);
Console
Click to execute the code.

İstemci kütüphaneleri genellikle program talimatları oluşturma detaylarını soyutlar. Bir kütüphane mevcut değilse, talimatı manuel olarak oluşturabilirsiniz. Bu, talimatın uygulama detaylarını bilmenizi gerektirir.

Aşağıdaki örnekler, transfer talimatının nasıl manuel olarak oluşturulacağını göstermektedir. Expanded Instruction sekmesi, işlevsel olarak Instruction sekmesine eşdeğerdir.

const transferAmount = 0.01; // 0.01 SOL
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount * LAMPORTS_PER_SOL
});

Aşağıdaki bölümlerde, işlemler ve talimatların ayrıntılarını inceleyeceğiz.

Talimatlar

Solana'daki bir talimat, Solana ağını kullanan herkes tarafından çağrılabilen bir program üzerindeki genel bir fonksiyon olarak düşünülebilir.

Bir Solana programını, Solana ağında barındırılan bir web sunucusu olarak düşünebilirsiniz; burada her talimat, kullanıcıların belirli eylemleri gerçekleştirmek için çağırabileceği genel bir API uç noktası gibidir. Bir talimatı çağırmak, bir API uç noktasına POST isteği göndermeye benzer ve kullanıcıların programın iş mantığını yürütmesine olanak tanır.

Solana'da bir programın talimatını çağırmak için, üç bilgi parçasıyla bir Instruction oluşturmanız gerekir:

  • Program ID: Çağrılan talimat için iş mantığına sahip programın adresi.
  • Hesaplar: Talimatın okuduğu veya yazdığı tüm hesapların listesi.
  • Instruction Data: Programda hangi talimatın çağrılacağını ve talimat tarafından gereken argümanları belirten bir bayt dizisi.
Instruction
pub struct Instruction {
/// Pubkey of the program that executes this instruction.
pub program_id: Pubkey,
/// Metadata describing accounts that should be passed to the program.
pub accounts: Vec<AccountMeta>,
/// Opaque data passed to the program for its own interpretation.
pub data: Vec<u8>,
}

İşlem Talimatıİşlem Talimatı

AccountMeta

Bir Instruction oluştururken, her gerekli hesabı bir AccountMeta olarak sağlamanız gerekir. AccountMeta aşağıdakileri belirtir:

  • pubkey: Hesabın adresi
  • is_signer: Hesabın işlemi imzalaması gerekip gerekmediği
  • is_writable: Talimatın hesabın verilerini değiştirip değiştirmediği
AccountMeta
pub struct AccountMeta {
/// An account's public key.
pub pubkey: Pubkey,
/// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.
pub is_signer: bool,
/// True if the account data or metadata may be mutated during program execution.
pub is_writable: bool,
}

Bir talimatın hangi hesapları okuduğunu veya yazdığını önceden belirterek, aynı hesapları değiştirmeyen işlemler paralel olarak yürütülebilir.

Bir talimatın hangi hesaplara ihtiyaç duyduğunu, hangilerinin yazılabilir, salt okunur olması gerektiğini veya işlemi imzalaması gerektiğini bilmek için, talimatın program tarafından tanımlandığı şekildeki uygulamasına başvurmanız gerekir.

Pratikte, genellikle bir Instruction manuel olarak oluşturmanız gerekmez. Çoğu program geliştiricisi, sizin için talimatları oluşturan yardımcı fonksiyonlar içeren istemci kütüphaneleri sağlar.

AccountMetaAccountMeta

Örnek Talimat Yapısı

Bir SOL transfer talimatının yapısını görmek için aşağıdaki örnekleri çalıştırın.

import { generateKeyPairSigner, lamports } from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
// 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
});
console.log(JSON.stringify(transferInstruction, null, 2));
Console
Click to execute the code.

Aşağıdaki örnekler, önceki kod parçacıklarından çıktıyı göstermektedir. Tam format SDK'ya bağlı olarak değişir, ancak her Solana talimatı aşağıdaki bilgileri gerektirir:

  • Program ID: Talimatı yürütecek programın adresi.
  • Hesaplar: Talimat tarafından gereken hesapların listesi. Her hesap için, talimat adresini, işlemi imzalaması gerekip gerekmediğini ve yazılıp yazılmayacağını belirtmelidir.
  • Veri: Programa hangi talimatı yürüteceğini söyleyen ve talimat tarafından gereken argümanları içeren bir bayt tamponu.
{
"accounts": [
{
"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC",
"role": 3,
"signer": {
"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC",
"keyPair": {
"privateKey": {},
"publicKey": {}
}
}
},
{
"address": "2mBY6CTgeyJNJDzo6d2Umipw2aGUquUA7hLdFttNEj7p",
"role": 1
}
],
"programAddress": "11111111111111111111111111111111",
"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
}
}

İşlemler

Çağırmak istediğiniz talimatları oluşturduktan sonra, bir sonraki adım bir Transaction oluşturmak ve talimatları işleme eklemektir. Bir Solana işlemi şunlardan oluşur:

  1. İmzalar: İşlemdeki talimatlar için imzalayıcı olarak gereken tüm hesaplardan alınan imzaların bir dizisi. İmza, işlemin Message hesabın özel anahtarıyla imzalanmasıyla oluşturulur.
  2. Mesaj: İşlem mesajı atomik olarak işlenecek talimatların listesini içerir.
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

İşlem Formatıİşlem Formatı

Bir işlem mesajının yapısı şunlardan oluşur:

  • Mesaj Başlığı: İmzalayan ve salt okunur hesap sayısını belirtir.
  • Hesap Adresleri: İşlemdeki talimatlar tarafından gereken hesap adreslerinin bir dizisi.
  • Son Blok Karması: İşlem için zaman damgası görevi görür.
  • Talimatlar: Yürütülecek talimatların bir dizisi.
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>,
}

İşlem Boyutu

Solana işlemleri 1232 baytlık bir boyut sınırına sahiptir. Bu sınır, IPv6 Maksimum İletim Birimi (MTU) boyutu olan 1280 bayttan, ağ başlıkları için 48 bayt (40 bayt IPv6 + 8 bayt başlık) çıkarılarak elde edilir.

Bir işlemin toplam boyutu (imzalar ve mesaj) bu sınırın altında kalmalıdır ve şunları içerir:

  • İmzalar: Her biri 64 bayt
  • Mesaj: Başlık (3 bayt), hesap anahtarları (her biri 32 bayt), son blok karması (32 bayt) ve talimatlar

İşlem Formatıİşlem Formatı

Mesaj Başlığı

Mesaj başlığı işlemdeki hesap için izinleri belirtir. Hangi hesapların imzalayıcı ve hangilerinin yazılabilir olduğunu belirlemek için kesin sıralı hesap adresleri ile birlikte çalışır.

  1. İşlemdeki tüm talimatlar için gereken imza sayısı.
  2. Salt okunur olan imzalı hesapların sayısı.
  3. Salt okunur olan imzasız hesapların sayısı.
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,
}

Mesaj BaşlığıMesaj Başlığı

Kompakt-Dizi Formatı

Bir işlem mesajındaki kompakt dizi, aşağıdaki formatta serileştirilmiş bir dizidir:

  1. Dizi uzunluğu (compact-u16 olarak kodlanmış)
  2. Dizi öğeleri art arda listelenmiş

Kompakt dizi formatıKompakt dizi formatı

Bu format, işlem mesajlarındaki Hesap Adresleri ve Talimatlar dizilerinin uzunluklarını kodlamak için kullanılır.

Hesap Adresleri Dizisi

Bir işlem mesajı, talimatları tarafından gereken tüm hesap adreslerinin tek bir listesini içerir. Dizi, kaç adres içerdiğini belirten bir compact-u16 sayısı ile başlar.

Alan tasarrufu sağlamak için, işlem her hesap için izinleri ayrı ayrı saklamaz. Bunun yerine, izinleri belirlemek için MessageHeader ve hesap adreslerinin katı bir sıralaması kombinasyonuna dayanır.

Adresler her zaman aşağıdaki şekilde sıralanır:

  1. Yazılabilir ve imzalayan hesaplar
  2. Salt okunur ve imzalayan hesaplar
  3. Yazılabilir ve imzalamayan hesaplar
  4. Salt okunur ve imzalamayan hesaplar

MessageHeader, her izin grubu için hesap sayısını belirlemek için kullanılan değerleri sağlar.

Hesap adreslerinin kompakt dizisiHesap adreslerinin kompakt dizisi

Son Blok Hash'i

Her işlem, iki amaca hizmet eden bir son blok hash'i gerektirir:

  1. İşlemin ne zaman oluşturulduğuna dair bir zaman damgası görevi görür
  2. Yinelenen işlemleri önler

Bir blok hash'i, 150 bloktan sonra (400ms blok süreleri varsayılarak yaklaşık 1 dakika) sona erer, bu süreden sonra işlem süresi dolmuş kabul edilir ve işlenemez.

Geçerli blok hash'ini ve blok hash'inin geçerli olacağı son blok yüksekliğini almak için getLatestBlockhash RPC yöntemini kullanabilirsiniz.

Talimatlar Dizisi

Bir işlem mesajı, CompiledInstruction tipinde talimatlar dizisi içerir. Talimatlar, bir işleme eklendiğinde bu tipe dönüştürülür.

Mesajdaki hesap adresleri dizisi gibi, compact-u16 uzunluğu ile başlar ve ardından talimat verileri gelir. Her talimat şunları içerir:

  1. Program ID İndeksi: Hesap adresleri dizisindeki programın adresini işaret eden bir indeks. Bu, talimatı işleyecek programı belirtir.
  2. Hesap İndeksleri: Bu talimat için gerekli olan hesap adreslerini işaret eden indeksler dizisi.
  3. instruction data: Programda hangi talimatın çağrılacağını ve talimat tarafından gereken ek verileri (örn. fonksiyon argümanları) belirten bir bayt dizisi.
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>,
}

Talimatların Kompakt DizisiTalimatların Kompakt Dizisi

Örnek İşlem Yapısı

Tek bir SOL transfer talimatı içeren bir işlemin yapısını görmek için aşağıdaki örnekleri çalıştırın.

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.

Aşağıdaki örnekler, önceki kod parçacıklarından işlem mesajı çıktısını göstermektedir. Tam format SDK'ya bağlı olarak farklılık gösterir, ancak aynı bilgileri içerir.

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

Bir işlemi gönderdikten sonra, getTransaction RPC yöntemini kullanarak ayrıntılarını alabilirsiniz. Yanıt, aşağıdaki parçaya benzer bir yapıya sahip olacaktır. Alternatif olarak, Solana Explorer kullanarak işlemi inceleyebilirsiniz.

Bir "işlem imzası" Solana'daki bir işlemi benzersiz şekilde tanımlar. Bu imzayı, işlemin ağdaki ayrıntılarını aramak için kullanırsınız. İşlem imzası, basitçe işlemdeki ilk imzadır. İlk imzanın aynı zamanda işlem ücreti ödeyicisinin imzası olduğunu unutmayın.

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?

İçindekiler

Sayfayı Düzenle