1use {
4 crate::error::FarmClientError,
5 solana_farm_sdk::{
6 farm::FarmRoute,
7 fund::{
8 FundAssetType, FundAssetsTrackingConfig, FundCustodyType, FundSchedule, FundVaultType,
9 },
10 id::zero,
11 instruction::fund::FundInstruction,
12 math,
13 pool::PoolRoute,
14 program::multisig::Multisig,
15 string::str_to_as64,
16 token::OracleType,
17 vault::VaultStrategy,
18 },
19 solana_sdk::{
20 instruction::{AccountMeta, Instruction},
21 program_error::ProgramError,
22 pubkey::Pubkey,
23 system_program, sysvar,
24 },
25};
26
27use super::FarmClient;
28
29impl FarmClient {
30 pub fn new_instruction_init_fund(
32 &self,
33 admin_address: &Pubkey,
34 fund_name: &str,
35 step: u64,
36 ) -> Result<Instruction, FarmClientError> {
37 let fund = self.get_fund(fund_name)?;
39 let fund_ref = self.get_fund_ref(fund_name)?;
40 let fund_token = self.get_token_by_ref(&fund.fund_token_ref)?;
41
42 let data = FundInstruction::Init { step }.to_vec()?;
44 let accounts = vec![
45 AccountMeta::new_readonly(*admin_address, true),
46 AccountMeta::new_readonly(fund_ref, false),
47 AccountMeta::new(fund.info_account, false),
48 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
49 AccountMeta::new(fund.fund_authority, false),
50 AccountMeta::new_readonly(fund.fund_program_id, false),
51 AccountMeta::new_readonly(system_program::id(), false),
52 AccountMeta::new_readonly(spl_token::id(), false),
53 AccountMeta::new_readonly(sysvar::rent::id(), false),
54 AccountMeta::new(fund_token.mint, false),
55 AccountMeta::new_readonly(fund.fund_token_ref, false),
56 AccountMeta::new(fund.vaults_assets_info, false),
57 AccountMeta::new(fund.custodies_assets_info, false),
58 ];
59
60 Ok(Instruction {
61 program_id: fund.fund_program_id,
62 data,
63 accounts,
64 })
65 }
66
67 pub fn new_instruction_user_init_fund(
69 &self,
70 wallet_address: &Pubkey,
71 fund_name: &str,
72 token_name: &str,
73 ) -> Result<Instruction, FarmClientError> {
74 let fund = self.get_fund(fund_name)?;
76 let fund_ref = self.get_fund_ref(fund_name)?;
77 let token_ref = self.get_token_ref(token_name)?;
78 let user_info_account = self.get_fund_user_info_account(wallet_address, fund_name)?;
79 let user_requests_account =
80 self.get_fund_user_requests_account(wallet_address, fund_name, token_name)?;
81
82 let data = FundInstruction::UserInit.to_vec()?;
84 let accounts = vec![
85 AccountMeta::new(*wallet_address, true),
86 AccountMeta::new_readonly(fund_ref, false),
87 AccountMeta::new(fund.info_account, false),
88 AccountMeta::new_readonly(*wallet_address, false),
89 AccountMeta::new(user_info_account, false),
90 AccountMeta::new(user_requests_account, false),
91 AccountMeta::new_readonly(token_ref, false),
92 AccountMeta::new_readonly(system_program::id(), false),
93 ];
94
95 Ok(Instruction {
96 program_id: fund.fund_program_id,
97 data,
98 accounts,
99 })
100 }
101
102 pub fn new_instruction_set_fund_admins(
104 &self,
105 admin_address: &Pubkey,
106 fund_name: &str,
107 admin_signers: &[Pubkey],
108 min_signatures: u8,
109 ) -> Result<Instruction, FarmClientError> {
110 if admin_signers.is_empty() || min_signatures == 0 {
111 return Err(FarmClientError::ValueError(
112 "At least one signer is required".to_string(),
113 ));
114 } else if min_signatures as usize > admin_signers.len()
115 || admin_signers.len() > Multisig::MAX_SIGNERS
116 {
117 return Err(FarmClientError::ValueError(
118 "Invalid number of signatures".to_string(),
119 ));
120 }
121
122 let fund = self.get_fund(fund_name)?;
124 let fund_ref = self.get_fund_ref(fund_name)?;
125
126 let mut inst = Instruction {
128 program_id: fund.fund_program_id,
129 data: FundInstruction::SetAdminSigners { min_signatures }.to_vec()?,
130 accounts: vec![
131 AccountMeta::new_readonly(*admin_address, true),
132 AccountMeta::new_readonly(fund_ref, false),
133 AccountMeta::new(fund.info_account, false),
134 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
135 AccountMeta::new(self.get_fund_multisig_account(fund_name)?, false),
136 AccountMeta::new_readonly(system_program::id(), false),
137 ],
138 };
139
140 for key in admin_signers {
141 inst.accounts.push(AccountMeta::new_readonly(*key, false));
142 }
143
144 Ok(inst)
145 }
146
147 pub fn new_instruction_remove_fund_multisig(
149 &self,
150 admin_address: &Pubkey,
151 fund_name: &str,
152 ) -> Result<Instruction, FarmClientError> {
153 let fund = self.get_fund(fund_name)?;
155 let fund_ref = self.get_fund_ref(fund_name)?;
156
157 let inst = Instruction {
159 program_id: fund.fund_program_id,
160 data: FundInstruction::RemoveMultisig.to_vec()?,
161 accounts: vec![
162 AccountMeta::new_readonly(*admin_address, true),
163 AccountMeta::new_readonly(fund_ref, false),
164 AccountMeta::new(fund.info_account, false),
165 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
166 AccountMeta::new(self.get_fund_multisig_account(fund_name)?, false),
167 ],
168 };
169
170 Ok(inst)
171 }
172
173 pub fn new_instruction_set_fund_assets_tracking_config(
175 &self,
176 admin_address: &Pubkey,
177 fund_name: &str,
178 config: &FundAssetsTrackingConfig,
179 ) -> Result<Instruction, FarmClientError> {
180 let fund = self.get_fund(fund_name)?;
182 let fund_ref = self.get_fund_ref(fund_name)?;
183
184 let data = FundInstruction::SetAssetsTrackingConfig { config: *config }.to_vec()?;
186 let accounts = vec![
187 AccountMeta::new_readonly(*admin_address, true),
188 AccountMeta::new_readonly(fund_ref, false),
189 AccountMeta::new(fund.info_account, false),
190 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
191 ];
192
193 Ok(Instruction {
194 program_id: fund.fund_program_id,
195 data,
196 accounts,
197 })
198 }
199
200 pub fn new_instruction_add_fund_custody(
202 &self,
203 admin_address: &Pubkey,
204 fund_name: &str,
205 token_name: &str,
206 custody_type: FundCustodyType,
207 ) -> Result<Instruction, FarmClientError> {
208 let fund = self.get_fund(fund_name)?;
210 let fund_ref = self.get_fund_ref(fund_name)?;
211 let token = self.get_token(token_name)?;
212 let token_ref = self.get_token_ref(token_name)?;
213
214 let custodies = self.get_fund_custodies(fund_name)?;
216 let custody_metadata =
217 self.get_fund_custody_account(fund_name, token_name, custody_type)?;
218 let fund_assets_account =
219 self.get_fund_assets_account(fund_name, FundAssetType::Custody)?;
220 let custody_token_account =
221 self.get_fund_custody_token_account(fund_name, token_name, custody_type)?;
222 let custody_fees_token_account =
223 self.get_fund_custody_fees_token_account(fund_name, token_name, custody_type)?;
224
225 let custody_id = if custodies.is_empty() {
227 0
228 } else if custodies.last().unwrap().custody_id < u32::MAX {
229 custodies.last().unwrap().custody_id + 1
230 } else {
231 return Err(FarmClientError::ValueError(
232 "Number of custodies are over the limit".to_string(),
233 ));
234 };
235
236 let current_hash = self
237 .get_fund_assets(fund_name, FundAssetType::Custody)?
238 .target_hash;
239
240 let target_hash = if FarmClient::is_liquidity_token(token_name) {
241 current_hash
242 } else {
243 math::hash_address(current_hash, &custody_token_account)
244 };
245
246 let data = FundInstruction::AddCustody {
248 target_hash,
249 custody_id,
250 custody_type,
251 }
252 .to_vec()?;
253 let accounts = vec![
254 AccountMeta::new_readonly(*admin_address, true),
255 AccountMeta::new_readonly(fund_ref, false),
256 AccountMeta::new(fund.info_account, false),
257 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
258 AccountMeta::new(self.get_fund_multisig_account(fund_name)?, false),
259 AccountMeta::new_readonly(fund.fund_authority, false),
260 AccountMeta::new_readonly(system_program::id(), false),
261 AccountMeta::new_readonly(spl_token::id(), false),
262 AccountMeta::new_readonly(spl_associated_token_account::id(), false),
263 AccountMeta::new_readonly(sysvar::rent::id(), false),
264 AccountMeta::new(fund_assets_account, false),
265 AccountMeta::new(custody_token_account, false),
266 AccountMeta::new(custody_fees_token_account, false),
267 AccountMeta::new(custody_metadata, false),
268 AccountMeta::new_readonly(token_ref, false),
269 AccountMeta::new(token.mint, false),
270 ];
271
272 Ok(Instruction {
273 program_id: fund.fund_program_id,
274 data,
275 accounts,
276 })
277 }
278
279 pub fn new_instruction_remove_fund_custody(
281 &self,
282 admin_address: &Pubkey,
283 fund_name: &str,
284 token_name: &str,
285 custody_type: FundCustodyType,
286 ) -> Result<Instruction, FarmClientError> {
287 let fund = self.get_fund(fund_name)?;
289 let fund_ref = self.get_fund_ref(fund_name)?;
290 let token_ref = self.get_token_ref(token_name)?;
291
292 let custodies = self.get_fund_custodies(fund_name)?;
294 if custodies.is_empty() {
295 return Err(FarmClientError::ValueError(
296 "No active custodies found".to_string(),
297 ));
298 }
299 let custody_metadata =
300 self.get_fund_custody_account(fund_name, token_name, custody_type)?;
301 let fund_assets_account =
302 self.get_fund_assets_account(fund_name, FundAssetType::Custody)?;
303 let custody_token_account =
304 self.get_fund_custody_token_account(fund_name, token_name, custody_type)?;
305 let custody_fees_token_account =
306 self.get_fund_custody_fees_token_account(fund_name, token_name, custody_type)?;
307
308 let mut target_hash = 0;
310 for custody in custodies {
311 if custody.address != custody_token_account && !custody.is_vault_token {
312 target_hash = math::hash_address(target_hash, &custody.address);
313 }
314 }
315
316 let data = FundInstruction::RemoveCustody {
318 target_hash,
319 custody_type,
320 }
321 .to_vec()?;
322 let accounts = vec![
323 AccountMeta::new_readonly(*admin_address, true),
324 AccountMeta::new_readonly(fund_ref, false),
325 AccountMeta::new(fund.info_account, false),
326 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
327 AccountMeta::new(self.get_fund_multisig_account(fund_name)?, false),
328 AccountMeta::new_readonly(fund.fund_authority, false),
329 AccountMeta::new_readonly(system_program::id(), false),
330 AccountMeta::new_readonly(spl_token::id(), false),
331 AccountMeta::new(fund_assets_account, false),
332 AccountMeta::new(custody_token_account, false),
333 AccountMeta::new(custody_fees_token_account, false),
334 AccountMeta::new(custody_metadata, false),
335 AccountMeta::new_readonly(token_ref, false),
336 ];
337
338 Ok(Instruction {
339 program_id: fund.fund_program_id,
340 data,
341 accounts,
342 })
343 }
344
345 pub fn new_instruction_add_fund_vault(
347 &self,
348 admin_address: &Pubkey,
349 fund_name: &str,
350 vault_name: &str,
351 vault_type: FundVaultType,
352 ) -> Result<Instruction, FarmClientError> {
353 let fund = self.get_fund(fund_name)?;
355 let fund_ref = self.get_fund_ref(fund_name)?;
356
357 let vaults = self.get_fund_vaults(fund_name)?;
359 let fund_vault_metadata = self.get_fund_vault_account(fund_name, vault_name, vault_type)?;
360 let fund_assets_account = self.get_fund_assets_account(fund_name, FundAssetType::Vault)?;
361 let target_vault_metadata = match vault_type {
362 FundVaultType::Vault => self.get_vault_ref(vault_name)?,
363 FundVaultType::Pool => self.get_pool_ref(vault_name)?,
364 FundVaultType::Farm => self.get_farm_ref(vault_name)?,
365 };
366 let underlying_pool_ref = match vault_type {
367 FundVaultType::Vault => {
368 let vault = self.get_vault(vault_name)?;
369 match vault.strategy {
370 VaultStrategy::StakeLpCompoundRewards { pool_ref, .. } => pool_ref,
371 _ => unreachable!(),
372 }
373 }
374 FundVaultType::Farm => {
375 let farm = self.get_farm(vault_name)?;
376 let lp_token = self.get_token_by_ref(&farm.lp_token_ref.ok_or_else(|| {
377 FarmClientError::ValueError("Farms w/o LP tokens are not supported".to_string())
378 })?)?;
379 let pools = self.find_pools_with_lp(&lp_token.name)?;
380 if pools.is_empty() {
381 return Err(FarmClientError::RecordNotFound(format!(
382 "Pools with LP token {}",
383 lp_token.name
384 )));
385 }
386 self.get_pool_ref(&pools[0].name)?
387 }
388 FundVaultType::Pool => target_vault_metadata,
389 };
390
391 let vault_id = if vaults.is_empty() {
393 0
394 } else if vaults.last().unwrap().vault_id < u32::MAX {
395 vaults.last().unwrap().vault_id + 1
396 } else {
397 return Err(FarmClientError::ValueError(
398 "Number of vaults are over the limit".to_string(),
399 ));
400 };
401
402 let current_hash = self
403 .get_fund_assets(fund_name, FundAssetType::Vault)?
404 .target_hash;
405
406 let target_hash = if vault_type == FundVaultType::Farm {
407 current_hash
408 } else {
409 math::hash_address(current_hash, &target_vault_metadata)
410 };
411
412 let data = FundInstruction::AddVault {
414 target_hash,
415 vault_id,
416 vault_type,
417 }
418 .to_vec()?;
419
420 let (router_program_id, underlying_pool_id, underlying_lp_token_metadata) = match vault_type
421 {
422 FundVaultType::Pool => {
423 let pool = self.get_pool(vault_name)?;
424 let pool_ammid = match pool.route {
425 PoolRoute::Raydium { amm_id, .. } => amm_id,
426 PoolRoute::Saber { swap_account, .. } => swap_account,
427 PoolRoute::Orca { amm_id, .. } => amm_id,
428 };
429 (
430 pool.router_program_id,
431 pool_ammid,
432 pool.lp_token_ref.ok_or_else(|| {
433 FarmClientError::ValueError(
434 "Pools w/o LP tokens are not supported".to_string(),
435 )
436 })?,
437 )
438 }
439 FundVaultType::Farm => {
440 let farm = self.get_farm(vault_name)?;
441 let farm_id = match farm.route {
442 FarmRoute::Raydium { farm_id, .. } => farm_id,
443 FarmRoute::Saber { quarry, .. } => quarry,
444 FarmRoute::Orca { farm_id, .. } => farm_id,
445 };
446 (
447 farm.router_program_id,
448 farm_id,
449 farm.lp_token_ref.ok_or_else(|| {
450 FarmClientError::ValueError(
451 "Farms w/o LP tokens are not supported".to_string(),
452 )
453 })?,
454 )
455 }
456 FundVaultType::Vault => {
457 let vault = self.get_vault(vault_name)?;
458 let pool = self.get_pool_by_ref(&underlying_pool_ref)?;
459
460 (
461 vault.vault_program_id,
462 target_vault_metadata,
463 pool.lp_token_ref.ok_or_else(|| {
464 FarmClientError::ValueError(
465 "Underlying Pools w/o LP tokens are not supported".to_string(),
466 )
467 })?,
468 )
469 }
470 };
471
472 let accounts = vec![
473 AccountMeta::new_readonly(*admin_address, true),
474 AccountMeta::new_readonly(fund_ref, false),
475 AccountMeta::new(fund.info_account, false),
476 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
477 AccountMeta::new_readonly(fund.fund_authority, false),
478 AccountMeta::new_readonly(system_program::id(), false),
479 AccountMeta::new(fund_assets_account, false),
480 AccountMeta::new(fund_vault_metadata, false),
481 AccountMeta::new_readonly(target_vault_metadata, false),
482 AccountMeta::new_readonly(router_program_id, false),
483 AccountMeta::new_readonly(underlying_pool_id, false),
484 AccountMeta::new_readonly(underlying_pool_ref, false),
485 AccountMeta::new_readonly(underlying_lp_token_metadata, false),
486 ];
487
488 Ok(Instruction {
489 program_id: fund.fund_program_id,
490 data,
491 accounts,
492 })
493 }
494
495 pub fn new_instruction_remove_fund_vault(
497 &self,
498 admin_address: &Pubkey,
499 fund_name: &str,
500 vault_name: &str,
501 vault_type: FundVaultType,
502 ) -> Result<Instruction, FarmClientError> {
503 let fund = self.get_fund(fund_name)?;
505 let fund_ref = self.get_fund_ref(fund_name)?;
506
507 let vaults = self.get_fund_vaults(fund_name)?;
509 if vaults.is_empty() {
510 return Err(FarmClientError::ValueError(
511 "No active vaults found".to_string(),
512 ));
513 }
514 let vault_metadata = self.get_fund_vault_account(fund_name, vault_name, vault_type)?;
515 let fund_assets_account = self.get_fund_assets_account(fund_name, FundAssetType::Vault)?;
516 let vault_info = match vault_type {
517 FundVaultType::Vault => self.get_vault_ref(vault_name)?,
518 FundVaultType::Pool => self.get_pool_ref(vault_name)?,
519 FundVaultType::Farm => self.get_farm_ref(vault_name)?,
520 };
521
522 let mut target_hash = 0;
524 for vault in vaults {
525 if vault.vault_ref != vault_info && vault.vault_type != FundVaultType::Farm {
526 target_hash = math::hash_address(target_hash, &vault.vault_ref);
527 }
528 }
529
530 let data = FundInstruction::RemoveVault {
532 target_hash,
533 vault_type,
534 }
535 .to_vec()?;
536 let accounts = vec![
537 AccountMeta::new_readonly(*admin_address, true),
538 AccountMeta::new_readonly(fund_ref, false),
539 AccountMeta::new(fund.info_account, false),
540 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
541 AccountMeta::new_readonly(fund.fund_authority, false),
542 AccountMeta::new_readonly(system_program::id(), false),
543 AccountMeta::new(fund_assets_account, false),
544 AccountMeta::new(vault_metadata, false),
545 ];
546
547 Ok(Instruction {
548 program_id: fund.fund_program_id,
549 data,
550 accounts,
551 })
552 }
553
554 pub fn new_instruction_set_fund_deposit_schedule(
556 &self,
557 admin_address: &Pubkey,
558 fund_name: &str,
559 schedule: &FundSchedule,
560 ) -> Result<Instruction, FarmClientError> {
561 let fund = self.get_fund(fund_name)?;
563 let fund_ref = self.get_fund_ref(fund_name)?;
564
565 let data = FundInstruction::SetDepositSchedule {
567 schedule: *schedule,
568 }
569 .to_vec()?;
570 let accounts = vec![
571 AccountMeta::new_readonly(*admin_address, true),
572 AccountMeta::new_readonly(fund_ref, false),
573 AccountMeta::new(fund.info_account, false),
574 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
575 ];
576
577 Ok(Instruction {
578 program_id: fund.fund_program_id,
579 data,
580 accounts,
581 })
582 }
583
584 pub fn new_instruction_disable_deposits_fund(
586 &self,
587 admin_address: &Pubkey,
588 fund_name: &str,
589 ) -> Result<Instruction, FarmClientError> {
590 let fund = self.get_fund(fund_name)?;
592 let fund_ref = self.get_fund_ref(fund_name)?;
593
594 let data = FundInstruction::DisableDeposits.to_vec()?;
596 let accounts = vec![
597 AccountMeta::new_readonly(*admin_address, true),
598 AccountMeta::new_readonly(fund_ref, false),
599 AccountMeta::new(fund.info_account, false),
600 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
601 ];
602
603 Ok(Instruction {
604 program_id: fund.fund_program_id,
605 data,
606 accounts,
607 })
608 }
609
610 pub fn new_instruction_request_deposit_fund(
612 &self,
613 wallet_address: &Pubkey,
614 fund_name: &str,
615 token_name: &str,
616 ui_amount: f64,
617 ) -> Result<Instruction, FarmClientError> {
618 if ui_amount < 0.0 {
619 return Err(FarmClientError::ValueError(format!(
620 "Invalid deposit amount {} specified for Fund {}: Must be greater or equal to zero.",
621 ui_amount, fund_name
622 )));
623 }
624 let fund = self.get_fund(fund_name)?;
626 let fund_ref = self.get_fund_ref(fund_name)?;
627 let fund_token = self.get_token_by_ref(&fund.fund_token_ref)?;
628 let token = self.get_token(token_name)?;
629 let token_ref = self.get_token_ref(token_name)?;
630 let user_info_account = self.get_fund_user_info_account(wallet_address, fund_name)?;
631 let user_requests_account =
632 self.get_fund_user_requests_account(wallet_address, fund_name, token_name)?;
633 let user_deposit_token_account =
634 self.get_associated_token_address(wallet_address, token.name.as_str())?;
635 let user_fund_token_account =
636 self.get_associated_token_address(wallet_address, fund_token.name.as_str())?;
637 let custody_metadata =
638 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::DepositWithdraw)?;
639 let custody_token_account = self.get_fund_custody_token_account(
640 fund_name,
641 token_name,
642 FundCustodyType::DepositWithdraw,
643 )?;
644 let custody_fees_token_account = self.get_fund_custody_fees_token_account(
645 fund_name,
646 token_name,
647 FundCustodyType::DepositWithdraw,
648 )?;
649 let (_, oracle_account) = self.get_oracle(token_name)?;
650
651 let data = FundInstruction::RequestDeposit {
653 amount: self.to_token_amount(ui_amount, &token)?,
654 }
655 .to_vec()?;
656 let accounts = vec![
657 AccountMeta::new_readonly(*wallet_address, true),
658 AccountMeta::new_readonly(fund_ref, false),
659 AccountMeta::new(fund.info_account, false),
660 AccountMeta::new_readonly(fund.fund_authority, false),
661 AccountMeta::new_readonly(spl_token::id(), false),
662 AccountMeta::new(fund_token.mint, false),
663 AccountMeta::new(user_info_account, false),
664 AccountMeta::new(user_requests_account, false),
665 AccountMeta::new(user_deposit_token_account, false),
666 AccountMeta::new(user_fund_token_account, false),
667 AccountMeta::new(custody_token_account, false),
668 AccountMeta::new(custody_fees_token_account, false),
669 AccountMeta::new_readonly(custody_metadata, false),
670 AccountMeta::new_readonly(token_ref, false),
671 AccountMeta::new_readonly(oracle_account.unwrap_or_else(zero::id), false),
672 ];
673
674 Ok(Instruction {
675 program_id: fund.fund_program_id,
676 data,
677 accounts,
678 })
679 }
680
681 pub fn new_instruction_cancel_deposit_fund(
683 &self,
684 wallet_address: &Pubkey,
685 fund_name: &str,
686 token_name: &str,
687 ) -> Result<Instruction, FarmClientError> {
688 let fund = self.get_fund(fund_name)?;
690 let fund_ref = self.get_fund_ref(fund_name)?;
691 let token = self.get_token(token_name)?;
692 let token_ref = self.get_token_ref(token_name)?;
693 let user_requests_account =
694 self.get_fund_user_requests_account(wallet_address, fund_name, token_name)?;
695 let user_deposit_token_account =
696 self.get_associated_token_address(wallet_address, token.name.as_str())?;
697
698 let data = FundInstruction::CancelDeposit.to_vec()?;
700 let accounts = vec![
701 AccountMeta::new_readonly(*wallet_address, true),
702 AccountMeta::new_readonly(fund_ref, false),
703 AccountMeta::new(fund.info_account, false),
704 AccountMeta::new_readonly(spl_token::id(), false),
705 AccountMeta::new(user_requests_account, false),
706 AccountMeta::new(user_deposit_token_account, false),
707 AccountMeta::new_readonly(token_ref, false),
708 ];
709
710 Ok(Instruction {
711 program_id: fund.fund_program_id,
712 data,
713 accounts,
714 })
715 }
716
717 pub fn new_instruction_approve_deposit_fund(
719 &self,
720 admin_address: &Pubkey,
721 user_address: &Pubkey,
722 fund_name: &str,
723 token_name: &str,
724 ui_amount: f64,
725 ) -> Result<Instruction, FarmClientError> {
726 if ui_amount < 0.0 {
727 return Err(FarmClientError::ValueError(format!(
728 "Invalid approve amount {} specified for Fund {}: Must be greater or equal to zero.",
729 ui_amount, fund_name
730 )));
731 }
732 let fund = self.get_fund(fund_name)?;
734 let fund_ref = self.get_fund_ref(fund_name)?;
735 let fund_token = self.get_token_by_ref(&fund.fund_token_ref)?;
736 let token = self.get_token(token_name)?;
737 let token_ref = self.get_token_ref(token_name)?;
738 let user_info_account = self.get_fund_user_info_account(user_address, fund_name)?;
739 let user_requests_account =
740 self.get_fund_user_requests_account(user_address, fund_name, token_name)?;
741 let user_deposit_token_account =
742 self.get_associated_token_address(user_address, token.name.as_str())?;
743 let user_fund_token_account =
744 self.get_associated_token_address(user_address, fund_token.name.as_str())?;
745 let custody_metadata =
746 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::DepositWithdraw)?;
747 let custody_token_account = self.get_fund_custody_token_account(
748 fund_name,
749 token_name,
750 FundCustodyType::DepositWithdraw,
751 )?;
752 let custody_fees_token_account = self.get_fund_custody_fees_token_account(
753 fund_name,
754 token_name,
755 FundCustodyType::DepositWithdraw,
756 )?;
757 let (_, oracle_account) = self.get_oracle(token_name)?;
758
759 let data = FundInstruction::ApproveDeposit {
761 amount: self.to_token_amount(ui_amount, &token)?,
762 }
763 .to_vec()?;
764 let accounts = vec![
765 AccountMeta::new_readonly(*admin_address, true),
766 AccountMeta::new_readonly(fund_ref, false),
767 AccountMeta::new(fund.info_account, false),
768 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
769 AccountMeta::new_readonly(fund.fund_authority, false),
770 AccountMeta::new_readonly(spl_token::id(), false),
771 AccountMeta::new(fund_token.mint, false),
772 AccountMeta::new_readonly(*user_address, false),
773 AccountMeta::new(user_info_account, false),
774 AccountMeta::new(user_requests_account, false),
775 AccountMeta::new(user_deposit_token_account, false),
776 AccountMeta::new(user_fund_token_account, false),
777 AccountMeta::new(custody_token_account, false),
778 AccountMeta::new(custody_fees_token_account, false),
779 AccountMeta::new_readonly(custody_metadata, false),
780 AccountMeta::new_readonly(token_ref, false),
781 AccountMeta::new_readonly(oracle_account.unwrap_or_else(zero::id), false),
782 ];
783
784 Ok(Instruction {
785 program_id: fund.fund_program_id,
786 data,
787 accounts,
788 })
789 }
790
791 pub fn new_instruction_deny_deposit_fund(
793 &self,
794 admin_address: &Pubkey,
795 user_address: &Pubkey,
796 fund_name: &str,
797 token_name: &str,
798 deny_reason: &str,
799 ) -> Result<Instruction, FarmClientError> {
800 let fund = self.get_fund(fund_name)?;
802 let fund_ref = self.get_fund_ref(fund_name)?;
803 let token_ref = self.get_token_ref(token_name)?;
804 let user_requests_account =
805 self.get_fund_user_requests_account(user_address, fund_name, token_name)?;
806
807 let data = FundInstruction::DenyDeposit {
809 deny_reason: str_to_as64(deny_reason)?,
810 }
811 .to_vec()?;
812 let accounts = vec![
813 AccountMeta::new_readonly(*admin_address, true),
814 AccountMeta::new_readonly(fund_ref, false),
815 AccountMeta::new(fund.info_account, false),
816 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
817 AccountMeta::new_readonly(*user_address, false),
818 AccountMeta::new(user_requests_account, false),
819 AccountMeta::new_readonly(token_ref, false),
820 ];
821
822 Ok(Instruction {
823 program_id: fund.fund_program_id,
824 data,
825 accounts,
826 })
827 }
828
829 pub fn new_instruction_set_fund_withdrawal_schedule(
831 &self,
832 admin_address: &Pubkey,
833 fund_name: &str,
834 schedule: &FundSchedule,
835 ) -> Result<Instruction, FarmClientError> {
836 let fund = self.get_fund(fund_name)?;
838 let fund_ref = self.get_fund_ref(fund_name)?;
839
840 let data = FundInstruction::SetWithdrawalSchedule {
842 schedule: *schedule,
843 }
844 .to_vec()?;
845 let accounts = vec![
846 AccountMeta::new_readonly(*admin_address, true),
847 AccountMeta::new_readonly(fund_ref, false),
848 AccountMeta::new(fund.info_account, false),
849 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
850 ];
851
852 Ok(Instruction {
853 program_id: fund.fund_program_id,
854 data,
855 accounts,
856 })
857 }
858
859 pub fn new_instruction_disable_withdrawals_fund(
861 &self,
862 admin_address: &Pubkey,
863 fund_name: &str,
864 ) -> Result<Instruction, FarmClientError> {
865 let fund = self.get_fund(fund_name)?;
867 let fund_ref = self.get_fund_ref(fund_name)?;
868
869 let data = FundInstruction::DisableWithdrawals.to_vec()?;
871 let accounts = vec![
872 AccountMeta::new_readonly(*admin_address, true),
873 AccountMeta::new_readonly(fund_ref, false),
874 AccountMeta::new(fund.info_account, false),
875 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
876 ];
877
878 Ok(Instruction {
879 program_id: fund.fund_program_id,
880 data,
881 accounts,
882 })
883 }
884
885 pub fn new_instruction_request_withdrawal_fund(
887 &self,
888 wallet_address: &Pubkey,
889 fund_name: &str,
890 token_name: &str,
891 ui_amount: f64,
892 ) -> Result<Instruction, FarmClientError> {
893 if ui_amount < 0.0 {
894 return Err(FarmClientError::ValueError(format!(
895 "Invalid withdrawal amount {} specified for Fund {}: Must be greater or equal to zero.",
896 ui_amount, fund_name
897 )));
898 }
899 let fund = self.get_fund(fund_name)?;
901 let fund_ref = self.get_fund_ref(fund_name)?;
902 let fund_token = self.get_token_by_ref(&fund.fund_token_ref)?;
903 let token = self.get_token(token_name)?;
904 let token_ref = self.get_token_ref(token_name)?;
905 let user_info_account = self.get_fund_user_info_account(wallet_address, fund_name)?;
906 let user_requests_account =
907 self.get_fund_user_requests_account(wallet_address, fund_name, token_name)?;
908 let user_withdrawal_token_account =
909 self.get_associated_token_address(wallet_address, token.name.as_str())?;
910 let user_fund_token_account =
911 self.get_associated_token_address(wallet_address, fund_token.name.as_str())?;
912 let custody_metadata =
913 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::DepositWithdraw)?;
914 let custody_token_account = self.get_fund_custody_token_account(
915 fund_name,
916 token_name,
917 FundCustodyType::DepositWithdraw,
918 )?;
919 let custody_fees_token_account = self.get_fund_custody_fees_token_account(
920 fund_name,
921 token_name,
922 FundCustodyType::DepositWithdraw,
923 )?;
924 let (_, oracle_account) = self.get_oracle(token_name)?;
925
926 let data = FundInstruction::RequestWithdrawal {
928 amount: self.to_token_amount(ui_amount, &fund_token)?,
929 }
930 .to_vec()?;
931 let accounts = vec![
932 AccountMeta::new_readonly(*wallet_address, true),
933 AccountMeta::new_readonly(fund_ref, false),
934 AccountMeta::new(fund.info_account, false),
935 AccountMeta::new_readonly(fund.fund_authority, false),
936 AccountMeta::new_readonly(spl_token::id(), false),
937 AccountMeta::new(fund_token.mint, false),
938 AccountMeta::new(user_info_account, false),
939 AccountMeta::new(user_requests_account, false),
940 AccountMeta::new(user_withdrawal_token_account, false),
941 AccountMeta::new(user_fund_token_account, false),
942 AccountMeta::new(custody_token_account, false),
943 AccountMeta::new(custody_fees_token_account, false),
944 AccountMeta::new_readonly(custody_metadata, false),
945 AccountMeta::new_readonly(token_ref, false),
946 AccountMeta::new_readonly(oracle_account.unwrap_or_else(zero::id), false),
947 ];
948
949 Ok(Instruction {
950 program_id: fund.fund_program_id,
951 data,
952 accounts,
953 })
954 }
955
956 pub fn new_instruction_cancel_withdrawal_fund(
958 &self,
959 wallet_address: &Pubkey,
960 fund_name: &str,
961 token_name: &str,
962 ) -> Result<Instruction, FarmClientError> {
963 let fund = self.get_fund(fund_name)?;
965 let fund_ref = self.get_fund_ref(fund_name)?;
966 let token = self.get_token(token_name)?;
967 let token_ref = self.get_token_ref(token_name)?;
968 let user_requests_account =
969 self.get_fund_user_requests_account(wallet_address, fund_name, token_name)?;
970 let user_withdrawal_token_account =
971 self.get_associated_token_address(wallet_address, token.name.as_str())?;
972
973 let data = FundInstruction::CancelWithdrawal.to_vec()?;
975 let accounts = vec![
976 AccountMeta::new_readonly(*wallet_address, true),
977 AccountMeta::new_readonly(fund_ref, false),
978 AccountMeta::new(fund.info_account, false),
979 AccountMeta::new_readonly(spl_token::id(), false),
980 AccountMeta::new(user_requests_account, false),
981 AccountMeta::new(user_withdrawal_token_account, false),
982 AccountMeta::new_readonly(token_ref, false),
983 ];
984
985 Ok(Instruction {
986 program_id: fund.fund_program_id,
987 data,
988 accounts,
989 })
990 }
991
992 pub fn new_instruction_approve_withdrawal_fund(
994 &self,
995 admin_address: &Pubkey,
996 user_address: &Pubkey,
997 fund_name: &str,
998 token_name: &str,
999 ui_amount: f64,
1000 ) -> Result<Instruction, FarmClientError> {
1001 if ui_amount < 0.0 {
1002 return Err(FarmClientError::ValueError(format!(
1003 "Invalid approve amount {} specified for Fund {}: Must be greater or equal to zero.",
1004 ui_amount, fund_name
1005 )));
1006 }
1007 let fund = self.get_fund(fund_name)?;
1009 let fund_ref = self.get_fund_ref(fund_name)?;
1010 let fund_token = self.get_token_by_ref(&fund.fund_token_ref)?;
1011 let token = self.get_token(token_name)?;
1012 let token_ref = self.get_token_ref(token_name)?;
1013 let user_info_account = self.get_fund_user_info_account(user_address, fund_name)?;
1014 let user_requests_account =
1015 self.get_fund_user_requests_account(user_address, fund_name, token_name)?;
1016 let user_withdrawal_token_account =
1017 self.get_associated_token_address(user_address, token.name.as_str())?;
1018 let user_fund_token_account =
1019 self.get_associated_token_address(user_address, fund_token.name.as_str())?;
1020 let custody_metadata =
1021 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::DepositWithdraw)?;
1022 let custody_token_account = self.get_fund_custody_token_account(
1023 fund_name,
1024 token_name,
1025 FundCustodyType::DepositWithdraw,
1026 )?;
1027 let custody_fees_token_account = self.get_fund_custody_fees_token_account(
1028 fund_name,
1029 token_name,
1030 FundCustodyType::DepositWithdraw,
1031 )?;
1032 let (_, oracle_account) = self.get_oracle(token_name)?;
1033
1034 let data = FundInstruction::ApproveWithdrawal {
1036 amount: self.to_token_amount(ui_amount, &fund_token)?,
1037 }
1038 .to_vec()?;
1039 let accounts = vec![
1040 AccountMeta::new_readonly(*admin_address, true),
1041 AccountMeta::new_readonly(fund_ref, false),
1042 AccountMeta::new(fund.info_account, false),
1043 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
1044 AccountMeta::new_readonly(fund.fund_authority, false),
1045 AccountMeta::new_readonly(spl_token::id(), false),
1046 AccountMeta::new(fund_token.mint, false),
1047 AccountMeta::new_readonly(*user_address, false),
1048 AccountMeta::new(user_info_account, false),
1049 AccountMeta::new(user_requests_account, false),
1050 AccountMeta::new(user_withdrawal_token_account, false),
1051 AccountMeta::new(user_fund_token_account, false),
1052 AccountMeta::new(custody_token_account, false),
1053 AccountMeta::new(custody_fees_token_account, false),
1054 AccountMeta::new_readonly(custody_metadata, false),
1055 AccountMeta::new_readonly(token_ref, false),
1056 AccountMeta::new_readonly(oracle_account.unwrap_or_else(zero::id), false),
1057 ];
1058
1059 Ok(Instruction {
1060 program_id: fund.fund_program_id,
1061 data,
1062 accounts,
1063 })
1064 }
1065
1066 pub fn new_instruction_deny_withdrawal_fund(
1068 &self,
1069 admin_address: &Pubkey,
1070 user_address: &Pubkey,
1071 fund_name: &str,
1072 token_name: &str,
1073 deny_reason: &str,
1074 ) -> Result<Instruction, FarmClientError> {
1075 let fund = self.get_fund(fund_name)?;
1077 let fund_ref = self.get_fund_ref(fund_name)?;
1078 let token_ref = self.get_token_ref(token_name)?;
1079 let user_requests_account =
1080 self.get_fund_user_requests_account(user_address, fund_name, token_name)?;
1081
1082 let data = FundInstruction::DenyWithdrawal {
1084 deny_reason: str_to_as64(deny_reason)?,
1085 }
1086 .to_vec()?;
1087 let accounts = vec![
1088 AccountMeta::new_readonly(*admin_address, true),
1089 AccountMeta::new_readonly(fund_ref, false),
1090 AccountMeta::new(fund.info_account, false),
1091 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
1092 AccountMeta::new_readonly(*user_address, false),
1093 AccountMeta::new(user_requests_account, false),
1094 AccountMeta::new_readonly(token_ref, false),
1095 ];
1096
1097 Ok(Instruction {
1098 program_id: fund.fund_program_id,
1099 data,
1100 accounts,
1101 })
1102 }
1103
1104 pub fn new_instruction_lock_assets_fund(
1106 &self,
1107 admin_address: &Pubkey,
1108 fund_name: &str,
1109 token_name: &str,
1110 ui_amount: f64,
1111 ) -> Result<Instruction, FarmClientError> {
1112 if ui_amount < 0.0 {
1113 return Err(FarmClientError::ValueError(format!(
1114 "Invalid lock amount {} specified for Fund {}: Must be greater or equal to zero.",
1115 ui_amount, fund_name
1116 )));
1117 }
1118 let fund = self.get_fund(fund_name)?;
1120 let fund_ref = self.get_fund_ref(fund_name)?;
1121 let token = self.get_token(token_name)?;
1122 let token_ref = self.get_token_ref(token_name)?;
1123 let wd_custody_metadata =
1124 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::DepositWithdraw)?;
1125 let wd_custody_token_account = self.get_fund_custody_token_account(
1126 fund_name,
1127 token_name,
1128 FundCustodyType::DepositWithdraw,
1129 )?;
1130 let trading_custody_metadata =
1131 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::Trading)?;
1132 let trading_custody_token_account =
1133 self.get_fund_custody_token_account(fund_name, token_name, FundCustodyType::Trading)?;
1134
1135 let data = FundInstruction::LockAssets {
1137 amount: self.to_token_amount(ui_amount, &token)?,
1138 }
1139 .to_vec()?;
1140 let accounts = vec![
1141 AccountMeta::new_readonly(*admin_address, true),
1142 AccountMeta::new_readonly(fund_ref, false),
1143 AccountMeta::new(fund.info_account, false),
1144 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
1145 AccountMeta::new_readonly(fund.fund_authority, false),
1146 AccountMeta::new_readonly(spl_token::id(), false),
1147 AccountMeta::new(wd_custody_token_account, false),
1148 AccountMeta::new(wd_custody_metadata, false),
1149 AccountMeta::new(trading_custody_token_account, false),
1150 AccountMeta::new(trading_custody_metadata, false),
1151 AccountMeta::new_readonly(token_ref, false),
1152 ];
1153
1154 Ok(Instruction {
1155 program_id: fund.fund_program_id,
1156 data,
1157 accounts,
1158 })
1159 }
1160
1161 pub fn new_instruction_unlock_assets_fund(
1163 &self,
1164 admin_address: &Pubkey,
1165 fund_name: &str,
1166 token_name: &str,
1167 ui_amount: f64,
1168 ) -> Result<Instruction, FarmClientError> {
1169 if ui_amount < 0.0 {
1170 return Err(FarmClientError::ValueError(format!(
1171 "Invalid unlock amount {} specified for Fund {}: Must be greater or equal to zero.",
1172 ui_amount, fund_name
1173 )));
1174 }
1175 let fund = self.get_fund(fund_name)?;
1177 let fund_ref = self.get_fund_ref(fund_name)?;
1178 let token = self.get_token(token_name)?;
1179 let token_ref = self.get_token_ref(token_name)?;
1180 let wd_custody_metadata =
1181 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::DepositWithdraw)?;
1182 let wd_custody_token_account = self.get_fund_custody_token_account(
1183 fund_name,
1184 token_name,
1185 FundCustodyType::DepositWithdraw,
1186 )?;
1187 let trading_custody_metadata =
1188 self.get_fund_custody_account(fund_name, token_name, FundCustodyType::Trading)?;
1189 let trading_custody_token_account =
1190 self.get_fund_custody_token_account(fund_name, token_name, FundCustodyType::Trading)?;
1191
1192 let data = FundInstruction::UnlockAssets {
1194 amount: self.to_token_amount(ui_amount, &token)?,
1195 }
1196 .to_vec()?;
1197 let accounts = vec![
1198 AccountMeta::new_readonly(*admin_address, true),
1199 AccountMeta::new_readonly(fund_ref, false),
1200 AccountMeta::new(fund.info_account, false),
1201 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
1202 AccountMeta::new_readonly(fund.fund_authority, false),
1203 AccountMeta::new_readonly(spl_token::id(), false),
1204 AccountMeta::new(wd_custody_token_account, false),
1205 AccountMeta::new(wd_custody_metadata, false),
1206 AccountMeta::new(trading_custody_token_account, false),
1207 AccountMeta::new(trading_custody_metadata, false),
1208 AccountMeta::new_readonly(token_ref, false),
1209 ];
1210
1211 Ok(Instruction {
1212 program_id: fund.fund_program_id,
1213 data,
1214 accounts,
1215 })
1216 }
1217
1218 pub fn new_instruction_start_liquidation_fund(
1220 &self,
1221 wallet_address: &Pubkey,
1222 fund_name: &str,
1223 ) -> Result<Instruction, FarmClientError> {
1224 let fund = self.get_fund(fund_name)?;
1226 let fund_ref = self.get_fund_ref(fund_name)?;
1227 let fund_token = self.get_token_by_ref(&fund.fund_token_ref)?;
1228 let user_info_account = self.get_fund_user_info_account(wallet_address, fund_name)?;
1229 let user_fund_token_account =
1230 self.get_associated_token_address(wallet_address, fund_token.name.as_str())?;
1231
1232 let data = FundInstruction::StartLiquidation.to_vec()?;
1234 let accounts = vec![
1235 AccountMeta::new_readonly(*wallet_address, true),
1236 AccountMeta::new_readonly(fund_ref, false),
1237 AccountMeta::new(fund.info_account, false),
1238 AccountMeta::new(fund_token.mint, false),
1239 AccountMeta::new_readonly(user_info_account, false),
1240 AccountMeta::new_readonly(user_fund_token_account, false),
1241 AccountMeta::new_readonly(sysvar::instructions::id(), false),
1242 ];
1243
1244 Ok(Instruction {
1245 program_id: fund.fund_program_id,
1246 data,
1247 accounts,
1248 })
1249 }
1250
1251 pub fn new_instruction_stop_liquidation_fund(
1253 &self,
1254 admin_address: &Pubkey,
1255 fund_name: &str,
1256 ) -> Result<Instruction, FarmClientError> {
1257 let fund = self.get_fund(fund_name)?;
1259 let fund_ref = self.get_fund_ref(fund_name)?;
1260
1261 let data = FundInstruction::StopLiquidation.to_vec()?;
1263 let accounts = vec![
1264 AccountMeta::new_readonly(*admin_address, true),
1265 AccountMeta::new_readonly(fund_ref, false),
1266 AccountMeta::new(fund.info_account, false),
1267 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
1268 ];
1269
1270 Ok(Instruction {
1271 program_id: fund.fund_program_id,
1272 data,
1273 accounts,
1274 })
1275 }
1276
1277 pub fn new_instruction_withdraw_fees_fund(
1279 &self,
1280 wallet_address: &Pubkey,
1281 fund_name: &str,
1282 token_name: &str,
1283 custody_type: FundCustodyType,
1284 ui_amount: f64,
1285 receiver: &Pubkey,
1286 ) -> Result<Instruction, FarmClientError> {
1287 let fund = self.get_fund(fund_name)?;
1289 let fund_ref = self.get_fund_ref(fund_name)?;
1290
1291 let custody_fees_token_account =
1293 self.get_fund_custody_fees_token_account(fund_name, token_name, custody_type)?;
1294
1295 let token = self.get_token(token_name)?;
1297 let data = FundInstruction::WithdrawFees {
1298 amount: self.ui_amount_to_tokens_with_decimals(ui_amount, token.decimals)?,
1299 }
1300 .to_vec()?;
1301 let accounts = vec![
1302 AccountMeta::new_readonly(*wallet_address, true),
1303 AccountMeta::new_readonly(fund_ref, false),
1304 AccountMeta::new(fund.info_account, false),
1305 AccountMeta::new(self.get_fund_active_multisig_account(fund_name)?, false),
1306 AccountMeta::new(self.get_fund_multisig_account(fund_name)?, false),
1307 AccountMeta::new_readonly(spl_token::id(), false),
1308 AccountMeta::new(custody_fees_token_account, false),
1309 AccountMeta::new(*receiver, false),
1310 ];
1311
1312 Ok(Instruction {
1313 program_id: fund.fund_program_id,
1314 data,
1315 accounts,
1316 })
1317 }
1318
1319 pub fn new_instruction_update_fund_assets_with_custody(
1321 &self,
1322 wallet_address: &Pubkey,
1323 fund_name: &str,
1324 custody_id: u32,
1325 ) -> Result<Instruction, FarmClientError> {
1326 let fund = self.get_fund(fund_name)?;
1328 let fund_ref = self.get_fund_ref(fund_name)?;
1329
1330 let custodies = self.get_fund_custodies(fund_name)?;
1332 let custody = custodies
1333 .iter()
1334 .find(|&c| c.custody_id == custody_id)
1335 .ok_or_else(|| {
1336 FarmClientError::RecordNotFound(format!("Custody with ID {}", custody_id))
1337 })?;
1338 let token = self.get_token_by_ref(&custody.token_ref)?;
1339 let custody_metadata =
1340 self.get_fund_custody_account(fund_name, token.name.as_str(), custody.custody_type)?;
1341 let custodies_assets_account =
1342 self.get_fund_assets_account(fund_name, FundAssetType::Custody)?;
1343 let vaults_assets_account =
1344 self.get_fund_assets_account(fund_name, FundAssetType::Vault)?;
1345 let custody_token_account = self.get_fund_custody_token_account(
1346 fund_name,
1347 token.name.as_str(),
1348 custody.custody_type,
1349 )?;
1350 let (_, oracle_account) = self.get_oracle(&token.name)?;
1351
1352 let accounts = vec![
1354 AccountMeta::new_readonly(*wallet_address, true),
1355 AccountMeta::new_readonly(fund_ref, false),
1356 AccountMeta::new(fund.info_account, false),
1357 AccountMeta::new(custodies_assets_account, false),
1358 AccountMeta::new_readonly(vaults_assets_account, false),
1359 AccountMeta::new(custody_token_account, false),
1360 AccountMeta::new(custody_metadata, false),
1361 AccountMeta::new_readonly(custody.token_ref, false),
1362 AccountMeta::new_readonly(oracle_account.unwrap_or_else(zero::id), false),
1363 ];
1364
1365 Ok(Instruction {
1366 program_id: fund.fund_program_id,
1367 data: FundInstruction::UpdateAssetsWithCustody.to_vec()?,
1368 accounts,
1369 })
1370 }
1371
1372 pub fn new_instruction_update_fund_assets_with_vault(
1374 &self,
1375 wallet_address: &Pubkey,
1376 fund_name: &str,
1377 vault_id: u32,
1378 ) -> Result<Instruction, FarmClientError> {
1379 let fund = self.get_fund(fund_name)?;
1381 let fund_ref = self.get_fund_ref(fund_name)?;
1382
1383 let vaults = self.get_fund_vaults(fund_name)?;
1385 let vault = vaults
1386 .iter()
1387 .find(|&c| c.vault_id == vault_id)
1388 .ok_or_else(|| {
1389 FarmClientError::RecordNotFound(format!("Fund Vault with ID {}", vault_id))
1390 })?;
1391 if vault.vault_type == FundVaultType::Farm {
1392 return Err(FarmClientError::ValueError(
1393 "Nothing to do: Farms are not processed to avoid double counting".to_string(),
1394 ));
1395 }
1396 let vault_name = match vault.vault_type {
1397 FundVaultType::Vault => self.get_vault_by_ref(&vault.vault_ref)?.name,
1398 FundVaultType::Pool => self.get_pool_by_ref(&vault.vault_ref)?.name,
1399 FundVaultType::Farm => unreachable!(),
1400 };
1401 let token_names = match vault.vault_type {
1402 FundVaultType::Vault => self.get_vault_token_names(&vault_name)?,
1403 FundVaultType::Pool => self.get_pool_token_names(&vault_name)?,
1404 FundVaultType::Farm => unreachable!(),
1405 };
1406 let target_vault_metadata = match vault.vault_type {
1407 FundVaultType::Vault => self.get_vault_ref(&vault_name)?,
1408 FundVaultType::Pool => self.get_pool_ref(&vault_name)?,
1409 FundVaultType::Farm => unreachable!(),
1410 };
1411 let underlying_pool_ref = if vault.vault_type == FundVaultType::Vault {
1412 match self.get_vault(&vault_name)?.strategy {
1413 VaultStrategy::StakeLpCompoundRewards { pool_ref, .. } => pool_ref,
1414 _ => unreachable!(),
1415 }
1416 } else {
1417 target_vault_metadata
1418 };
1419 let underlying_pool = self.get_pool_by_ref(&underlying_pool_ref)?;
1420 let underlying_lp_token = self.get_token_by_ref(
1421 &underlying_pool
1422 .lp_token_ref
1423 .ok_or(ProgramError::UninitializedAccount)?,
1424 )?;
1425 let (amm_id, amm_open_orders) = match underlying_pool.route {
1426 PoolRoute::Raydium {
1427 amm_id,
1428 amm_open_orders,
1429 ..
1430 } => (amm_id, amm_open_orders),
1431 PoolRoute::Orca { amm_id, .. } => (amm_id, zero::id()),
1432 _ => {
1433 return Err(FarmClientError::ValueError(
1434 "Unsupported pool route".to_string(),
1435 ));
1436 }
1437 };
1438 let vault_metadata =
1439 self.get_fund_vault_account(fund_name, vault_name.as_str(), vault.vault_type)?;
1440 let custodies_assets_account =
1441 self.get_fund_assets_account(fund_name, FundAssetType::Custody)?;
1442 let vaults_assets_account =
1443 self.get_fund_assets_account(fund_name, FundAssetType::Vault)?;
1444 let (_, oracle_account_token_a) = if token_names.0.is_empty() {
1445 (OracleType::Unsupported, None)
1446 } else {
1447 self.get_oracle(&token_names.0)?
1448 };
1449 let (_, oracle_account_token_b) = if token_names.1.is_empty() {
1450 (OracleType::Unsupported, None)
1451 } else {
1452 self.get_oracle(&token_names.1)?
1453 };
1454
1455 let accounts = vec![
1457 AccountMeta::new_readonly(*wallet_address, true),
1458 AccountMeta::new_readonly(fund_ref, false),
1459 AccountMeta::new(fund.info_account, false),
1460 AccountMeta::new_readonly(custodies_assets_account, false),
1461 AccountMeta::new(vaults_assets_account, false),
1462 AccountMeta::new(vault_metadata, false),
1463 AccountMeta::new_readonly(vault.vault_ref, false),
1464 AccountMeta::new_readonly(underlying_pool_ref, false),
1465 AccountMeta::new_readonly(
1466 underlying_pool
1467 .token_a_ref
1468 .ok_or(ProgramError::UninitializedAccount)?,
1469 false,
1470 ),
1471 AccountMeta::new_readonly(
1472 underlying_pool
1473 .token_b_ref
1474 .ok_or(ProgramError::UninitializedAccount)?,
1475 false,
1476 ),
1477 AccountMeta::new_readonly(underlying_lp_token.mint, false),
1478 AccountMeta::new_readonly(
1479 underlying_pool
1480 .token_a_account
1481 .ok_or(ProgramError::UninitializedAccount)?,
1482 false,
1483 ),
1484 AccountMeta::new_readonly(
1485 underlying_pool
1486 .token_b_account
1487 .ok_or(ProgramError::UninitializedAccount)?,
1488 false,
1489 ),
1490 AccountMeta::new_readonly(amm_id, false),
1491 AccountMeta::new_readonly(amm_open_orders, false),
1492 AccountMeta::new_readonly(oracle_account_token_a.unwrap_or_else(zero::id), false),
1493 AccountMeta::new_readonly(oracle_account_token_b.unwrap_or_else(zero::id), false),
1494 AccountMeta::new_readonly(sysvar::instructions::id(), false),
1495 ];
1496
1497 Ok(Instruction {
1498 program_id: fund.fund_program_id,
1499 data: FundInstruction::UpdateAssetsWithVault.to_vec()?,
1500 accounts,
1501 })
1502 }
1503
1504 pub fn all_instructions_request_deposit_fund(
1506 &self,
1507 wallet_address: &Pubkey,
1508 fund_name: &str,
1509 token_name: &str,
1510 ui_amount: f64,
1511 ) -> Result<Vec<Instruction>, FarmClientError> {
1512 let mut inst = Vec::<Instruction>::new();
1513 let _ =
1514 self.check_fund_accounts(wallet_address, fund_name, token_name, ui_amount, &mut inst)?;
1515
1516 inst.push(self.new_instruction_request_deposit_fund(
1518 wallet_address,
1519 fund_name,
1520 token_name,
1521 ui_amount,
1522 )?);
1523
1524 Ok(inst)
1525 }
1526
1527 pub fn all_instructions_request_withdrawal_fund(
1529 &self,
1530 wallet_address: &Pubkey,
1531 fund_name: &str,
1532 token_name: &str,
1533 ui_amount: f64,
1534 ) -> Result<Vec<Instruction>, FarmClientError> {
1535 let mut inst = Vec::<Instruction>::new();
1536 let fund = self.get_fund(fund_name)?;
1537 let fund_token = Some(self.get_token_by_ref(&fund.fund_token_ref)?);
1538 let asset_token = Some(self.get_token(token_name)?);
1539 let _ = self.check_token_account(wallet_address, &fund_token, ui_amount, &mut inst)?;
1540 let _ = self.check_token_account(wallet_address, &asset_token, 0.0, &mut inst)?;
1541
1542 if self
1543 .get_fund_user_requests(wallet_address, fund_name, token_name)
1544 .is_err()
1545 {
1546 inst.push(self.new_instruction_user_init_fund(
1547 wallet_address,
1548 fund_name,
1549 token_name,
1550 )?);
1551 }
1552
1553 inst.push(self.new_instruction_request_withdrawal_fund(
1555 wallet_address,
1556 fund_name,
1557 token_name,
1558 ui_amount,
1559 )?);
1560
1561 Ok(inst)
1562 }
1563}