Track cumulative net deposits (deposits - withdraws) (#91)
Track cumulative net deposits (deposits - withdraws) using prices at the time of the deposit and withdraw. This is used for calculating overall pnl (across all tokens). I want to store UI amount * UI price = (native amount / base decimals) * (oracle price * base decimals / quote decimals) => native amount * oracle price / quote decimals. I have used f32 here to reduce the space required on the mango account - we don't need so much precision for this as it's purely a display value. I've also included a field for net_settled - this will be used for perp pnl but is not implemented yet (as perp settling instructions are not ready). Co-authored-by: Christian Kamm <mail@ckamm.de>
This commit is contained in:
parent
5b72e85634
commit
348fbc1cb9
|
@ -6,6 +6,7 @@ use fixed::types::I80F48;
|
|||
|
||||
use crate::error::*;
|
||||
use crate::state::*;
|
||||
use crate::util::checked_math as cm;
|
||||
|
||||
use crate::logs::{DepositLog, TokenBalanceLog};
|
||||
|
||||
|
@ -65,9 +66,10 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
|||
let (position, raw_token_index, active_token_index) =
|
||||
account.tokens.get_mut_or_create(token_index)?;
|
||||
|
||||
let amount_i80f48 = I80F48::from(amount);
|
||||
let position_is_active = {
|
||||
let mut bank = ctx.accounts.bank.load_mut()?;
|
||||
bank.deposit(position, I80F48::from(amount))?
|
||||
bank.deposit(position, amount_i80f48)?
|
||||
};
|
||||
|
||||
// Transfer the actual tokens
|
||||
|
@ -78,6 +80,10 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
|||
let retriever = new_fixed_order_account_retriever(ctx.remaining_accounts, &account)?;
|
||||
let (bank, oracle_price) =
|
||||
retriever.bank_and_oracle(&ctx.accounts.group.key(), active_token_index, token_index)?;
|
||||
|
||||
// Update the net deposits - adjust by price so different tokens are on the same basis (in USD terms)
|
||||
account.net_deposits += cm!(amount_i80f48 * oracle_price * QUOTE_NATIVE_TO_UI).to_num::<f32>();
|
||||
|
||||
emit!(TokenBalanceLog {
|
||||
mango_account: ctx.accounts.account.key(),
|
||||
token_index: token_index,
|
||||
|
|
|
@ -8,6 +8,7 @@ use fixed::types::I80F48;
|
|||
|
||||
use crate::logs::{TokenBalanceLog, WithdrawLog};
|
||||
use crate::state::new_fixed_order_account_retriever;
|
||||
use crate::util::checked_math as cm;
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct TokenWithdraw<'info> {
|
||||
|
@ -71,7 +72,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
|||
|
||||
// The bank will also be passed in remainingAccounts. Use an explicit scope
|
||||
// to drop the &mut before we borrow it immutably again later.
|
||||
let position_is_active = {
|
||||
let (position_is_active, amount_i80f48) = {
|
||||
let mut bank = ctx.accounts.bank.load_mut()?;
|
||||
let native_position = position.native(&bank);
|
||||
|
||||
|
@ -105,7 +106,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
|||
amount,
|
||||
)?;
|
||||
|
||||
position_is_active
|
||||
(position_is_active, amount_i80f48)
|
||||
};
|
||||
|
||||
let indexed_position = position.indexed_position;
|
||||
|
@ -113,6 +114,10 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
|||
let retriever = new_fixed_order_account_retriever(ctx.remaining_accounts, &account)?;
|
||||
let (bank, oracle_price) =
|
||||
retriever.bank_and_oracle(&ctx.accounts.group.key(), active_token_index, token_index)?;
|
||||
|
||||
// Update the net deposits - adjust by price so different tokens are on the same basis (in USD terms)
|
||||
account.net_deposits -= cm!(amount_i80f48 * oracle_price * QUOTE_NATIVE_TO_UI).to_num::<f32>();
|
||||
|
||||
emit!(TokenBalanceLog {
|
||||
mango_account: ctx.accounts.account.key(),
|
||||
token_index: token_index,
|
||||
|
|
|
@ -736,6 +736,14 @@ pub struct MangoAccount {
|
|||
|
||||
// pub info: [u8; INFO_LEN], // TODO: Info could be in a separate PDA?
|
||||
pub reserved: [u8; 4],
|
||||
|
||||
// Cumulative (deposits - withdraws)
|
||||
// using USD prices at the time of the deposit/withdraw
|
||||
// in UI USD units
|
||||
pub net_deposits: f32,
|
||||
// Cumulative settles on perp positions
|
||||
// TODO: unimplemented
|
||||
pub net_settled: f32,
|
||||
}
|
||||
const_assert_eq!(
|
||||
size_of::<MangoAccount>(),
|
||||
|
@ -745,6 +753,7 @@ const_assert_eq!(
|
|||
+ size_of::<MangoAccountPerpPositions>()
|
||||
+ 4
|
||||
+ 4
|
||||
+ 2 * 4 // net_deposits and net_settled
|
||||
);
|
||||
const_assert_eq!(size_of::<MangoAccount>() % 8, 0);
|
||||
|
||||
|
@ -810,6 +819,8 @@ impl Default for MangoAccount {
|
|||
account_num: 0,
|
||||
bump: 0,
|
||||
reserved: Default::default(),
|
||||
net_deposits: 0.0,
|
||||
net_settled: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ use crate::accounts_zerocopy::*;
|
|||
use crate::checked_math as cm;
|
||||
use crate::error::MangoError;
|
||||
|
||||
pub const QUOTE_DECIMALS: i8 = 6;
|
||||
|
||||
const LOOKUP_START: i8 = -12;
|
||||
const LOOKUP: [I80F48; 25] = [
|
||||
I80F48::from_bits((1 << 48) / 10i128.pow(12u32)),
|
||||
|
@ -44,6 +42,9 @@ const LOOKUP: [I80F48; 25] = [
|
|||
];
|
||||
const LOOKUP_FN: fn(i8) -> usize = |decimals: i8| (decimals - LOOKUP_START) as usize;
|
||||
|
||||
pub const QUOTE_DECIMALS: i8 = 6;
|
||||
pub const QUOTE_NATIVE_TO_UI: I80F48 = LOOKUP[(-QUOTE_DECIMALS - LOOKUP_START) as usize];
|
||||
|
||||
pub mod switchboard_v1_devnet_oracle {
|
||||
use solana_program::declare_id;
|
||||
declare_id!("7azgmy1pFXHikv36q1zZASvFq5vFa39TT9NweVugKKTU");
|
||||
|
|
|
@ -81,6 +81,13 @@ async fn test_basic() -> Result<(), TransportError> {
|
|||
);
|
||||
let bank_data: Bank = solana.get_account(bank).await;
|
||||
assert!(bank_data.native_deposits() - I80F48::from_num(deposit_amount) < dust_threshold);
|
||||
|
||||
let account_data: MangoAccount = solana.get_account(account).await;
|
||||
// Assumes oracle price of 1
|
||||
assert_eq!(
|
||||
account_data.net_deposits,
|
||||
(I80F48::from_num(deposit_amount) * QUOTE_NATIVE_TO_UI).to_num::<f32>()
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -132,6 +139,13 @@ async fn test_basic() -> Result<(), TransportError> {
|
|||
bank_data.native_deposits() - I80F48::from_num(start_amount - withdraw_amount)
|
||||
< dust_threshold
|
||||
);
|
||||
|
||||
let account_data: MangoAccount = solana.get_account(account).await;
|
||||
// Assumes oracle price of 1
|
||||
assert_eq!(
|
||||
account_data.net_deposits,
|
||||
(I80F48::from_num(start_amount - withdraw_amount) * QUOTE_NATIVE_TO_UI).to_num::<f32>()
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue