Clarkeni/perp logging (#265)

* Perp instruction logging
* Onchain funding calculation
* perp_spot_transfers field on perp positions
* Logging on register token, perp market
* Additional fields on update token index logging
* maker and taker volume fields on perp position
This commit is contained in:
Nicholas Clarke 2022-10-07 12:12:55 -07:00 committed by GitHub
parent 1ee1c2e114
commit 96344d7de1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 445 additions and 54 deletions

View File

@ -63,7 +63,7 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
emit_perp_balances(
ctx.accounts.group.key(),
fill.maker,
perp_market.perp_market_index as u64,
perp_market.perp_market_index,
ma.perp_position(perp_market.perp_market_index).unwrap(),
&perp_market,
);
@ -103,14 +103,14 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
emit_perp_balances(
ctx.accounts.group.key(),
fill.maker,
perp_market.perp_market_index as u64,
perp_market.perp_market_index,
maker.perp_position(perp_market.perp_market_index).unwrap(),
&perp_market,
);
emit_perp_balances(
ctx.accounts.group.key(),
fill.taker,
perp_market.perp_market_index as u64,
perp_market.perp_market_index,
taker.perp_position(perp_market.perp_market_index).unwrap(),
&perp_market,
);

View File

@ -5,6 +5,8 @@ use crate::error::*;
use crate::state::*;
use crate::util::fill_from_str;
use crate::logs::PerpMarketMetaDataLog;
#[derive(Accounts)]
#[instruction(perp_market_index: PerpMarketIndex)]
pub struct PerpCreateMarket<'info> {
@ -130,5 +132,15 @@ pub fn perp_create_market(
let mut asks = ctx.accounts.asks.load_init()?;
asks.book_side_type = BookSideType::Asks;
emit!(PerpMarketMetaDataLog {
mango_group: ctx.accounts.group.key(),
perp_market: ctx.accounts.perp_market.key(),
perp_market_index,
base_decimals,
base_lot_size,
quote_lot_size,
oracle: ctx.accounts.oracle.key(),
});
Ok(())
}

View File

@ -50,9 +50,10 @@ pub fn perp_deactivate_position(ctx: Context<PerpDeactivatePosition>) -> Result<
"perp position still has events on event queue"
);
account.deactivate_perp_position(
account.deactivate_perp_position_and_log(
perp_market.perp_market_index,
perp_market.settle_token_index,
ctx.accounts.account.key(),
)?;
Ok(())

View File

@ -2,6 +2,8 @@ use crate::state::*;
use anchor_lang::prelude::*;
use fixed::types::I80F48;
use crate::logs::PerpMarketMetaDataLog;
#[derive(Accounts)]
pub struct PerpEditMarket<'info> {
#[account(
@ -135,5 +137,15 @@ pub fn perp_edit_market(
perp_market.settle_fee_fraction_low_health = settle_fee_fraction_low_health;
}
emit!(PerpMarketMetaDataLog {
mango_group: ctx.accounts.group.key(),
perp_market: ctx.accounts.perp_market.key(),
perp_market_index: perp_market.perp_market_index,
base_decimals: perp_market.base_decimals,
base_lot_size: perp_market.base_lot_size,
quote_lot_size: perp_market.quote_lot_size,
oracle: perp_market.oracle.key(),
});
Ok(())
}

View File

