PDA'da SOL Depolama
Burada SOL'u bir PDA'da nasıl depolayacağınızı öğreneceksiniz. Örnek olarak, bunu bir oyunda oyunculara ödül vermek için kullanacağız. Oyuncuların sağa hareket edebileceği ve belirli bir pozisyona ulaştıklarında bir ödül toplayabileceği basit bir oyun inşa edeceğiz. Ödül, bir PDA'da depolanacak ve oyuncular oyuna etkileşimde bulunarak bunu talep edebilecekler.
İpucu: Oyuncuların ödülleri alabilmesi için doğru pozisyona ulaşmaları önemlidir.
Video geçişi:
Küçük Macera Anchor Programı - İkinci Bölüm
Bu eğitimde, Küçük Macera oyununu yeniden inşa edeceğiz ve 0.1 SOL ödülü olan bir sandık tanıtacağız. Sandık belirli bir konumda "oluşacak" ve oyuncu o konuma ulaştığında ödülü alacak. Bu programın amacı, SOL'u bir program hesabında nasıl depolayacağınızı ve bunu oyunculara nasıl dağıtacağınızı göstermek.
Küçük Macera İki Programı 3 talimattan oluşmaktadır:
initialize_level_one
- Bu talimat, oyuncunun pozisyonunu kaydetmek için bir ve "ödül sandığı" temsil eden SOL ödülünü tutmak için başka bir on-chain hesabı başlatır.reset_level_and_spawn_chest
- Bu talimat, oyuncunun pozisyonunu sıfırlar ve talimatı çağıran kullanıcıdan ödemeyi alarak bir ödül sandığını "yeniden oluşturur".move_right
- Bu talimat, oyuncunun pozisyonunu sağa hareket ettirmesine ve belirli bir pozisyona ulaştıklarında ödül sandığında SOL'u toplamalarına olanak tanır.
Aşağıdaki bölümlerde, programı adım adım nasıl inşa edeceğinizi anlatacağız. Tam kaynak kodunu, doğrudan tarayıcınızdan Solana Playground kullanarak dağıtabileceğiniz bağlantıda bulabilirsiniz: Playground'da Aç.
Başlarken
Küçük Macera oyununu inşa etmeye başlamak için bu adımları izleyin:
- Solana Playground adresini ziyaret edin ve yeni bir Anchor projesi oluşturun.
- Solana Playground'a yeni iseniz, ayrıca bir Playground Cüzdanı oluşturmanız gerekecek.
Yeni bir proje oluşturduktan sonra, varsayılan başlangıç kodunu aşağıdaki kodla değiştirin:
use anchor_lang::prelude::*;
use anchor_lang::solana_program::native_token::LAMPORTS_PER_SOL;
use anchor_lang::system_program;
declare_id!("11111111111111111111111111111111");
#[program]
mod tiny_adventure_two {
use super::*;
}
fn print_player(player_position: u8) {
if player_position == 0 {
msg!("Bir Yolculuk Başlıyor!");
msg!("o.........💎");
} else if player_position == 1 {
msg!("..o.......💎");
} else if player_position == 2 {
msg!("....o.....💎");
} else if player_position == 3 {
msg!("........\\o/💎");
msg!("..........\\o/");
msg!("Sonuna ulaştınız! Harika!");
}
}
Bu oyunda, oyuncu 0 pozisyonunda başlar ve sadece sağa hareket edebilir. Oyuncunun ödül sandığına doğru ilerleyişini görselleştirmek için mesaj günlüklerini kullanacağız!
Sandık Kasa Hesabını Tanımlama
Programın başında CHEST_REWARD
sabitini ekleyin. CHEST_REWARD
, sandığa konulacak ve ödül olarak verilecek lamport miktarını temsil eder. Lamportlar, 1 SOL eşdeğerinde 1 milyar lamportun en küçük kesirleridir.
SOL ödülünü depolamak için yeni bir ChestVaultAccount
yapısı tanımlayacağız. Bu boş bir yapıdır çünkü doğrudan hesapta lamportları güncelleyeceğiz. Hesap, SOL ödülünü tutacak ve ek veri saklamasına ihtiyaç duymayacaktır.
use anchor_lang::prelude::*;
use anchor_lang::solana_program::native_token::LAMPORTS_PER_SOL;
use anchor_lang::system_program;
declare_id!("11111111111111111111111111111111");
#[program]
mod tiny_adventure_two {
use super::*;
// Sandıklara konulacak ve ödül olarak verilecek lamport miktarı.
const CHEST_REWARD: u64 = LAMPORTS_PER_SOL / 10; // 0.1 SOL
}
// Sandık Kasa Hesabı yapısını tanımla
#[account]
pub struct ChestVaultAccount {}
Oyun Verisi Hesabını Tanımlama
Oyuncunun pozisyonunu oyunda takip etmek için, oyuncunun pozisyonunu saklayacak olan on-chain hesabı için bir yapı tanımlamamız gerekiyor.
GameDataAccount
yapısı, oyuncunun mevcut pozisyonunu unsigned 8-bit tamsayı olarak saklayan tek bir alana sahiptir.
use anchor_lang::prelude::*;
use anchor_lang::solana_program::native_token::LAMPORTS_PER_SOL;
use anchor_lang::system_program;
declare_id!("11111111111111111111111111111111");
#[program]
mod tiny_adventure_two {
use super::*;
...
}
// Oyun Verisi Hesabı yapısını tanımla
#[account]
pub struct GameDataAccount {
player_position: u8,
}
GameDataAccount
yapısı tanımlandıktan sonra, oyuna etkileşimde bulundukça oyuncunun pozisyonunu saklamak ve güncellemek için bunu kullanabilirsiniz. Oyuncu sağa doğru hareket ettikçe ve oyunda ilerledikçe, pozisyonları GameDataAccount
içinde güncellenecek ve SOL ödülünü içeren sandığa doğru ilerlemelerini takip edebileceksiniz.
Seviye Bir Talimatını Başlat
GameDataAccount
ve ChestVaultAccount
tanımlı olduğu için, initialize_level_one
talimatını uygulayalım. Bu talimat, hem GameDataAccount
hem de ChestVaultAccount
'ı başlatır, oyuncunun pozisyonunu 0 olarak ayarlar ve başlangıç mesajını gösterir.
initialize_level_one
talimatı 4 hesap gerektirir:
new_game_data_account
- oyuncunun pozisyonunu saklamak için başlattığımızGameDataAccount
chest_vault
- SOL ödülünü saklamak için başlattığımızChestVaultAccount
signer
- hesapların başlatılması için ödeme yapan oyuncusystem_program
- yeni bir hesap oluştururken gereken bir hesap
#[program]
pub mod tiny_adventure_two {
use super::*;
pub fn initialize_level_one(_ctx: Context<InitializeLevelOne>) -> Result<()> {
msg!("Bir Yolculuk Başlıyor!");
msg!("o.......💎");
Ok(())
}
...
}
// initialize_level_one talimatının gerektirdiği hesapları belirt
#[derive(Accounts)]
pub struct InitializeLevelOne<'info> {
#[account(
init_if_needed,
seeds = [b"level1"],
bump,
payer = signer,
space = 8 + 1
)]
pub new_game_data_account: Account<'info, GameDataAccount>,
#[account(
init_if_needed,
seeds = [b"chestVault"],
bump,
payer = signer,
space = 8
)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
...
Her iki GameDataAccount
ve ChestVaultAccount
, hesap adresinin bulunabilirliğini sağlamak için bir Program Türetilmiş Adres (PDA) kullanılarak oluşturulmaktadır; bu, bize daha sonra adresi deterministik olarak bulma olanağı sağlar. init_if_needed
kısıtlaması, hesapların yalnızca mevcut olmadıklarında başlatılmasını sağlar. Bu talimatta her iki hesabın PDA'ları tek bir sabit tohum kullanır, bu nedenle program yalnızca birer adet oluşturabilir. Sonuç olarak, bu talimat yalnızca bir kez çağrılmalıdır.
Mevcut uygulamanın, GameDataAccount
'ı kimlerin değiştirebileceği konusunda bir kısıtlama olmadığını belirtmek gerekir; bu, oyunu çok oyunculu bir deneyime dönüştürerek herkesin oyuncunun hareketlerini kontrol etmesine olanak tanır.
Not: Alternatif olarak, initialize
talimatında imzalayanın adresini ekstra bir tohum olarak kullanabilir ve her oyuncunun kendi GameDataAccount
'ını oluşturmasına izin verebilirsiniz.
Seviye Sıfırla ve Sandığı Oluştur Talimatı
Sonraki olarak, oyuncunun pozisyonunu başlangıç noktasına sıfırlayan ve sandığı 0.1 SOL'luk bir ödülle dolduran reset_level_and_spawn_chest
talimatını uygulayalım.
reset_level_and_spawn_chest
talimatı 4 hesap gerektirir:
new_game_data_account
- oyuncunun pozisyonunu saklayanGameDataAccount
chest_vault
- SOL ödülünü saklayanChestVaultAccount
signer
- sandık için SOL ödülünü sağlayan oyuncusystem_program
- SOL transferinde kullanacağımız program (CPI)
#[program]
pub mod tiny_adventure_two {
use super::*;
...
pub fn reset_level_and_spawn_chest(ctx: Context<SpawnChest>) -> Result<()> {
ctx.accounts.game_data_account.player_position = 0;
let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.payer.to_account_info().clone(),
to: ctx.accounts.chest_vault.to_account_info().clone(),
},
);
system_program::transfer(cpi_context, CHEST_REWARD)?;
msg!("Seviye Sıfırlandı ve Sandık pozisyon 3'te Oluşturuldu");
Ok(())
}
...
}
// reset_level_and_spawn_chest talimatının gerektirdiği hesapları belirt
#[derive(Accounts)]
pub struct SpawnChest<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut, seeds = [b"chestVault"], bump)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub game_data_account: Account<'info, GameDataAccount>,
pub system_program: Program<'info, System>,
}
...
Bu talimat, ödül sandığına SOL transferinde kullanmak üzere bir çapraz program çağrısı (CPI) içerir. Çapraz program çağrısı, bir programın başka bir programın talimatını çağırmasıdır. Bu durumda, payer
'dan ChestVaultAccount
'a SOL'u aktarmak için system_program
'dan Transfer
talimatını çağırmak için bir CPI kullanıyoruz.
Uyarı: Çapraz program çağrıları, Solana programlama modelinde önemli bir kavramdır; bu nedenle, programların diğer programların talimatlarıyla doğrudan etkileşimde bulunmasını sağlamak için doğru yapılması gerekir.
Sağa Hareket Talimatı
Son olarak, sandık ödülünü toplama mantığını içeren move_right
talimatını uygulayalım. Bir oyuncu pozisyon 3'e ulaştığında ve doğru "şifreyi" girerse, ödül ChestVaultAccount
'dan oyuncunun hesabına transfer edilir. Eğer yanlış bir şifre girilirse, özel bir Anchor Hatası döndürülür. Eğer oyuncu zaten pozisyon 3'teyse, bir mesaj kaydedilir. Aksi takdirde, pozisyon 1 arttırılır ve sağa hareket ettiği görüntülenir.
Bu "şifre" işlevinin ana amacı, bir talimata parametreler eklemeyi ve geliştirilen özel Anchor Hatalarını uygulamayı göstermektir. Bu örnekte, doğru şifre "gib" olacaktır.
move_right
talimatı 3 hesap gerektirir:
new_game_data_account
- oyuncunun pozisyonunu saklayanGameDataAccount
chest_vault
- SOL ödülünü saklayanChestVaultAccount
player_wallet
- talimatı çağıran oyuncunun cüzdanı ve SOL ödülü alacak potansiyel alıcı
#[program]
pub mod tiny_adventure_two {
use super::*;
...
// Sağa hareket talimatı
pub fn move_right(ctx: Context<MoveRight>, password: String) -> Result<()> {
let game_data_account = &mut ctx.accounts.game_data_account;
if game_data_account.player_position == 3 {
msg!("Sonuna ulaştınız! Harika!");
} else if game_data_account.player_position == 2 {
if password != "gib" {
return err!(MyError::WrongPassword);
}
game_data_account.player_position = game_data_account.player_position + 1;
msg!(
"Başardınız! İşte ödülünüz {0} lamport",
CHEST_REWARD
);
**ctx
.accounts
.chest_vault
.to_account_info()
.try_borrow_mut_lamports()? -= CHEST_REWARD;
**ctx
.accounts
.player
.to_account_info()
.try_borrow_mut_lamports()? += CHEST_REWARD;
} else {
game_data_account.player_position = game_data_account.player_position + 1;
print_player(game_data_account.player_position);
}
Ok(())
}
...
}
// move_right talimatının gerektirdiği hesapları belirt
#[derive(Accounts)]
pub struct MoveRight<'info> {
#[account(mut, seeds = [b"chestVault"], bump)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub game_data_account: Account<'info, GameDataAccount>,
#[account(mut)]
pub player: Signer<'info>,
}
// Özel Anchor Hatası
#[error_code]
pub enum MyError {
#[msg("Şifre yanlıştı")]
WrongPassword,
}
...
Ödülü sandıktan oyuncu hesabına lamport transfer etmek için, daha önce yaptığımız gibi bir Çapraz Program Çağrısı (CPI) kullanamayız çünkü ChestVaultAccount
sistem programı tarafından korunmamaktadır. Bunun yerine, try_borrow_mut_lamports
kullanarak hesapların içindeki lamportları doğrudan değiştiriyoruz. Lamportların düşürüldüğü hesabın imzalayıcı olması gerektiğine dikkat edin; çalışma zamanı her işlemden sonra toplam hesap bakiyelerinin eşit kalmasını sağlıyor.
Kritik Uyarı: Program Türetilmiş Hesapların (PDA'ların) iki ana özelliği vardır:
- Bir hesabın adresini bulmanın deterministik bir yolunu sağlamak
- PDA'dan türetilen programın onlar için "imzalamasına" izin vermek
Bu, ChestVaultAccount
'tan lamportları düşürebilmemizi sağlar; çünkü talimat için açıkça ek bir imzalayıcı talep etmeye gerek yoktur.
İnşa Et ve Dağıt
Tebrikler! Artık Küçük Macera programının ikinci bölümünü tamamladınız! Nihai programınız şöyle görünmelidir:
use anchor_lang::prelude::*;
use anchor_lang::solana_program::native_token::LAMPORTS_PER_SOL;
use anchor_lang::system_program;
// Bu, programınızın genel anahtarıdır ve proje derlendiğinde otomatik olarak güncellenecektir.
declare_id!("7gZTdZg86YsYbs92Rhv63kZUAkoww1kLexJg8sNpgVQ3");
#[program]
mod tiny_adventure_two {
use super::*;
// Sandıklara konulacak ve ödül olarak verilecek lamport miktarı.
const CHEST_REWARD: u64 = LAMPORTS_PER_SOL / 10; // 0.1 SOL
pub fn initialize_level_one(_ctx: Context<InitializeLevelOne>) -> Result<()> {
// Genellikle üretim kodunuzda çok fazla metin yazmazdınız çünkü bu işlem birimi maliyetlidir.
msg!("Bir Yolculuk Başlıyor!");
msg!("o.......💎");
Ok(())
}
pub fn reset_level_and_spawn_chest(ctx: Context<SpawnChest>) -> Result<()> {
ctx.accounts.game_data_account.player_position = 0;
let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.payer.to_account_info().clone(),
to: ctx.accounts.chest_vault.to_account_info().clone(),
},
);
system_program::transfer(cpi_context, CHEST_REWARD)?;
msg!("Seviye Sıfırlandı ve Sandık pozisyon 3'te Oluşturuldu");
Ok(())
}
pub fn move_right(ctx: Context<MoveRight>, password: String) -> Result<()> {
let game_data_account = &mut ctx.accounts.game_data_account;
if game_data_account.player_position == 3 {
msg!("Sonuna ulaştınız! Harika!");
} else if game_data_account.player_position == 2 {
if password != "gib" {
return err!(MyError::WrongPassword);
}
game_data_account.player_position = game_data_account.player_position + 1;
msg!(
"Başardınız! İşte ödülünüz {0} lamport",
CHEST_REWARD
);
**ctx
.accounts
.chest_vault
.to_account_info()
.try_borrow_mut_lamports()? -= CHEST_REWARD;
**ctx
.accounts
.player
.to_account_info()
.try_borrow_mut_lamports()? += CHEST_REWARD;
} else {
game_data_account.player_position = game_data_account.player_position + 1;
print_player(game_data_account.player_position);
}
Ok(())
}
}
fn print_player(player_position: u8) {
if player_position == 0 {
msg!("Bir Yolculuk Başlıyor!");
msg!("o.........💎");
} else if player_position == 1 {
msg!("..o.......💎");
} else if player_position == 2 {
msg!("....o.....💎");
} else if player_position == 3 {
msg!("........\\o/💎");
msg!("..........\\o/");
msg!("Sonuna ulaştınız! Harika!");
}
}
#[derive(Accounts)]
pub struct InitializeLevelOne<'info> {
// Bir hesabı başlatmak için alanı belirtmemiz gerekir.
// İlk 8 bayt varsayılan hesap ayırtıcısıdır,
// sonraki 1 bayt YeniHesap.data'nın türü olan u8'den gelir.
// (u8 = 8 bitlik unsigned tamsayı = 8 bayt)
// Ayrıca imzalayanı tohum olarak kullanabilirsiniz [imzalayan.key().as_ref()],
#[account(
init_if_needed,
seeds = [b"level1"],
bump,
payer = signer,
space = 8 + 1
)]
pub new_game_data_account: Account<'info, GameDataAccount>,
// Bu, ödül SOL'unu yatıracağımız PDA'dır ve
// ilk sandığı bulan oyuncuya geri gönderiyoruz.
#[account(
init_if_needed,
seeds = [b"chestVault"],
bump,
payer = signer,
space = 8
)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct SpawnChest<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut, seeds = [b"chestVault"], bump)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub game_data_account: Account<'info, GameDataAccount>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct MoveRight<'info> {
#[account(mut, seeds = [b"chestVault"], bump)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub game_data_account: Account<'info, GameDataAccount>,
#[account(mut)]
pub player: Signer<'info>,
}
#[account]
pub struct GameDataAccount {
player_position: u8,
}
#[account]
pub struct ChestVaultAccount {}
#[error_code]
pub enum MyError {
#[msg("Şifre yanlıştı")]
WrongPassword,
}
Artık program tamamlandı, onu Solana Playground kullanarak inşa edip dağıtalım!
Solana Playground'a yeni iseniz, önce bir Playground cüzdanı oluşturun ve bir Devnet uç noktasına bağlı olduğunuzdan emin olun. Ardından, solana airdrop 2
komutunu çalıştırarak 6 SOL alana kadar devam edin. Yeterince SOL'unuz olduğunda, programı inşa edin ve dağıtın.
Müşteri ile Başlayın
Bu bölümde, oyunun etkileşimde bulunmak için basit bir istemci tarafı uygulamasını adım adım inceleyeceğiz. Kodu parçalayacağız ve her adım için detaylı açıklamalar vereceğiz. Başlamak için Solana Playground'daki client.ts
dosyasına gidin, yer tutucu kodunu kaldırın ve aşağıdaki bölümlerden kod parçalarını ekleyin.
Öncelikle, GameDataAccount
ve ChestVaultAccount
için PDA'ları (Program Türetilmiş Adresleri) elde edelim. PDA, programın kimliği ve ek tohumlar kullanılarak türetilen, bir genel anahtar formatında benzersiz bir adrestir.
// PDA adresi, herkes karakteri kontrol edebilecek, eğer programınızla etkileşime geçerse
const [globalLevel1GameDataAccount, bump] =
await anchor.web3.PublicKey.findProgramAddress(
[Buffer.from("level1", "utf8")], //[pg.wallet.publicKey.toBuffer()], <- Oyuncu cüzdanını da bir tohum olarak ekleyebilirsiniz, böylece her oyuncu için bir örneğe sahip olursunuz. Rust kısmındaki tohumu da değiştirmeyi unutmayın.
pg.program.programId,
);
// Programın, sandıklardaki SOL ödülünü kaydedeceği ve ödülün yeniden ödeneceği yerdir.
const [chestVaultAccount, chestBump] =
await anchor.web3.PublicKey.findProgramAddress(
[Buffer.from("chestVault", "utf8")],
pg.program.programId,
);
Sonrasında, GameDataAccount
ve ChestVaultAccount
'ı ayarlamak için initializeLevelOne
talimatını çağıracağız.
// Seviyeyi başlat
let txHash = await pg.program.methods
.initializeLevelOne()
.accounts({
chestVault: chestVaultAccount,
newGameDataAccount: globalLevel1GameDataAccount,
signer: pg.wallet.publicKey,
systemProgram: web3.SystemProgram.programId,
})
.signers([pg.wallet.keypair])
.rpc();
console.log(`Logları görmek için 'solana confirm -v ${txHash}' kullanın`);
await pg.connection.confirmTransaction(txHash);
let balance = await pg.connection.getBalance(pg.wallet.publicKey);
console.log(
`Bir sandık çıkmadan önceki bakiyem: ${balance / web3.LAMPORTS_PER_SOL} SOL`,
);
Bundan sonra, oyuncunun konumunu 0 yapmak ve ChestVaultAccount
'ı 0.1 SOL ile doldurmak için resetLevelAndSpawnChest
talimatını kullanacağız.
// Oyuncu pozisyonunu 0'a geri döndür ve sandığı sol ile doldur
txHash = await pg.program.methods
.resetLevelAndSpawnChest()
.accounts({
chestVault: chestVaultAccount,
gameDataAccount: globalLevel1GameDataAccount,
payer: pg.wallet.publicKey,
systemProgram: web3.SystemProgram.programId,
})
.signers([pg.wallet.keypair])
.rpc();
console.log(`Logları görmek için 'solana confirm -v ${txHash}' kullanın`);
await pg.connection.confirmTransaction(txHash);
console.log("Seviye sıfırlandı ve sandık oluşturuldu 💎");
console.log("o........💎");
Artık oyunu etkileşimde bulunarak çağırabiliriz moveRight
talimatını. Bu örnekte, oyuncu ChestVaultAccount
'tan ödülü almak için pozisyona ulaşana kadar bu talimatı döngü şeklinde kullanacağız.
// Burada sağa üç kez hareket ediyoruz ve seviyenin sonunda sandığı topluyoruz
for (let i = 0; i < 3; i++) {
txHash = await pg.program.methods
.moveRight("gib")
.accounts({
chestVault: chestVaultAccount,
gameDataAccount: globalLevel1GameDataAccount,
player: pg.wallet.publicKey,
})
.signers([pg.wallet.keypair])
.rpc();
console.log(`Logları görmek için 'solana confirm -v ${txHash}' kullanın`);
await pg.connection.confirmTransaction(txHash);
let balance = await pg.connection.getBalance(pg.wallet.publicKey);
console.log(`Bakiyem: ${balance / web3.LAMPORTS_PER_SOL} SOL`);
let gameDateAccount = await pg.program.account.gameDataAccount.fetch(
globalLevel1GameDataAccount,
);
console.log("Oyuncu pozisyonu:", gameDateAccount.playerPosition.toString());
switch (gameDateAccount.playerPosition) {
case 0:
console.log("Bir yolculuk başlıyor...");
console.log("o........💎");
break;
case 1:
console.log("....o....💎");
break;
case 2:
console.log("......o..💎");
break;
case 3:
console.log(".........\\o/💎");
console.log("...........\\o/");
break;
}
}
Son olarak, Solana Playground'daki "Çalıştır" butonuna basarak istemciyi çalıştırın. moveRight
talimatı için "gib" dışında bir şey girdiğinizde, sandık ödülünü talep etmek için pozisyona ulaştığınızda aşağıdaki hata mesajı ile karşılaşacaksınız:
Hata Kodu: WrongPassword. Hata Numarası: 6000. Hata Mesajı: Şifre yanlıştı.
Ancak doğru şifreyi girerseniz, çıktı şu şekilde olmalıdır:
İstemci çalışıyor...
client.ts:
Logları görmek için 'solana confirm -v CX8VWV5Jp1kXDkZrTdeeyibgZg3B3cXAzchzCfNHvJoqARSGHeEU5injypxFwiKFcHPcWFG9BeNSrqZAdENtL2t' kullanın
Bir sandık çıkmadan önceki bakiyem: 6.396630254 SOL
Logları görmek için 'solana confirm -v 3HwAS1RK7beL3mGoNdFYWteJXF3NdJXiEskJrHtuJ6Tu9ow67Zo3yScQBEPQyish33hP8WyuVanmq93wEFJ2LQcx' kullanın
Seviye sıfırlandı ve sandık oluşturuldu 💎
o........💎
Logları görmek için 'solana confirm -v 43KnGrx5VQYd8LctsNaNqN1hg69vE6wiiTbdxTC1uM3Hasnq7ZdM9zWx4JS39AKNz2FpQr9a3ZnEA7XscEzmXQ5U' kullanın
Bakiyem: 6.296620254 SOL
Oyuncu pozisyonu: 1
....o....💎
Logları görmek için 'solana confirm -v AGxYWDw49d4y5dLon5M42eu1qG8g2Yf7FeTr3Dpbf1uFXnMeUzp4XWmHyQP1YRNpT8acz4aTJU9f2FQpL6BSAkY' kullanın
Bakiyem: 6.296615254 SOL
Oyuncu pozisyonu: 2
......o..💎
Logları görmek için 'solana confirm -v 5pjAU5NrS4u91QLWZTvo9aXBtR3c6g981UGSxrWDoDW5MehXnx5LnAxu4jKLp1p75RKpVSgMBgg2zHX3WDyci7AK' kullanın
Bakiyem: 6.396610254 SOL
Oyuncu pozisyonu: 3
.........\o/💎
...........\o/
Tebrikler! Müşteri tarafında Tiny Adventure Two'yu başarıyla oluşturmuş, dağıtmış ve etkileşimde bulunmuş oldunuz. Oyuncuların seviyenin sonunda sandığı alarak ödül toplamasını sağlayan yeni bir özellik eklediniz. Ayrıca, Anchor programı içinde SOL transfer etmeyi ve hesaplarda lamportları doğrudan değiştirmeyi öğrendiniz.
Bağımsız olarak devam etmeye ve oyunu yeni seviyeler veya alternatif ödüller gibi ek özelliklerle geliştirmeye devam edebilirsiniz!
Daha ileri örnekler için Solana Oyun Örneklerine
göz atabilirsiniz.