Search code examples
rustsolanaanchor-solana

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,    
}

Solution

  • 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.