@ -9,6 +9,8 @@ use crate::state::ScanningAccountRetriever;
use crate::state::*;
use crate::util::checked_math as cm;
use crate::logs::{emit_perp_balances, PerpLiqBankruptcyLog, TokenBalanceLog};
// Remaining accounts:
// - merged health accounts for liqor+liqee
#[derive(Accounts)]
@ -159,22 +161,58 @@ pub fn perp_liq_bankruptcy(ctx: Context<PerpLiqBankruptcy>, max_liab_transfer: u
let (liqor_quote, _, _) = liqor.ensure_token_position(settle_token_index)?;
settle_bank.deposit(liqor_quote, insurance_transfer_i80f48)?;
emit!(TokenBalanceLog {
mango_group: ctx.accounts.group.key(),
mango_account: ctx.accounts.liqor.key(),
token_index: settle_token_index,
indexed_position: liqor_quote.indexed_position.to_bits(),
deposit_index: settle_bank.deposit_index.to_bits(),
borrow_index: settle_bank.borrow_index.to_bits(),
});
// transfer perp quote loss from the liqee to the liqor
let liqor_perp_position = liqor
.ensure_perp_position(perp_market.perp_market_index, settle_token_index)?
.0;
liqee_perp_position.change_quote_position(insurance_liab_transfer);
liqor_perp_position.change_quote_position(-insurance_liab_transfer);
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.liqor.key(),
perp_market.perp_market_index,
liqor_perp_position,
&perp_market,
);
}
// Socialize loss if the insurance fund is exhausted
let remaining_liab = liab_transfer - insurance_liab_transfer;
let mut socialized_loss = I80F48::ZERO;
if insurance_fund_exhausted && remaining_liab.is_positive() {
perp_market.socialize_loss(-remaining_liab)?;
liqee_perp_position.change_quote_position(remaining_liab);
require_eq!(liqee_perp_position.quote_position_native(), 0);
socialized_loss = remaining_liab;
}
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.liqee.key(),
perp_market.perp_market_index,
liqee_perp_position,
&perp_market,
);
emit!(PerpLiqBankruptcyLog {
mango_group: ctx.accounts.group.key(),
liqee: ctx.accounts.liqee.key(),
liqor: ctx.accounts.liqor.key(),
perp_market_index: perp_market.perp_market_index,
insurance_transfer: insurance_transfer_i80f48.to_bits(),
socialized_loss: socialized_loss.to_bits()
});
// Check liqee health again
liqee_health_cache.recompute_perp_info(liqee_perp_position, &perp_market)?;
let liqee_init_health = liqee_health_cache.health(HealthType::Init);

View File

@ -6,6 +6,8 @@ use crate::accounts_zerocopy::*;
use crate::error::*;
use crate::state::*;
use crate::logs::{emit_perp_balances, PerpLiqBasePositionLog};
#[derive(Accounts)]
pub struct PerpLiqBasePosition<'info> {
pub group: AccountLoader<'info, Group>,
@ -176,6 +178,32 @@ pub fn perp_liq_base_position(
quote_transfer,
);
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.liqor.key(),
perp_market.perp_market_index,
liqor_perp_position,
&perp_market,
);
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.liqee.key(),
perp_market.perp_market_index,
liqee_perp_position,
&perp_market,
);
emit!(PerpLiqBasePositionLog {
mango_group: ctx.accounts.group.key(),
perp_market_index: perp_market.perp_market_index,
liqor: ctx.accounts.liqor.key(),
liqee: ctx.accounts.liqee.key(),
base_transfer: base_transfer,
quote_transfer: quote_transfer.to_bits(),
price: oracle_price.to_bits(),
});
// Check liqee health again
liqee_health_cache.recompute_perp_info(liqee_perp_position, &perp_market)?;
let liqee_init_health = liqee_health_cache.health(HealthType::Init);

View File

@ -11,6 +11,8 @@ use crate::state::HealthType;
use crate::state::MangoAccount;
use crate::state::{AccountLoaderDynamic, Group, PerpMarket};
use crate::logs::{emit_perp_balances, PerpSettleFeesLog, TokenBalanceLog};
#[derive(Accounts)]
pub struct PerpSettleFees<'info> {
pub group: AccountLoader<'info, Group>,
@ -80,9 +82,18 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: u64) ->
perp_position.change_quote_position(settlement);
perp_market.fees_accrued = cm!(perp_market.fees_accrued - settlement);
// Update the account's net_settled with the new PnL
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.account.key(),
perp_market.perp_market_index,
perp_position,
&perp_market,
);
// Update the account's perp_spot_transfers with the new PnL
let settlement_i64 = settlement.round().checked_to_num::<i64>().unwrap();
account.fixed.net_settled = cm!(account.fixed.net_settled - settlement_i64);
cm!(perp_position.perp_spot_transfers -= settlement_i64);
cm!(account.fixed.perp_spot_transfers -= settlement_i64);
// Transfer token balances
let token_position = account
@ -92,6 +103,22 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: u64) ->
// Update the settled balance on the market itself
perp_market.fees_settled = cm!(perp_market.fees_settled + settlement);
emit!(TokenBalanceLog {
mango_group: ctx.accounts.group.key(),
mango_account: ctx.accounts.account.key(),
token_index: perp_market.settle_token_index,
indexed_position: token_position.indexed_position.to_bits(),
deposit_index: bank.deposit_index.to_bits(),
borrow_index: bank.borrow_index.to_bits(),
});
emit!(PerpSettleFeesLog {
mango_group: ctx.accounts.group.key(),
mango_account: ctx.accounts.account.key(),
perp_market_index: perp_market.perp_market_index,
settlement: settlement.to_bits(),
});
// Bank & perp_market are dropped to prevent re-borrow from remaining_accounts
drop(bank);
drop(perp_market);

