Programlarda tokenlarla etkileşim nasıl yapılır
Solana üzerindeki tokenlar, oyun içi ödüller, teşvikler veya diğer uygulamalar gibi çeşitli amaçlar için kullanılabilir. Örneğin, belirli oyun içi eylemleri tamamladıklarında oyunculara tokenlar oluşturup dağıtabilirsiniz. Bu örnekte, bir oyunda tokenları mintlemek ve yakmak için bir Anchor programının nasıl ayarlanacağını öğreneceğiz. Eğer tokenları bir PDA'da nasıl depolayacağınızı öğrenmek isterseniz, Solana Playground'daki Token Vault örneği kısmına göz atabilirsiniz.
Genel Bakış
Bu eğitimde, Solana üzerinde Token Programı
ile etkileşimin temelini tanıtmak için Anchor kullanarak bir oyun inşa edeceğiz. Oyun, yeni bir token mintleme, oyuncu hesaplarını başlatma, düşmanları yenmek için oyunculara ödül verme ve oyuncuların tokenları yakarak iyileşmelerine izin verme gibi dört ana eylem etrafında yapılandırılacaktır.
Program 4 talimat içerir:
create_mint
- bu talimat, mint yetkisi olarak birProgram Türetilmiş Adres (PDA)
ile yeni bir token mint oluşturur ve mint için metadata hesabını oluşturur. Bu talimatın yalnızca bir "admin" tarafından çağrılmasına izin veren bir kısıtlama ekleyeceğiz.init_player
- bu talimat, başlangıç sağlık değeri 100 olan yeni bir oyuncu hesabı başlatır.kill_enemy
- bu talimat, bir oyuncunun hesabından "bir düşmanı yenme" durumunda 10 sağlık puanı düşürür ve oyuncu için 1 token mintler.heal
- bu talimat, bir oyuncunun 1 token yakarak sağlık değerini yeniden 100'e döndürmesine izin verir.
Bu örnek, tokenlarla çalışmak için Metaplex tarafından oluşturulmuş bazı harici araçlar ve programlar kullanmaktadır. Kullanıcı cüzdanları, token mintleri, token hesapları ve token metadata hesapları arasındaki ilişkiye dair yüksek seviyeli bir görünüm elde etmek için Metaplex belgeleri kısmını keşfetmeyi düşünebilirsiniz.
Başlarken
Programı oluşturmaya başlamak için Solana Playground adresini ziyaret edin ve yeni bir Anchor projesi oluşturun. Eğer Solana Playground'a yeniyseniz, bir Playground Cüzdanı oluşturmanız gerekecek. Son örneği burada bulunan Savaş jetonları kısmında bulabilirsiniz.
Yeni bir proje oluşturduktan sonra, varsayılan başlangıç kodunu aşağıdaki kodla değiştirin:
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
metadata::{create_metadata_accounts_v3, CreateMetadataAccountsV3, Metadata},
token::{burn, mint_to, Burn, Mint, MintTo, Token, TokenAccount},
};
use mpl_token_metadata::{pda::find_metadata_account, state::DataV2};
use solana_program::{pubkey, pubkey::Pubkey};
declare_id!("11111111111111111111111111111111");
#[program]
pub mod anchor_token {
use super::*;
}
Burada sadece bu program için kullanacağımız crate'leri ve ilgili modülleri kapsamımıza alıyoruz. SPL Token programı ile etkileşimde bulunmamıza yardımcı olmak için anchor_spl
ve mpl_token_metadata
crate'lerini kullanacağız.
Mint Oluşturma Talimatı
Öncelikle, yeni bir token mint ve metadata hesabı oluşturmak için bir talimat uygulayalım. Zincir üzerindeki token metadata'sı, isim, sembol ve URI gibi parametreler olarak talimata sağlanacaktır.
Ayrıca, yalnızca bir "admin" bu talimatı kullanabilecektir ve bunu sağlamanın yolu bir ADMIN_PUBKEY
sabiti tanımlamak ve bunu bir kısıtlama olarak kullanmaktır. ADMIN_PUBKEY
değerini Solana Playground cüzdanınızın public key'i ile değiştirmeyi unutmayın.
create_mint
talimatı, aşağıdaki hesapları gerektirir:
admin
- işlemi imzalayan ve hesapların başlatılması için ödeme yapanADMIN_PUBKEY
reward_token_mint
- bir PDA kullanarak başlattığımız yeni token mintmetadata_account
- token mint için başlattığımız metadata hesabıtoken_program
- Token programında talimatlarla etkileşimde bulunmak için gereklidirtoken_metadata_program
- Token Metadata programındaki talimatlarla etkileşimde bulunmak için gerekli olan hesapsystem_program
- yeni bir hesap oluşturulurken gerekli olan bir hesaprent
- metadata hesabını oluştururken gerekli olan Sysvar Rent
// Sadece bu public key bu talimatı çağırabilir
const ADMIN_PUBKEY: Pubkey = pubkey!("REPLACE_WITH_YOUR_WALLET_PUBKEY");
#[program]
pub mod anchor_token {
use super::*;
// PDA ile mint authority olarak yeni token mint oluştur
pub fn create_mint(
ctx: Context<CreateMint>,
uri: String,
name: String,
symbol: String,
) -> Result<()> {
// CPI için "imzalamak" için PDA tohumları ve bump
let seeds = b"reward";
let bump = *ctx.bumps.get("reward_token_mint").unwrap();
let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];
// Mint için zincir üzerindeki token metadata'sı
let data_v2 = DataV2 {
name: name,
symbol: symbol,
uri: uri,
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
};
// CPI Bağlamı
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_metadata_program.to_account_info(),
CreateMetadataAccountsV3 {
metadata: ctx.accounts.metadata_account.to_account_info(), // oluşturulan metadata hesabı
mint: ctx.accounts.reward_token_mint.to_account_info(), // metadata hesabının mint hesabı
mint_authority: ctx.accounts.reward_token_mint.to_account_info(), // mint hesabının mint authority'si
update_authority: ctx.accounts.reward_token_mint.to_account_info(), // metadata hesabının güncelleme yetkisi
payer: ctx.accounts.admin.to_account_info(), // metadata hesabını oluşturmak için ödeme yapan
system_program: ctx.accounts.system_program.to_account_info(), // sistem program hesabı
rent: ctx.accounts.rent.to_account_info(), // rent sysvar hesabı
},
signer,
);
create_metadata_accounts_v3(
cpi_ctx, // cpi bağlamı
data_v2, // token metadata'sı
true, // değiştirilebilir
true, // güncelleme yetkisi imzacı
None, // koleksiyon detayları
)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateMint<'info> {
#[account(
mut,
address = ADMIN_PUBKEY
)]
pub admin: Signer<'info>,
// PDA, hem mint hesabının adresi hem de mint authoritysidir
#[account(
init,
seeds = [b"reward"],
bump,
payer = admin,
mint::decimals = 9,
mint::authority = reward_token_mint,
)]
pub reward_token_mint: Account<'info, Mint>,
///CHECK: metadata hesap adresinin doğrulanması için "address" kısıtlaması kullanılıyor
#[account(
mut,
address=find_metadata_account(&reward_token_mint.key()).0
)]
pub metadata_account: UncheckedAccount<'info>,
pub token_program: Program<'info, Token>,
pub token_metadata_program: Program<'info, Metadata>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}
create_mint
talimatı, bir Program Türetilmiş Adres (PDA) kullanarak yeni bir token mint oluşturur; bu adres hem token mint'in adresi hem de mint authority olarak kullanılır. Talimat, bir URI (offchain metadata), isim ve sembol alır.
Bu talimat daha sonra, Token Metadata programından create_metadata_accounts_v3
talimatını çağırarak token mint için bir metadata hesabı oluşturur.
PDA, metadata hesabı oluşturulurken gerekli bir imza olan mint authority'dir. Talimat verileri (URI, isim, sembol), yeni token mint'inin metadata'sını belirtmek için DataV2
struct'ında yer alır.
Ayrıca, işlemi imzalayan admin
hesabının adresinin ADMIN_PUBKEY
sabitinin değeriyle eşleştiğini doğruluyoruz; böylece yalnızca istediğimiz cüzdan bu talimatı kullanabilir.
const ADMIN_PUBKEY: Pubkey = pubkey!("REPLACE_WITH_YOUR_WALLET_PUBKEY");
Oyuncu Başlatma Talimatı
Sonraki adımda, başlangıç sağlık değeri 100 olan yeni bir oyuncu hesabı oluşturan init_player
talimatını implement edelim. MAX_HEALTH
sabiti, başlangıç sağlık değerini temsil etmek için 100 olarak ayarlanmıştır.
init_player
talimatı aşağıdaki hesapları gerektirir:
player_data
- başlattığımız yeni oyuncu hesabı, oyuncunun sağlığını saklayacaktırplayer
- işlemi imzalayan ve hesabın başlatılması için ödeyen kullanıcısystem_program
- yeni bir hesap oluşturulurken gerekli olan bir hesap
// Oyuncunun maksimum sağlığı
const MAX_HEALTH: u8 = 100;
#[program]
pub mod anchor_token {
use super::*;
...
// Yeni oyuncu hesabı oluştur
pub fn init_player(ctx: Context<InitPlayer>) -> Result<()> {
ctx.accounts.player_data.health = MAX_HEALTH;
Ok(())
}
}
...
#[derive(Accounts)]
pub struct InitPlayer<'info> {
#[account(
init,
payer = player,
space = 8 + 8,
seeds = [b"player".as_ref(), player.key().as_ref()],
bump,
)]
pub player_data: Account<'info, PlayerData>,
#[account(mut)]
pub player: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct PlayerData {
pub health: u8,
}
player_data
hesabı, player
public key'inin bir tohum olarak kullanılmasıyla bir Program Türetilmiş Adres (PDA) kullanılarak başlatılır. Bu, her player_data
hesabının benzersiz olmasını ve player
ile ilişkilendirilmesini sağlar, böylece her oyuncu kendi player_data
hesabını oluşturabilir.
Düşmanı Öldürme Talimatı
Bir sonraki adımda, oyuncunun sağlığını 10 azaltan ve oyuncunun token hesabına ödül olarak 1 token mintleyen kill_enemy
talimatını uygulayalım.
kill_enemy
talimatı aşağıdaki hesapları gerektirir:
player
- tokenı alan oyuncuplayer_data
- oyuncunun mevcut sağlığını saklayan oyuncu verisi hesabıplayer_token_account
- tokenların mintleneceği oyuncunun bağlı token hesabıreward_token_mint
- mintlenecek token türünü belirten token mint hesabıtoken_program
- token programındaki talimatlarla etkileşimde bulunmak için gereklidirassociated_token_program
- bağlı token hesapları ile çalışırken gereklidirsystem_program
- yeni bir hesap oluşturulurken gerekli olan bir hesap
#[program]
pub mod anchor_token {
use super::*;
...
// Oyuncu token hesabına token mintle
pub fn kill_enemy(ctx: Context<KillEnemy>) -> Result<()> {
// Oyuncunun yeterli sağlığı olup olmadığını kontrol et
if ctx.accounts.player_data.health == 0 {
return err!(ErrorCode::NotEnoughHealth);
}
// Oyuncunun sağlığından 10 çıkar
ctx.accounts.player_data.health = ctx.accounts.player_data.health.checked_sub(10).unwrap();
// CPI için "imzalamak" için PDA tohumları ve bump
let seeds = b"reward";
let bump = *ctx.bumps.get("reward_token_mint").unwrap();
let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];
// CPI Bağlamı
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
MintTo {
mint: ctx.accounts.reward_token_mint.to_account_info(),
to: ctx.accounts.player_token_account.to_account_info(),
authority: ctx.accounts.reward_token_mint.to_account_info(),
},
signer,
);
// 1 token mintle, mintin ondalıklarına dikkat et
let amount = (1u64)
.checked_mul(10u64.pow(ctx.accounts.reward_token_mint.decimals as u32))
.unwrap();
mint_to(cpi_ctx, amount)?;
Ok(())
}
}
...
#[derive(Accounts)]
pub struct KillEnemy<'info> {
#[account(mut)]
pub player: Signer<'info>,
#[account(
mut,
seeds = [b"player".as_ref(), player.key().as_ref()],
bump,
)]
pub player_data: Account<'info, PlayerData>,
// Oyuncu token hesabı yoksa başlat
#[account(
init_if_needed,
payer = player,
associated_token::mint = reward_token_mint,
associated_token::authority = player
)]
pub player_token_account: Account<'info, TokenAccount>,
#[account(
mut,
seeds = [b"reward"],
bump,
)]
pub reward_token_mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
}
#[error_code]
pub enum ErrorCode {
#[msg("Yeterli sağlık yok")]
NotEnoughHealth,
}
Oyuncunun sağlığı, "düşmanla mücadelenin" bir temsilcisi olarak 10 azaltılır. Ayrıca, oyuncunun mevcut sağlığını kontrol ediyor ve eğer sağlık 0 ise özel bir Anchor hatası döndürüyoruz.
Talimat daha sonra mint_to
talimatını Token programından çağırarak bir çapraz program çağrısı (CPI) gerçekleştirir ve 1 tokenı reward_token_mint
'ten player_token_account
'a öldürme ödülü olarak mintler.
Token mintinin mint authority'si bir Program Türetilmiş Adres (PDA) olduğundan, ek imzacılara ihtiyaç duymadan bu talimatı çağırarak tokenları mintleyebiliriz. Program, PDA adına "imzalamak" için imza atabilir, böylece token mintleme işlemi için ek imzacı gerektirmeden gerçekleştirilir.
İyileştirme Talimatı
Sonraki adımda, oyuncunun 1 token yakarak sağlığını maksimum değerine yeniden döndürmesine izin veren heal
talimatını uygulayalım.
heal
talimatı aşağıdaki hesapları gerektirir:
player
- iyileştirme eylemini gerçekleştiren oyuncuplayer_data
- oyuncunun mevcut sağlığını saklayan oyuncu veri hesabıplayer_token_account
- tokenların yakılacağı oyuncunun bağlı token hesabıreward_token_mint
- yakılacak token türünü belirten token mint hesabıtoken_program
- token programındaki talimatlarla etkileşimde bulunmak için gereklidirassociated_token_program
- bağlı token hesapları ile çalışırken gereklidir
#[program]
pub mod anchor_token {
use super::*;
...
// Oyuncunun sağlığı için token yak
pub fn heal(ctx: Context<Heal>) -> Result<()> {
ctx.accounts.player_data.health = MAX_HEALTH;
// CPI Bağlamı
let cpi_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Burn {
mint: ctx.accounts.reward_token_mint.to_account_info(),
from: ctx.accounts.player_token_account.to_account_info(),
authority: ctx.accounts.player.to_account_info(),
},
);
// 1 token yak, mintin ondalıklarına dikkat et
let amount = (1u64)
.checked_mul(10u64.pow(ctx.accounts.reward_token_mint.decimals as u32))
.unwrap();
burn(cpi_ctx, amount)?;
Ok(())
}
}
...
#[derive(Accounts)]
pub struct Heal<'info> {
#[account(mut)]
pub player: Signer<'info>,
#[account(
mut,
seeds = [b"player".as_ref(), player.key().as_ref()],
bump,
)]
pub player_data: Account<'info, PlayerData>,
#[account(
mut,
associated_token::mint = reward_token_mint,
associated_token::authority = player
)]
pub player_token_account: Account<'info, TokenAccount>,
#[account(
mut,
seeds = [b"reward"],
bump,
)]
pub reward_token_mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
}
Oyuncunun sağlığı heal
talimatı ile maksimum değerine geri döndürülür. Talimat daha sonra, oyuncunun player_token_account
'ından tokenı yakmak için Token programından burn
talimatını çağırmak üzere bir çapraz program çağrısı (CPI) gerçekleştirir.
Inşa Et ve Dağıt
Harika bir iş çıkardınız! Şimdi programı oluşturup dağıtabilirsiniz. Nihai programınız aşağıdaki gibi görünmelidir:
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
metadata::{create_metadata_accounts_v3, CreateMetadataAccountsV3, Metadata},
token::{burn, mint_to, Burn, Mint, MintTo, Token, TokenAccount},
};
use mpl_token_metadata::{pda::find_metadata_account, state::DataV2};
use solana_program::{pubkey, pubkey::Pubkey};
declare_id!("CCLnXJAJYFjCHLCugpBCEQKrpiSApiRM4UxkBUHJRrv4");
const ADMIN_PUBKEY: Pubkey = pubkey!("REPLACE_WITH_YOUR_WALLET_PUBKEY");
const MAX_HEALTH: u8 = 100;
#[program] pub mod anchor_token { use super::*;
// Yeni bir token mint oluştur PDA'yı mint yetkisi olarak kullanarak
pub fn create_mint(
ctx: Context,
uri: String,
name: String,
symbol: String,
) -> Result {
// PDA tohumları ve bump CPI için "imzalama"
let seeds = b"reward";
let bump = *ctx.bumps.get("reward_token_mint").unwrap();
let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];
// Mint için on-chain token metadata
let data_v2 = DataV2 {
name: name,
symbol: symbol,
uri: uri,
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
};
// CPI Bağlamı
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_metadata_program.to_account_info(),
CreateMetadataAccountsV3 {
metadata: ctx.accounts.metadata_account.to_account_info(), // oluşturulan metadata hesabı
mint: ctx.accounts.reward_token_mint.to_account_info(), // metadata hesabının mint hesabı
mint_authority: ctx.accounts.reward_token_mint.to_account_info(), // mint hesabının mint yetkisi
update_authority: ctx.accounts.reward_token_mint.to_account_info(), // metadata hesabının güncelleme yetkisi
payer: ctx.accounts.admin.to_account_info(), // metadata hesabının oluşturulması için ödeyici
system_program: ctx.accounts.system_program.to_account_info(), // sistem program hesabı
rent: ctx.accounts.rent.to_account_info(), // kiralama sysvar hesabı
},
signer,
);
create_metadata_accounts_v3(
cpi_ctx, // cpi bağlamı
data_v2, // token metadata
true, // değiştirilebilir mi
true, // güncelleme yetkisi imzalayıcı mı
None, // koleksiyon detayları
)?;
Ok(())
}
// Yeni bir oyuncu hesabı oluştur
pub fn init_player(ctx: Context) -> Result {
ctx.accounts.player_data.health = MAX_HEALTH;
Ok(())
}
// Oyuncu token hesabına token mint et
pub fn kill_enemy(ctx: Context) -> Result {
// Oyuncunun yeterli sağlığı olup olmadığını kontrol et
if ctx.accounts.player_data.health == 0 {
return err!(ErrorCode::NotEnoughHealth);
}
// Oyuncudan 10 sağlık çıkar
ctx.accounts.player_data.health = ctx.accounts.player_data.health.checked_sub(10).unwrap();
// PDA tohumları ve bump CPI için "imzalama"
let seeds = b"reward";
let bump = *ctx.bumps.get("reward_token_mint").unwrap();
let signer: &[&[&[u8]]] = &[&[seeds, &[bump]]];
// CPI Bağlamı
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
MintTo {
mint: ctx.accounts.reward_token_mint.to_account_info(),
to: ctx.accounts.player_token_account.to_account_info(),
authority: ctx.accounts.reward_token_mint.to_account_info(),
},
signer,
);
// Mint 1 token, mint'in ondalıklarını hesaba kat
let amount = (1u64)
.checked_mul(10u64.pow(ctx.accounts.reward_token_mint.decimals as u32))
.unwrap();
mint_to(cpi_ctx, amount)?;
Ok(())
}
// Oyuncunun sağlığını iyileştir
pub fn heal(ctx: Context) -> Result {
ctx.accounts.player_data.health = MAX_HEALTH;
// CPI Bağlamı
let cpi_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Burn {
mint: ctx.accounts.reward_token_mint.to_account_info(),
from: ctx.accounts.player_token_account.to_account_info(),
authority: ctx.accounts.player.to_account_info(),
},
);
// 1 token yak, mint'in ondalıklarını hesaba kat
let amount = (1u64)
.checked_mul(10u64.pow(ctx.accounts.reward_token_mint.decimals as u32))
.unwrap();
burn(cpi_ctx, amount)?;
Ok(())
}
}
#[derive(Accounts)] pub struct CreateMint { #[account( mut, address = ADMIN_PUBKEY )] pub admin: Signer,
// PDA hem mint hesabının adresi hem de mint yetkisi
#[account(
init,
seeds = [b"reward"],
bump,
payer = admin,
mint::decimals = 9,
mint::authority = reward_token_mint,
)]
pub reward_token_mint: Account,
///CHECK: Metadata hesabı adresini doğrulamak için "address" kısıtlaması kullanılıyor
#[account(
mut,
address=find_metadata_account(&reward_token_mint.key()).0
)]
pub metadata_account: UncheckedAccount,
pub token_program: Program,
pub token_metadata_program: Program,
pub system_program: Program,
pub rent: Sysvar,
}
#[derive(Accounts)] pub struct InitPlayer { #[account( init, payer = player, space = 8 + 8, seeds = [b"player".as_ref(), player.key().as_ref()], bump, )] pub player_data: Account, #[account(mut)] pub player: Signer, pub system_program: Program, }
#[derive(Accounts)] pub struct KillEnemy { #[account(mut)] pub player: Signer,
#[account(
mut,
seeds = [b"player".as_ref(), player.key().as_ref()],
bump,
)]
pub player_data: Account,
// Oyuncu token hesabını initialize et, eğer mevcut değilse
#[account(
init_if_needed,
payer = player,
associated_token::mint = reward_token_mint,
associated_token::authority = player
)]
pub player_token_account: Account,
#[account(
mut,
seeds = [b"reward"],
bump,
)]
pub reward_token_mint: Account,
pub token_program: Program,
pub associated_token_program: Program,
pub system_program: Program,
}
#[derive(Accounts)] pub struct Heal { #[account(mut)] pub player: Signer,
#[account(
mut,
seeds = [b"player".as_ref(), player.key().as_ref()],
bump,
)]
pub player_data: Account,
#[account(
mut,
associated_token::mint = reward_token_mint,
associated_token::authority = player
)]
pub player_token_account: Account,
#[account(
mut,
seeds = [b"reward"],
bump,
)]
pub reward_token_mint: Account,
pub token_program: Program,
pub associated_token_program: Program,
}
#[account] pub struct PlayerData { pub health: u8, }
#[error_code] pub enum ErrorCode { #[msg("Yeterli sağlık yok")] NotEnoughHealth, }
Müşteri ile Başlarken
Bu bölümde, program ile etkileşim kurmak için basit bir istemci tarafı uygulamasını yürüyerek göstereceğiz. Başlamak için, Solana Playground'daki client.ts
dosyasına gidin, yer tutucu kodu kaldırın ve aşağıdaki bölümlerden kod parçalarını ekleyin.
Öncelikle, kurulum için aşağıdaki kodu ekleyin.
import { Metaplex } from "@metaplex-foundation/js";
import { getMint, getAssociatedTokenAddressSync } from "@solana/spl-token";
// metaplex token metadata program ID
const TOKEN_METADATA_PROGRAM_ID = new web3.PublicKey(
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
);
// metaplex ayarlama
const metaplex = Metaplex.make(pg.connection);
// token metadata
const metadata = {
uri: "https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/spl-token.json",
name: "Solana Gold",
symbol: "GOLDSOL",
};
// ödül token mint PDA'sı
const [rewardTokenMintPda] = anchor.web3.PublicKey.findProgramAddressSync(
[Buffer.from("reward")],
pg.PROGRAM_ID,
);
// oyuncu veri hesabı PDA'sı
const [playerPDA] = anchor.web3.PublicKey.findProgramAddressSync(
[Buffer.from("player"), pg.wallet.publicKey.toBuffer()],
pg.PROGRAM_ID,
);
// ödül token mint metadata hesabı adresi
const rewardTokenMintMetadataPDA = await metaplex
.nfts()
.pdas()
.metadata({ mint: rewardTokenMintPda });
// oyuncu token hesabı adresi
const playerTokenAccount = getAssociatedTokenAddressSync(
rewardTokenMintPda,
pg.wallet.publicKey,
);
Sonraki iki yardımcı fonksiyonu ekleyin. Bu fonksiyonlar, işlemleri onaylamak ve hesap verilerini almak için kullanılacaktır.
async function logTransaction(txHash) {
const { blockhash, lastValidBlockHeight } =
await pg.connection.getLatestBlockhash();
await pg.connection.confirmTransaction({
blockhash,
lastValidBlockHeight,
signature: txHash,
});
console.log(`Loglar için 'solana confirm -v ${txHash}' kullanın`);
}
async function fetchAccountData() {
const [playerBalance, playerData] = await Promise.all([
pg.connection.getTokenAccountBalance(playerTokenAccount),
pg.program.account.playerData.fetch(playerPDA),
]);
console.log("Oyuncu Token Bakiyesi: ", playerBalance.value.uiAmount);
console.log("Oyuncu Sağlığı: ", playerData.health);
}
Ardından, mevcut değilse yeni bir token mint oluşturmak için createMint
talimatını çağırın:
let txHash;
try {
const mintData = await getMint(pg.connection, rewardTokenMintPda);
console.log("Mint Zaten Var");
} catch {
txHash = await pg.program.methods
.createMint(metadata.uri, metadata.name, metadata.symbol)
.accounts({
rewardTokenMint: rewardTokenMintPda,
metadataAccount: rewardTokenMintMetadataPDA,
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
})
.rpc();
await logTransaction(txHash);
}
console.log("Token Mint: ", rewardTokenMintPda.toString());
Sonrasında, mevcut değilse yeni bir oyuncu hesabı oluşturmak için initPlayer
talimatını çağırın.
try {
const playerData = await pg.program.account.playerData.fetch(playerPDA);
console.log("Oyuncu Zaten Var");
console.log("Oyuncu Sağlığı: ", playerData.health);
} catch {
txHash = await pg.program.methods
.initPlayer()
.accounts({
playerData: playerPDA,
player: pg.wallet.publicKey,
})
.rpc();
await logTransaction(txHash);
console.log("Oyuncu Hesabı Oluşturuldu");
}
Sonra, killEnemy
talimatını çağırın:
txHash = await pg.program.methods
.killEnemy()
.accounts({
playerData: playerPDA,
playerTokenAccount: playerTokenAccount,
rewardTokenMint: rewardTokenMintPda,
})
.rpc();
await logTransaction(txHash);
console.log("Düşman Yenildi");
await fetchAccountData();
Son olarak, heal
talimatını çağırın:
txHash = await pg.program.methods
.heal()
.accounts({
playerData: playerPDA,
playerTokenAccount: playerTokenAccount,
rewardTokenMint: rewardTokenMintPda,
})
.rpc();
await logTransaction(txHash);
console.log("Oyuncu İyileştirildi");
await fetchAccountData();
Son olarak, Solana Playground'da “Çalıştır” butonuna tıklayarak istemciyi çalıştırın. Konsola yazdırılan Token Mint adresini kopyalayabilir ve Solana Explorer'da tokenin şimdi metadata'ya sahip olduğunu doğrulayabilirsiniz. Çıktı aşağıdakine benzer olmalıdır:
İstemci çalışıyor...
client.ts:
Loglar için 'solana confirm -v 3AWnpt2Wy6jQckue4QeKsgDNKhKkhpewPmRtxvJpzxGgvK9XK9KEpTiUzAQ5vSC6CUoUjc6xWZCtrihVrFy8sACC' kullanın
Token Mint: 3eS7hdyeVX5g8JGhn3Z7qFXJaewoJ8hzgvubovQsPm4S
Loglar için 'solana confirm -v 63jbBr5U4LG75TiiHfz65q7yKJfHDhGP2ocCiDat5M2k4cWtUMAx9sHvxhnEguLDKXMbDUQKUt1nhvyQkXoDhxst' kullanın
Oyuncu Hesabı Oluşturuldu
Loglar için 'solana confirm -v 2ziK41WLoxfEHvtUgc5c1SyKCAr5FvAS54ARBJrjqh9GDwzYqu7qWCwHJCgMZyFEVovYK5nUZhDRHPTMrTjq1Mm6' kullanın
Düşman Yenildi
Oyuncu Token Bakiyesi: 1
Oyuncu Sağlığı: 90
Loglar için 'solana confirm -v 2QoAH22Q3xXz9t2TYRycQMqpEmauaRvmUfZ7ZNKUEoUyHWqpjW972VD3eZyeJrXsviaiCC3g6TE54oKmKbFQf2Q7' kullanın
Oyuncu İyileştirildi
Oyuncu Token Bakiyesi: 0
Oyuncu Sağlığı: 100