solana_fund/
user_info.rs

1//! User info account management.
2
3use {
4    solana_farm_sdk::{
5        error::FarmError,
6        fund::Fund,
7        refdb,
8        refdb::{RefDB, Reference, ReferenceType, StorageType},
9        string::{str_to_as64, ArrayString64},
10    },
11    solana_program::{
12        account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
13        pubkey::Pubkey,
14    },
15    std::cell::RefMut,
16};
17
18pub struct UserInfo<'a, 'b> {
19    pub key: &'a Pubkey,
20    pub data: RefMut<'a, &'b mut [u8]>,
21}
22
23impl<'a, 'b> UserInfo<'a, 'b> {
24    pub const LEN: usize = StorageType::get_storage_size_for_records(ReferenceType::U64, 2);
25    pub const VIRTUAL_TOKENS_BALANCE_INDEX: usize = 0;
26    pub const USER_BUMP_INDEX: usize = 1;
27
28    pub fn new(account: &'a AccountInfo<'b>) -> Self {
29        Self {
30            key: account.key,
31            data: account.data.borrow_mut(),
32        }
33    }
34
35    pub fn init(&mut self, refdb_name: &ArrayString64, user_bump: u8) -> ProgramResult {
36        if RefDB::is_initialized(&self.data) {
37            return Ok(());
38        }
39        RefDB::init(&mut self.data, refdb_name, ReferenceType::U64)?;
40
41        self.init_refdb_field(
42            UserInfo::VIRTUAL_TOKENS_BALANCE_INDEX,
43            "VirtualTokensBalance",
44            Reference::U64 { data: 0 },
45        )?;
46        self.init_refdb_field(
47            UserInfo::USER_BUMP_INDEX,
48            "UserBump",
49            Reference::U64 {
50                data: user_bump as u64,
51            },
52        )
53    }
54
55    pub fn set_virtual_tokens_balance(&mut self, virtual_tokens_balance: u64) -> ProgramResult {
56        RefDB::update_at(
57            &mut self.data,
58            UserInfo::VIRTUAL_TOKENS_BALANCE_INDEX,
59            &Reference::U64 {
60                data: virtual_tokens_balance,
61            },
62        )
63        .map(|_| ())
64    }
65
66    pub fn get_virtual_tokens_balance(&self) -> Result<u64, ProgramError> {
67        if let Some(rec) = RefDB::read_at(&self.data, UserInfo::VIRTUAL_TOKENS_BALANCE_INDEX)? {
68            if let Reference::U64 { data } = rec.reference {
69                return Ok(data);
70            }
71        }
72        Err(FarmError::InvalidRefdbRecord.into())
73    }
74
75    pub fn get_user_bump(&self) -> Result<u8, ProgramError> {
76        if let Some(user_bump_rec) = RefDB::read_at(&self.data, UserInfo::USER_BUMP_INDEX)? {
77            if let Reference::U64 { data } = user_bump_rec.reference {
78                return Ok(data as u8);
79            }
80        }
81        Err(ProgramError::UninitializedAccount)
82    }
83
84    pub fn validate_account(
85        fund: &Fund,
86        user_info_account: &'a AccountInfo<'b>,
87        user_account: &Pubkey,
88    ) -> bool {
89        if let Ok(refdb) = user_info_account.try_borrow_data() {
90            if let Ok(Some(user_bump_rec)) = RefDB::read_at(&refdb, UserInfo::USER_BUMP_INDEX) {
91                if let Reference::U64 { data } = user_bump_rec.reference {
92                    if let Ok(key) = Pubkey::create_program_address(
93                        &[
94                            b"user_info_account",
95                            user_account.as_ref(),
96                            fund.name.as_bytes(),
97                            &[data as u8],
98                        ],
99                        &fund.fund_program_id,
100                    ) {
101                        if user_info_account.key == &key {
102                            return true;
103                        }
104                    }
105                }
106            }
107        }
108        false
109    }
110
111    // private helpers
112    fn init_refdb_field(
113        &mut self,
114        index: usize,
115        field_name: &str,
116        reference: Reference,
117    ) -> ProgramResult {
118        RefDB::write(
119            &mut self.data,
120            &refdb::Record {
121                index: Some(index as u32),
122                counter: 0,
123                tag: 0,
124                name: str_to_as64(field_name)?,
125                reference,
126            },
127        )
128        .map(|_| ())
129    }
130}