Program Derived Address
Dans cette section, vous apprendrez à construire un programme de base de type Créer, Lire, Mettre à jour, Supprimer (CRUD).
Ce guide présente un programme simple où les utilisateurs peuvent créer, mettre à jour et supprimer un message. Chaque message existe dans un compte avec une adresse déterministe dérivée du programme lui-même (Program Derived Address ou PDA).
Ce guide vous accompagne dans la construction et le test d'un programme Solana en utilisant le framework Anchor tout en démontrant les Program Derived Addresses (PDAs). Pour plus de détails, consultez la page Program Derived Addresses.
Pour référence, vous pouvez consulter le code final après avoir terminé les sections PDA et Cross Program Invocation (CPI).
Code de départ
Commencez par ouvrir ce lien Solana Playground avec le code de départ. Puis cliquez sur le bouton "Import" pour ajouter le programme à vos projets Solana Playground.
Importer
Dans le fichier lib.rs
, vous trouverez un programme avec les instructions
create
, update
, et delete
à
ajouter dans les étapes suivantes.
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 {}
Avant de commencer, exécutez build
dans le terminal Playground pour
vérifier que le programme de départ se compile correctement.
$build
Définir le type de compte Message
Tout d'abord, définissez la structure pour le compte de message que le programme crée. Cette structure définit les données à stocker dans le compte créé par le programme.
Dans lib.rs
, mettez à jour la structure MessageAccount
avec ce qui suit
:
#[account]pub struct MessageAccount {pub user: Pubkey,pub message: String,pub bump: u8,}
Compilez à nouveau le programme en exécutant build
dans le terminal.
$build
Ce code définit quelles données stocker sur le compte de message. Ensuite, vous allez ajouter les instructions du programme.
Ajouter l'instruction de création
Maintenant, ajoutez l'instruction create
qui crée et initialise le
MessageAccount
.
Commencez par définir les comptes requis pour l'instruction en mettant à jour la
structure Create
avec ce qui suit :
#[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>,}
Ensuite, ajoutez la logique métier pour l'instruction create
en mettant à
jour la fonction create
avec ce qui suit :
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(())}
Recompilez le programme.
$build
Ajouter l'instruction de mise à jour
Ensuite, ajoutez l'instruction update
pour modifier le MessageAccount
avec
un nouveau message.
Comme à l'étape précédente, spécifiez d'abord les comptes requis par
l'instruction update
.
Mettez à jour la structure Update
avec ce qui suit :
#[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>,}
Ensuite, ajoutez la logique pour l'instruction 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(())}
Recompilez le programme
$build
Ajouter l'instruction de suppression
Ensuite, ajoutez l'instruction delete
pour fermer le MessageAccount
.
Mettez à jour la structure Delete
avec ce qui suit :
#[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>,}
Ensuite, ajoutez la logique pour l'instruction delete
.
pub fn delete(_ctx: Context<Delete>) -> Result<()> {msg!("Delete Message");Ok(())}
Recompilez le programme.
$build
Déployer le programme
Vous avez maintenant terminé le programme CRUD de base. Déployez le programme en
exécutant deploy
dans le terminal du Playground.
Dans cet exemple, vous déploierez le programme sur le devnet, un cluster Solana pour les tests de développement.
Le portefeuille du Playground se connecte au devnet par défaut. Assurez-vous que votre portefeuille Playground dispose de SOL devnet pour payer le déploiement du programme. Obtenez du SOL devnet depuis le Solana Faucet.
$deploy
Configurer le fichier de test
Le code de démarrage comprend également un fichier de test dans
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 () => {});});
Ajoutez le code ci-dessous à l'intérieur de describe()
, mais avant les
sections it()
.
const program = pg.program;const wallet = pg.wallet;const [messagePda, messageBump] = PublicKey.findProgramAddressSync([Buffer.from("message"), wallet.publicKey.toBuffer()],program.programId);
Exécutez le fichier de test en lançant test
dans le terminal Playground
pour vérifier qu'il fonctionne comme prévu. Les étapes suivantes ajoutent les
tests proprement dits.
$test
Invoquer l'instruction Create
Mettez à jour le premier test avec ce qui suit :
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`);});
Invoquer l'instruction de mise à jour
Mettez à jour le second test avec ce qui suit :
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`);});
Invoquer l'instruction de suppression
Mettez à jour le troisième test avec ce qui suit :
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`);});
Exécuter le test
Après avoir préparé vos tests, exécutez le fichier de test avec test
dans le terminal Playground. Cette commande exécute les tests sur le programme
déployé sur le devnet et enregistre des liens vers SolanaFM pour visualiser les
détails de la transaction.
$test
Examinez les liens SolanaFM pour voir les détails de la transaction.
Notez que dans cet exemple, si vous exécutez à nouveau le test, l'instruction
create
échoue car messageAccount
existe déjà en tant que compte. Un seul
compte peut exister pour un PDA donné.
Is this page helpful?