PDA аккаунты

Кратко

Создавайте PDA аккаунты через invoke_signed с использованием сидов PDA. Только владеющая программа может подписывать за PDA. Ограничение Anchor init автоматизирует создание PDA аккаунта.

Подпись PDA через invoke_signed

Когда программе нужно подписать от имени PDA во время CPI, используется invoke_signed с сидом PDA. В рантайме проверяется, что сиды действительно порождают ожидаемый PDA с использованием ID вызывающей программы, что гарантирует, что только владеющая программа может подписывать. Полный процесс проверки смотрите в разделе PDA-подпись.

Создание PDA аккаунта

Получение PDA и создание аккаунта по адресу PDA — это разные операции. После получения адреса необходимо явно создать аккаунт.

Чтобы создать аккаунт по адресу PDA, программа-инициатор вызывает инструкцию System Program create_account через invoke_signed, передавая сиды PDA, чтобы рантайм мог проверить полномочия программы на этот адрес.

В примере ниже используется Anchor framework для создания нового аккаунта с адресом, производным от программы. Программа содержит единственную инструкцию initialize для создания нового аккаунта, который будет хранить адрес пользователя и bump seed, использованные для получения PDA.

Program
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 bump
account_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 PDA
seeds = [b"data", user.key().as_ref()],
// use the canonical bump
bump,
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,
}

Ограничение init сообщает Anchor вызвать System Program для создания нового аккаунта с использованием PDA в качестве адреса. Сиды, используемые для создания PDA, следующие:

  • Фиксированная строка: "data"
  • Адрес аккаунта пользователя, переданный в инструкции
  • Канонический bump seed

В этом примере ограничение bump не задано явно, поэтому Anchor использует find_program_address для деривации PDA и поиска bump.

pda_account
#[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>,

Тестовый файл ниже содержит транзакцию, которая вызывает инструкцию initialize для создания нового аккаунта с адресом, производным от программы. В файле есть код для деривации PDA.

В примере также показано, как получить новый аккаунт, который будет создан.

Test
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 program
const [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));
});
});

Если вы снова вызовете инструкцию initialize с тем же адресом seed user, транзакция завершится неудачей. Это произойдет потому, что аккаунт уже существует по этому производному адресу.

Is this page helpful?

Содержание

Редактировать страницу

Управляется

© 2026 Solana Foundation.
Все права защищены.
Связаться с нами