0

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

  • 1
    Usually the simulation has a detail information about the failure, either the `try_borrow_mut_data` paniced or the account you created is not large enough, you have no rights to write to it, or any other failure. Check for a detailed error message. – AlexN Jun 24 '22 at 06:30
  • I will try to get more detail today and maybe increase the size of the account and get back to you with my findings. Thank you – Anders Palm Jun 24 '22 at 14:22
  • 1
    @AlexN you were right, the account was not provided the correct size allocation and minimum balance, thus it was rejecting the data I was trying to insert. I put the block to set the instance of the object first, then created the vector from it, took the length of that, and set that length as the min balance for the rent, and the length also as the size of the account. – Anders Palm Jun 25 '22 at 23:52

0 Answers0