From defbd8c2b16d52fbd5baa517c677040856d2b1ed Mon Sep 17 00:00:00 2001 From: Nicholas Clarke Date: Sun, 11 Jun 2023 15:37:33 -0700 Subject: [PATCH] =?UTF-8?q?Add=20loan=20amount=20to=20WithdrawLoanLog.=20A?= =?UTF-8?q?dd=20withdraw=20loan=20logging=20to=20toke=E2=80=A6=20(#599)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add loan amount to WithdrawLoanLog. Add withdraw loan logging to token_force_close_borrows_with_token * Address review comments. --- .../src/instructions/serum3_settle_funds.rs | 16 ++-- .../token_force_close_borrows_with_token.rs | 10 ++- .../src/instructions/token_liq_bankruptcy.rs | 17 ++-- .../src/instructions/token_liq_with_token.rs | 17 ++-- .../src/instructions/token_withdraw.rs | 15 ++-- programs/mango-v4/src/logs.rs | 10 +++ programs/mango-v4/src/state/bank.rs | 85 +++++++++++++------ 7 files changed, 113 insertions(+), 57 deletions(-) diff --git a/programs/mango-v4/src/instructions/serum3_settle_funds.rs b/programs/mango-v4/src/instructions/serum3_settle_funds.rs index 6ec68d430..81e9af44e 100644 --- a/programs/mango-v4/src/instructions/serum3_settle_funds.rs +++ b/programs/mango-v4/src/instructions/serum3_settle_funds.rs @@ -9,7 +9,7 @@ use crate::state::*; use super::{apply_settle_changes, OpenOrdersAmounts, OpenOrdersSlim}; use crate::accounts_ix::*; use crate::logs::Serum3OpenOrdersBalanceLogV2; -use crate::logs::{LoanOriginationFeeInstruction, WithdrawLoanOriginationFeeLog}; +use crate::logs::{LoanOriginationFeeInstruction, WithdrawLoanLog}; /// Settling means moving free funds from the serum3 open orders account /// back into the mango account wallet. @@ -171,17 +171,18 @@ pub fn charge_loan_origination_fees( // now that the loan is actually materialized, charge the loan origination fee // note: the withdraw has already happened while placing the order let base_token_account = account.token_position_mut(base_bank.token_index)?.0; - let (_, fee) = base_bank.withdraw_loan_origination_fee( + let withdraw_result = base_bank.withdraw_loan_origination_fee( base_token_account, actualized_base_loan, now_ts, )?; - emit!(WithdrawLoanOriginationFeeLog { + emit!(WithdrawLoanLog { mango_group: *group_pubkey, mango_account: *account_pubkey, token_index: base_bank.token_index, - loan_origination_fee: fee.to_bits(), + loan_amount: withdraw_result.loan_amount.to_bits(), + loan_origination_fee: withdraw_result.loan_origination_fee.to_bits(), instruction: LoanOriginationFeeInstruction::Serum3SettleFunds, }); } @@ -199,17 +200,18 @@ pub fn charge_loan_origination_fees( // now that the loan is actually materialized, charge the loan origination fee // note: the withdraw has already happened while placing the order let quote_token_account = account.token_position_mut(quote_bank.token_index)?.0; - let (_, fee) = quote_bank.withdraw_loan_origination_fee( + let withdraw_result = quote_bank.withdraw_loan_origination_fee( quote_token_account, actualized_quote_loan, now_ts, )?; - emit!(WithdrawLoanOriginationFeeLog { + emit!(WithdrawLoanLog { mango_group: *group_pubkey, mango_account: *account_pubkey, token_index: quote_bank.token_index, - loan_origination_fee: fee.to_bits(), + loan_amount: withdraw_result.loan_amount.to_bits(), + loan_origination_fee: withdraw_result.loan_origination_fee.to_bits(), instruction: LoanOriginationFeeInstruction::Serum3SettleFunds, }); } diff --git a/programs/mango-v4/src/instructions/token_force_close_borrows_with_token.rs b/programs/mango-v4/src/instructions/token_force_close_borrows_with_token.rs index 27e85cc3c..00beb6cce 100644 --- a/programs/mango-v4/src/instructions/token_force_close_borrows_with_token.rs +++ b/programs/mango-v4/src/instructions/token_force_close_borrows_with_token.rs @@ -98,7 +98,7 @@ pub fn token_force_close_borrows_with_token( liab_bank.deposit_with_dusting(liqee_liab_position, liab_transfer, now_ts)?; let liqee_liab_indexed_position = liqee_liab_position.indexed_position; - let (liqor_liab_active, loan_origination_fee) = + let liqor_liab_withdraw_result = liab_bank.withdraw_with_fee(liqor_liab_position, liab_transfer, now_ts)?; let liqor_liab_indexed_position = liqor_liab_position.indexed_position; let liqee_liab_native_after = liqee_liab_position.native(liab_bank); @@ -174,6 +174,12 @@ pub fn token_force_close_borrows_with_token( fee_factor: fee_factor.to_bits(), }); + // liqor should never have a borrow + require!( + liqor_liab_withdraw_result.loan_amount.is_zero(), + MangoError::SomeError + ); + let liqee_health_cache = new_health_cache(&liqee.borrow(), &mut account_retriever) .context("create liqee health cache")?; let liqee_liq_end_health = liqee_health_cache.health(HealthType::LiquidationEnd); @@ -191,7 +197,7 @@ pub fn token_force_close_borrows_with_token( if !liqor_asset_active { liqor.deactivate_token_position_and_log(liqor_asset_raw_index, liqor_key); } - if !liqor_liab_active { + if !liqor_liab_withdraw_result.position_is_active { liqor.deactivate_token_position_and_log(liqor_liab_raw_index, liqor_key) } }; diff --git a/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs b/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs index 76eec9ba5..a8ca866de 100644 --- a/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs +++ b/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs @@ -9,8 +9,7 @@ use crate::state::*; use crate::accounts_ix::*; use crate::logs::{ - LoanOriginationFeeInstruction, TokenBalanceLog, TokenLiqBankruptcyLog, - WithdrawLoanOriginationFeeLog, + LoanOriginationFeeInstruction, TokenBalanceLog, TokenLiqBankruptcyLog, WithdrawLoanLog, }; pub fn token_liq_bankruptcy( @@ -149,7 +148,7 @@ pub fn token_liq_bankruptcy( // transfer liab from liqee to liqor let (liqor_liab, liqor_liab_raw_token_index, _) = liqor.ensure_token_position(liab_token_index)?; - let (liqor_liab_active, loan_origination_fee) = + let liqor_liab_withdraw_result = liab_bank.withdraw_with_fee(liqor_liab, liab_transfer, now_ts)?; // liqor liab @@ -169,12 +168,16 @@ pub fn token_liq_bankruptcy( require!(liqor_health >= 0, MangoError::HealthMustBePositive); } - if loan_origination_fee.is_positive() { - emit!(WithdrawLoanOriginationFeeLog { + if liqor_liab_withdraw_result + .loan_origination_fee + .is_positive() + { + emit!(WithdrawLoanLog { mango_group: ctx.accounts.group.key(), mango_account: ctx.accounts.liqor.key(), token_index: liab_token_index, - loan_origination_fee: loan_origination_fee.to_bits(), + loan_amount: liqor_liab_withdraw_result.loan_amount.to_bits(), + loan_origination_fee: liqor_liab_withdraw_result.loan_origination_fee.to_bits(), instruction: LoanOriginationFeeInstruction::LiqTokenBankruptcy }); } @@ -185,7 +188,7 @@ pub fn token_liq_bankruptcy( ctx.accounts.liqor.key(), ); } - if !liqor_liab_active { + if !liqor_liab_withdraw_result.position_is_active { liqor.deactivate_token_position_and_log( liqor_liab_raw_token_index, ctx.accounts.liqor.key(), diff --git a/programs/mango-v4/src/instructions/token_liq_with_token.rs b/programs/mango-v4/src/instructions/token_liq_with_token.rs index 9a03e3fcb..1807d49a4 100644 --- a/programs/mango-v4/src/instructions/token_liq_with_token.rs +++ b/programs/mango-v4/src/instructions/token_liq_with_token.rs @@ -5,8 +5,7 @@ use crate::accounts_ix::*; use crate::error::*; use crate::health::*; use crate::logs::{ - LoanOriginationFeeInstruction, TokenBalanceLog, TokenLiqWithTokenLog, - WithdrawLoanOriginationFeeLog, + LoanOriginationFeeInstruction, TokenBalanceLog, TokenLiqWithTokenLog, WithdrawLoanLog, }; use crate::state::*; @@ -221,7 +220,7 @@ pub(crate) fn liquidation_action( let (liqor_liab_position, liqor_liab_raw_index, _) = liqor.ensure_token_position(liab_token_index)?; - let (liqor_liab_active, loan_origination_fee) = + let liqor_liab_withdraw_result = liab_bank.withdraw_with_fee(liqor_liab_position, liab_transfer, now_ts)?; let liqor_liab_indexed_position = liqor_liab_position.indexed_position; let liqee_liab_native_after = liqee_liab_position.native(liab_bank); @@ -289,12 +288,16 @@ pub(crate) fn liquidation_action( borrow_index: liab_bank.borrow_index.to_bits(), }); - if loan_origination_fee.is_positive() { - emit!(WithdrawLoanOriginationFeeLog { + if liqor_liab_withdraw_result + .loan_origination_fee + .is_positive() + { + emit!(WithdrawLoanLog { mango_group: liqee.fixed.group, mango_account: liqor_key, token_index: liab_token_index, - loan_origination_fee: loan_origination_fee.to_bits(), + loan_amount: liqor_liab_withdraw_result.loan_amount.to_bits(), + loan_origination_fee: liqor_liab_withdraw_result.loan_origination_fee.to_bits(), instruction: LoanOriginationFeeInstruction::LiqTokenWithToken }); } @@ -309,7 +312,7 @@ pub(crate) fn liquidation_action( if !liqor_asset_active { liqor.deactivate_token_position_and_log(liqor_asset_raw_index, liqor_key); } - if !liqor_liab_active { + if !liqor_liab_withdraw_result.position_is_active { liqor.deactivate_token_position_and_log(liqor_liab_raw_index, liqor_key) } diff --git a/programs/mango-v4/src/instructions/token_withdraw.rs b/programs/mango-v4/src/instructions/token_withdraw.rs index cf6386d27..754d8cb0d 100644 --- a/programs/mango-v4/src/instructions/token_withdraw.rs +++ b/programs/mango-v4/src/instructions/token_withdraw.rs @@ -7,9 +7,7 @@ use anchor_spl::token; use fixed::types::I80F48; use crate::accounts_ix::*; -use crate::logs::{ - LoanOriginationFeeInstruction, TokenBalanceLog, WithdrawLoanOriginationFeeLog, WithdrawLog, -}; +use crate::logs::{LoanOriginationFeeInstruction, TokenBalanceLog, WithdrawLoanLog, WithdrawLog}; pub fn token_withdraw(ctx: Context, amount: u64, allow_borrow: bool) -> Result<()> { require_msg!(amount > 0, "withdraw amount must be positive"); @@ -65,7 +63,7 @@ pub fn token_withdraw(ctx: Context, amount: u64, allow_borrow: bo )?; // Update the bank and position - let (position_is_active, loan_origination_fee) = bank.withdraw_with_fee( + let withdraw_result = bank.withdraw_with_fee( position, amount_i80f48, Clock::get()?.unix_timestamp.try_into().unwrap(), @@ -116,7 +114,7 @@ pub fn token_withdraw(ctx: Context, amount: u64, allow_borrow: bo // remaining_accounts for all banks/oracles, including the account that will now be // deactivated. // - if !position_is_active { + if !withdraw_result.position_is_active { account.deactivate_token_position_and_log(raw_token_index, ctx.accounts.account.key()); } @@ -129,12 +127,13 @@ pub fn token_withdraw(ctx: Context, amount: u64, allow_borrow: bo price: oracle_price.to_bits(), }); - if loan_origination_fee.is_positive() { - emit!(WithdrawLoanOriginationFeeLog { + if withdraw_result.loan_origination_fee.is_positive() { + emit!(WithdrawLoanLog { mango_group: ctx.accounts.group.key(), mango_account: ctx.accounts.account.key(), token_index, - loan_origination_fee: loan_origination_fee.to_bits(), + loan_amount: withdraw_result.loan_amount.to_bits(), + loan_origination_fee: withdraw_result.loan_origination_fee.to_bits(), instruction: LoanOriginationFeeInstruction::TokenWithdraw, }); } diff --git a/programs/mango-v4/src/logs.rs b/programs/mango-v4/src/logs.rs index d4e0c4760..c83eab50e 100644 --- a/programs/mango-v4/src/logs.rs +++ b/programs/mango-v4/src/logs.rs @@ -257,6 +257,16 @@ pub struct WithdrawLoanOriginationFeeLog { pub instruction: LoanOriginationFeeInstruction, } +#[event] +pub struct WithdrawLoanLog { + pub mango_group: Pubkey, + pub mango_account: Pubkey, + pub token_index: u16, + pub loan_amount: i128, + pub loan_origination_fee: i128, + pub instruction: LoanOriginationFeeInstruction, +} + #[event] pub struct TokenLiqBankruptcyLog { pub mango_group: Pubkey, diff --git a/programs/mango-v4/src/state/bank.rs b/programs/mango-v4/src/state/bank.rs index 05d4a4158..6b899461f 100644 --- a/programs/mango-v4/src/state/bank.rs +++ b/programs/mango-v4/src/state/bank.rs @@ -170,6 +170,12 @@ const_assert_eq!( const_assert_eq!(size_of::(), 3064); const_assert_eq!(size_of::() % 8, 0); +pub struct WithdrawResult { + pub position_is_active: bool, + pub loan_origination_fee: I80F48, + pub loan_amount: I80F48, +} + impl Bank { pub fn from_existing_bank( existing_bank: &Bank, @@ -403,13 +409,15 @@ impl Bank { native_amount: I80F48, now_ts: u64, ) -> Result { - let (position_is_active, _) = self.withdraw_internal_wrapper( - position, - native_amount, - false, - !position.is_in_use(), - now_ts, - )?; + let position_is_active = self + .withdraw_internal_wrapper( + position, + native_amount, + false, + !position.is_in_use(), + now_ts, + )? + .position_is_active; Ok(position_is_active) } @@ -424,7 +432,7 @@ impl Bank { now_ts: u64, ) -> Result { self.withdraw_internal_wrapper(position, native_amount, false, true, now_ts) - .map(|(not_dusted, _)| not_dusted || position.is_in_use()) + .map(|withdraw_result| withdraw_result.position_is_active || position.is_in_use()) } /// Withdraws `native_amount` while applying the loan origination fee if a borrow is created. @@ -440,7 +448,7 @@ impl Bank { position: &mut TokenPosition, native_amount: I80F48, now_ts: u64, - ) -> Result<(bool, I80F48)> { + ) -> Result { self.withdraw_internal_wrapper(position, native_amount, true, !position.is_in_use(), now_ts) } @@ -452,7 +460,7 @@ impl Bank { with_loan_origination_fee: bool, allow_dusting: bool, now_ts: u64, - ) -> Result<(bool, I80F48)> { + ) -> Result { let opening_indexed_position = position.indexed_position; let res = self.withdraw_internal( position, @@ -473,7 +481,7 @@ impl Bank { with_loan_origination_fee: bool, allow_dusting: bool, now_ts: u64, - ) -> Result<(bool, I80F48)> { + ) -> Result { require_gte!(native_amount, 0); let native_position = position.native(self); @@ -486,13 +494,21 @@ impl Bank { self.dust += new_native_position; self.indexed_deposits -= position.indexed_position; position.indexed_position = I80F48::ZERO; - return Ok((false, I80F48::ZERO)); + return Ok(WithdrawResult { + position_is_active: false, + loan_origination_fee: I80F48::ZERO, + loan_amount: I80F48::ZERO, + }); } else { // withdraw some deposits leaving a positive balance let indexed_change = native_amount / self.deposit_index; self.indexed_deposits -= indexed_change; position.indexed_position -= indexed_change; - return Ok((true, I80F48::ZERO)); + return Ok(WithdrawResult { + position_is_active: true, + loan_origination_fee: I80F48::ZERO, + loan_amount: I80F48::ZERO, + }); } } @@ -519,7 +535,11 @@ impl Bank { // withdraws and not borrows self.update_net_borrows(native_amount, now_ts); - Ok((true, loan_origination_fee)) + Ok(WithdrawResult { + position_is_active: true, + loan_origination_fee, + loan_amount: native_amount, + }) } // withdraw the loan origination fee for a borrow that happenend earlier @@ -528,19 +548,26 @@ impl Bank { position: &mut TokenPosition, already_borrowed_native_amount: I80F48, now_ts: u64, - ) -> Result<(bool, I80F48)> { + ) -> Result { let loan_origination_fee = self.loan_origination_fee_rate * already_borrowed_native_amount; self.collected_fees_native += loan_origination_fee; - let (position_is_active, _) = self.withdraw_internal_wrapper( - position, - loan_origination_fee, - false, - !position.is_in_use(), - now_ts, - )?; + let position_is_active = self + .withdraw_internal_wrapper( + position, + loan_origination_fee, + false, + !position.is_in_use(), + now_ts, + )? + .position_is_active; - Ok((position_is_active, loan_origination_fee)) + Ok(WithdrawResult { + position_is_active, + loan_origination_fee, + // To avoid double counting of loans return loan_amount of 0 here (as the loan_amount has already been returned earlier with loan_origination_fee == 0) + loan_amount: I80F48::ZERO, + }) } /// Change a position without applying the loan origination fee @@ -563,9 +590,13 @@ impl Bank { position: &mut TokenPosition, native_amount: I80F48, now_ts: u64, - ) -> Result<(bool, I80F48)> { + ) -> Result { if native_amount >= 0 { - Ok((self.deposit(position, native_amount, now_ts)?, I80F48::ZERO)) + Ok(WithdrawResult { + position_is_active: self.deposit(position, native_amount, now_ts)?, + loan_origination_fee: I80F48::ZERO, + loan_amount: I80F48::ZERO, + }) } else { self.withdraw_with_fee(position, -native_amount, now_ts) } @@ -948,7 +979,9 @@ mod tests { let change = I80F48::from(change); let dummy_now_ts = 1 as u64; let dummy_price = I80F48::ZERO; - let (is_active, _) = bank.change_with_fee(&mut account, change, dummy_now_ts)?; + let is_active = bank + .change_with_fee(&mut account, change, dummy_now_ts)? + .position_is_active; let mut expected_native = start_native + change; if expected_native >= 0.0 && expected_native < 1.0 && !is_in_use {