View File

@ -4,6 +4,7 @@ use fixed::types::I80F48;
use crate::accounts_zerocopy::*;
use crate::error::*;
use crate::logs::{emit_perp_balances, PerpSettlePnlLog, TokenBalanceLog};
use crate::state::new_health_cache;
use crate::state::Bank;
use crate::state::HealthType;
@ -127,6 +128,22 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
a_perp_position.change_quote_position(-settlement);
b_perp_position.change_quote_position(settlement);
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.account_a.key(),
perp_market.perp_market_index,
a_perp_position,
&perp_market,
);
emit_perp_balances(
ctx.accounts.group.key(),
ctx.accounts.account_b.key(),
perp_market.perp_market_index,
b_perp_position,
&perp_market,
);
// A percentage fee is paid to the settler when account_a's health is low.
// That's because the settlement could avoid it getting liquidated.
let low_health_fee = if a_init_health < 0 {
@ -154,8 +171,10 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
// Applying the fee here means that it decreases the displayed perp pnl.
let settlement_i64 = settlement.checked_to_num::<i64>().unwrap();
let fee_i64 = fee.checked_to_num::<i64>().unwrap();
cm!(account_a.fixed.net_settled += settlement_i64 - fee_i64);
cm!(account_b.fixed.net_settled -= settlement_i64);
cm!(a_perp_position.perp_spot_transfers += settlement_i64 - fee_i64);
cm!(b_perp_position.perp_spot_transfers -= settlement_i64);
cm!(account_a.fixed.perp_spot_transfers += settlement_i64 - fee_i64);
cm!(account_b.fixed.perp_spot_transfers -= settlement_i64);
// Transfer token balances
// The fee is paid by the account with positive unsettled pnl
@ -164,6 +183,24 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
bank.deposit(a_token_position, cm!(settlement - fee))?;
bank.withdraw_with_fee(b_token_position, settlement)?;
emit!(TokenBalanceLog {
mango_group: ctx.accounts.group.key(),
mango_account: ctx.accounts.settler.key(),
token_index: settle_token_index,
indexed_position: a_token_position.indexed_position.to_bits(),
deposit_index: bank.deposit_index.to_bits(),
borrow_index: bank.borrow_index.to_bits(),
});
emit!(TokenBalanceLog {
mango_group: ctx.accounts.group.key(),
mango_account: ctx.accounts.settler.key(),
token_index: settle_token_index,
indexed_position: b_token_position.indexed_position.to_bits(),
deposit_index: bank.deposit_index.to_bits(),
borrow_index: bank.borrow_index.to_bits(),
});
// settler might be the same as account a or b
drop(account_a);
drop(account_b);
@ -179,10 +216,32 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
let (settler_token_position, settler_token_raw_index, _) =
settler.ensure_token_position(settle_token_index)?;
if !bank.deposit(settler_token_position, fee)? {
settler.deactivate_token_position(settler_token_raw_index);
let settler_token_position_active = bank.deposit(settler_token_position, fee)?;
emit!(TokenBalanceLog {
mango_group: ctx.accounts.group.key(),
mango_account: ctx.accounts.settler.key(),
token_index: settler_token_position.token_index,
indexed_position: settler_token_position.indexed_position.to_bits(),
deposit_index: bank.deposit_index.to_bits(),
borrow_index: bank.borrow_index.to_bits(),
});
if !settler_token_position_active {
settler
.deactivate_token_position_and_log(settler_token_raw_index, ctx.accounts.settler.key());
}
emit!(PerpSettlePnlLog {
mango_group: ctx.accounts.group.key(),
mango_account_a: ctx.accounts.account_a.key(),
mango_account_b: ctx.accounts.account_b.key(),
perp_market_index: perp_market_index,
settlement: settlement.to_bits(),
settler: ctx.accounts.settler.key(),
fee: fee.to_bits(),
});
msg!("settled pnl = {}, fee = {}", settlement, fee);
Ok(())
}

View File

@ -3,6 +3,8 @@ use anchor_lang::prelude::*;
use crate::accounts_zerocopy::*;
use crate::state::{Book, BookSide, Group, PerpMarket};
use crate::logs::PerpUpdateFundingLog;
#[derive(Accounts)]
pub struct PerpUpdateFunding<'info> {
pub group: AccountLoader<'info, Group>, // Required for group metadata parsing
@ -37,5 +39,15 @@ pub fn perp_update_funding(ctx: Context<PerpUpdateFunding>) -> Result<()> {
perp_market.update_funding(&book, oracle_price, now_ts as u64)?;
emit!(PerpUpdateFundingLog {
mango_group: ctx.accounts.group.key(),
market_index: perp_market.perp_market_index,
long_funding: perp_market.long_funding.to_bits(),
short_funding: perp_market.long_funding.to_bits(),
price: oracle_price.to_bits(),
fees_accrued: perp_market.fees_accrued.to_bits(),
open_interest: perp_market.open_interest,
});
Ok(())
}

View File

@ -5,6 +5,8 @@ use crate::serum3_cpi::{load_market_state, pubkey_from_u64_array};
use crate::state::*;
use crate::util::fill_from_str;
use crate::logs::Serum3RegisterMarketLog;
#[derive(Accounts)]
#[instruction(market_index: Serum3MarketIndex)]
pub struct Serum3RegisterMarket<'info> {
@ -97,5 +99,15 @@ pub fn serum3_register_market(
reserved: [0; 38],
};
emit!(Serum3RegisterMarketLog {
mango_group: ctx.accounts.group.key(),
serum_market: ctx.accounts.serum_market.key(),
market_index: market_index,
base_token_index: base_bank.token_index,
quote_token_index: quote_bank.token_index,
serum_program: ctx.accounts.serum_program.key(),
serum_program_external: ctx.accounts.serum_market_external.key(),
});
Ok(())
}

