1use {
2 crate::{
3 error::VaultError,
4 state::{Key, Vault},
5 },
6 borsh::BorshDeserialize,
7 solana_program::{
8 account_info::AccountInfo,
9 borsh::try_from_slice_unchecked,
10 entrypoint::ProgramResult,
11 msg,
12 program::{invoke, invoke_signed},
13 program_error::ProgramError,
14 program_pack::{IsInitialized, Pack},
15 pubkey::Pubkey,
16 system_instruction,
17 sysvar::{rent::Rent, Sysvar},
18 },
19 std::convert::TryInto,
20};
21
22pub fn assert_initialized<T: Pack + IsInitialized>(
24 account_info: &AccountInfo,
25) -> Result<T, ProgramError> {
26 let account: T = T::unpack_unchecked(&account_info.data.borrow())?;
27 if !account.is_initialized() {
28 Err(VaultError::Uninitialized.into())
29 } else {
30 Ok(account)
31 }
32}
33
34pub fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult {
35 if !rent.is_exempt(account_info.lamports(), account_info.data_len()) {
36 Err(VaultError::NotRentExempt.into())
37 } else {
38 Ok(())
39 }
40}
41
42pub fn assert_owned_by(account: &AccountInfo, owner: &Pubkey) -> ProgramResult {
43 if account.owner != owner {
44 Err(VaultError::IncorrectOwner.into())
45 } else {
46 Ok(())
47 }
48}
49
50pub fn assert_token_matching(vault: &Vault, token: &AccountInfo) -> ProgramResult {
51 if vault.token_program != *token.key {
52 return Err(VaultError::TokenProgramProvidedDoesNotMatchVault.into());
53 }
54 Ok(())
55}
56
57pub fn assert_vault_authority_correct(
58 vault: &Vault,
59 vault_authority_info: &AccountInfo,
60) -> ProgramResult {
61 if !vault_authority_info.is_signer {
62 return Err(VaultError::AuthorityIsNotSigner.into());
63 }
64
65 if *vault_authority_info.key != vault.authority {
66 return Err(VaultError::AuthorityDoesNotMatch.into());
67 }
68 Ok(())
69}
70
71pub fn assert_token_program_matches_package(token_program_info: &AccountInfo) -> ProgramResult {
72 if *token_program_info.key != spl_token::id() {
73 return Err(VaultError::InvalidTokenProgram.into());
74 }
75
76 Ok(())
77}
78
79#[inline(always)]
82pub fn create_or_allocate_account_raw<'a>(
83 program_id: Pubkey,
84 new_account_info: &AccountInfo<'a>,
85 rent_sysvar_info: &AccountInfo<'a>,
86 system_program_info: &AccountInfo<'a>,
87 payer_info: &AccountInfo<'a>,
88 size: usize,
89 signer_seeds: &[&[u8]],
90) -> Result<(), ProgramError> {
91 let rent = &Rent::from_account_info(rent_sysvar_info)?;
92 let required_lamports = rent
93 .minimum_balance(size)
94 .max(1)
95 .saturating_sub(new_account_info.lamports());
96
97 if required_lamports > 0 {
98 msg!("Transfer {} lamports to the new account", required_lamports);
99 invoke(
100 &system_instruction::transfer(payer_info.key, new_account_info.key, required_lamports),
101 &[
102 payer_info.clone(),
103 new_account_info.clone(),
104 system_program_info.clone(),
105 ],
106 )?;
107 }
108
109 msg!("Allocate space for the account");
110 invoke_signed(
111 &system_instruction::allocate(new_account_info.key, size.try_into().unwrap()),
112 &[new_account_info.clone(), system_program_info.clone()],
113 &[signer_seeds],
114 )?;
115
116 msg!("Assign the account to the owning program");
117 invoke_signed(
118 &system_instruction::assign(new_account_info.key, &program_id),
119 &[new_account_info.clone(), system_program_info.clone()],
120 &[signer_seeds],
121 )?;
122 msg!("Completed assignation!");
123
124 Ok(())
125}
126
127#[inline(always)]
129pub fn spl_token_transfer(params: TokenTransferParams<'_, '_>) -> ProgramResult {
130 let TokenTransferParams {
131 source,
132 destination,
133 authority,
134 token_program,
135 amount,
136 authority_signer_seeds,
137 } = params;
138 let result = invoke_signed(
139 &spl_token::instruction::transfer(
140 token_program.key,
141 source.key,
142 destination.key,
143 authority.key,
144 &[],
145 amount,
146 )?,
147 &[source, destination, authority, token_program],
148 &[authority_signer_seeds],
149 );
150 result.map_err(|_| VaultError::TokenTransferFailed.into())
151}
152
153pub fn spl_token_mint_to(params: TokenMintToParams<'_, '_>) -> ProgramResult {
155 let TokenMintToParams {
156 mint,
157 destination,
158 authority,
159 token_program,
160 amount,
161 authority_signer_seeds,
162 } = params;
163 let result = invoke_signed(
164 &spl_token::instruction::mint_to(
165 token_program.key,
166 mint.key,
167 destination.key,
168 authority.key,
169 &[],
170 amount,
171 )?,
172 &[mint, destination, authority, token_program],
173 &[authority_signer_seeds],
174 );
175 result.map_err(|_| VaultError::TokenMintToFailed.into())
176}
177
178#[inline(always)]
180pub fn spl_token_burn(params: TokenBurnParams<'_, '_>) -> ProgramResult {
181 let TokenBurnParams {
182 mint,
183 source,
184 authority,
185 token_program,
186 amount,
187 authority_signer_seeds,
188 } = params;
189 let result = invoke_signed(
190 &spl_token::instruction::burn(
191 token_program.key,
192 source.key,
193 mint.key,
194 authority.key,
195 &[],
196 amount,
197 )?,
198 &[source, mint, authority, token_program],
199 &[authority_signer_seeds],
200 );
201 result.map_err(|_| VaultError::TokenBurnFailed.into())
202}
203
204pub struct TokenTransferParams<'a: 'b, 'b> {
206 pub source: AccountInfo<'a>,
208 pub destination: AccountInfo<'a>,
210 pub amount: u64,
212 pub authority: AccountInfo<'a>,
214 pub authority_signer_seeds: &'b [&'b [u8]],
216 pub token_program: AccountInfo<'a>,
218}
219pub struct TokenMintToParams<'a: 'b, 'b> {
221 pub mint: AccountInfo<'a>,
223 pub destination: AccountInfo<'a>,
225 pub amount: u64,
227 pub authority: AccountInfo<'a>,
229 pub authority_signer_seeds: &'b [&'b [u8]],
231 pub token_program: AccountInfo<'a>,
233}
234pub struct TokenBurnParams<'a: 'b, 'b> {
236 pub mint: AccountInfo<'a>,
238 pub source: AccountInfo<'a>,
240 pub amount: u64,
242 pub authority: AccountInfo<'a>,
244 pub authority_signer_seeds: &'b [&'b [u8]],
246 pub token_program: AccountInfo<'a>,
248}
249
250pub fn try_from_slice_checked<T: BorshDeserialize>(
251 data: &[u8],
252 data_type: Key,
253 data_size: usize,
254) -> Result<T, ProgramError> {
255 if (data[0] != data_type as u8 && data[0] != Key::Uninitialized as u8)
256 || data.len() != data_size
257 {
258 return Err(VaultError::DataTypeMismatch.into());
259 }
260
261 let result: T = try_from_slice_unchecked(data)?;
262
263 Ok(result)
264}
265
266pub fn assert_derivation(
267 program_id: &Pubkey,
268 account: &AccountInfo,
269 path: &[&[u8]],
270) -> Result<u8, ProgramError> {
271 let (key, bump) = Pubkey::find_program_address(path, program_id);
272 if key != *account.key {
273 return Err(VaultError::DerivedKeyInvalid.into());
274 }
275 Ok(bump)
276}