Bu hızlı başlangıç kılavuzu, anchor init tarafından oluşturulan
Anchor framework başlangıç projesini
kullanır. Projeyi yerel olarak oluşturacak, testlerini çalıştıracak, programı
derleyecek ve Anchor'ın oluşturduğu program kodunu inceleyeceksiniz.
Ön Koşullar
Başlamadan önce, Solana geliştirme araçlarını yükleyin. Kurulum; Rust, Solana CLI ve Anchor CLI'yi içerir.
Bu şablon için Anchor CLI sürüm 1.1.2 veya daha yüksek bir sürüm kullanın. Yüklü sürümünüzü kontrol edin:
$anchor --version
Projeyi Oluşturun
Terminalinizde aşağıdaki komutları çalıştırın:
$anchor init my-program$cd my-program
Başlangıç projesi, programs/my-program altında bir Solana programı içerir.
Program, bir sayaç hesabını başlatmak ve sayacı artırmak için iki talimat
içerir.
Şablonun bazı bölümleri yaygın Solana program kalıplarını gösterir: PDA hesap adreslerini türetme, SOL aktarmak için Cross Program Invocation (CPI) yapma ve bir koşul başarısız olduğunda talimatı durdurmak için özel hata denetimleri kullanma.
Programı Derleyin
Başlangıç programını derlemek için anchor build komutunu çalıştırın:
$anchor build
Derlenen program, target/deploy/my_program.so konumuna yazılır. Program
dağıtıldığında, bu .so dosyasının içeriği zincir üzerinde bir hesapta
depolanır.
Testi Çalıştır
Varsayılan testi çalıştırın:
$anchor test
Bu şablonun Anchor.toml'i Rust test komutunu kullanır:
skip_local_validator = true[scripts]test = "cargo test"
Test, derlenmiş programı LiteSVM içine yükler, bir
ödeyici oluşturur, initialize ve increment talimatlarını gönderir,
ardından sayaç hesabının durumunu kontrol eder.
anchor test komutunun çalıştırılması programı da derler; bu nedenle yerel
testlerde anchor build komutunu önceden çalıştırmanız gerekmez.
Programı Dağıt
Yerel testler en hızlı geri bildirim döngüsüdür. Bir ağa, örneğin devnet'e dağıtmaya hazır olduğunuzda önce derleyin, ardından bir kümeye dağıtın.
Bir Solana programının dağıtılması SOL gerektirir; çünkü program bir hesapta depolanır ve hesap kullandığı alan için ücret ödemek zorundadır. Devnet'te, Solana Faucet adresinden veya Solana CLI ile ücretsiz devnet SOL talep edebilirsiniz:
$solana airdrop 2 --url devnet
$anchor build$anchor deploy --provider.cluster devnet
Kaynak dosyalar
src dizini Solana programını içerir. Anchor'ın
Program Yapısı
dokümantasyonu, declare_id!, #[program], #[derive(Accounts)]
ve #[account] dahil olmak üzere burada kullanılan temel makroları açıklar.
Bu bölüm şablon dosyalarını adım adım ele alır.
lib.rs
lib.rs, programın giriş noktasıdır. Kaynak dosyaları birbirine bağlar, program
adresini tanımlar ve kullanıcıların çağırabileceği program talimatlarını
tanımlar.
pub mod constants;pub mod error;pub mod instructions;pub mod state;use anchor_lang::prelude::*;pub use constants::*;pub use instructions::*;pub use state::*;declare_id!("82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd");#[program]pub mod my_program {use super::*;pub fn initialize(ctx: Context<Initialize>) -> Result<()> {crate::instructions::initialize::handle_initialize(ctx)}pub fn increment(ctx: Context<Increment>) -> Result<()> {crate::instructions::increment::handle_increment(ctx)}}
[programs.localnet]my_program = "82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd"
declare_id!("82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd");
Aynı program adresi hem yapılandırmada hem de kodda yer alır. Anchor.toml,
Anchor'a bir küme için hangi adresi dağıtacağını veya çağıracağını bildirir.
declare_id! ise güvenlik denetimleri için program adresini program içinde
tanımlar.
constants.rs
constants.rs, paylaşılan değerleri tek bir yerde tutar. Bu şablonda
COUNTER_SEED sayaç PDA'sını türetir, HELLO_WORLD_LAMPORTS başlatma
sırasında aktarılır ve MAX_COUNT artırmadan önce kontrol edilir.
use anchor_lang::prelude::*;#[constant]pub const COUNTER_SEED: &[u8] = b"counter";#[constant]pub const HELLO_WORLD_LAMPORTS: u64 = 1;#[constant]pub const MAX_COUNT: u64 = 10;
#[derive(Accounts)]pub struct Initialize<'info> {// ...#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,// ...}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {// ...anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;Ok(())}
initialize.rs iki sabit kullanır:
COUNTER_SEEDsayaç PDA adresini türetir.HELLO_WORLD_LAMPORTSödeyiciden sayaç hesabına aktarılacak miktarı belirler.
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {// ...require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;Ok(())}
increment.rs, sayacın üst sınırı olarak MAX_COUNT
değerini kullanır. Mevcut sayım zaten maksimum değerdeyse, require! değeri
CounterOverflow döndürür ve hesap verisi değiştirilmez.
state.rs
state.rs, programın oluşturduğu ve sahip olduğu hesaplar için özel veri
türlerini tanımlar. Program bu veriyi oluşturmak, başlatmak ve güncellemek için
talimatlar tanımlar; ancak sayaç verisi programın kendisinde saklanmaz. Kendi
adresiyle ayrı bir hesapta saklanır.
use anchor_lang::prelude::*;#[account]#[derive(InitSpace)]pub struct Counter {pub count: u64,pub authority: Pubkey,}
#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,// ...pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();Ok(())}
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {// ...ctx.accounts.counter.count += 1;Ok(())}
state.rs, Counter hesap verilerini tanımlar. Talimat dosyaları, hesabı
oluştururken ve güncellerken bu türü kullanır:
Counter::INIT_SPACE,state.rsiçinde tanımlanan alanlar için hesabı boyutlandırır.countveauthority, hesap başlatıldığında yazılan alan değerleridir.count += 1, doğrulama geçtikten sonra depolanan sayaç değerini günceller.
error.rs
error.rs, programın özel hatalarını tanımlar. Bu şablonda hatalar, bir çağıran
sayacı güncelleme yetkisine sahip olmadığında veya sayaç zaten MAX_COUNT
değerine ulaştığında talimat işleyicilerinin nasıl durduğunu göstermektedir.
use anchor_lang::prelude::*;#[error_code]pub enum ErrorCode {#[msg("Only the counter authority can update this counter")]Unauthorized,#[msg("Counter has reached the maximum value")]CounterOverflow,}
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
error.rs, talimatın döndürebileceği hataları listeler:
ErrorCode::Unauthorized, imzalayıcı sayaç hesabında depolanan yetkili olmadığında döndürülür.ErrorCode::CounterOverflow, sayaç zatenMAX_COUNTdeğerine ulaştığında döndürülür.
instructions.rs
instructions.rs, talimat dosyalarını program crate'ine bağlar; böylece
lib.rs, initialize ve increment talimat kodlarına erişebilir. Her
talimat dosyası, o talimat için gereken hesapları ve Anchor bu hesapları
doğruladıktan sonra çalışan işleyici mantığını tanımlar.
pub mod initialize;pub mod increment;pub use initialize::*;pub use increment::*;
initialize.rs
initialize.rs, sayaç hesabını oluşturmak için gereken hesapları tanımlar ve
hesabın ilk değerlerini yazar. #[derive(Accounts)] yapısı, hangi
hesapların gerekli olduğunu ve yeni sayaç hesabının nasıl oluşturulduğunu
belirtmek için Anchor
hesap kısıtlamalarını
kullanır.
use anchor_lang::prelude::*;use crate::{constants::*, state::Counter};#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Hesap Bağlamı
Initialize yapısı, bir kullanıcı initialize talimatını çağırdığında
dahil edilmesi gereken hesapları tanımlar. Anchor, işleyici çalışmadan önce bu
hesapları doğrular.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Ödeyici Hesabı
payer hesabı, sayaç hesabını oluşturma maliyetini karşılar.
Signer<'info> türü, ödeyicinin işlemi imzalaması gerektiği anlamına gelir;
#[account(mut)] ise lamport düşüleceğinden ödeyici hesabının
değiştirilebileceği anlamına gelir.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Sayaç Hesabı
counter hesabı, state.rs kaynağından gelen Counter verilerini
depolar. init, Anchor'a işleyici çalışmadan önce bu hesabı oluşturmasını
söyler; payer = payer ise Anchor'a oluşturma ücretini hangi hesabın
ödeyeceğini bildirir.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Hesap Boyutu
space kısıtlaması, Anchor'a ne kadar hesap verisi tahsis edeceğini söyler.
Anchor önce 8 baytlık bir ayrımcı depolar, ardından Counter alanları için
gereken baytları ekler. Ayrımcı, Anchor'ın hesap verilerini seri dışı bırakmadan
önce bu hesabı bir Counter hesabı olarak tanımasını sağlar.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Sayaç Adresi
seeds ve bump kısıtlamaları, sayaç hesabı için beklenen PDA adresini
tanımlar. Anchor, sağlanan counter hesabının bu adresle eşleştiğini
doğrular. Şablon, kullanıcıların program kimliği ve seed'den sayaç adresini
türetebilmesi için bir PDA kullanır; bu da sayaç adresini deterministik hale
getirir.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
System Program
system_program hesabı, yeni bir hesap oluşturmak System Program
kullandığından zorunludur.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
İşleyici Fonksiyon
handle_initialize fonksiyonu, Anchor Initialize içindeki hesapları
doğruladıktan sonra çalışır. ctx değeri, işleyiciye bu doğrulanmış
hesaplara erişim sağlar.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Başlangıç Verileri
İşleyici, yeni sayaç hesabına ilk değerleri yazar. Sayım 0 ile başlar ve
ödeyici, daha sonra sayacı artırma iznine sahip yetkili olur.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Transfer Hesapları
Bu transfer CPI, yalnızca bir CPI'nin hesapları başka bir programa nasıl
ilettiğini göstermek amacıyla eklenmiştir. Transfer yapısı, System Program
transferi tarafından kullanılan hesapları listeler.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
CPI Bağlamı
CpiContext::new, çağrılan programı o programa iletilen hesaplarla
birleştirir. Bu, bir CPI'nin temel yapısıdır: çağrılacak programı seçin, o
programın beklediği hesapları toplayın, ardından ikisini de çağrıya iletin.
Burada çağrılan program System Program'dır.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Transfer'i Çağır
anchor_lang::system_program::transfer, System Program transfer talimatını
çağırır. Bu şablonda transfer, programınızdan başka bir programı çağırmanın
küçük bir örneğidir. Transfer CPI başarısız olursa, initialize talimatı da
başarısız olur.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Mesaj Kaydet
msg!, program günlüklerine bir mesaj yazar. Ok(()), talimatın
başarıyla döndüğünü gösterir.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
increment.rs
increment.rs, mevcut bir sayaç hesabını güncellemek için gereken hesapları
tanımlar. İşleyici, imzalayanın kayıtlı yetkili olup olmadığını kontrol eder,
sayının belirtilen MAX_COUNT değerine ulaşıp ulaşmadığını kontrol eder ve
ardından sayımı artırır.
use anchor_lang::prelude::*;use crate::{constants::*, error::ErrorCode, state::Counter};#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Hesap Bağlamı
Increment yapısı, bir kullanıcının increment talimatını çağırırken
dahil edilmesi gereken hesapları tanımlar. Anchor, işleyici çalışmadan önce bu
hesapları doğrular.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Sayaç Hesabı
counter hesabı, Counter verisini depolar. mut kısıtlaması,
işleyicinin depolanan sayımı güncellemesine olanak tanır; seeds ve
bump kısıtlamaları ise sayaç PDA adresini doğrular.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Yetkili İmzalayan
authority hesabının işlemi imzalaması gerekir. İşleyici daha sonra bu
imzalayanın sayaç hesabında kayıtlı yetkiliyle eşleştiğini doğrular.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
İşleyici Fonksiyon
handle_increment fonksiyonu, Anchor Increment içindeki hesapları
doğruladıktan sonra çalışır. ctx değeri, işleyiciye doğrulanmış hesaplara
erişim sağlar.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Yetki Kontrolü
İlk kontrol, imzalayanın bu sayacı güncellemesine izin verilip verilmediğini
doğrular. İmzalayanın adresi counter.authority ile eşleşmiyorsa, talimat
ErrorCode::Unauthorized ile durur. Bu, uygulama düzeyinde yetkilendirmeyi
gösterir: program sayaç verilerine sahipken, hangi imzalayanın bu verileri
değiştirebileceğine dair bir kural uygular.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Maksimum Sayı Kontrolü
İkinci kontrol, sayacın MAX_COUNT değerini aşmasını engeller. Sayaç zaten
limite ulaşmışsa, talimat ErrorCode::CounterOverflow ile durur. Bu limit,
şablondaki yapay bir kuraldır; böylece özel hataların hesap verisi
değiştirilmeden önce bir talimatı nasıl durdurabileceğini görebilirsiniz.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Sayacı Güncelle
İşleyici, yalnızca her iki kontrol de geçildikten sonra hesap verilerini günceller. Bu satır, depolanan sayaç değerine bir ekler.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Günlük Mesajı
msg! güncellenmiş sayıyı program günlüklerine yazar. Ok(()),
talimatın başarıyla döndüğünü belirtir.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Test dosyası
programs/my-program/tests/test_initialize.rs, bir Rust entegrasyon testidir.
Yerel bir validator başlatmaz. Bunun yerine, derlenmiş .so dosyasını LiteSVM'e
yükler, programı çağıran işlemler oluşturur ve her işlemden sonra sayaç hesabını
okur. Test, çağrılacak programın program kimliğini belirterek, instruction data
sağlayarak ve gerekli hesapları ileterek bir Solana işlemi için talimatlar
oluşturur.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Initialize {}.data(),my_program::accounts::Initialize {payer: payer.pubkey(),counter,system_program: system_program::ID,}.to_account_metas(None),);
Initialize hesap bağlamı, initialize talimatı için gereken hesapları
tanımlar. Test, bu hesapları oluşturulan my_program::accounts::Initialize
yardımcısına iletir:
payer,payer: payer.pubkey()olarak iletilir.counter,counterolarak iletilir.system_program,system_program::IDolarak iletilir.
my_program::instruction::Initialize {}.data(),
instruction data oluşturur. Talimat argümanlarının kodlanacağı yer burasıdır;
ancak bu initialize talimatı herhangi bir argüman gerektirmez.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Increment {}.data(),my_program::accounts::Increment {counter,authority: payer.pubkey(),}.to_account_metas(None),);
Increment hesap bağlamı, increment talimatı için gereken hesapları
tanımlar. Test, bu hesapları oluşturulan my_program::accounts::Increment
yardımcısına iletir:
counter,counterolarak iletilir.authority,authority: payer.pubkey()olarak iletilir.
my_program::instruction::Increment {}.data(),
instruction data oluşturur. Talimat argümanlarının kodlanacağı yer burasıdır;
ancak bu increment talimatı herhangi bir argüman gerektirmez.
use {anchor_lang::{prelude::Pubkey,solana_program::{instruction::Instruction, system_program},AccountDeserialize, InstructionData, ToAccountMetas,},litesvm::LiteSVM,solana_keypair::Keypair,solana_message::{Message, VersionedMessage},solana_signer::Signer,solana_transaction::versioned::VersionedTransaction,};#[test]fn test_initialize() {let program_id = my_program::id();let payer = Keypair::new();let counter = Pubkey::find_program_address(&[my_program::constants::COUNTER_SEED],&program_id,).0;let mut svm = LiteSVM::new();let bytes = include_bytes!(concat!(env!("CARGO_TARGET_TMPDIR"),"/../deploy/my_program.so"));svm.add_program(program_id, bytes).unwrap();svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Initialize {}.data(),my_program::accounts::Initialize {payer: payer.pubkey(),counter,system_program: system_program::ID,}.to_account_metas(None),);let blockhash = svm.latest_blockhash();let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap();let res = svm.send_transaction(tx);assert!(res.is_ok());let counter_account = svm.get_account(&counter).unwrap();let mut data: &[u8] = &counter_account.data;let counter_state = my_program::state::Counter::try_deserialize(&mut data).unwrap();assert_eq!(counter_state.count, 0);assert_eq!(counter_state.authority, payer.pubkey());let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Increment {}.data(),my_program::accounts::Increment {counter,authority: payer.pubkey(),}.to_account_metas(None),);let blockhash = svm.latest_blockhash();let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap();let res = svm.send_transaction(tx);assert!(res.is_ok());let counter_account = svm.get_account(&counter).unwrap();let mut data: &[u8] = &counter_account.data;let counter_state = my_program::state::Counter::try_deserialize(&mut data).unwrap();assert_eq!(counter_state.count, 1);assert_eq!(counter_state.authority, payer.pubkey());}
Proje yapılandırması
Kök proje dosyaları, Anchor ve Cargo'ya programı nasıl derleyeceğini, test edeceğini ve dağıtacağını söyler. Tam referans için Anchor.toml yapılandırması ve Anchor CLI için Anchor belgelerine bakın.
skip_local_validator = true[toolchain][features]resolution = trueskip-lint = false[programs.localnet]my_program = "82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd"[provider]cluster = "localnet"wallet = "~/.config/solana/id.json"[scripts]test = "cargo test"[hooks]
Is this page helpful?