View File

@ -7,6 +7,8 @@ use crate::accounts_zerocopy::LoadMutZeroCopyRef;
use crate::state::*;
use crate::logs::TokenMetaDataLog;
/// Changes a token's parameters.
///
/// In addition to these accounts, all banks must be passed as remaining_accounts
@ -125,5 +127,17 @@ pub fn token_edit(
// reserved
}
// Assumes that there is at least one bank
let bank = ctx.remaining_accounts.first().unwrap().load_mut::<Bank>()?;
emit!(TokenMetaDataLog {
mango_group: ctx.accounts.group.key(),
mint: mint_info.mint.key(),
token_index: bank.token_index,
mint_decimals: bank.mint_decimals,
oracle: mint_info.oracle.key(),
mint_info: ctx.accounts.mint_info.key(),
});
Ok(())
}

View File

@ -11,7 +11,7 @@ use crate::state::*;
use crate::util::checked_math as cm;
use crate::logs::{
LiquidateTokenBankruptcyLog, LoanOriginationFeeInstruction, TokenBalanceLog,
LoanOriginationFeeInstruction, TokenBalanceLog, TokenLiqBankruptcyLog,
WithdrawLoanOriginationFeeLog,
};
@ -305,7 +305,7 @@ pub fn token_liq_bankruptcy(
liqee.deactivate_token_position_and_log(liqee_raw_token_index, ctx.accounts.liqee.key());
}
emit!(LiquidateTokenBankruptcyLog {
emit!(TokenLiqBankruptcyLog {
mango_group: ctx.accounts.group.key(),
liqee: ctx.accounts.liqee.key(),
liqor: ctx.accounts.liqor.key(),

View File

@ -4,7 +4,7 @@ use std::cmp::min;
use crate::error::*;
use crate::logs::{
LiquidateTokenAndTokenLog, LoanOriginationFeeInstruction, TokenBalanceLog,
LoanOriginationFeeInstruction, TokenBalanceLog, TokenLiqWithTokenLog,
WithdrawLoanOriginationFeeLog,
};
use crate::state::ScanningAccountRetriever;
@ -253,7 +253,7 @@ pub fn token_liq_with_token(
require!(liqor_health >= 0, MangoError::HealthMustBePositive);
}
emit!(LiquidateTokenAndTokenLog {
emit!(TokenLiqWithTokenLog {
mango_group: ctx.accounts.group.key(),
liqee: ctx.accounts.liqee.key(),
liqor: ctx.accounts.liqor.key(),

View File

@ -7,6 +7,8 @@ use crate::error::*;
use crate::state::*;
use crate::util::fill_from_str;
use crate::logs::TokenMetaDataLog;
pub const INDEX_START: I80F48 = I80F48!(1_000_000);
const FIRST_BANK_NUM: u32 = 0;
@ -156,5 +158,14 @@ pub fn token_register(
mint_info.banks[0] = ctx.accounts.bank.key();
mint_info.vaults[0] = ctx.accounts.vault.key();
emit!(TokenMetaDataLog {
mango_group: ctx.accounts.group.key(),
mint: ctx.accounts.mint.key(),
token_index,
mint_decimals: ctx.accounts.mint.decimals,
oracle: ctx.accounts.oracle.key(),
mint_info: ctx.accounts.mint_info.key(),
});
Ok(())
}

View File

@ -7,6 +7,8 @@ use crate::instructions::INDEX_START;
use crate::state::*;
use crate::util::fill_from_str;
use crate::logs::TokenMetaDataLog;
const FIRST_BANK_NUM: u32 = 0;
#[derive(Accounts)]
@ -131,5 +133,14 @@ pub fn token_register_trustless(
mint_info.banks[0] = ctx.accounts.bank.key();
mint_info.vaults[0] = ctx.accounts.vault.key();
emit!(TokenMetaDataLog {
mango_group: ctx.accounts.group.key(),
mint: ctx.accounts.mint.key(),
token_index: token_index,
mint_decimals: ctx.accounts.mint.decimals,
oracle: ctx.accounts.oracle.key(),
mint_info: ctx.accounts.mint_info.key(),
});
Ok(())
}

View File

@ -116,7 +116,9 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
avg_utilization: new_avg_utilization.to_bits(),
price: price.to_bits(),
collected_fees: some_bank.collected_fees_native.to_bits(),
loan_fee_rate: some_bank.loan_fee_rate.to_bits()
loan_fee_rate: some_bank.loan_fee_rate.to_bits(),
total_deposits: cm!(deposit_index * indexed_total_deposits).to_bits(),
total_borrows: cm!(borrow_index * indexed_total_borrows).to_bits(),
});
drop(some_bank);

View File

@ -5,11 +5,10 @@ use crate::{
use anchor_lang::prelude::*;
use borsh::BorshSerialize;
/// Warning: This function needs 512+ bytes free on the stack
pub fn emit_perp_balances(
mango_group: Pubkey,
mango_account: Pubkey,
market_index: u64,
market_index: u16,
pp: &PerpPosition,
pm: &PerpMarket,
) {
@ -30,7 +29,7 @@ pub fn emit_perp_balances(
pub struct PerpBalanceLog {
pub mango_group: Pubkey,
pub mango_account: Pubkey,
pub market_index: u64, // IDL doesn't support usize
pub market_index: u16,
pub base_position: i64,
pub quote_position: i128, // I80F48
pub long_settled_funding: i128, // I80F48
@ -117,12 +116,14 @@ pub struct FillLog {
}
#[event]
pub struct UpdateFundingLog {
pub struct PerpUpdateFundingLog {
pub mango_group: Pubkey,
pub market_index: u16,
pub long_funding: i128, // I80F48
pub short_funding: i128, // I80F48
pub price: i128, // I80F48
pub long_funding: i128,
pub short_funding: i128,
pub price: i128,
pub fees_accrued: i128,
pub open_interest: i64,
}
#[event]
@ -135,6 +136,8 @@ pub struct UpdateIndexLog {
pub price: i128, // I80F48
pub collected_fees: i128, // I80F48
pub loan_fee_rate: i128, // I80F48
pub total_borrows: i128,
pub total_deposits: i128,
}
#[event]
@ -147,7 +150,7 @@ pub struct UpdateRateLog {
}
#[event]
pub struct LiquidateTokenAndTokenLog {
pub struct TokenLiqWithTokenLog {
pub mango_group: Pubkey,
pub liqee: Pubkey,
pub liqor: Pubkey,
@ -196,7 +199,7 @@ pub struct WithdrawLoanOriginationFeeLog {
}
#[event]
pub struct LiquidateTokenBankruptcyLog {
pub struct TokenLiqBankruptcyLog {
pub mango_group: Pubkey,
pub liqee: Pubkey,
pub liqor: Pubkey,
@ -213,6 +216,90 @@ pub struct DeactivateTokenPositionLog {
pub mango_group: Pubkey,
pub mango_account: Pubkey,
pub token_index: u16,
pub cumulative_deposit_interest: f32,
pub cumulative_borrow_interest: f32,
pub cumulative_deposit_interest: f64,
pub cumulative_borrow_interest: f64,
}
#[event]
pub struct DeactivatePerpPositionLog {
pub mango_group: Pubkey,
pub mango_account: Pubkey,
pub market_index: u16,
pub cumulative_long_funding: f64,
pub cumulative_short_funding: f64,
pub maker_volume: u64,
pub taker_volume: u64,
pub perp_spot_transfers: i64,
}
#[event]
pub struct TokenMetaDataLog {
pub mango_group: Pubkey,
pub mint: Pubkey,
pub token_index: u16,
pub mint_decimals: u8,
pub oracle: Pubkey,
pub mint_info: Pubkey,
}
#[event]
pub struct PerpMarketMetaDataLog {
pub mango_group: Pubkey,
pub perp_market: Pubkey,
pub perp_market_index: u16,
pub base_decimals: u8,
pub base_lot_size: i64,
pub quote_lot_size: i64,
pub oracle: Pubkey,
}
#[event]
pub struct Serum3RegisterMarketLog {
pub mango_group: Pubkey,
pub serum_market: Pubkey,
pub market_index: u16,
pub base_token_index: u16,
pub quote_token_index: u16,
pub serum_program: Pubkey,
pub serum_program_external: Pubkey,
}
#[event]
pub struct PerpLiqBasePositionLog {
pub mango_group: Pubkey,
pub perp_market_index: u16,
pub liqor: Pubkey,
pub liqee: Pubkey,
pub base_transfer: i64,
pub quote_transfer: i128,
pub price: i128,
}
#[event]
pub struct PerpLiqBankruptcyLog {
pub mango_group: Pubkey,
pub liqee: Pubkey,
pub liqor: Pubkey,
pub perp_market_index: u16,
pub insurance_transfer: i128,
pub socialized_loss: i128,
}
#[event]
pub struct PerpSettlePnlLog {
pub mango_group: Pubkey,
pub mango_account_a: Pubkey,
pub mango_account_b: Pubkey,
pub perp_market_index: u16,
pub settlement: i128,
pub settler: Pubkey,
pub fee: i128,
}
#[event]
pub struct PerpSettleFeesLog {
pub mango_group: Pubkey,
pub mango_account: Pubkey,
pub perp_market_index: u16,
pub settlement: i128,
}

View File

@ -417,13 +417,13 @@ impl Bank {
if opening_indexed_position.is_positive() {
let interest =
cm!((self.deposit_index - position.previous_index) * opening_indexed_position)
.to_num::<f32>();
.to_num::<f64>();
position.cumulative_deposit_interest += interest;
} else {
let interest =
cm!((self.borrow_index - position.previous_index) * opening_indexed_position)
.to_num::<f32>();
position.cumulative_borrow_interest += interest;
.to_num::<f64>();
position.cumulative_borrow_interest -= interest;
}
if position.indexed_position.is_positive() {
@ -670,7 +670,7 @@ mod tests {
cumulative_borrow_interest: 0.0,
previous_index: I80F48::ZERO,
padding: Default::default(),
reserved: [0; 16],
reserved: [0; 8],
};
account.indexed_position = indexed(I80F48::from_num(start), &bank);

View File

@ -24,7 +24,7 @@ use super::TokenIndex;
use super::FREE_ORDER_SLOT;
use super::{HealthCache, HealthType};
use super::{PerpPosition, Serum3Orders, TokenPosition};
use crate::logs::DeactivateTokenPositionLog;
use crate::logs::{DeactivatePerpPositionLog, DeactivateTokenPositionLog};
use checked_math as cm;
type BorshVecLength = u32;
@ -79,9 +79,8 @@ pub struct MangoAccount {
// in USD units with 6 decimals
pub net_deposits: i64,
// (Display only)
// Cumulative settles on perp positions
// TODO: unimplemented
pub net_settled: i64,
// Cumulative transfers from perp to spot positions
pub perp_spot_transfers: i64,
/// Init health as calculated during HealthReginBegin, rounded up.
pub health_region_pre_init_health: i64,
@ -119,7 +118,7 @@ impl MangoAccount {
bump: 0,
padding: Default::default(),
net_deposits: 0,
net_settled: 0,
perp_spot_transfers: 0,
health_region_pre_init_health: 0,
reserved: [0; 240],
header_version: DEFAULT_MANGO_ACCOUNT_VERSION,
@ -199,7 +198,7 @@ pub struct MangoAccountFixed {
pub bump: u8,
pub padding: [u8; 1],
pub net_deposits: i64,
pub net_settled: i64,
pub perp_spot_transfers: i64,
pub health_region_begin_init_health: i64,
pub reserved: [u8; 240],
}
@ -623,7 +622,7 @@ impl<
cumulative_borrow_interest: 0.0,
previous_index: I80F48::ZERO,
padding: Default::default(),
reserved: [0; 16],
reserved: [0; 8],
};
}
Ok((v, raw_index, bank_index))
@ -765,6 +764,34 @@ impl<
Ok(())
}
pub fn deactivate_perp_position_and_log(
&mut self,
perp_market_index: PerpMarketIndex,
settle_token_index: TokenIndex,
mango_account_pubkey: Pubkey,
) -> Result<()> {
let mango_group = self.fixed.deref_or_borrow().group;
let perp_position = self.perp_position_mut(perp_market_index)?;
emit!(DeactivatePerpPositionLog {
mango_group: mango_group,
mango_account: mango_account_pubkey,
market_index: perp_market_index,
cumulative_long_funding: perp_position.cumulative_long_funding,
cumulative_short_funding: perp_position.cumulative_long_funding,
maker_volume: perp_position.maker_volume,
taker_volume: perp_position.taker_volume,
perp_spot_transfers: perp_position.perp_spot_transfers,
});
perp_position.market_index = PerpMarketIndex::MAX;
let mut settle_token_position = self.token_position_mut(settle_token_index)?.0;
cm!(settle_token_position.in_use_count -= 1);
Ok(())
}
pub fn add_perp_order(
&mut self,
perp_market_index: PerpMarketIndex,
@ -837,6 +864,8 @@ impl<
let quote_change_native = cm!(quote - fees);
pa.change_base_and_quote_positions(perp_market, base_change, quote_change_native);
cm!(pa.maker_volume += quote.abs().to_num::<u64>());
if fill.maker_out {
self.remove_perp_order(fill.maker_slot as usize, base_change.abs())
} else {
@ -868,6 +897,8 @@ impl<
cm!(I80F48::from(perp_market.quote_lot_size) * I80F48::from(quote_change));
pa.change_base_and_quote_positions(perp_market, base_change, quote_change_native);
cm!(pa.taker_volume += quote_change_native.abs().to_num::<u64>());
Ok(())
}
@ -1064,7 +1095,7 @@ mod tests {
account.in_health_region = 3;
account.bump = 4;
account.net_deposits = 5;
account.net_settled = 6;
account.perp_spot_transfers = 6;
account.health_region_pre_init_health = 7;
account.tokens.resize(8, TokenPosition::default());
account.tokens[0].token_index = 8;
@ -1089,7 +1120,10 @@ mod tests {
assert_eq!(account.in_health_region, account2.fixed.in_health_region);
assert_eq!(account.bump, account2.fixed.bump);
assert_eq!(account.net_deposits, account2.fixed.net_deposits);
assert_eq!(account.net_settled, account2.fixed.net_settled);
assert_eq!(
account.perp_spot_transfers,
account2.fixed.perp_spot_transfers
);
assert_eq!(
account.health_region_pre_init_health,
account2.fixed.health_region_begin_init_health

View File

@ -32,18 +32,17 @@ pub struct TokenPosition {
pub padding: [u8; 5],
#[derivative(Debug = "ignore")]
pub reserved: [u8; 16],
pub reserved: [u8; 8],
// bookkeeping variable for onchain interest calculation
// either deposit_index or borrow_index at last indexed_position change
pub previous_index: I80F48,
// (Display only)
// Cumulative deposit interest in token native units
pub cumulative_deposit_interest: f32,
pub cumulative_deposit_interest: f64,
// (Display only)
// Cumulative borrow interest in token native units
pub cumulative_borrow_interest: f32,
pub cumulative_borrow_interest: f64,
}
unsafe impl bytemuck::Pod for TokenPosition {}
@ -62,7 +61,7 @@ impl Default for TokenPosition {
cumulative_borrow_interest: 0.0,
previous_index: I80F48::ZERO,
padding: Default::default(),
reserved: [0; 16],
reserved: [0; 8],
}
}
}
@ -193,7 +192,23 @@ pub struct PerpPosition {
pub taker_quote_lots: i64,
#[derivative(Debug = "ignore")]
pub reserved: [u8; 64],
pub reserved: [u8; 24],
// (Display only)
// Cumulative long funding in base native units
pub cumulative_long_funding: f64,
// (Display only)
// Cumulative short funding in base native units
pub cumulative_short_funding: f64,
// (Display only)
// Cumulative maker volume in quote native units
pub maker_volume: u64,
// (Display only)
// Cumulative taker volume in quote native units
pub taker_volume: u64,
// (Display only)
// Cumulative realized pnl in quote native units
pub perp_spot_transfers: i64,
}
const_assert_eq!(size_of::<PerpPosition>(), 8 + 7 * 8 + 3 * 16 + 64);
const_assert_eq!(size_of::<PerpPosition>() % 8, 0);
@ -213,10 +228,15 @@ impl Default for PerpPosition {
asks_base_lots: 0,
taker_base_lots: 0,
taker_quote_lots: 0,
reserved: [0; 64],
reserved: [0; 24],
long_settled_funding: I80F48::ZERO,
short_settled_funding: I80F48::ZERO,
padding: Default::default(),
cumulative_long_funding: 0.0,
cumulative_short_funding: 0.0,
maker_volume: 0,
taker_volume: 0,
perp_spot_transfers: 0,
}
}
}
@ -288,6 +308,13 @@ impl PerpPosition {
pub fn settle_funding(&mut self, perp_market: &PerpMarket) {
let funding = self.unsettled_funding(perp_market);
cm!(self.quote_position_native -= funding);
if self.base_position_lots.is_positive() {
self.cumulative_long_funding += funding.to_num::<f64>();
} else {
self.cumulative_short_funding -= funding.to_num::<f64>();
}
self.long_settled_funding = perp_market.long_funding;
self.short_settled_funding = perp_market.short_funding;
}

View File

@ -469,9 +469,13 @@ fn apply_fees(
let taker_fees = cm!(taker_quote_native * market.taker_fee);
// taker fees should never be negative
require_gte!(taker_fees, 0);
let perp_account = mango_account.perp_position_mut(market.perp_market_index)?;
perp_account.change_quote_position(-taker_fees);
cm!(market.fees_accrued += taker_fees + maker_fees);
cm!(perp_account.taker_volume += taker_fees.to_num::<u64>());
Ok(())
}

View File

@ -421,11 +421,11 @@ async fn test_perp_settle_pnl() -> Result<(), TransportError> {
);
assert_eq!(
mango_account_0.net_settled, expected_total_settle,
mango_account_0.perp_spot_transfers, expected_total_settle,
"net_settled on account 0 updated with profit from settlement"
);
assert_eq!(
mango_account_1.net_settled, -expected_total_settle,
mango_account_1.perp_spot_transfers, -expected_total_settle,
"net_settled on account 1 updated with loss from settlement"
);
}
@ -517,11 +517,11 @@ async fn test_perp_settle_pnl() -> Result<(), TransportError> {
);
assert_eq!(
mango_account_0.net_settled, expected_total_settle,
mango_account_0.perp_spot_transfers, expected_total_settle,
"net_settled on account 0 updated with loss from settlement"
);
assert_eq!(
mango_account_1.net_settled, -expected_total_settle,
mango_account_1.perp_spot_transfers, -expected_total_settle,
"net_settled on account 1 updated with profit from settlement"
);
}

View File

@ -469,9 +469,9 @@ async fn test_perp_settle_fees() -> Result<(), TransportError> {
);
assert_eq!(
mango_account_1.net_settled,
mango_account_1.perp_spot_transfers,
-(partial_settle_amount as i64),
"net_settled on account 1 updated with loss from settlement"
"perp_spot_transfers on account 1 updated with loss from settlement"
);
assert_eq!(
@ -523,8 +523,8 @@ async fn test_perp_settle_fees() -> Result<(), TransportError> {
);
assert_eq!(
mango_account_1.net_settled, -initial_fees,
"net_settled on account 1 updated with loss from settlement"
mango_account_1.perp_spot_transfers, -initial_fees,
"perp_spot_transfers on account 1 updated with loss from settlement"
);
assert_eq!(