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.
Related
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.
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
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.
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
This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 6 months ago.
I'm trying to create a simple collection:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=361258962c9a25b953aab2a9e4999cc9
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex<'a> {
list: Vec<User>,
index: HashMap<u32, &'a User>,
}
impl UsersIndex<'_> {
pub fn new() -> Self {
UsersIndex {
list: Vec::new(),
index: HashMap::new(),
}
}
pub fn add(&mut self, user: User) {
self.list.push(user);
self.index.insert(user.id, &user);
}
pub fn get(&self, id: u32) -> Option<&&User> {
self.index.get(&id)
}
}
but can not fix the errors:
use of moved value: user
user does not live long enough
As I understand I have to take ownership of User, but google doesn't help me how to do it.
Rust says that I need to implement the Copy trait, but User contains a field of type String.
The issue with this code is the following:
pub fn add(&mut self, user: User) {
self.list.push(user); // Here, you move user to the list. The list owns user
self.index.insert(user.id, &user); // Then, you create a reference of a moved value
}
So, in your UserIndex struct, you want to store values and references of these values. These are called self-referential structs. With the ownership rules of Rust, you need to use unsafe Rust code to achieve this. If I were you, I'd think of a different way of implementing your collection following the Rust conventions. For example, you could do something like this:
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex {
index: HashMap<u32, User>, // I only use the HashMap. The HashMap owns the User structs
}
impl UsersIndex {
pub fn new() -> Self {
UsersIndex {
index: HashMap::new(),
}
}
pub fn add(&mut self, user: User) {
self.index.insert(user.id, user); // user is moved to the HashMap
}
pub fn get(&self, id: u32) -> Option<&User> { // Instead of a nested reference, we get a regular reference
self.index.get(&id)
}
}
fn main() {
let user = User {
id: 42,
name: "test".to_string(),
};
let mut index = UsersIndex::new();
index.add(user);
match index.get(42) {
Some(usr) => println!("{}", usr.name),
_ => println!("Not Found"),
}
}
Here you can find a playground with this implementation.
EDIT
If you need different HashMaps for the same User structs depending on the key used, you can use Rc smart pointers. They allow you to create more than one pointer that owns the same struct. It would look more or less like this:
use std::rc::Rc;
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex {
index: HashMap<u32, Rc<User>>, // Keys will be the user indeces
name: HashMap<String, Rc<User>>, // Keys will be the user names
}
impl UsersIndex {
pub fn new() -> Self {
UsersIndex {
index: HashMap::new(),
name: HashMap::new()
}
}
pub fn add(&mut self, user: User) {
// user will be moved, so we copy the keys before that:
let user_id = user.id;
let user_name = user.name.clone();
let user_rc = Rc::new(user); // We create the Rc pointer; user is moved to user_rc
self.index.insert(user_id, user_rc.clone()); // Rc pointers can be cloned
self.name.insert(user_name, user_rc); // user_rc is moved to the self.name HashMap
}
pub fn get(&self, id: u32) -> Option<Rc<User>> {
match self.index.get(&id) {
Some(user) => Some(user.clone()),
None => None
}
}
}
fn main() {
let user = User {
id: 42,
name: "test".to_string(),
};
let mut index = UsersIndex::new();
index.add(user);
match index.get(42) {
Some(usr) => println!("{}", usr.name),
_ => println!("Not Found"),
}
}
Here you can find a playground with this new implementation.
Again, if you also need the User structs to be mutable, then you'll probably need to use an Rc<RefCell<User>> (link here).
Hope you find this useful!