Program Derived Address
In questa sezione, imparerai a costruire un programma base di tipo Create, Read, Update, Delete (CRUD).
Questa guida dimostra un semplice programma in cui gli utenti possono creare, aggiornare ed eliminare un messaggio. Ogni messaggio esiste in un account con un indirizzo deterministico derivato dal programma stesso (Program Derived Address o PDA).
Questa guida ti accompagna nella costruzione e nel test di un programma Solana utilizzando il framework Anchor mentre dimostra l'uso dei Program Derived Addresses (PDAs). Per maggiori dettagli, consulta la pagina Program Derived Addresses.
Come riferimento, puoi visualizzare il codice finale dopo aver completato sia la sezione PDA che quella Cross-Program Invocation (CPI).
Codice iniziale
Inizia aprendo questo link di Solana Playground con il codice iniziale. Poi clicca sul pulsante "Import" per aggiungere il programma ai tuoi progetti di Solana Playground.
Importa
Nel file lib.rs
, troverai un programma con le istruzioni
create
, update
e delete
da
aggiungere nei passaggi successivi.
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 {}
Prima di iniziare, esegui build
nel terminale di Playground per
verificare che il programma iniziale si compili correttamente.
$build
Definire il tipo di account per i messaggi
Per prima cosa, definisci la struttura per l'account del messaggio che il programma creerà. Questa struttura definisce i dati da memorizzare nell'account creato dal programma.
In lib.rs
, aggiorna la struttura MessageAccount
con quanto segue:
#[account]pub struct MessageAccount {pub user: Pubkey,pub message: String,pub bump: u8,}
Compila nuovamente il programma eseguendo build
nel terminale.
$build
Questo codice definisce quali dati memorizzare nell'account del messaggio. Successivamente, aggiungerai le istruzioni del programma.
Aggiungi istruzione di creazione
Ora, aggiungi l'istruzione create
che crea e inizializza
MessageAccount
.
Inizia definendo gli account richiesti per l'istruzione aggiornando la struct
Create
con quanto segue:
#[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>,}
Successivamente, aggiungi la logica di business per l'istruzione create
aggiornando la funzione create
con quanto segue:
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(())}
Ricompila il programma.
$build
Aggiungi l'istruzione di aggiornamento
Successivamente, aggiungi l'istruzione update
per modificare il
MessageAccount
con un nuovo messaggio.
Come nel passaggio precedente, specifica prima gli account richiesti
dall'istruzione update
.
Aggiorna la struttura Update
con quanto segue:
#[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>,}
Successivamente, aggiungi la logica per l'istruzione 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(())}
Ricompila il programma
$build
Aggiungi l'istruzione Delete
Successivamente, aggiungi l'istruzione delete
per chiudere
MessageAccount
.
Aggiorna la struttura Delete
con quanto segue:
#[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>,}
Successivamente, aggiungi la logica per l'istruzione delete
.
pub fn delete(_ctx: Context<Delete>) -> Result<()> {msg!("Delete Message");Ok(())}
Ricompila il programma.
$build
Distribuisci il programma
Hai ora completato il programma CRUD di base. Distribuisci il programma
eseguendo deploy
nel terminale di Playground.
In questo esempio, distribuirai il programma sulla devnet, un cluster Solana per i test di sviluppo.
Il wallet di Playground si connette alla devnet per impostazione predefinita. Assicurati che il tuo wallet di Playground abbia SOL della devnet per pagare la distribuzione del programma. Ottieni SOL della devnet dal Solana Faucet.
$deploy
Configura il file di test
Il codice iniziale include anche un file di test in 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 () => {});});
Aggiungi il codice seguente all'interno di describe()
, ma prima delle
sezioni it()
.
const program = pg.program;const wallet = pg.wallet;const [messagePda, messageBump] = PublicKey.findProgramAddressSync([Buffer.from("message"), wallet.publicKey.toBuffer()],program.programId);
Esegui il file di test lanciando test
nel terminale di Playground per
verificare che funzioni come previsto. I prossimi passaggi aggiungono i test
effettivi.
$test
Invoca l'istruzione Create
Aggiorna il primo test con quanto segue:
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`);});
Invoca l'istruzione di aggiornamento
Aggiorna il secondo test con quanto segue:
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`);});
Invoca l'istruzione Delete
Aggiorna il terzo test con quanto segue:
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`);});
Esegui il test
Dopo aver preparato i test, esegui il file di test con test
nel
terminale di Playground. Questo comando esegue i test sul programma distribuito
sulla devnet e registra i link a SolanaFM per visualizzare i dettagli della
transazione.
$test
Esamina i link di SolanaFM per visualizzare i dettagli della transazione.
Nota che in questo esempio, se esegui nuovamente il test, l'istruzione
create
fallisce perché messageAccount
esiste già come account. Solo un
account può esistere per un determinato PDA.
Is this page helpful?