1use {
4 crate::{fund_info::FundInfo, user_info::UserInfo},
5 solana_farm_sdk::{
6 fund::{
7 Fund, FundAssetType, FundAssets, FundCustody, FundCustodyType, FundUserRequests,
8 FundVault, FundVaultType, DISCRIMINATOR_FUND_CUSTODY, DISCRIMINATOR_FUND_VAULT,
9 },
10 id::{main_router, zero},
11 math,
12 program::{account, clock},
13 token::Token,
14 traits::Packed,
15 },
16 solana_program::{
17 account_info::AccountInfo, clock::UnixTimestamp, entrypoint::ProgramResult, msg,
18 program_error::ProgramError, pubkey::Pubkey,
19 },
20};
21
22#[allow(clippy::too_many_arguments)]
23pub fn check_wd_custody_accounts<'a, 'b>(
24 fund_program_id: &Pubkey,
25 fund_metadata: &Pubkey,
26 custody_token: &Token,
27 custody_token_metadata: &'a AccountInfo<'b>,
28 user_wd_token_account: &'a AccountInfo<'b>,
29 custody_account: &'a AccountInfo<'b>,
30 custody_fees_account: &'a AccountInfo<'b>,
31 custody_metadata: &'a AccountInfo<'b>,
32 oracle_account: &'a AccountInfo<'b>,
33) -> ProgramResult {
34 let deposit_token_mint =
35 if let Ok(mint) = account::get_token_account_mint(user_wd_token_account) {
36 mint
37 } else {
38 msg!("Error: Invalid user's deposit token account");
39 return Err(ProgramError::Custom(500));
40 };
41
42 let custody_account_mint = if let Ok(mint) = account::get_token_account_mint(custody_account) {
43 mint
44 } else {
45 msg!("Error: Invalid custody token account mint");
46 return Err(ProgramError::Custom(501));
47 };
48
49 if custody_token.mint != custody_account_mint || deposit_token_mint != custody_account_mint {
50 msg!("Error: Custody token mint mismatch");
51 return Err(ProgramError::Custom(502));
52 }
53
54 let custody = account::unpack::<FundCustody>(custody_metadata, "custody")?;
55
56 if &custody.token_ref != custody_token_metadata.key
57 || custody_token_metadata.owner != &main_router::id()
58 {
59 msg!("Error: Invalid custody token account");
60 return Err(ProgramError::Custom(503));
61 }
62
63 if custody_metadata.owner != fund_program_id
64 || custody.discriminator != DISCRIMINATOR_FUND_CUSTODY
65 || &custody.fund_ref != fund_metadata
66 || custody.custody_type != FundCustodyType::DepositWithdraw
67 || &custody.address != custody_account.key
68 || &custody.fees_address != custody_fees_account.key
69 || &custody_token.oracle_account.unwrap_or_else(zero::id) != oracle_account.key
70 {
71 msg!("Error: Invalid custody accounts");
72 Err(ProgramError::Custom(504))
73 } else {
74 Ok(())
75 }
76}
77
78#[allow(clippy::too_many_arguments)]
79pub fn check_custody_account<'a, 'b>(
80 fund_program_id: &Pubkey,
81 fund_metadata: &Pubkey,
82 custody_token: &Token,
83 custody_token_metadata: &'a AccountInfo<'b>,
84 custody_metadata: &'a AccountInfo<'b>,
85 custody_type: FundCustodyType,
86 custody_account: &'a AccountInfo<'b>,
87 custody_fees_account: Option<&Pubkey>,
88) -> ProgramResult {
89 let custody_account_mint = if let Ok(mint) = account::get_token_account_mint(custody_account) {
90 mint
91 } else {
92 msg!("Error: Invalid custody token account mint");
93 return Err(ProgramError::Custom(501));
94 };
95
96 if custody_token.mint != custody_account_mint {
97 msg!("Error: Custody token mint mismatch");
98 return Err(ProgramError::Custom(502));
99 }
100
101 let custody = account::unpack::<FundCustody>(custody_metadata, "custody")?;
102
103 if &custody.token_ref != custody_token_metadata.key
104 || custody_token_metadata.owner != &main_router::id()
105 {
106 msg!("Error: Invalid custody token account");
107 return Err(ProgramError::Custom(503));
108 }
109
110 if custody_metadata.owner != fund_program_id
111 || custody.discriminator != DISCRIMINATOR_FUND_CUSTODY
112 || &custody.fund_ref != fund_metadata
113 || custody.custody_type != custody_type
114 || &custody.address != custody_account.key
115 || &custody.fees_address != custody_fees_account.unwrap_or(&custody.fees_address)
116 {
117 msg!("Error: Invalid custody accounts");
118 return Err(ProgramError::Custom(504));
119 }
120
121 Ok(())
122}
123
124pub fn check_and_get_fund_assets_account(
125 fund: &Fund,
126 fund_assets_account: &AccountInfo,
127 assets_type: FundAssetType,
128) -> Result<FundAssets, ProgramError> {
129 let fund_assets = account::unpack::<FundAssets>(fund_assets_account, "Fund assets")?;
130
131 let fund_assets_info_derived = Pubkey::create_program_address(
132 &[
133 if assets_type == FundAssetType::Custody {
134 b"custodies_assets_info"
135 } else {
136 b"vaults_assets_info"
137 },
138 fund.name.as_bytes(),
139 &[fund_assets.bump],
140 ],
141 &fund.fund_program_id,
142 )?;
143
144 if &fund_assets_info_derived != fund_assets_account.key {
145 msg!("Error: Invalid fund assets account");
146 return Err(ProgramError::Custom(505));
147 }
148
149 Ok(fund_assets)
150}
151
152pub fn check_vault_account<'a, 'b>(
153 fund_program_id: &Pubkey,
154 fund_metadata: &'a AccountInfo<'b>,
155 vault_metadata: &'a AccountInfo<'b>,
156 vault_type: FundVaultType,
157) -> ProgramResult {
158 if vault_metadata.owner != fund_program_id {
159 msg!("Error: Invalid custody owner");
160 return Err(ProgramError::IllegalOwner);
161 }
162
163 let vault = account::unpack::<FundVault>(vault_metadata, "Vault")?;
164
165 if vault.discriminator != DISCRIMINATOR_FUND_VAULT
166 || vault.fund_ref != *fund_metadata.key
167 || vault_type != vault.vault_type
168 {
169 msg!("Error: Invalid vault metadata account");
170 return Err(ProgramError::Custom(506));
171 }
172
173 Ok(())
174}
175
176pub fn check_unpack_target_vault<'a, 'b>(
177 fund_program_id: &Pubkey,
178 router_program_id: &Pubkey,
179 fund_metadata: &Pubkey,
180 underlying_pool_id: &Pubkey,
181 fund_vault_metadata: &'a AccountInfo<'b>,
182) -> Result<FundVault, ProgramError> {
183 if fund_vault_metadata.owner != fund_program_id {
184 msg!("Error: Invalid Fund Vault metadata owner");
185 return Err(ProgramError::IllegalOwner);
186 }
187
188 let fund_vault = account::unpack::<FundVault>(fund_vault_metadata, "Fund Vault")?;
189
190 if &fund_vault.fund_ref != fund_metadata {
191 msg!("Error: Specified Vault doesn't belong to this Fund");
192 return Err(ProgramError::Custom(507));
193 }
194
195 if &fund_vault.router_program_id != router_program_id
196 || &fund_vault.underlying_pool_id != underlying_pool_id
197 {
198 msg!("Error: Invalid target Vault");
199 return Err(ProgramError::Custom(508));
200 }
201
202 Ok(fund_vault)
203}
204
205pub fn increase_vault_balance(
206 fund_vault_metadata: &AccountInfo,
207 vault: &FundVault,
208 lp_balance_increase: u64,
209) -> ProgramResult {
210 if lp_balance_increase == 0 {
211 return Ok(());
212 }
213
214 let updated_lp_balance = math::checked_add(vault.lp_balance, lp_balance_increase)?;
215 let vault_new = FundVault {
216 lp_balance: updated_lp_balance,
217 balance_update_time: clock::get_time()?,
218 ..*vault
219 };
220 vault_new.pack(*fund_vault_metadata.try_borrow_mut_data()?)?;
221
222 Ok(())
223}
224
225pub fn decrease_vault_balance(
226 fund_vault_metadata: &AccountInfo,
227 vault: &FundVault,
228 lp_balance_decrease: u64,
229) -> ProgramResult {
230 if lp_balance_decrease == 0 {
231 return Ok(());
232 }
233
234 let updated_lp_balance = math::checked_sub(vault.lp_balance, lp_balance_decrease)?;
235 let vault_new = FundVault {
236 lp_balance: updated_lp_balance,
237 balance_update_time: clock::get_time()?,
238 ..*vault
239 };
240 vault_new.pack(*fund_vault_metadata.try_borrow_mut_data()?)?;
241
242 Ok(())
243}
244
245pub fn check_user_requests_account<'a, 'b>(
246 fund: &Fund,
247 custody_token: &Token,
248 user_requests: &FundUserRequests,
249 user_account: &'a AccountInfo<'b>,
250 user_requests_account: &'a AccountInfo<'b>,
251) -> ProgramResult {
252 let user_requests_derived = Pubkey::create_program_address(
253 &[
254 b"user_requests_account",
255 custody_token.name.as_bytes(),
256 user_account.key.as_ref(),
257 fund.name.as_bytes(),
258 &[user_requests.bump],
259 ],
260 &fund.fund_program_id,
261 )?;
262
263 if user_requests_account.key != &user_requests_derived {
264 msg!("Error: Invalid user requests address");
265 Err(ProgramError::Custom(509))
266 } else {
267 Ok(())
268 }
269}
270
271pub fn check_fund_token_mint(fund: &Fund, fund_token_mint: &AccountInfo) -> ProgramResult {
272 let fund_token_mint_derived = Pubkey::create_program_address(
273 &[
274 b"fund_token_mint",
275 fund.name.as_bytes(),
276 &[fund.fund_token_bump],
277 ],
278 &fund.fund_program_id,
279 )?;
280
281 if fund_token_mint.key != &fund_token_mint_derived {
282 msg!("Error: Invalid Fund token mint");
283 Err(ProgramError::Custom(510))
284 } else {
285 Ok(())
286 }
287}
288
289pub fn check_assets_update_time(
290 assets_update_time: UnixTimestamp,
291 max_update_age_sec: u64,
292) -> ProgramResult {
293 let last_update_age_sec = math::checked_sub(clock::get_time()?, assets_update_time)?;
294 if last_update_age_sec > max_update_age_sec as i64 {
295 msg!("Error: Assets balance is stale. Contact Fund administrator.");
296 Err(ProgramError::Custom(222))
297 } else {
298 Ok(())
299 }
300}
301
302pub fn check_assets_limit_usd(
303 fund_info: &FundInfo,
304 deposit_value_usd: f64,
305) -> Result<(), ProgramError> {
306 let current_assets_usd = fund_info.get_current_assets_usd()?;
307 let assets_limit = fund_info.get_assets_limit_usd()?;
308
309 if assets_limit > 0.0 && assets_limit < deposit_value_usd + current_assets_usd {
310 let amount_left = if current_assets_usd < assets_limit {
311 assets_limit - current_assets_usd
312 } else {
313 0.0
314 };
315 msg!(
316 "Error: Fund assets limit reached ({}). Allowed max desposit USD: {}",
317 assets_limit,
318 amount_left
319 );
320 return Err(ProgramError::Custom(223));
321 }
322
323 Ok(())
324}
325
326pub fn get_fund_token_to_mint_amount(
327 current_assets_usd: f64,
328 deposit_amount: u64,
329 deposit_value_usd: f64,
330 ft_supply_amount: u64,
331) -> Result<u64, ProgramError> {
332 let ft_to_mint = if ft_supply_amount == 0 {
333 deposit_amount
334 } else if current_assets_usd <= 0.0001 {
335 msg!("Error: Assets balance is stale. Contact Fund administrator.");
336 return Err(ProgramError::Custom(222));
337 } else {
338 math::checked_as_u64(
339 math::checked_mul(
340 math::checked_as_u128(deposit_value_usd / current_assets_usd * 1000000000.0)?,
341 ft_supply_amount as u128,
342 )? / 1000000000u128,
343 )?
344 };
345
346 Ok(ft_to_mint)
347}
348
349pub fn get_fund_token_balance(
350 fund_token_account: &AccountInfo,
351 user_info: &UserInfo,
352) -> Result<u64, ProgramError> {
353 math::checked_add(
354 account::get_token_balance(fund_token_account)?,
355 user_info.get_virtual_tokens_balance()?,
356 )
357}
358
359pub fn get_fund_token_supply(
360 fund_token_mint: &AccountInfo,
361 fund_info: &FundInfo,
362) -> Result<u64, ProgramError> {
363 math::checked_add(
364 account::get_token_supply(fund_token_mint)?,
365 fund_info.get_virtual_tokens_supply()?,
366 )
367}