Program Derived Address

Bu bölümde, temel bir Oluştur, Oku, Güncelle, Sil (CRUD) programı oluşturmayı öğreneceksiniz.

Bu rehber, kullanıcıların bir mesaj oluşturabileceği, güncelleyebileceği ve silebileceği basit bir programı göstermektedir. Her mesaj, programın kendisinden türetilen deterministik bir adrese sahip bir hesapta bulunur (Program Derived Address veya PDA).

Bu rehber, Anchor framework kullanarak bir Solana programı oluşturma ve test etme sürecinde size Program Derived Address'leri (PDA'lar) göstermektedir. Daha fazla ayrıntı için Program Derived Addresses sayfasına bakın.

Referans olarak, hem PDA hem de Cross Program Invocation (CPI) bölümlerini tamamladıktan sonra nihai kodu görüntüleyebilirsiniz.

Başlangıç Kodu

Başlangıç koduyla birlikte bu Solana Playground bağlantısını açarak başlayın. Ardından programı Solana Playground projelerinize eklemek için "Import" düğmesine tıklayın.

İçe Aktarİçe Aktar

lib.rs dosyasında, create, update ve delete talimatlarını içeren bir program bulacaksınız sonraki adımlarda ekleyeceksiniz.

lib.rs
use anchor_lang::prelude::*;
declare_id!("8KPzbM2Cwn4Yjak7QYAEH9wyoQh86NcBicaLuzPaejdw");
#[program]
pub mod pda {
use super::*;
pub fn create(_ctx: Context<Create>) -> Result<()> {
Ok(())
}
pub fn update(_ctx: Context<Update>) -> Result<()> {
Ok(())
}
pub fn delete(_ctx: Context<Delete>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Create {}
#[derive(Accounts)]
pub struct Update {}
#[derive(Accounts)]
pub struct Delete {}
#[account]
pub struct MessageAccount {}

Başlamadan önce, başlangıç programının başarıyla derlendiğini kontrol etmek için Playground terminalinde build komutunu çalıştırın.

Terminal
$
build

Mesaj Hesap Türünü Tanımlama

İlk olarak, programın oluşturacağı mesaj hesabı için yapıyı tanımlayın. Bu yapı, program tarafından oluşturulan hesapta saklanacak verileri tanımlar.

lib.rs içinde, MessageAccount yapısını aşağıdaki şekilde güncelleyin:

lib.rs
#[account]
pub struct MessageAccount {
pub user: Pubkey,
pub message: String,
pub bump: u8,
}

Terminalde build komutunu çalıştırarak programı tekrar derleyin.

Terminal
$
build

Bu kod, mesaj hesabında hangi verilerin saklanacağını tanımlar. Sonraki adımda, program talimatlarını ekleyeceksiniz.

Oluşturma talimatı ekle

Şimdi, MessageAccount oluşturan ve başlatan create talimatını ekleyin.

Create yapısını aşağıdakilerle güncelleyerek, talimat için gereken hesapları tanımlamaya başlayın:

lib.rs
#[derive(Accounts)]
#[instruction(message: String)]
pub struct Create<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(
init,
seeds = [b"message", user.key().as_ref()],
bump,
payer = user,
space = 8 + 32 + 4 + message.len() + 1
)]
pub message_account: Account<'info, MessageAccount>,
pub system_program: Program<'info, System>,
}

Şimdi, create fonksiyonunu aşağıdakilerle güncelleyerek create talimatı için iş mantığını ekleyin:

lib.rs
pub fn create(ctx: Context<Create>, message: String) -> Result<()> {
msg!("Create Message: {}", message);
let account_data = &mut ctx.accounts.message_account;
account_data.user = ctx.accounts.user.key();
account_data.message = message;
account_data.bump = ctx.bumps.message_account;
Ok(())
}

Programı yeniden derleyin.

Terminal
$
build

Güncelleme Talimatı Ekleme

Sonraki adımda, MessageAccount değerini yeni bir mesajla değiştirmek için update talimatını ekleyin.

Önceki adımda olduğu gibi, önce update talimatı için gereken hesapları belirtin.

Update yapısını aşağıdaki gibi güncelleyin:

lib.rs
#[derive(Accounts)]
#[instruction(message: String)]
pub struct Update<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(
mut,
seeds = [b"message", user.key().as_ref()],
bump = message_account.bump,
realloc = 8 + 32 + 4 + message.len() + 1,
realloc::payer = user,
realloc::zero = true,
)]
pub message_account: Account<'info, MessageAccount>,
pub system_program: Program<'info, System>,
}

Şimdi, update talimatı için mantığı ekleyin.

