I need help in storing data into an account.
Here's what I've done so far.
Below is my 'process_instruction' entry point,
pub fn process_instruction(...) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
// get account where we have to store state
let states_account_info = next_account_info(account_info_iter)?;
let xyz: u64 = 1234567;
let counter_struct = Counter {
data: xyz
};
counter_struct.serialize(&mut &mut states_account_info.data.borrow_mut()[..])?;
Ok(())
}
Here's my Counter Struct
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct Counter {
/// mappings of the keys
pub data: u64,
}
And this is how, I create the account inside the test,
let state_account_pubkey = Pubkey::new_unique();
let mut program_test = ProgramTest::new(
"solana_states_save_program",
program_id,
processor!(process_instruction),
);
program_test.add_account(
state_account_pubkey,
Account {
lamports: 5,
owner: program_id,
..Account::default()
},
);
But after executing the test, I get the following error, inside process_instruction() method (on counter_struct.serialize(...) statement),
thread 'main' panicked at 'called Result::unwrap() on an Err value:
TransactionError(InstructionError(0, BorshIoError("Unknown")))'
Kindly help.
We also needed to add account data space when creating an Account in tests, which I was missing.
program_test.add_account(
state_account_pubkey,
Account {
lamports: 5,
data: vec![0_u8; mem::size_of::<u32>()],
owner: program_id,
..Account::default()
},
);
Hope, it helps someone.
Related
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
I am writing this Solana program without using Anchor. I want the program to know the current timestamp without using any external SystemProgram.
This is the current version of my program:
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
sysvar,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
) -> ProgramResult {
msg!("Hello Solana! (From Rust)");
let nowtime = sysvar::clock::Clock.unix_timestamp;
Ok(())
}
However, I'm getting this error:
expected value, found struct `sysvar::clock::Clock
How am I supposed to use this sysvar variable?
You should be able to get the time within a program with the following:
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Clock,
entrypoint,
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
sysvar::Sysvar,
};
entrypoint!(process_instruction);
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct HelloState {
is_initialized: bool,
}
// Accounts required
/// 1. [signer, writable] Payer
/// 2. [writable] Hello state account
pub fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
// Payer account
let _payer_account = next_account_info(accounts_iter)?;
// Hello state account
let hello_state_account = next_account_info(accounts_iter)?;
// Getting clock directly
let clock = Clock::get()?;
let mut hello_state = HelloState::try_from_slice(&hello_state_account.data.borrow())?;
hello_state.is_initialized = true;
hello_state.serialize(&mut &mut hello_state_account.data.borrow_mut()[..])?;
msg!("Account initialized :)");
// Getting timestamp
let current_timestamp = clock.unix_timestamp;
msg!("Current Timestamp: {}", current_timestamp);
Ok(())
}
More specifically
let clock = Clock::get()?;
let current_timestamp = clock.unix_timestamp;
Note: The time on-chain is an estimation. The only reliable clock is slot number on chain. You can read more implementations for getting time here.
You can get the solana time by following function.
use anchor_lang::{prelude::*, solana_program::clock};
use std::convert::TryInto;
pub fn now_ts() -> Result<u64> {
Ok(clock::Clock::get()?.unix_timestamp.try_into().unwrap())
}
I am trying to dynamically allocate a PDA with system_program's create_account instruction, but I feel like I am missing some key piece of information. I keep getting an error saying:
Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account
I managed to deduce that the create_account call itself is what fails; I am not sure if I passed in the wrong seeds, or if my signers on the frontend/context are configured wrong (previously I was going with a Signer<'info> directly, but then following the Anchor Book article on PDAs, I turned that into an unchecked account, for better or worse.
pub fn init_collection(
_ctx: Context<InitCollection>,
) -> Result<()> {
msg!("checking if account is initialized");
if !_ctx.accounts.col.data_is_empty() {
msg!("account is already initialized");
return Ok(());
}
let acc = _ctx.accounts.borrow_mut();
init_pda(
&_ctx.accounts.col.to_account_info(),
&_ctx.accounts.rent,
14,
&crate::id(),
&_ctx.accounts.payer.to_account_info(),
&_ctx.accounts.system_program,
&[
b"col".as_ref(),
&[255],
])?;
Ok(())
}
#[derive(Accounts)]
pub struct InitCollection<'info> {
/// CHECK: Is checked in the program.
#[account(mut, seeds = [b"col".as_ref()], bump)]
col: UncheckedAccount<'info>,
/// CHECK: Is checked in the CPI.
payer: UncheckedAccount<'info>,
system_program: Program<'info, System>,
rent: Sysvar<'info, Rent>,
}
For creating the account itself, I basically lifted the functionality from the anchor project:
pub fn init_pda<'a>(
new_account: &AccountInfo<'a>,
rent_key: &Sysvar<'a, Rent>,
space: usize,
owner: &Pubkey,
payer: &AccountInfo<'a>,
system_program: &AccountInfo<'a>,
seeds_with_nonce: &[&[u8]],
) -> Result<()>
{
let rent = &Rent::from_account_info(&rent_key.to_account_info())?;
// If the account being initialized already has lamports, then
// return them all back to the payer so that the account has
// zero lamports when the system program's create instruction
// is eventually called.
let __current_lamports = new_account.lamports();
if __current_lamports == 0 {
// Create the token account with right amount of lamports and space, and the correct owner.
let lamports = rent.minimum_balance(space);
let cpi_accounts = anchor_lang::system_program::CreateAccount {
from: payer.to_account_info(),
to: new_account.to_account_info()
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_owned(), cpi_accounts);
anchor_lang::system_program::create_account(cpi_context.with_signer(&[seeds_with_nonce]), lamports, space as u64, owner)?; // <== This seems to be where it's failing
} else {
// Fund the account for rent exemption.
let required_lamports = rent
.minimum_balance(space)
.max(1)
.saturating_sub(__current_lamports);
if required_lamports > 0 {
let cpi_accounts = anchor_lang::system_program::Transfer {
from: payer.to_owned(),
to: new_account.to_owned(),
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_owned(), cpi_accounts);
anchor_lang::system_program::transfer(cpi_context, required_lamports)?;
}
// Allocate space.
let cpi_accounts = anchor_lang::system_program::Allocate {
account_to_allocate: new_account.to_owned(),
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
anchor_lang::system_program::allocate(cpi_context.with_signer(&[seeds_with_nonce]), space as u64)?;
// Assign to the spl token program.
let cpi_accounts = anchor_lang::system_program::Assign {
account_to_assign: new_account.to_owned()
};
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
anchor_lang::system_program::assign(cpi_context.with_signer(&[seeds_with_nonce]), owner)?;
}
Ok(())
}
Can anyone point me to the right direction? I can't seem to be able to figure out where exactly what I am missing.
Thank you!
I am trying to run the following code in Anchor Solana, with program in rust as follows:
use anchor_lang::prelude::*;
declare_id!("RnbXAWg5mCvmSafjd1CnYaz32qLgZHdeHK6xzHDi1yU");
#[program]
pub mod sol_proj_1 {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> ProgramResult {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
println!("hello there!");
Ok(())
}
pub fn update(ctx: Context<Update>, data: u64) -> ProgramResult {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
}
// #[derive(Accounts)]
// pub struct Initialize {}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut)]
pub my_account: Account<'info, MyAccount>,
}
#[account]
pub struct MyAccount {
pub data: u64,
}
The test program is as follow:
import * as anchor from '#project-serum/anchor';
import { Program } from '#project-serum/anchor';
import { SolProj1 } from '../target/types/sol_proj_1';
const assert = require("assert");
describe('sol_proj_1', () => {
// Configure the client to use the local cluster.
const provider = anchor.Provider.local();
anchor.setProvider(provider);
// The Account to create.
const myAccount = anchor.web3.Keypair.generate();
const program = anchor.workspace.SolProj1 as Program<SolProj1>;
it('Is initialized!', async () => {
// Add your test here.
const tx = await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: program.programId,
},
signers: [myAccount],
});
/
console.log("Your transaction signature", tx);
});
});
With error when I run the following command
Anchor test
1) sol_proj_1
Is initialized!:
Error: failed to send transaction: invalid transaction: Transaction failed to sanitize accounts offsets correctly
at Connection.sendEncodedTransaction (node_modules/#solana/web3.js/src/connection.ts:3740:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Connection.sendRawTransaction (node_modules/#solana/web3.js/src/connection.ts:3700:20)
at sendAndConfirmRawTransaction (node_modules/#solana/web3.js/src/util/send-and-confirm-raw-transaction.ts:27:21)
at Provider.send (node_modules/#project-serum/anchor/src/provider.ts:118:18)
at Object.rpc [as initialize] (node_modules/#project-serum/anchor/src/program/namespace/rpc.ts:25:23)
I tried the following
change the program ownership as I found that this can cause the issue but that did not work.
I also added entries in Anchor.toml, the tests are running on localhost
I found that empty wallet may also cause this issue but I have airdrop 100 sols in it
The Rust code deploys correctly but the test is failing the 'sanitizationfailure' is listed as follow "Transaction failed to sanitize accounts offsets correctly implies that account locks are not taken for this TX, and should not be unlocked." I couldn't find any info how to take out the locks
source: https://docs.rs/solana-sdk/1.9.2/solana_sdk/transaction/enum.TransactionError.html
Any help is appreciated!
The only thing I can see obvious is maybe the way you're passing in the system program from the frontend. You're passing in your program's id, when you should be passing the in system program id. So instead, try:
const tx = await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [myAccount],
});
I had this error before and have solved.
In my case, I find that the Program_Id in Anchor.toml is different from lib.rs, they should be the same.
Program_Id is generated by running "anchor deploy" in terminal.
Anchor.toml
[programs.localnet]
solana_app = "<Program_ID>"
lib.rs
declare_id!("<Program_ID>");
Running "anchor deploy" in terminal.
And copy the program id in Anchor.toml and lib.rs
I am writing a simple contract of transferring tokens from 2 accounts in a common check vault.
I took the cashiers-check as base example and using this.
// Create check
#[access_control(CreateCheck::accounts(&ctx, nonce))]
pub fn create_check(ctx: Context<CreateCheck>, amount: u64, nonce: u8) -> Result<()> {
// Transfer funds to the check.
let cpi_accounts = Transfer {
from: ctx.accounts.from.to_account_info().clone(),
to: ctx.accounts.vault.to_account_info().clone(),
authority: ctx.accounts.owner.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;
// Print the check.
let check = &mut ctx.accounts.check;
check.amount = amount;
check.from = *ctx.accounts.from.to_account_info().key;
check.vault = *ctx.accounts.vault.to_account_info().key;
check.nonce = nonce;
Ok(())
}
this is to collect from second account
pub fn second_send(ctx: Context<SecondSend>) -> Result<()> {
let seeds = &[
ctx.accounts.check.to_account_info().key.as_ref(),
&[ctx.accounts.check.nonce],
];
let signer = &[&seeds[..]];
let cpi_accounts = Transfer {
from: ctx.accounts.from.to_account_info().clone(),
to: ctx.accounts.vault.to_account_info().clone(),
authority: ctx.accounts.check_signer.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
token::transfer(cpi_ctx, ctx.accounts.check.amount)?;
// Burn the check for one time use.
ctx.accounts.check.burned = true;
Ok(())
}
struct:
#[derive(Accounts)]
pub struct SecondSend<'info> {
#[account(mut, has_one = vault)]
check: ProgramAccount<'info, Check>,
#[account(mut)]
vault: AccountInfo<'info>,
check_signer: AccountInfo<'info>,
#[account(mut, has_one = owner)]
from: CpiAccount<'info, TokenAccount>,
#[account(signer)]
owner: AccountInfo<'info>,
token_program: AccountInfo<'info>,
}
this is the test i wrote:
it("Sends token from satan", async () => {
await program.rpc.secondSend({
accounts: {
check: check.publicKey,
vault: vault.publicKey,
checkSigner: checkSigner,
from: satan,
owner: program.provider.wallet.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
},
});
});
whatever i do im getting this error :
Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x3
im doing something wrong?
I think you missed out on looking at the Program Logs above the error you mentioned here. Try scrolling up, there should be certain logs.
That should provide more context, but if you're still stuck, you can update your question here :D