gpl_governance_tools/
account.rs1use borsh::{BorshDeserialize, BorshSerialize};
4use gemachain_program::{
5 account_info::AccountInfo, borsh::try_from_slice_unchecked, msg, program::invoke,
6 program::invoke_signed, program_error::ProgramError, program_pack::IsInitialized,
7 pubkey::Pubkey, rent::Rent, system_instruction::create_account, system_program, sysvar::Sysvar,
8};
9
10use crate::error::GovernanceToolsError;
11
12pub trait AccountMaxSize {
14 fn get_max_size(&self) -> Option<usize> {
16 None
17 }
18}
19
20pub fn create_and_serialize_account<'a, T: BorshSerialize + AccountMaxSize>(
22 payer_info: &AccountInfo<'a>,
23 account_info: &AccountInfo<'a>,
24 account_data: &T,
25 program_id: &Pubkey,
26 system_info: &AccountInfo<'a>,
27) -> Result<(), ProgramError> {
28 if !(account_info.data_is_empty() && *account_info.owner == system_program::id()) {
30 return Err(GovernanceToolsError::AccountAlreadyInitialized.into());
31 }
32
33 let (serialized_data, account_size) = if let Some(max_size) = account_data.get_max_size() {
34 (None, max_size)
35 } else {
36 let serialized_data = account_data.try_to_vec()?;
37 let account_size = serialized_data.len();
38 (Some(serialized_data), account_size)
39 };
40
41 let rent = Rent::get()?;
42
43 let create_account_instruction = create_account(
44 payer_info.key,
45 account_info.key,
46 rent.minimum_balance(account_size),
47 account_size as u64,
48 program_id,
49 );
50
51 invoke(
52 &create_account_instruction,
53 &[
54 payer_info.clone(),
55 account_info.clone(),
56 system_info.clone(),
57 ],
58 )?;
59
60 if let Some(serialized_data) = serialized_data {
61 account_info
62 .data
63 .borrow_mut()
64 .copy_from_slice(&serialized_data);
65 } else {
66 account_data.serialize(&mut *account_info.data.borrow_mut())?;
67 }
68
69 Ok(())
70}
71
72pub fn create_and_serialize_account_signed<'a, T: BorshSerialize + AccountMaxSize>(
75 payer_info: &AccountInfo<'a>,
76 account_info: &AccountInfo<'a>,
77 account_data: &T,
78 account_address_seeds: &[&[u8]],
79 program_id: &Pubkey,
80 system_info: &AccountInfo<'a>,
81 rent: &Rent,
82) -> Result<(), ProgramError> {
83 let (account_address, bump_seed) =
85 Pubkey::find_program_address(account_address_seeds, program_id);
86
87 if account_address != *account_info.key {
88 msg!(
89 "Create account with PDA: {:?} was requested while PDA: {:?} was expected",
90 account_info.key,
91 account_address
92 );
93 return Err(ProgramError::InvalidSeeds);
94 }
95
96 let (serialized_data, account_size) = if let Some(max_size) = account_data.get_max_size() {
97 (None, max_size)
98 } else {
99 let serialized_data = account_data.try_to_vec()?;
100 let account_size = serialized_data.len();
101 (Some(serialized_data), account_size)
102 };
103
104 let create_account_instruction = create_account(
105 payer_info.key,
106 account_info.key,
107 rent.minimum_balance(account_size),
108 account_size as u64,
109 program_id,
110 );
111
112 let mut signers_seeds = account_address_seeds.to_vec();
113 let bump = &[bump_seed];
114 signers_seeds.push(bump);
115
116 invoke_signed(
117 &create_account_instruction,
118 &[
119 payer_info.clone(),
120 account_info.clone(),
121 system_info.clone(),
122 ],
123 &[&signers_seeds[..]],
124 )?;
125
126 if let Some(serialized_data) = serialized_data {
127 account_info
128 .data
129 .borrow_mut()
130 .copy_from_slice(&serialized_data);
131 } else {
132 account_data.serialize(&mut *account_info.data.borrow_mut())?;
133 }
134
135 Ok(())
136}
137
138pub fn get_account_data<T: BorshDeserialize + IsInitialized>(
140 owner_program_id: &Pubkey,
141 account_info: &AccountInfo,
142) -> Result<T, ProgramError> {
143 if account_info.data_is_empty() {
144 return Err(GovernanceToolsError::AccountDoesNotExist.into());
145 }
146 if account_info.owner != owner_program_id {
147 return Err(GovernanceToolsError::InvalidAccountOwner.into());
148 }
149
150 let account: T = try_from_slice_unchecked(&account_info.data.borrow())?;
151 if !account.is_initialized() {
152 Err(ProgramError::UninitializedAccount)
153 } else {
154 Ok(account)
155 }
156}
157
158pub fn assert_is_valid_account<T: BorshDeserialize + PartialEq>(
161 account_info: &AccountInfo,
162 expected_account_type: T,
163 owner_program_id: &Pubkey,
164) -> Result<(), ProgramError> {
165 if account_info.owner != owner_program_id {
166 return Err(GovernanceToolsError::InvalidAccountOwner.into());
167 }
168
169 if account_info.data_is_empty() {
170 return Err(GovernanceToolsError::AccountDoesNotExist.into());
171 }
172
173 let account_type: T = try_from_slice_unchecked(&account_info.data.borrow())?;
174
175 if account_type != expected_account_type {
176 return Err(GovernanceToolsError::InvalidAccountType.into());
177 };
178
179 Ok(())
180}
181
182pub fn dispose_account(account_info: &AccountInfo, beneficiary_info: &AccountInfo) {
185 let account_carats = account_info.carats();
186 **account_info.carats.borrow_mut() = 0;
187
188 **beneficiary_info.carats.borrow_mut() = beneficiary_info
189 .carats()
190 .checked_add(account_carats)
191 .unwrap();
192
193 let mut account_data = account_info.data.borrow_mut();
194
195 account_data.fill(0);
196}