lib.rs
pub fn update(ctx: Context<Update>, message: String) -> Result<()> {
msg!("Update Message: {}", message);
let account_data = &mut ctx.accounts.message_account;
account_data.message = message;
Ok(())
}

Programı yeniden derleyin

Terminal
$
build

Silme talimatını ekleyin

Şimdi, MessageAccount hesabını kapatmak için delete talimatını ekleyin.

Delete yapısını aşağıdaki gibi güncelleyin:

lib.rs
#[derive(Accounts)]
pub struct Delete<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(
mut,
seeds = [b"message", user.key().as_ref()],
bump = message_account.bump,
close = user,
)]
pub message_account: Account<'info, MessageAccount>,
}

Şimdi, delete talimatı için mantığı ekleyin.

lib.rs
pub fn delete(_ctx: Context<Delete>) -> Result<()> {
msg!("Delete Message");
Ok(())
}

Programı yeniden derleyin.

Terminal
$
build

Programı dağıtın

Artık temel CRUD programını tamamladınız. Playground terminalinde deploy komutunu çalıştırarak programı dağıtın.

Bu örnekte, programı geliştirme testi için bir Solana kümesi olan devnet'e dağıtacaksınız.

Playground cüzdanı varsayılan olarak devnet'e bağlanır. Program dağıtımı için ödeme yapmak üzere Playground cüzdanınızda devnet SOL olduğundan emin olun. Devnet SOL'u Solana Faucet adresinden alabilirsiniz.

Terminal
$
deploy

Test dosyasını ayarlayın

Başlangıç kodu ayrıca anchor.test.ts içinde bir test dosyası içerir.

anchor.test.ts
import { PublicKey } from "@solana/web3.js";
describe("pda", () => {
it("Create Message Account", async () => {});
it("Update Message Account", async () => {});
it("Delete Message Account", async () => {});
});

Aşağıdaki kodu describe() içine, ancak it() bölümlerinden önce ekleyin.

anchor.test.ts
const program = pg.program;
const wallet = pg.wallet;
const [messagePda, messageBump] = PublicKey.findProgramAddressSync(
[Buffer.from("message"), wallet.publicKey.toBuffer()],
program.programId
);

Playground terminalinde test komutunu çalıştırarak test dosyasını çalıştırın ve beklendiği gibi çalıştığını kontrol edin. Sonraki adımlar gerçek testleri ekler.

Terminal
$
test

Create talimatını çağırma

İlk testi aşağıdaki şekilde güncelleyin:

anchor.test.ts
it("Create Message Account", async () => {
const message = "Hello, World!";
const transactionSignature = await program.methods
.create(message)
.accounts({
messageAccount: messagePda
})
.rpc({ commitment: "confirmed" });
const messageAccount = await program.account.messageAccount.fetch(
messagePda,
"confirmed"
);
console.log(JSON.stringify(messageAccount, null, 2));
console.log(
"Transaction Signature:",
`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
);
});

Güncelleme Talimatını Çağırma

İkinci testi aşağıdaki şekilde güncelleyin:

anchor.test.ts
it("Update Message Account", async () => {
const message = "Hello, Solana!";
const transactionSignature = await program.methods
.update(message)
.accounts({
messageAccount: messagePda
})
.rpc({ commitment: "confirmed" });
const messageAccount = await program.account.messageAccount.fetch(
messagePda,
"confirmed"
);
console.log(JSON.stringify(messageAccount, null, 2));
console.log(
"Transaction Signature:",
`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
);
});

Silme Talimatını Çağır

Üçüncü testi aşağıdaki gibi güncelleyin:

anchor.test.ts
it("Delete Message Account", async () => {
const transactionSignature = await program.methods
.delete()
.accounts({
messageAccount: messagePda
})
.rpc({ commitment: "confirmed" });
const messageAccount = await program.account.messageAccount.fetchNullable(
messagePda,
"confirmed"
);
console.log("Expect Null:", JSON.stringify(messageAccount, null, 2));
console.log(
"Transaction Signature:",
`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
);
});

Testi çalıştır

Testlerinizi hazırladıktan sonra, Playground terminalinde test ile test dosyasını çalıştırın. Bu komut, devnet üzerinde dağıtılan programa karşı testleri çalıştırır ve işlem detaylarını görüntülemek için SolanaFM bağlantılarını kaydeder.

Terminal
$
test

İşlem detaylarını görüntülemek için SolanaFM bağlantılarını inceleyin.

Bu örnekte, testi tekrar çalıştırırsanız, create talimatının başarısız olacağını unutmayın çünkü messageAccount zaten bir hesap olarak var. Belirli bir PDA için yalnızca bir hesap var olabilir.

Is this page helpful?