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:
Nicholas Clarke 2022-07-06 03:07:30 -07:00 committed by GitHub
parent 5b72e85634
commit 348fbc1cb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 5 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,
}
}
}

View File

@ -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");

View File

@ -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>()
);
}
//