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.

ImporterImporter

Dans le fichier lib.rs, vous trouverez un programme avec les instructions create, update, et delete à ajouter dans les étapes suivantes.

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 {}

Avant de commencer, exécutez build dans le terminal Playground pour vérifier que le programme de départ se compile correctement.

Terminal
$
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 :

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

Compilez à nouveau le programme en exécutant build dans le terminal.

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 :

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>,
}

Ensuite, ajoutez la logique métier pour l'instruction create en mettant à jour la fonction create avec ce qui suit :

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(())
}

Recompilez le programme.

Terminal
$
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 :

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>,
}

Ensuite, ajoutez la logique pour l'instruction update.

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(())
}

Recompilez le programme

Terminal
$
build

Ajouter l'instruction de suppression

Ensuite, ajoutez l'instruction delete pour fermer le MessageAccount.

Mettez à jour la structure Delete avec ce qui suit :

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>,
}

Ensuite, ajoutez la logique pour l'instruction delete.

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

Recompilez le programme.

Terminal
$
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.

Terminal
$
deploy

Configurer le fichier de test

Le code de démarrage comprend également un fichier de test dans anchor.test.ts.

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().

anchor.test.ts
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.

Terminal
$
test

Invoquer l'instruction Create

Mettez à jour le premier test avec ce qui suit :

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`
);
});

Invoquer l'instruction de mise à jour

Mettez à jour le second test avec ce qui suit :

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`
);
});

Invoquer l'instruction de suppression

Mettez à jour le troisième test avec ce qui suit :

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`
);
});

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.

Terminal
$
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?