Resumo
Crie contas PDA via invoke_signed com as seeds da PDA. Apenas o programa
proprietário pode assinar por uma PDA. A restrição init do Anchor
automatiza a criação de contas PDA.
Assinatura de PDA via invoke_signed
Quando um programa precisa assinar em nome de uma PDA durante um CPI, ele usa
invoke_signed com as seeds da PDA. O runtime verifica se as seeds derivam
a PDA esperada usando o ID do programa chamador, garantindo que apenas o
programa proprietário possa assinar. Para o fluxo de verificação completo,
consulte Assinatura de PDA.
Criar uma conta PDA
Derivar uma PDA e criar uma conta numa PDA são operações separadas. Você deve criar explicitamente a conta após derivar o endereço.
Para criar uma conta numa PDA, o programa que deriva invoca a instrução
create_account do System Program via invoke_signed,
passando as seeds da PDA para que o runtime possa verificar a autoridade do
programa sobre aquele endereço.
O exemplo abaixo usa o framework Anchor para
criar uma nova conta com um endereço derivado do programa. O programa inclui uma
única instrução initialize para criar a nova conta, que
armazenará o endereço do utilizador e a
bump seed usada para derivar a PDA.
use anchor_lang::prelude::*;declare_id!("75GJVCJNhaukaa2vCCqhreY31gaphv7XTScBChmr1ueR");#[program]pub mod pda_account {use super::*;pub fn initialize(ctx: Context<Initialize>) -> Result<()> {let account_data = &mut ctx.accounts.pda_account;// store the address of the `user`account_data.user = *ctx.accounts.user.key;// store the canonical bumpaccount_data.bump = ctx.bumps.pda_account;Ok(())}}#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub user: Signer<'info>,#[account(init,// define the seeds to derive the PDAseeds = [b"data", user.key().as_ref()],// use the canonical bumpbump,payer = user,space = 8 + DataAccount::INIT_SPACE // 8 bytes for Anchor account discriminator)]pub pda_account: Account<'info, DataAccount>,pub system_program: Program<'info, System>,}#[account]#[derive(InitSpace)]pub struct DataAccount {pub user: Pubkey,pub bump: u8,}
A restrição init instrui o Anchor a
invocar o System Program
para criar uma nova conta usando a PDA como endereço. As seeds
usadas para criar a PDA são:
- A string fixa: "data"
- O endereço da conta de utilizador fornecido na instrução
- O bump seed canónico
Neste exemplo, a restrição bump não tem um valor atribuído, portanto o Anchor
usará find_program_address para derivar o PDA e encontrar o bump.
#[account(init,seeds = [b"data", user.key().as_ref()],bump,payer = user,space = 8 + DataAccount::INIT_SPACE // 8 bytes for Anchor account discriminator)]pub pda_account: Account<'info, DataAccount>,
O ficheiro de teste abaixo contém uma transação que invoca a instrução
initialize para criar uma nova conta com um endereço
derivado de programa. O ficheiro contém código para
derivar o PDA.
O exemplo também mostra como obter a nova conta que será criada.
import * as anchor from "@coral-xyz/anchor";import { Program } from "@coral-xyz/anchor";import { PdaAccount } from "../target/types/pda_account";import { PublicKey } from "@solana/web3.js";describe("pda-account", () => {const provider = anchor.AnchorProvider.env();anchor.setProvider(provider);const program = anchor.workspace.PdaAccount as Program<PdaAccount>;const user = provider.wallet as anchor.Wallet;// Derive the PDA address using the seeds specified on the programconst [PDA] = PublicKey.findProgramAddressSync([Buffer.from("data"), user.publicKey.toBuffer()],program.programId);it("Is initialized!", async () => {const transactionSignature = await program.methods.initialize().accounts({user: user.publicKey}).rpc();console.log("Transaction Signature:", transactionSignature);});it("Fetch Account", async () => {const pdaAccount = await program.account.dataAccount.fetch(PDA);console.log(JSON.stringify(pdaAccount, null, 2));});});
Se invocar a instrução initialize novamente com o mesmo seed de endereço
user, a transação falhará. Isto acontece porque já existe uma conta no
endereço derivado.
Is this page helpful?