diff --git a/programs/mango-v4/src/instructions/compute_health.rs b/programs/mango-v4/src/instructions/compute_health.rs new file mode 100644 index 000000000..eb6082961 --- /dev/null +++ b/programs/mango-v4/src/instructions/compute_health.rs @@ -0,0 +1,21 @@ +use crate::state::*; +use anchor_lang::prelude::*; +use fixed::types::I80F48; + +#[derive(Accounts)] +pub struct ComputeHealth<'info> { + pub group: AccountLoader<'info, Group>, + + #[account( + has_one = group, + )] + pub account: AccountLoader<'info, MangoAccount>, +} + +pub fn compute_health(ctx: Context, health_type: HealthType) -> Result { + let account = ctx.accounts.account.load()?; + let health = compute_health_from_fixed_accounts(&account, health_type, ctx.remaining_accounts)?; + msg!("health: {}", health); + + Ok(health) +} diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index 231427528..ce5ac18a4 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -3,6 +3,7 @@ pub use benchmark::*; pub use close_account::*; pub use close_group::*; pub use close_stub_oracle::*; +pub use compute_health::*; pub use create_account::*; pub use create_group::*; pub use create_stub_oracle::*; @@ -36,6 +37,7 @@ mod benchmark; mod close_account; mod close_group; mod close_stub_oracle; +mod compute_health; mod create_account; mod create_group; mod create_stub_oracle; diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index e70cfc739..d3c9c1181 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -17,7 +17,9 @@ mod serum3_cpi; pub mod state; pub mod types; -use state::{OracleConfig, OrderType, PerpMarketIndex, Serum3MarketIndex, Side, TokenIndex}; +use state::{ + HealthType, OracleConfig, OrderType, PerpMarketIndex, Serum3MarketIndex, Side, TokenIndex, +}; declare_id!("m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD"); @@ -340,6 +342,10 @@ pub mod mango_v4 { // resolve_banktruptcy + pub fn compute_health(ctx: Context, health_type: HealthType) -> Result { + instructions::compute_health(ctx, health_type) + } + /// /// benchmark /// diff --git a/programs/mango-v4/src/state/health.rs b/programs/mango-v4/src/state/health.rs index c49e92ec2..941b166d4 100644 --- a/programs/mango-v4/src/state/health.rs +++ b/programs/mango-v4/src/state/health.rs @@ -258,7 +258,7 @@ impl<'a, 'info> AccountRetriever for ScanningAccountRetriever<'a, 'info> { /// slightly smaller weights for the liabilities. Zero is used as the bright line for both /// i.e. if your init health falls below zero, you cannot open new positions and if your maint. health /// falls below zero you will be liquidated. -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Copy, Clone, AnchorSerialize, AnchorDeserialize)] pub enum HealthType { Init, Maint, diff --git a/programs/mango-v4/tests/program_test/mango_client.rs b/programs/mango-v4/tests/program_test/mango_client.rs index dfdf1f1a4..185c988eb 100644 --- a/programs/mango-v4/tests/program_test/mango_client.rs +++ b/programs/mango-v4/tests/program_test/mango_client.rs @@ -2059,3 +2059,47 @@ impl ClientInstruction for UpdateIndexInstruction { vec![] } } + +pub struct ComputeHealthInstruction { + pub account: Pubkey, + pub health_type: HealthType, +} +#[async_trait::async_trait(?Send)] +impl ClientInstruction for ComputeHealthInstruction { + type Accounts = mango_v4::accounts::ComputeHealth; + type Instruction = mango_v4::instruction::ComputeHealth; + async fn to_instruction( + &self, + account_loader: impl ClientAccountLoader + 'async_trait, + ) -> (Self::Accounts, instruction::Instruction) { + let program_id = mango_v4::id(); + let instruction = Self::Instruction { + health_type: self.health_type, + }; + + let account: MangoAccount = account_loader.load(&self.account).await.unwrap(); + + let health_check_metas = derive_health_check_remaining_account_metas( + &account_loader, + &account, + None, + false, + None, + ) + .await; + + let accounts = Self::Accounts { + group: account.group, + account: self.account, + }; + + let mut instruction = make_instruction(program_id, &accounts, instruction); + instruction.accounts.extend(health_check_metas.into_iter()); + + (accounts, instruction) + } + + fn signers(&self) -> Vec<&Keypair> { + vec![] + } +} diff --git a/programs/mango-v4/tests/test_basic.rs b/programs/mango-v4/tests/test_basic.rs index 34a76123c..49e5ff117 100644 --- a/programs/mango-v4/tests/test_basic.rs +++ b/programs/mango-v4/tests/test_basic.rs @@ -84,6 +84,19 @@ async fn test_basic() -> Result<(), TransportError> { ); } + // + // TEST: Compute the account health + // + send_tx( + solana, + ComputeHealthInstruction { + account, + health_type: HealthType::Init, + }, + ) + .await + .unwrap(); + // // TEST: Withdraw funds //