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 z wykorzystaniem frameworka Anchor, jednocześnie demonstrując użycie 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(())}
Przebuduj program.
$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(())}
Zbuduj program ponownie
$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(())}
Zbuduj program ponownie.
$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 zobaczyć szczegóły transakcji.
$test
Sprawdź linki SolanaFM, aby zobaczyć szczegóły transakcji.
Zauważ, że w tym przykładzie, jeśli uruchomisz test ponownie, instrukcja
create
nie powiedzie się, ponieważ messageAccount
już istnieje jako konto.
Tylko jedno konto może istnieć dla danego PDA.
Is this page helpful?