I have figured out how to create a PDA in the rust contract, but each time I try to serialize the data into the new PDA account, it fails with the following message:
WalletSendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Program failed to complete
As you can see its not very helpful, the following are my different files, I believe I have added everything you need to be able to just copy and paste to try yourselves if desired.
Main.js - function connected to a simple onClick
const createAdminPda = async () => {
const ownerKey = new PublicKey(publicKey.toString());
let [pda_key_pair, bump] = await PublicKey.findProgramAddress([Buffer.from("AdminPda3", "utf8")], animal_program_id)
const instructionTOOurProgram = new TransactionInstruction({
keys: [
{
pubkey: SystemProgram.programId,
isWritable: true,
isSigner: false,
},
{
pubkey: ownerKey,
isWritable: true,
isSigner: true,
},
{
pubkey: pda_key_pair,
isWritable: true,
isSigner: false,
}
],
programId: animal_program_id,
data: new Uint8Array([0]),
});
console.log("instructionTOOurProgram: ", instructionTOOurProgram)
const trans = await setPayerAndBlockhashTransaction(
[instructionTOOurProgram], ownerKey
);
console.log("trans: ", trans)
// Submit transaction
const transactionId = await sendTransaction(
trans,
connection
);
console.log("end transactionId", transactionId);
const result = await connection.confirmTransaction(transactionId);
console.log("end confirmTransaction", result);
}
lib.rs
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
instruction::{AccountMeta, Instruction},
msg,
program::invoke,
program::invoke_signed,
program_error::ProgramError,
program_pack::{IsInitialized, Pack, Sealed},
pubkey::Pubkey,
rent::Rent,
system_instruction, system_program,
sysvar::Sysvar,
};
const ADMIN_ACCOUNT_SEED: &[u8; 9] = b"AdminPda3";
entrypoint!(process_instruction);
#[derive(BorshDeserialize, BorshSerialize)]
pub enum MainInstruction {
CreateAdminPda2,
}
pub fn create_admin_pda(
program_id: Pubkey,
payer_account: Pubkey,
pda_account: Pubkey,
) -> Instruction {
Instruction {
program_id,
accounts: vec![
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new(payer_account, true),
AccountMeta::new(pda_account, true),
],
data: MainInstruction::CreateAdminPda2 {}
.try_to_vec()
.expect("IO error"),
}
}
#[derive(Debug, BorshDeserialize, BorshSerialize)]
pub struct AdminPda2 {
pub st_type: String,
pub st_typeb: String,
}
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let processor = Processor::new(program_id, accounts, data);
processor.process()?;
Ok(())
}
struct Processor<'a, 'b> {
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'b>],
data: &'a [u8],
}
impl<'a, 'b> Processor<'a, 'b> {
fn new(program_id: &'a Pubkey, accounts: &'a [AccountInfo<'b>], data: &'a [u8]) -> Self {
Self {
program_id,
accounts,
data,
}
}
fn process(&self) -> ProgramResult {
msg!("ProgramResult process function");
let transaction = MainInstruction::try_from_slice(self.data)?;
msg!("ProgramResult data input {:?}", self.data);
match transaction {
MainInstruction::CreateAdminPda2 {} => {
self.create_admin_pda()?
}
}
Ok(())
}
fn create_admin_pda(&self) -> ProgramResult {
let account_info_iter = &mut self.accounts.iter();
// get Sys Program account
let system_program_account = next_account_info(account_info_iter)?;
msg!("Given system_program_account Key {:?}: ", *system_program_account.key);
// get payer account
let payer = next_account_info(account_info_iter)?;
msg!("Given payer Key {:?}: ", *payer.key);
// get admin pda
let admin_account_pda = next_account_info(account_info_iter)?;
msg!("Given admin_account_pda Key {:?}: ", *admin_account_pda.key);
let (admin_account_key, bump) = Pubkey::find_program_address(&[ADMIN_ACCOUNT_SEED], self.program_id);
msg!("Corrent PDA Key {:?}: ", admin_account_key);
msg!("Given PDA Key {:?}: ", *admin_account_pda.key);
// Do a check that the PDA does not already exist
//// Check that the function was fed the correct Public Key
if admin_account_key == *admin_account_pda.key {
msg!("The keys match");
// Check that the PDA is empty before creating it
if admin_account_pda.data_is_empty() {
msg!("The account is not created yet, create it");
// Create new account
// payer
// pda key
// system program
invoke_signed(
&system_instruction::create_account(
payer.key,
admin_account_pda.key,
Rent::get()?.minimum_balance(AdminPda2::SIZE),
AdminPda2::SIZE as u64,
self.program_id,
),
&[
payer.clone(),
admin_account_pda.clone(),
system_program_account.clone(),
],
&[&[ADMIN_ACCOUNT_SEED, &[bump]]],
)?;
} else {
msg!("The account is already created");
}
// Create a Animal with the given owner and time
let admin_data = AdminPda2 {
st_type: "AdminPda2".to_owned(),
st_typeb: "AdminPda2".to_owned()
};
msg!("admin_data {:?}", admin_data);
// Serialize the Animal to it's raw bytes
let mut admin_vec = admin_data.try_to_vec()?;
msg!("admin_vec {:?}", admin_vec);
// Write the serialized data to the new pda [commented out the function finishes ok, uncomment the next line it fails]
admin_vec.swap_with_slice(*admin_account_pda.try_borrow_mut_data()?);
msg!("Finished");
} else {
msg!("The keys did not match, the account will not be created");
}
Ok(())
}
}
The following is one of the executions where I successfully created the account but had the final line with 'admin_vec.swap_with_slice' commented out, so only the account was created and the data serialized but not yet written to the account. https://explorer.solana.com/tx/4uhamGkpSo2Sf8kTn5Xz5u5bLXWL4TtpJf2DKAJ77HoKAsUrsVN9AwDuK3R9eJkEbN4YvcaQXMLHMvFqFVscWzEZ?cluster=devnet