How to reference a specific account in Solana programs with multiple accounts? - rust

This program allows users to make an account to receive money. So 'Alice' can create the account Fundraiser. In Fundraiser, there is a specific variable amount_raised which keeps track of how much SOL has been sent to her account. This program allows for multiple people to create new Fundraisers..SO, how do I reference the correct account in the function 'donate'? I am suspecting that I need to use PDAs or at least iterate through all of the programs accounts and match it to the creator's pubkey. Thank you in advance. (Sol is sent in client, I just want to keep track of the amount_raised by adding the amount).
use super::*;
pub fn donate(ctx: Context<Donate>, amount: u32) -> ProgramResult {
let fundraiser: &mut Account<Fundraiser> = &mut ctx.accounts.fundraiser;
let user: &Signer = &ctx.accounts.user;
fundraiser.amount_raised += amount;
Ok(())
}
pub fn start_fund(ctx: Context<StartFund>, amount: u32, reason: String) -> ProgramResult {
let fundraiser: &mut Account<Fundraiser> = &mut ctx.accounts.fundraiser;
let author: &Signer = &ctx.accounts.author;
let clock: Clock = Clock::get().unwrap();
if reason.chars().count() > 350 {
return Err(ErrorCode::ContentTooLong.into())
}
fundraiser.author = *author.key;
fundraiser.amount_to_raise = amount;
fundraiser.timestamp = clock.unix_timestamp;
fundraiser.reason = reason;
Ok(())
}
pub struct StartFund<'info> {
#[account(init, payer = author, space = 64 + 64)]
pub fundraiser: Account<'info, Fundraiser>,
#[account(mut)]
pub author: Signer<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct Donate<'info> {
#[account(mut)]
pub fundraiser: Account<'info, Fundraiser>,
#[account(mut)]
pub user: Signer<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
}
#[account]
pub struct Fundraiser {
pub author: Pubkey,
pub amount_to_raise: u32,
pub amount_raised: u32,
pub timestamp: i64,
pub reason: String, //reason
}
}
`

You've pretty much got it right -- in your Donate instruction, you'll also need to pass in Alice's author account, so that you can move transfer funds from user to author during Donate. So instead, it'll look something like:
#[derive(Accounts)]
pub struct Donate<'info> {
#[account(mut)]
pub fundraiser: Account<'info, Fundraiser>,
#[account(mut)]
pub user: Signer<'info>,
#[account(mut)]
pub author: AccountInfo<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
}
And you'll have to check that the author provided matches fundraiser.author.

Related

How to store ACCOUNT instance for sol transfers

I want to implement a voting based payment susyem (multisig?) in my smart contract
basically frontend adds a transaction containing the account and the amount
users provide votes for every transaction
once enough votes received for a transaction the SOL is transferred from project wallet to the user's account.
Problem - how to store the list of accounts in the struct.
I can store the public key but my understanding is that I cannot transfer SOL to a user just by his public key but need his account instance
Code below.
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod basic_1 {
use super::*;
// add a transaction which will be stored in the smart contract
// once a number of APPROVALS are received the amount will be transferred to to_account
// ==== HOW DO I STORE USER'S ACCOUNT/ACCOUNT_INFO so that SOL can be TRANSFERRED TO IT LATER ====
// ==== LOOK AT TRANSACTION STRUCT IN THE END ====
pub fn add_transaction(ctx: Context<Update>, to_account: Account, project_id: u64, amount:u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
let trans1 = Transaction {
id: project_id,
to_account: to_account,
amount: amount,
is_complete: false,
};
Ok(())
}
// add a signatory who can approve transfers
pub fn add_signatory(ctx: Context<Update>, signatory: Signatory, project_id: u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
my_account.signatories.push(signatory);
Ok(())
}
// set how many APPROVALS are required for the SOL to be transferred to the user
pub fn set_approve_threshold(ctx: Context<Update>, project_id: u64, threshold:u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
my_account.approve_threshold = threshold;
Ok(())
}
// TRANSFER THE SOL if approval THRESHOLD REACHED!!!
pub fn approve_transaction(ctx: Context<Update>, signatorypublickey: Pubkey, project_id: u64, transaction_id:u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
// TODO
// =========== HOW TO TRANSFER WITHOUT ACCOUNT INSTANCE =================
let amount_of_lamports = 42; // could be an argument ;-)
let from = my_account.to_account_info(); // ctx.accounts.from.to_account_info();
let to = ctx.accounts.to.to_account_info();
// Debit from_account and credit to_account
**from.try_borrow_mut_lamports()? -= amount_of_lamports;
**to.try_borrow_mut_lamports()? += amount_of_lamports;
Ok(())
}
}
#[account]
pub struct MyAccount {
pub data: u64,
pub approve_threshold: u64,
pub project_id: u64,
pub project_name: String,
pub signatories: Vec<Signatory>,
pub transactions: Vec<Transaction>,
}
#[derive(Default, AnchorSerialize, AnchorDeserialize, Clone)]
pub struct Signatory {
pub name: String,
pub public_key: Pubkey,
}
#[derive(Default, AnchorSerialize, AnchorDeserialize, Clone)]
pub struct Transaction {
pub id: u64,
// ==== HOW TO STORE USER ACCOUNT / ACCOUNT INFO OBJECT BELOW FOR SOL TRANSFER ====
pub to_account: Account,
pub amount: u64,
pub approvals: Vec<Signatory>,
pub is_complete: bool,
}
You can not serialize AccountInfo to account state. When you need to access AccountInfo you must pass it in with the instruction and it is only good for the duration of your program instruction execution.

How to add a Pubkey to an account containing a vector of Pubkeys in an anchor program

use anchor_lang::prelude::*;
use rand::Rng;
use solana_program::{declare_id, pubkey::Pubkey};
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod raffle_impl {
use super::*;
pub fn create_raffle(ctx: Context<CreateRaffle>, authority: Pubkey) -> ProgramResult{
let payer = &mut ctx.accounts.wallet;
let escrow_account = &mut ctx.accounts.escrow_account;
Ok(())
}
This wallet_address_to_add is a Pubkey that would be passed in the transaction from the frontend. It should be pushed to a vector defined in the account macro defined below.
pub fn add_participants(ctx: Context<AddParticipants>, wallet_address_to_add: Pubkey) ->
ProgramResult{
let payer = &mut ctx.accounts.wallet;
let mut data = &mut ctx.accounts.escrow_account.data;
data = data.push(wallet_address_to_add); // Error occurs here
Ok(())
}
}
#[derive(Accounts)]
pub struct AddParticipants<'info>{
#[account(mut,signer)]
pub wallet: AccountInfo<'info>,
#[account(mut)]
pub owner: AccountInfo<'info>,
#[account(init_if_needed, payer = wallet, space=8+16)]
pub escrow_account: Account<'info, EscrowAccount>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
#[account]
#[derive(Default)]
pub struct EscrowAccount{
pub data: Vec<Pubkey>,
pub length: usize,
pub payer: Pubkey,
pub authority: Pubkey,
}
The error I keep getting is "expected mutable reference &mut Vec<anchor_lang::prelude::Pubkey> found unit type ()."
Instead of data = data.push(wallet_address_to_add);, you should just do data.push(wallet_address_to_add); without the data = part.

Anchor test Transaction simulation failed: An account required by the instruction is missing

I am trying to create a simple program that lets users donates. This program is bootstrapped by Anchor. Unfortunately, It failed on very first step creating a PDA account by CPI. Please see the detailed info below:
Anchor test fails with:
PublicKey {
_bn: <BN: 265af4367675fa484c7148bf59dd7392a0a290cee149a6912c35a01495da14df>
} PublicKey {
_bn: <BN: ee4f6de6f56f9606ca2af702908a5655ede672356998a6139802e3cdde9325f3>
}
Transaction simulation failed: Error processing Instruction 0: An account required by the instruction is missing
Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS invoke [1]
Program log: Instruction: SendDonation
Instruction references an unknown account 11111111111111111111111111111111
Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS consumed 5599 of 1400000 compute units
Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS failed: An account required by the instruction is missing
1) Should send donation
Here the lib.rs
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod crypto_donut {
use super::*;
use anchor_lang::solana_program::entrypoint::ProgramResult;
use anchor_lang::solana_program::program::invoke;
use anchor_lang::solana_program::system_instruction::transfer;
pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
let base_account = &mut ctx.accounts.base_account;
base_account.owner = ctx.accounts.user.to_account_info().key();
Ok(())
}
pub fn send_donation(ctx: Context<Donation>, amount: u64) -> ProgramResult {
let base_account = &mut ctx.accounts.base_account;
base_account.donators.push(ctx.accounts.user.key());
let instruction = transfer(ctx.accounts.user.key, &base_account.key(), amount);
invoke(
&instruction,
&[
ctx.accounts.user.to_account_info(),
base_account.to_account_info(),
],
)
.unwrap();
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 64 + 1024)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct BaseAccount {
pub donators: Vec<Pubkey>,
pub owner: Pubkey,
}
#[derive(Accounts)]
pub struct Donation<'info> {
#[account(mut)]
pub base_account: Account<'info, BaseAccount>,
pub user: Signer<'info>,
}
Here the test which failed:
it("Should send donation", async () => {
const donator = anchor.web3.Keypair.generate();
console.log(donator.publicKey, baseAccount.publicKey);
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(donator.publicKey, 10000000000),
"confirmed"
);
const tx = await program.rpc.sendDonation(new anchor.BN(100), {
accounts: {
baseAccount: baseAccount.publicKey,
user: donator.publicKey,
},
signers: [donator],
});
const balance = await program.account.baseAccount.getAccountInfo(donator.publicKey);
console.log(balance);
expect(balance.lamports.toString()).equal("100");
});
});
What am I doing wrong?
Lol, i forget #[account(mut)] before pub user: Signer<'info> :))
#[derive(Accounts)]
pub struct Donation<'info> {
#[account(mut)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
and system_program too. Thanks for Stepan comment

how to handle the moved exception in a loop in rust

I want to map entity from one Vec(this entity list search from database) into another Vec(this response entity list return to frontend) in rust, this is my code:
fn main() {
let musics:Vec<Music> = Vec::new();
for x in 0..10 {
let filtered_music:Vec<Music> = musics.into_iter()
.filter(|item| item.source_id == "1")
.collect();
let resp = MusicRes{
music: take(filtered_music, 0).unwrap()
};
}
}
fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
if index < vec.len() {
Some(vec.swap_remove(index))
} else {
None
}
}
pub struct Music {
pub id: i64,
pub name: String,
pub artists: String,
pub album_id: i64,
pub publishtime: i64,
pub status: i32,
pub duration: i32,
pub source_id: String,
pub source: i32,
pub created_time: i64,
pub updated_time:i64,
pub album: String,
pub fetched_download_url: i32
}
pub struct MusicRes {
pub music:Music
}
this code give me tips that musics was moved. I could change the musics to &musics, but the returned response need to Music. How to handle this situation properly?
I'm not exactly sure what your requirements are, ie. what's the point of the 0..10 then taking the 0th element, but you could do something like this:
let musics: Vec<_> = musics
.into_iter()
.take(10)
.filter(|m| m.source_id == "1")
.map(|m| MusicRes { music: m })
.collect();

Solana Anchor: How to make #[account(seeds)] for/ read associated accounts?

in the Basic-5 tutorial of the project-serum/anchor repo
How can I replace #[associated]
with something like this:
#[account(seeds = [user_data.deposit_last.as_ref(), &[user_data.__nonce]])]
There is something not correct above, then Anchor fails to read the associated account's values
const userData = await program.account.userData.associated(wallet1, usdcMint);
So what is the correct way to replace this soon-to-be-deprecated #[associated] above the associated account struct?
#[associated]
#[derive(Default)]
pub struct UserData {
pub authority: Pubkey,
pub deposit_last: i64,
pub shares: u64,
pub reward_debt: u64,
}
//UserData is initialized here first
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, associated = authority, with = usdc_mint)]
pub user_data: ProgramAccount<'info, UserData>,
...
}
So the seed approach is a PDA, which is actually what #associated was using underneath the hood
You will need a function that initializes the seed with the below init and payer trait. payer should also be the same user who is actually paying the for transaction.
Please note that #[instruction(bump: u8] is matching the signature of the function here, hence you will need to pass in the bump in the signature as the first argument.
#[instruction(bump: u8)]
pub struct Ctx<'info> {
#[account(init, seeds = [user_data.deposit_last.as_ref(), &[bump]], payer = payer)]
pub user_data = ProgramAccount<'info, UserData>,
}
Later on for other functions if you want just want to read the account, you can just use
#[account(seeds = [user_data.deposit_last.as_ref(), &[user_data.__nonce]])]
pub user_data = ProgramAccount<'info, UserData>,
Change your account data to use #[account] instead of #[associated]
#[account]
#[derive(Default)]
pub struct UserData {
pub authority: Pubkey,
pub deposit_last: i64,
pub shares: u64,
pub reward_debt: u64,
}
Here is an example https://github.com/project-serum/anchor/blob/master/examples/misc/programs/misc/src/context.rs#L10

Resources