How do I pass the system_program to process_instruction? - rust

I need to pass the system_program to the function. The function needs an AccountInfo type but I imported it as a module or a Pubkey and i do not know how to make it an AccountInfo type. I would really appreciate some help. Thank you.
I left out some of the other accounts that I passed to process_instruction
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
program::{invoke_signed, invoke},
system_instruction,
system_program::ID as SYSTEM_PROGRAM_ID,
system_program,
};
pub fn process_instruction(
program_id: &Pubkey, // Public key of the account the hello world program was loaded into
accounts: &[AccountInfo], // The account to say hello to
_instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
let accounts_iter = &mut accounts.iter();
let user = next_account_info(accounts_iter)?;
let forecast_ai = next_account_info(accounts_iter)?;
let question = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
let (forecast_key, fore_bump) = Pubkey::find_program_address(
&[user.key.as_ref(), question.key.as_ref()],
program_id,
);
invoke_signed(
&system_instruction::create_account(
user.key,
forecast_ai.key,
Rent::get()?.minimum_balance(42),
42,
program_id
),
&[user.clone(), forecast_ai.clone(), system_program.clone()],
&[&[user.key.as_ref(), question.key.as_ref(), &[fore_bump]]],
);enter code here
let system = AccountInfo::new(
&SYSTEM_PROGRAM_ID,
false,
true,
&mut lamports4,
&mut sys_data,
&owner,
false,
Epoch::default(),
);
/**irrevelant code here*/
let accounts = vec![user_ai, forecast_ai, question_ai, system];
process_instruction(&program_id, &accounts, &instruction_data).unwrap();

You pass in the system_program as one of your AccountMeta instances in the account list from client (in Rust):
AccountMeta::new(solana_sdk::system_program::id(), false)

Related

My Rust OR_INSERT Hashmap code to update a struct content works without dereferencing. Why?

From Rust documentation, this count variable wouldn't work without dereferencing (*)
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}", map);
However, I have the following code which tries to update a u8 variable (i.e team_val.goals_scored ) in a Struct if the key is found in a hashmap. It works without dereferencing. My understanding from the above Rust documentation was I need to dereference the team_val.goals_scored in order to update the content of the struct which is also a value for the hash map. Whelp!
My code:
#[derive(Debug)]
struct Team {
name: String,
goals_scored: u8,
goals_conceded: u8,
}
fn build_scores_table(results: String) -> HashMap<String, Team> {
// The name of the team is the key and its associated struct is the value.
let mut scores: HashMap<String, Team> = HashMap::new();
for r in results.lines() {
let v: Vec<&str> = r.split(',').collect();
let team_1_name = v[0].to_string();
let team_1_score: u8 = v[2].parse().unwrap();
let team_2_name = v[1].to_string();
let team_2_score: u8 = v[3].parse().unwrap();
// TODO: Populate the scores table with details extracted from the
// current line. Keep in mind that goals scored by team_1
// will be number of goals conceded from team_2, and similarly
// goals scored by team_2 will be the number of goals conceded by
// team_1.
let mut team_1_struct= Team {
name: team_1_name.clone(),
goals_scored: team_1_score,
goals_conceded: team_2_score
};
let mut team_2_struct= Team {
name: team_2_name.clone(),
goals_scored: team_2_score,
goals_conceded: team_1_score
};
if scores.contains_key(&team_1_name) {
let team_val = scores.entry(team_1_name.clone()).or_insert(team_1_struct);
println!("Yooo {:#?}",team_val);
team_val.goals_scored +=team_1_score;
team_val.goals_conceded += team_2_score;
} else {
scores.insert(team_1_name,team_1_struct);
}
if scores.contains_key(&team_2_name) {
let team_val = scores.entry(team_2_name.clone()).or_insert(team_2_struct);
println!("Yooo {:#?}",team_val);
team_val.goals_scored +=team_2_score;
team_val.goals_conceded += team_1_score;
} else {
scores.insert(team_2_name,team_2_struct);
}
}
scores
}
Rust does some automatic dereferencing, described here. We can see the difference between the documentation code and what you wrote:
// This
*count += 1
// versus this
team_val.goals_scored += team_1_score
^--- Causes an automatic dereferencing
If you're coming from C I think this documentation may be even clearer.
Answering the follow-up question 'can you use entry() without using clone() on the key - you cannot. entry() consumes what you send it, and doesn't give it back, so the borrow checker will prevent you from doing this. It's currently a 'limitation' on the API - but if you're dealing with something as cheap as a short string to copy, then this shouldn't impact you much.
You can do a fair amount to slim down your code, though (with the caveat that I only did this for one team - it's easily extensible):
use std::collections::HashMap;
struct Team {
name: String,
goals: u8
}
type Scorecard = HashMap<String, Team>;
fn scoring(records: String) -> Scorecard {
let mut scores : Scorecard = HashMap::new();
for r in records.lines() {
let record: Vec<&str> = r.split(',').collect();
let team_name = record[0].to_string();
let team_score: u8 = record[1].parse().unwrap();
// Note that we do not need to create teams here on every iteration.
// There is a chance they already exist, and we can create them only if
// the `or_insert()` clause is activated.
// Note that the `entry()` clause when used with `or_insert()`
// gives you an implicit 'does this key exist?' check.
let team = scores.entry(team_name.clone()).or_insert(Team {
name: team_name,
goals: 0,
});
team.goals += team_score;
}
scores
}
fn main() {
let record = "Thunderers,1\nFlashians,1\nThunderers,2";
let scores = scoring(record.to_string());
for (key, value) in &scores {
println!("{}: The mighty {} scored {} points.", key, value.name, value.goals)
}
// Flattening Some Options!
// let o = Some(Some(5));
// let p = Some(5);
// println!("o: {:#?}", o);
// println!("p: {:#?}", p);
// println!("flattened o: {:#?}", o.flatten());
// println!("flattened p: {:#?}", p.flatten());
}

How to get the current time in Solana program without using any external SystemProgram account

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())
}

Manually creating a PDA in the instruction function

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!

Problems in storing data in Solana Account

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.

How to transfer from 2 accounts to the same vault in Serum Anchor?

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

Resources