Program Derived Address
W tej sekcji dowiesz się, jak zbudować podstawowy program CRUD (Create, Read, Update, Delete).
Ten przewodnik pokazuje prosty program, w którym użytkownicy mogą tworzyć, aktualizować i usuwać wiadomości. Każda wiadomość istnieje na koncie z deterministycznym adresem pochodzącym bezpośrednio z programu (Program Derived Address lub PDA).
Ten przewodnik przeprowadzi Cię przez budowanie i testowanie programu Solana przy użyciu frameworka Anchor, jednocześnie demonstrując Program Derived Addresses (PDAs). Więcej szczegółów znajdziesz na stronie Program Derived Addresses.
Dla odniesienia możesz zobaczyć ostateczny kod po ukończeniu sekcji dotyczących PDA i Cross-Program Invocation (CPI).
Kod początkowy
Rozpocznij, otwierając ten link do Solana Playground z kodem początkowym. Następnie kliknij przycisk "Import", aby dodać program do swoich projektów w Solana Playground.
Import
W pliku lib.rs
znajdziesz program z instrukcjami create
,
update
i delete
, które należy dodać w
kolejnych krokach.
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 {}
Przed rozpoczęciem uruchom build
w terminalu Playground, aby sprawdzić,
czy program początkowy kompiluje się poprawnie.
$build
Zdefiniuj typ konta wiadomości
Najpierw zdefiniuj strukturę konta wiadomości, które program tworzy. Ta struktura określa dane do przechowywania na koncie utworzonym przez program.
W lib.rs
zaktualizuj strukturę MessageAccount
za pomocą następujących
elementów:
#[account]pub struct MessageAccount {pub user: Pubkey,pub message: String,pub bump: u8,}
Zbuduj program ponownie, uruchamiając build
w terminalu.
$build
Ten kod definiuje, jakie dane należy przechowywać na koncie wiadomości. Następnie dodasz instrukcje programu.
Dodaj instrukcję tworzenia
Teraz dodaj instrukcję create
, która tworzy i inicjalizuje
MessageAccount
.
Rozpocznij od zdefiniowania kont wymaganych dla instrukcji, aktualizując
strukturę Create
za pomocą następujących elementów:
#[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>,}
Następnie dodaj logikę biznesową dla instrukcji create
poprzez
aktualizację funkcji create
za pomocą następującego kodu:
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(())}
Zbuduj program ponownie.
$build
Dodaj instrukcję aktualizacji
Następnie dodaj instrukcję update
, aby zmienić MessageAccount
na nową
wiadomość.
Podobnie jak w poprzednim kroku, najpierw określ konta wymagane przez instrukcję
update
.
Zaktualizuj strukturę Update
w następujący sposób:
#[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>,}
Następnie dodaj logikę dla instrukcji update
.
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(())}
Przebuduj program
$build
Dodaj instrukcję usuwania
Następnie dodaj instrukcję delete
do zamknięcia MessageAccount
.
Zaktualizuj strukturę Delete
o następujące elementy:
#[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>,}
Następnie dodaj logikę dla instrukcji delete
.
pub fn delete(_ctx: Context<Delete>) -> Result<()> {msg!("Delete Message");Ok(())}
Przebuduj program.
$build
Wdróż program
Właśnie ukończyłeś podstawowy program CRUD. Wdróż program, uruchamiając deploy
w terminalu Playground.
W tym przykładzie wdrożysz program na devnet, klaster Solana przeznaczony do testów deweloperskich.
Portfel Playground łączy się z devnet domyślnie. Upewnij się, że Twój portfel Playground ma devnet SOL, aby zapłacić za wdrożenie programu. Zdobądź devnet SOL z Solana Faucet.
$deploy
Skonfiguruj plik testowy
Kod początkowy zawiera również plik testowy w 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 () => {});});
Dodaj poniższy kod wewnątrz describe()
, ale przed sekcjami it()
.
const program = pg.program;const wallet = pg.wallet;const [messagePda, messageBump] = PublicKey.findProgramAddressSync([Buffer.from("message"), wallet.publicKey.toBuffer()],program.programId);
Uruchom plik testowy, wykonując test
w terminalu Playground, aby
sprawdzić, czy działa zgodnie z oczekiwaniami. Kolejne kroki dodają rzeczywiste
testy.
$test
Wywołaj instrukcję Create
Zaktualizuj pierwszy test za pomocą poniższego kodu:
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`);});
Wywołaj instrukcję aktualizacji
Zaktualizuj drugi test w następujący sposób:
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`);});
Wywołaj instrukcję usunięcia
Zaktualizuj trzeci test w następujący sposób:
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`);});
Uruchom test
Po przygotowaniu testów uruchom plik testowy za pomocą test
w terminalu
Playground. To polecenie uruchamia testy na programie wdrożonym na devnecie i
loguje linki do SolanaFM, aby wyświetlić szczegóły transakcji.
$test
Sprawdź linki SolanaFM, aby zobaczyć szczegóły transakcji.
Zwróć uwagę, że w tym przykładzie, jeśli uruchomisz test ponownie, instrukcja
create
zakończy się niepowodzeniem, ponieważ messageAccount
już istnieje
jako konto. Tylko jedno konto może istnieć dla danego PDA.
Is this page helpful?