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:
parent
1ee1c2e114
commit
96344d7de1
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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!(
|
||||
|
|
Loading…
Reference in New Issue