1#![cfg(not(feature = "no-entrypoint"))]
4
5solana_security_txt::security_txt! {
6 name: "Solana Farms",
7 project_url: "https://github.com/solana-labs/solana-program-library/tree/master/farms",
8 contacts: "email:solana.farms@protonmail.com",
9 policy: "",
10 preferred_languages: "en",
11 auditors: "Halborn"
12}
13
14use {
15 crate::{
16 fund_info::FundInfo,
17 instructions::{
18 add_custody::add_custody, add_vault::add_vault, approve_deposit::approve_deposit,
19 approve_withdrawal::approve_withdrawal, cancel_deposit::cancel_deposit,
20 cancel_withdrawal::cancel_withdrawal, deny_deposit::deny_deposit,
21 deny_withdrawal::deny_withdrawal, disable_deposits::disable_deposits,
22 disable_withdrawals::disable_withdrawals, init::init, lock_assets::lock_assets, orca,
23 raydium, remove_custody::remove_custody, remove_multisig::remove_multisig,
24 remove_vault::remove_vault, request_deposit::request_deposit,
25 request_withdrawal::request_withdrawal, set_admin_signers::set_admin_signers,
26 set_assets_tracking_config::set_assets_tracking_config,
27 set_deposit_schedule::set_deposit_schedule,
28 set_withdrawal_schedule::set_withdrawal_schedule, start_liquidation::start_liquidation,
29 stop_liquidation::stop_liquidation, unlock_assets::unlock_assets,
30 update_assets_with_custody::update_assets_with_custody,
31 update_assets_with_vault::update_assets_with_vault, user_init::user_init,
32 withdraw_fees::withdraw_fees,
33 },
34 },
35 solana_farm_sdk::{
36 error::FarmError,
37 fund::Fund,
38 id::{main_router, main_router_admin, main_router_multisig},
39 instruction::{amm::AmmInstruction, fund::FundInstruction, vault::VaultInstruction},
40 log::sol_log_params_short,
41 program::{account, multisig},
42 refdb,
43 string::ArrayString64,
44 },
45 solana_program::{
46 account_info::{next_account_info, AccountInfo},
47 entrypoint,
48 entrypoint::ProgramResult,
49 log::sol_log_compute_units,
50 msg,
51 program_error::ProgramError,
52 pubkey::Pubkey,
53 },
54};
55
56fn log_start(instruction: &str, fund_name: &ArrayString64) {
57 msg!(
58 "Processing FundInstruction::{} for {}",
59 instruction,
60 fund_name.as_str()
61 );
62 sol_log_compute_units();
63}
64
65fn log_end(fund_name: &ArrayString64) {
66 sol_log_compute_units();
67 msg!("Fund {} end of instruction", fund_name.as_str());
68}
69
70fn check_admin_authority(
71 accounts: &[AccountInfo],
72 instruction_data: &[u8],
73 fund: &Fund,
74) -> Result<bool, ProgramError> {
75 let account_info_iter = &mut accounts.iter();
76 let admin_account = next_account_info(account_info_iter)?;
77 let _fund_metadata = next_account_info(account_info_iter)?;
78 let _fund_info_account = next_account_info(account_info_iter)?;
79 let multisig_account = next_account_info(account_info_iter)?;
80
81 if multisig_account.key != &fund.multisig_account
82 && multisig_account.key != &main_router_multisig::id()
83 {
84 msg!("Error: Invalid multisig account");
85 return Err(FarmError::IncorrectAccountAddress.into());
86 }
87
88 let signatures_left = multisig::sign_multisig(
89 multisig_account,
90 admin_account,
91 &main_router_admin::id(),
92 &accounts[1..],
93 instruction_data,
94 )?;
95 if signatures_left > 0 {
96 msg!(
97 "Instruction has been signed but more signatures are required: {}",
98 signatures_left
99 );
100 return Ok(false);
101 }
102
103 Ok(true)
104}
105
106fn check_manager_authority(user_account: &AccountInfo, fund: &Fund) -> ProgramResult {
107 if user_account.key != &fund.fund_manager {
108 msg!(
109 "Error: Instruction must be performed by the fund manager {}",
110 fund.fund_manager
111 );
112 Err(ProgramError::IllegalOwner)
113 } else if !user_account.is_signer {
114 Err(ProgramError::MissingRequiredSignature)
115 } else {
116 Ok(())
117 }
118}
119
120fn check_manager_authority_or_admin(
121 user_account: &AccountInfo,
122 multisig_account: &AccountInfo,
123 fund: &Fund,
124) -> ProgramResult {
125 if user_account.key != &fund.fund_manager
126 && !multisig::is_signer(multisig_account, &main_router_admin::id(), user_account.key)?
127 {
128 msg!("Error: Instruction must be performed by the fund manager or one of admin signers",);
129 Err(ProgramError::IllegalOwner)
130 } else if !user_account.is_signer {
131 Err(ProgramError::MissingRequiredSignature)
132 } else {
133 Ok(())
134 }
135}
136
137fn check_manager_authority_or_liquidation(
138 user_account: &AccountInfo,
139 fund_info_account: &AccountInfo,
140 fund: &Fund,
141) -> ProgramResult {
142 if FundInfo::new(fund_info_account).get_liquidation_start_time()? > 0 {
143 if !user_account.is_signer {
144 return Err(ProgramError::MissingRequiredSignature);
145 } else {
146 return Ok(());
147 }
148 }
149 check_manager_authority(user_account, fund)
150}
151
152fn check_manager_authority_or_admin_or_liquidation(
153 user_account: &AccountInfo,
154 fund_info_account: &AccountInfo,
155 multisig_account: &AccountInfo,
156 fund: &Fund,
157) -> ProgramResult {
158 if FundInfo::new(fund_info_account).get_liquidation_start_time()? > 0 {
159 if !user_account.is_signer {
160 return Err(ProgramError::MissingRequiredSignature);
161 } else {
162 return Ok(());
163 }
164 }
165 check_manager_authority_or_admin(user_account, multisig_account, fund)
166}
167
168entrypoint!(process_instruction);
169pub fn process_instruction(
176 program_id: &Pubkey,
177 accounts: &[AccountInfo],
178 instruction_data: &[u8],
179) -> ProgramResult {
180 msg!("Fund entrypoint");
181 if cfg!(feature = "debug") {
182 sol_log_params_short(accounts, instruction_data);
183 }
184
185 let account_info_iter = &mut accounts.iter();
186 let user_account = next_account_info(account_info_iter)?;
187 let fund_metadata = next_account_info(account_info_iter)?;
188 let fund_info_account = next_account_info(account_info_iter)?;
189
190 let fund = account::unpack::<Fund>(fund_metadata, "Fund")?;
192 let derived_fund_metadata =
193 refdb::find_target_pda_with_bump(refdb::StorageType::Fund, &fund.name, fund.metadata_bump)?;
194 if &fund.info_account != fund_info_account.key
195 || &derived_fund_metadata != fund_metadata.key
196 || fund_metadata.owner != &main_router::id()
197 {
198 msg!("Error: Invalid Fund accounts");
199 return Err(ProgramError::Custom(511));
200 }
201 if &fund.fund_program_id != program_id {
202 msg!("Error: Invalid Fund program id");
203 return Err(ProgramError::IncorrectProgramId);
204 }
205
206 let instruction = FundInstruction::unpack(instruction_data)?;
208
209 match instruction {
210 FundInstruction::UserInit => {
211 log_start("UserInit", &fund.name);
212 user_init(&fund, accounts)?;
213 }
214 FundInstruction::RequestDeposit { amount } => {
215 log_start("RequestDeposit", &fund.name);
216 request_deposit(&fund, accounts, amount)?;
217 }
218 FundInstruction::CancelDeposit => {
219 log_start("CancelDeposit", &fund.name);
220 cancel_deposit(&fund, accounts)?;
221 }
222 FundInstruction::RequestWithdrawal { amount } => {
223 log_start("RequestWithdrawal", &fund.name);
224 request_withdrawal(&fund, accounts, amount)?;
225 }
226 FundInstruction::CancelWithdrawal => {
227 log_start("CancelWithdrawal", &fund.name);
228 cancel_withdrawal(&fund, accounts)?;
229 }
230 FundInstruction::Init { step } => {
231 log_start("Init", &fund.name);
232 if check_admin_authority(accounts, instruction_data, &fund)? {
233 init(&fund, accounts, step)?;
234 }
235 }
236 FundInstruction::SetDepositSchedule { schedule } => {
237 log_start("SetDepositSchedule", &fund.name);
238 check_manager_authority_or_admin(
239 user_account,
240 next_account_info(account_info_iter)?,
241 &fund,
242 )?;
243 set_deposit_schedule(
244 &fund,
245 &mut FundInfo::new(fund_info_account),
246 accounts,
247 &schedule,
248 )?;
249 }
250 FundInstruction::DisableDeposits => {
251 log_start("DisableDeposits", &fund.name);
252 check_manager_authority_or_admin(
253 user_account,
254 next_account_info(account_info_iter)?,
255 &fund,
256 )?;
257 disable_deposits(&fund, &mut FundInfo::new(fund_info_account), accounts)?;
258 }
259 FundInstruction::ApproveDeposit { amount } => {
260 log_start("ApproveDeposit", &fund.name);
261 check_manager_authority_or_admin(
262 user_account,
263 next_account_info(account_info_iter)?,
264 &fund,
265 )?;
266 approve_deposit(&fund, accounts, amount)?;
267 }
268 FundInstruction::DenyDeposit { deny_reason } => {
269 log_start("DenyDeposit", &fund.name);
270 check_manager_authority_or_admin(
271 user_account,
272 next_account_info(account_info_iter)?,
273 &fund,
274 )?;
275 deny_deposit(&fund, accounts, &deny_reason)?;
276 }
277 FundInstruction::SetWithdrawalSchedule { schedule } => {
278 log_start("SetWithdrawalSchedule", &fund.name);
279 check_manager_authority_or_admin(
280 user_account,
281 next_account_info(account_info_iter)?,
282 &fund,
283 )?;
284 set_withdrawal_schedule(
285 &fund,
286 &mut FundInfo::new(fund_info_account),
287 accounts,
288 &schedule,
289 )?;
290 }
291 FundInstruction::DisableWithdrawals => {
292 log_start("DisableWithdrawals", &fund.name);
293 check_manager_authority_or_admin(
294 user_account,
295 next_account_info(account_info_iter)?,
296 &fund,
297 )?;
298 disable_withdrawals(&fund, &mut FundInfo::new(fund_info_account), accounts)?;
299 }
300 FundInstruction::ApproveWithdrawal { amount } => {
301 log_start("ApproveWithdrawal", &fund.name);
302 check_manager_authority_or_admin(
303 user_account,
304 next_account_info(account_info_iter)?,
305 &fund,
306 )?;
307 approve_withdrawal(&fund, accounts, amount)?;
308 }
309 FundInstruction::DenyWithdrawal { deny_reason } => {
310 log_start("DenyWithdrawal", &fund.name);
311 check_manager_authority_or_admin(
312 user_account,
313 next_account_info(account_info_iter)?,
314 &fund,
315 )?;
316 deny_withdrawal(&fund, accounts, &deny_reason)?;
317 }
318 FundInstruction::LockAssets { amount } => {
319 log_start("LockAssets", &fund.name);
320 check_manager_authority_or_admin(
321 user_account,
322 next_account_info(account_info_iter)?,
323 &fund,
324 )?;
325 lock_assets(&fund, accounts, amount)?;
326 }
327 FundInstruction::UnlockAssets { amount } => {
328 log_start("UnlockAssets", &fund.name);
329 check_manager_authority_or_admin_or_liquidation(
330 user_account,
331 fund_info_account,
332 next_account_info(account_info_iter)?,
333 &fund,
334 )?;
335 unlock_assets(&fund, accounts, amount)?;
336 }
337 FundInstruction::SetAssetsTrackingConfig { config } => {
338 log_start("SetAssetsTrackingConfig", &fund.name);
339 if check_admin_authority(accounts, instruction_data, &fund)? {
340 set_assets_tracking_config(
341 &fund,
342 &mut FundInfo::new(fund_info_account),
343 accounts,
344 &config,
345 )?;
346 }
347 }
348 FundInstruction::UpdateAssetsWithVault => {
349 log_start("UpdateAssetsWithVault", &fund.name);
350 update_assets_with_vault(&fund, accounts)?;
351 }
352 FundInstruction::UpdateAssetsWithCustody => {
353 log_start("UpdateAssetsWithCustody", &fund.name);
354 update_assets_with_custody(&fund, accounts)?;
355 }
356 FundInstruction::AddVault {
357 target_hash,
358 vault_id,
359 vault_type,
360 } => {
361 log_start("AddVault", &fund.name);
362 if check_admin_authority(accounts, instruction_data, &fund)? {
363 add_vault(&fund, accounts, target_hash, vault_id, vault_type)?;
364 }
365 }
366 FundInstruction::RemoveVault {
367 target_hash,
368 vault_type,
369 } => {
370 log_start("RemoveVault", &fund.name);
371 if check_admin_authority(accounts, instruction_data, &fund)? {
372 remove_vault(&fund, accounts, target_hash, vault_type)?;
373 }
374 }
375 FundInstruction::AddCustody {
376 target_hash,
377 custody_id,
378 custody_type,
379 } => {
380 log_start("AddCustody", &fund.name);
381 if check_admin_authority(accounts, instruction_data, &fund)? {
382 add_custody(&fund, accounts, target_hash, custody_id, custody_type)?;
383 }
384 }
385 FundInstruction::RemoveCustody {
386 target_hash,
387 custody_type,
388 } => {
389 log_start("RemoveCustody", &fund.name);
390 if check_admin_authority(accounts, instruction_data, &fund)? {
391 remove_custody(&fund, accounts, target_hash, custody_type)?;
392 }
393 }
394 FundInstruction::StartLiquidation => {
395 log_start("StartLiquidation", &fund.name);
396 start_liquidation(&fund, accounts)?;
397 }
398 FundInstruction::StopLiquidation => {
399 log_start("StopLiquidation", &fund.name);
400 if check_admin_authority(accounts, instruction_data, &fund)? {
401 stop_liquidation(&fund, accounts)?;
402 }
403 }
404 FundInstruction::WithdrawFees { amount } => {
405 log_start("WithdrawFees", &fund.name);
406 if check_admin_authority(accounts, instruction_data, &fund)? {
407 withdraw_fees(&fund, accounts, amount)?;
408 }
409 }
410 FundInstruction::SetAdminSigners { min_signatures } => {
411 log_start("SetAdminSigners", &fund.name);
412 if check_admin_authority(accounts, instruction_data, &fund)? {
413 set_admin_signers(&fund, accounts, min_signatures)?;
414 }
415 }
416 FundInstruction::RemoveMultisig => {
417 log_start("RemoveMultisig", &fund.name);
418 if check_admin_authority(accounts, instruction_data, &fund)? {
419 remove_multisig(&fund, accounts)?;
420 }
421 }
422 FundInstruction::AmmInstructionRaydium { instruction } => match instruction {
423 AmmInstruction::UserInit => {
424 log_start("UserInitRaydium", &fund.name);
425 check_manager_authority(user_account, &fund)?;
426 raydium::user_init::user_init(&fund, accounts)?;
427 }
428 AmmInstruction::AddLiquidity {
429 max_token_a_amount,
430 max_token_b_amount,
431 } => {
432 log_start("AddLiquidityRaydium", &fund.name);
433 check_manager_authority(user_account, &fund)?;
434 raydium::add_liquidity::add_liquidity(
435 &fund,
436 accounts,
437 max_token_a_amount,
438 max_token_b_amount,
439 )?;
440 }
441 AmmInstruction::RemoveLiquidity { amount } => {
442 log_start("RemoveLiquidityRaydium", &fund.name);
443 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
444 raydium::remove_liquidity::remove_liquidity(&fund, accounts, amount)?;
445 }
446 AmmInstruction::Swap {
447 token_a_amount_in,
448 token_b_amount_in,
449 min_token_amount_out,
450 } => {
451 log_start("SwapRaydium", &fund.name);
452 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
453 raydium::swap::swap(
454 &fund,
455 accounts,
456 token_a_amount_in,
457 token_b_amount_in,
458 min_token_amount_out,
459 )?;
460 }
461 AmmInstruction::Stake { amount } => {
462 log_start("StakeRaydium", &fund.name);
463 check_manager_authority(user_account, &fund)?;
464 raydium::stake::stake(&fund, accounts, amount, false)?;
465 }
466 AmmInstruction::Unstake { amount } => {
467 log_start("UnstakeRaydium", &fund.name);
468 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
469 raydium::unstake::unstake(&fund, accounts, amount)?;
470 }
471 AmmInstruction::Harvest => {
472 log_start("HarvestRaydium", &fund.name);
473 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
474 raydium::stake::stake(&fund, accounts, 0, true)?;
475 }
476 _ => {
477 msg!("Error: Unimplemented");
478 return Err(ProgramError::Custom(512));
479 }
480 },
481 FundInstruction::VaultInstructionRaydium { instruction } => match instruction {
482 VaultInstruction::AddLiquidity {
483 max_token_a_amount,
484 max_token_b_amount,
485 } => {
486 log_start("VaultAddLiquidityRaydium", &fund.name);
487 check_manager_authority(user_account, &fund)?;
488 raydium::vault_add_liquidity::add_liquidity(
489 &fund,
490 accounts,
491 max_token_a_amount,
492 max_token_b_amount,
493 )?;
494 }
495 VaultInstruction::LockLiquidity { amount } => {
496 log_start("VaultLockLiquidityRaydium", &fund.name);
497 check_manager_authority(user_account, &fund)?;
498 raydium::vault_lock_liquidity::lock_liquidity(&fund, accounts, amount)?;
499 }
500 VaultInstruction::UnlockLiquidity { amount } => {
501 log_start("VaultUnlockLiquidityRaydium", &fund.name);
502 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
503 raydium::vault_unlock_liquidity::unlock_liquidity(&fund, accounts, amount)?;
504 }
505 VaultInstruction::RemoveLiquidity { amount } => {
506 log_start("VaultRemoveLiquidityRaydium", &fund.name);
507 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
508 raydium::vault_remove_liquidity::remove_liquidity(&fund, accounts, amount)?;
509 }
510 VaultInstruction::UserInit {} => {
511 log_start("VaultUserInitRaydium", &fund.name);
512 check_manager_authority(user_account, &fund)?;
513 raydium::vault_user_init::user_init(&fund, accounts)?;
514 }
515 _ => {
516 msg!("Error: Unimplemented");
517 return Err(ProgramError::Custom(513));
518 }
519 },
520 FundInstruction::AmmInstructionOrca { instruction } => match instruction {
521 AmmInstruction::UserInit => {
522 log_start("UserInitOrca", &fund.name);
523 check_manager_authority(user_account, &fund)?;
524 orca::user_init::user_init(&fund, accounts)?;
525 }
526 AmmInstruction::AddLiquidity {
527 max_token_a_amount,
528 max_token_b_amount,
529 } => {
530 log_start("AddLiquidityOrca", &fund.name);
531 check_manager_authority(user_account, &fund)?;
532 orca::add_liquidity::add_liquidity(
533 &fund,
534 accounts,
535 max_token_a_amount,
536 max_token_b_amount,
537 )?;
538 }
539 AmmInstruction::RemoveLiquidity { amount } => {
540 log_start("RemoveLiquidityOrca", &fund.name);
541 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
542 orca::remove_liquidity::remove_liquidity(&fund, accounts, amount)?;
543 }
544 AmmInstruction::Swap {
545 token_a_amount_in,
546 token_b_amount_in,
547 min_token_amount_out,
548 } => {
549 log_start("SwapOrca", &fund.name);
550 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
551 orca::swap::swap(
552 &fund,
553 accounts,
554 token_a_amount_in,
555 token_b_amount_in,
556 min_token_amount_out,
557 )?;
558 }
559 AmmInstruction::Stake { amount } => {
560 log_start("StakeOrca", &fund.name);
561 check_manager_authority(user_account, &fund)?;
562 orca::stake::stake(&fund, accounts, amount)?;
563 }
564 AmmInstruction::Unstake { amount } => {
565 log_start("UnstakeOrca", &fund.name);
566 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
567 orca::unstake::unstake(&fund, accounts, amount)?;
568 }
569 AmmInstruction::Harvest => {
570 log_start("HarvestOrca", &fund.name);
571 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
572 orca::harvest::harvest(&fund, accounts)?;
573 }
574 _ => {
575 msg!("Error: Unimplemented");
576 return Err(ProgramError::Custom(512));
577 }
578 },
579 FundInstruction::VaultInstructionOrca { instruction } => match instruction {
580 VaultInstruction::AddLiquidity {
581 max_token_a_amount,
582 max_token_b_amount,
583 } => {
584 log_start("VaultAddLiquidityOrca", &fund.name);
585 check_manager_authority(user_account, &fund)?;
586 orca::vault_add_liquidity::add_liquidity(
587 &fund,
588 accounts,
589 max_token_a_amount,
590 max_token_b_amount,
591 )?;
592 }
593 VaultInstruction::LockLiquidity { amount } => {
594 log_start("VaultLockLiquidityOrca", &fund.name);
595 check_manager_authority(user_account, &fund)?;
596 orca::vault_lock_liquidity::lock_liquidity(&fund, accounts, amount)?;
597 }
598 VaultInstruction::UnlockLiquidity { amount } => {
599 log_start("VaultUnlockLiquidityOrca", &fund.name);
600 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
601 orca::vault_unlock_liquidity::unlock_liquidity(&fund, accounts, amount)?;
602 }
603 VaultInstruction::RemoveLiquidity { amount } => {
604 log_start("VaultRemoveLiquidityOrca", &fund.name);
605 check_manager_authority_or_liquidation(user_account, fund_info_account, &fund)?;
606 orca::vault_remove_liquidity::remove_liquidity(&fund, accounts, amount)?;
607 }
608 VaultInstruction::UserInit {} => {
609 log_start("VaultUserInitOrca", &fund.name);
610 check_manager_authority(user_account, &fund)?;
611 orca::vault_user_init::user_init(&fund, accounts)?;
612 }
613 _ => {
614 msg!("Error: Unimplemented");
615 return Err(ProgramError::Custom(513));
616 }
617 },
618 }
619
620 log_end(&fund.name);
621 Ok(())
622}