2022-02-22 04:15:13 -08:00
|
|
|
use anchor_lang::prelude::*;
|
2022-05-27 22:05:34 -07:00
|
|
|
use anchor_spl::token::Mint;
|
2022-04-02 21:06:03 -07:00
|
|
|
use checked_math as cm;
|
2022-02-10 01:04:47 -08:00
|
|
|
use fixed::types::I80F48;
|
2022-03-31 05:01:08 -07:00
|
|
|
use static_assertions::const_assert_eq;
|
2022-05-18 08:16:14 -07:00
|
|
|
use std::cmp::Ordering;
|
2022-03-31 05:01:08 -07:00
|
|
|
use std::mem::size_of;
|
2022-02-10 01:04:47 -08:00
|
|
|
|
2022-02-28 02:07:04 -08:00
|
|
|
use crate::error::*;
|
|
|
|
use crate::state::*;
|
|
|
|
|
2022-03-19 00:52:38 -07:00
|
|
|
// todo: these are arbitrary
|
|
|
|
// ckamm: Either we put hard limits on everything, or we have a simple model for how much
|
|
|
|
// compute a token/serum/perp market needs, so users who don't use serum markets can have
|
|
|
|
// more perp markets open at the same time etc
|
|
|
|
// In particular if perp markets don't require the base token to be active on the account,
|
|
|
|
// we could probably support 1 token (quote currency) + 15 active perp markets at the same time
|
2022-03-19 00:53:20 -07:00
|
|
|
// It's a tradeoff between allowing users to trade on many markets with one account,
|
|
|
|
// MangoAccount size and health compute needs.
|
2022-06-21 07:52:40 -07:00
|
|
|
const MAX_TOKEN_POSITIONS: usize = 16;
|
2022-04-01 03:22:03 -07:00
|
|
|
const MAX_SERUM3_ACCOUNTS: usize = 8;
|
|
|
|
const MAX_PERP_ACCOUNTS: usize = 8;
|
2022-03-28 12:13:16 -07:00
|
|
|
pub const MAX_PERP_OPEN_ORDERS: usize = 8;
|
|
|
|
|
|
|
|
pub const FREE_ORDER_SLOT: PerpMarketIndex = PerpMarketIndex::MAX;
|
2022-02-22 00:32:21 -08:00
|
|
|
|
|
|
|
#[zero_copy]
|
2022-05-05 01:25:32 -07:00
|
|
|
#[derive(Debug)]
|
2022-06-21 07:52:40 -07:00
|
|
|
pub struct TokenPosition {
|
2022-02-22 00:32:21 -08:00
|
|
|
// TODO: Why did we have deposits and borrows as two different values
|
|
|
|
// if only one of them was allowed to be != 0 at a time?
|
|
|
|
// todo: maybe we want to split collateral and lending?
|
|
|
|
// todo: see https://github.com/blockworks-foundation/mango-v4/issues/1
|
|
|
|
// todo: how does ftx do this?
|
|
|
|
/// The deposit_index (if positive) or borrow_index (if negative) scaled position
|
2022-06-21 07:52:40 -07:00
|
|
|
pub indexed_position: I80F48,
|
2022-02-10 00:07:34 -08:00
|
|
|
|
2022-03-07 07:16:34 -08:00
|
|
|
/// index into Group.tokens
|
2022-02-22 05:23:13 -08:00
|
|
|
pub token_index: TokenIndex,
|
2022-03-15 06:44:47 -07:00
|
|
|
|
|
|
|
/// incremented when a market requires this position to stay alive
|
|
|
|
pub in_use_count: u8,
|
2022-03-31 05:01:08 -07:00
|
|
|
|
|
|
|
pub reserved: [u8; 5],
|
2022-02-22 00:32:21 -08:00
|
|
|
}
|
2022-06-21 07:52:40 -07:00
|
|
|
const_assert_eq!(size_of::<TokenPosition>(), 24);
|
|
|
|
const_assert_eq!(size_of::<TokenPosition>() % 8, 0);
|
2022-02-22 00:32:21 -08:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl TokenPosition {
|
2022-02-22 00:32:21 -08:00
|
|
|
pub fn is_active(&self) -> bool {
|
2022-03-04 04:33:27 -08:00
|
|
|
self.token_index != TokenIndex::MAX
|
2022-02-23 01:09:01 -08:00
|
|
|
}
|
|
|
|
|
2022-03-14 05:34:54 -07:00
|
|
|
pub fn is_active_for_token(&self, token_index: TokenIndex) -> bool {
|
|
|
|
self.token_index == token_index
|
2022-02-25 06:14:15 -08:00
|
|
|
}
|
|
|
|
|
2022-03-07 07:16:34 -08:00
|
|
|
pub fn native(&self, bank: &Bank) -> I80F48 {
|
2022-06-21 07:52:40 -07:00
|
|
|
if self.indexed_position.is_positive() {
|
|
|
|
self.indexed_position * bank.deposit_index
|
2022-02-23 01:09:01 -08:00
|
|
|
} else {
|
2022-06-21 07:52:40 -07:00
|
|
|
self.indexed_position * bank.borrow_index
|
2022-02-23 01:09:01 -08:00
|
|
|
}
|
2022-02-22 00:32:21 -08:00
|
|
|
}
|
2022-03-15 06:44:47 -07:00
|
|
|
|
2022-05-27 22:05:34 -07:00
|
|
|
pub fn ui(&self, bank: &Bank, mint: &Mint) -> I80F48 {
|
2022-06-21 07:52:40 -07:00
|
|
|
if self.indexed_position.is_positive() {
|
|
|
|
(self.indexed_position * bank.deposit_index)
|
2022-05-27 22:05:34 -07:00
|
|
|
/ I80F48::from_num(10u64.pow(mint.decimals as u32))
|
|
|
|
} else {
|
2022-06-21 07:52:40 -07:00
|
|
|
(self.indexed_position * bank.borrow_index)
|
2022-05-27 22:05:34 -07:00
|
|
|
/ I80F48::from_num(10u64.pow(mint.decimals as u32))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-15 06:44:47 -07:00
|
|
|
pub fn is_in_use(&self) -> bool {
|
|
|
|
self.in_use_count > 0
|
|
|
|
}
|
2022-02-22 00:32:21 -08:00
|
|
|
}
|
|
|
|
|
2022-02-22 08:13:26 -08:00
|
|
|
#[zero_copy]
|
2022-06-21 07:52:40 -07:00
|
|
|
pub struct MangoAccountTokenPositions {
|
|
|
|
pub values: [TokenPosition; MAX_TOKEN_POSITIONS],
|
2022-02-22 08:13:26 -08:00
|
|
|
}
|
2022-04-02 02:21:41 -07:00
|
|
|
const_assert_eq!(
|
2022-06-21 07:52:40 -07:00
|
|
|
size_of::<MangoAccountTokenPositions>(),
|
|
|
|
MAX_TOKEN_POSITIONS * size_of::<TokenPosition>()
|
2022-04-02 02:21:41 -07:00
|
|
|
);
|
2022-06-21 07:52:40 -07:00
|
|
|
const_assert_eq!(size_of::<MangoAccountTokenPositions>() % 8, 0);
|
2022-02-22 08:13:26 -08:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl std::fmt::Debug for MangoAccountTokenPositions {
|
2022-05-05 01:25:32 -07:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("MangoAccountTokens")
|
|
|
|
.field(
|
|
|
|
"values",
|
|
|
|
&self
|
|
|
|
.values
|
|
|
|
.iter()
|
|
|
|
.filter(|value| value.is_active())
|
2022-06-21 07:52:40 -07:00
|
|
|
.collect::<Vec<&TokenPosition>>(),
|
2022-05-05 01:25:32 -07:00
|
|
|
)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl Default for MangoAccountTokenPositions {
|
2022-03-23 01:33:51 -07:00
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl MangoAccountTokenPositions {
|
2022-03-04 04:33:27 -08:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2022-06-21 07:52:40 -07:00
|
|
|
values: [TokenPosition {
|
|
|
|
indexed_position: I80F48::ZERO,
|
2022-03-04 04:33:27 -08:00
|
|
|
token_index: TokenIndex::MAX,
|
2022-03-15 06:44:47 -07:00
|
|
|
in_use_count: 0,
|
2022-03-31 05:37:05 -07:00
|
|
|
reserved: Default::default(),
|
2022-06-21 07:52:40 -07:00
|
|
|
}; MAX_TOKEN_POSITIONS],
|
2022-03-04 04:33:27 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn get(&self, token_index: TokenIndex) -> Result<&TokenPosition> {
|
2022-03-25 08:15:56 -07:00
|
|
|
self.values
|
|
|
|
.iter()
|
|
|
|
.find(|p| p.is_active_for_token(token_index))
|
|
|
|
.ok_or_else(|| error!(MangoError::SomeError)) // TODO: not found error
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn get_mut(&mut self, token_index: TokenIndex) -> Result<&mut TokenPosition> {
|
2022-02-25 06:14:15 -08:00
|
|
|
self.values
|
|
|
|
.iter_mut()
|
2022-03-04 04:33:27 -08:00
|
|
|
.find(|p| p.is_active_for_token(token_index))
|
2022-02-28 03:56:26 -08:00
|
|
|
.ok_or_else(|| error!(MangoError::SomeError)) // TODO: not found error
|
2022-02-25 06:14:15 -08:00
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn get_mut_raw(&mut self, raw_token_index: usize) -> &mut TokenPosition {
|
2022-05-24 04:00:32 -07:00
|
|
|
&mut self.values[raw_token_index]
|
|
|
|
}
|
|
|
|
|
2022-03-04 04:33:27 -08:00
|
|
|
pub fn get_mut_or_create(
|
|
|
|
&mut self,
|
2022-03-14 05:34:54 -07:00
|
|
|
token_index: TokenIndex,
|
2022-06-21 07:52:40 -07:00
|
|
|
) -> Result<(&mut TokenPosition, usize)> {
|
2022-02-22 08:13:26 -08:00
|
|
|
// This function looks complex because of lifetimes.
|
|
|
|
// Maybe there's a smart way to write it with double iter_mut()
|
|
|
|
// that doesn't confuse the borrow checker.
|
|
|
|
let mut pos = self
|
|
|
|
.values
|
|
|
|
.iter()
|
2022-03-04 04:33:27 -08:00
|
|
|
.position(|p| p.is_active_for_token(token_index));
|
2022-02-22 08:13:26 -08:00
|
|
|
if pos.is_none() {
|
|
|
|
pos = self.values.iter().position(|p| !p.is_active());
|
|
|
|
if let Some(i) = pos {
|
2022-06-21 07:52:40 -07:00
|
|
|
self.values[i] = TokenPosition {
|
|
|
|
indexed_position: I80F48::ZERO,
|
2022-03-22 03:19:12 -07:00
|
|
|
token_index,
|
2022-03-15 06:44:47 -07:00
|
|
|
in_use_count: 0,
|
2022-03-31 05:37:05 -07:00
|
|
|
reserved: Default::default(),
|
2022-02-22 08:13:26 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(i) = pos {
|
2022-03-04 04:33:27 -08:00
|
|
|
Ok((&mut self.values[i], i))
|
2022-02-22 08:13:26 -08:00
|
|
|
} else {
|
|
|
|
err!(MangoError::SomeError) // TODO: No free space
|
|
|
|
}
|
|
|
|
}
|
2022-02-26 08:47:16 -08:00
|
|
|
|
2022-03-04 04:33:27 -08:00
|
|
|
pub fn deactivate(&mut self, index: usize) {
|
2022-03-15 06:44:47 -07:00
|
|
|
assert!(self.values[index].in_use_count == 0);
|
2022-03-04 04:33:27 -08:00
|
|
|
self.values[index].token_index = TokenIndex::MAX;
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn iter_active(&self) -> impl Iterator<Item = &TokenPosition> {
|
2022-02-26 08:47:16 -08:00
|
|
|
self.values.iter().filter(|p| p.is_active())
|
|
|
|
}
|
2022-03-15 06:44:47 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn find(&self, token_index: TokenIndex) -> Option<&TokenPosition> {
|
2022-03-15 06:44:47 -07:00
|
|
|
self.values
|
|
|
|
.iter()
|
|
|
|
.find(|p| p.is_active_for_token(token_index))
|
|
|
|
}
|
2022-02-22 08:13:26 -08:00
|
|
|
}
|
|
|
|
|
2022-03-11 08:49:40 -08:00
|
|
|
#[zero_copy]
|
2022-05-05 01:25:32 -07:00
|
|
|
#[derive(Debug)]
|
2022-06-21 07:52:40 -07:00
|
|
|
pub struct Serum3Orders {
|
2022-03-11 08:49:40 -08:00
|
|
|
pub open_orders: Pubkey,
|
|
|
|
|
2022-06-21 02:45:38 -07:00
|
|
|
// tracks reserved funds in open orders account,
|
|
|
|
// used for bookkeeping of potentital loans which
|
|
|
|
// can be charged with loan origination fees
|
|
|
|
// e.g. serum3 settle funds ix
|
|
|
|
pub previous_native_coin_reserved: u64,
|
|
|
|
pub previous_native_pc_reserved: u64,
|
|
|
|
|
2022-03-18 05:42:20 -07:00
|
|
|
pub market_index: Serum3MarketIndex,
|
2022-03-16 05:48:43 -07:00
|
|
|
|
|
|
|
/// Store the base/quote token index, so health computations don't need
|
|
|
|
/// to get passed the static SerumMarket to find which tokens a market
|
|
|
|
/// uses and look up the correct oracles.
|
|
|
|
pub base_token_index: TokenIndex,
|
|
|
|
pub quote_token_index: TokenIndex,
|
2022-03-31 05:01:08 -07:00
|
|
|
|
|
|
|
pub reserved: [u8; 2],
|
2022-03-11 08:49:40 -08:00
|
|
|
}
|
2022-06-21 07:52:40 -07:00
|
|
|
const_assert_eq!(size_of::<Serum3Orders>(), 32 + 8 * 2 + 2 * 3 + 2);
|
|
|
|
const_assert_eq!(size_of::<Serum3Orders>() % 8, 0);
|
2022-03-11 08:49:40 -08:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl Serum3Orders {
|
2022-03-11 08:49:40 -08:00
|
|
|
pub fn is_active(&self) -> bool {
|
2022-03-18 05:42:20 -07:00
|
|
|
self.market_index != Serum3MarketIndex::MAX
|
2022-03-11 08:49:40 -08:00
|
|
|
}
|
|
|
|
|
2022-03-18 05:42:20 -07:00
|
|
|
pub fn is_active_for_market(&self, market_index: Serum3MarketIndex) -> bool {
|
2022-03-11 08:49:40 -08:00
|
|
|
self.market_index == market_index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl Default for Serum3Orders {
|
2022-03-16 05:48:43 -07:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
open_orders: Pubkey::default(),
|
2022-03-18 05:42:20 -07:00
|
|
|
market_index: Serum3MarketIndex::MAX,
|
2022-03-16 05:48:43 -07:00
|
|
|
base_token_index: TokenIndex::MAX,
|
|
|
|
quote_token_index: TokenIndex::MAX,
|
2022-03-31 05:37:05 -07:00
|
|
|
reserved: Default::default(),
|
2022-06-21 02:45:38 -07:00
|
|
|
previous_native_coin_reserved: 0,
|
|
|
|
previous_native_pc_reserved: 0,
|
2022-03-16 05:48:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-11 08:49:40 -08:00
|
|
|
#[zero_copy]
|
2022-06-21 07:52:40 -07:00
|
|
|
pub struct MangoAccountSerum3Orders {
|
|
|
|
pub values: [Serum3Orders; MAX_SERUM3_ACCOUNTS],
|
2022-03-11 08:49:40 -08:00
|
|
|
}
|
2022-04-02 02:21:41 -07:00
|
|
|
const_assert_eq!(
|
2022-06-21 07:52:40 -07:00
|
|
|
size_of::<MangoAccountSerum3Orders>(),
|
|
|
|
MAX_SERUM3_ACCOUNTS * size_of::<Serum3Orders>()
|
2022-04-02 02:21:41 -07:00
|
|
|
);
|
2022-06-21 07:52:40 -07:00
|
|
|
const_assert_eq!(size_of::<MangoAccountSerum3Orders>() % 8, 0);
|
2022-03-11 08:49:40 -08:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl std::fmt::Debug for MangoAccountSerum3Orders {
|
2022-05-05 01:25:32 -07:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("MangoAccountSerum3")
|
|
|
|
.field(
|
|
|
|
"values",
|
|
|
|
&self
|
|
|
|
.values
|
|
|
|
.iter()
|
|
|
|
.filter(|value| value.is_active())
|
2022-06-21 07:52:40 -07:00
|
|
|
.collect::<Vec<&Serum3Orders>>(),
|
2022-05-05 01:25:32 -07:00
|
|
|
)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl Default for MangoAccountSerum3Orders {
|
2022-03-23 01:33:51 -07:00
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl MangoAccountSerum3Orders {
|
2022-03-11 08:49:40 -08:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2022-06-21 07:52:40 -07:00
|
|
|
values: [Serum3Orders::default(); MAX_SERUM3_ACCOUNTS],
|
2022-03-11 08:49:40 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn create(&mut self, market_index: Serum3MarketIndex) -> Result<&mut Serum3Orders> {
|
2022-03-14 05:19:50 -07:00
|
|
|
if self.find(market_index).is_some() {
|
2022-03-11 08:49:40 -08:00
|
|
|
return err!(MangoError::SomeError); // exists already
|
|
|
|
}
|
|
|
|
if let Some(v) = self.values.iter_mut().find(|p| !p.is_active()) {
|
2022-06-21 07:52:40 -07:00
|
|
|
*v = Serum3Orders {
|
2022-03-18 05:42:20 -07:00
|
|
|
market_index: market_index as Serum3MarketIndex,
|
2022-06-21 07:52:40 -07:00
|
|
|
..Serum3Orders::default()
|
2022-03-11 08:49:40 -08:00
|
|
|
};
|
|
|
|
Ok(v)
|
|
|
|
} else {
|
|
|
|
err!(MangoError::SomeError) // no space
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
pub fn deactivate(&mut self, market_index: Serum3MarketIndex) -> Result<()> {
|
|
|
|
let index = self
|
|
|
|
.values
|
|
|
|
.iter()
|
|
|
|
.position(|p| p.is_active_for_market(market_index))
|
|
|
|
.ok_or(MangoError::SomeError)?;
|
|
|
|
|
2022-03-18 05:42:20 -07:00
|
|
|
self.values[index].market_index = Serum3MarketIndex::MAX;
|
2022-06-09 09:27:31 -07:00
|
|
|
|
|
|
|
Ok(())
|
2022-03-11 08:49:40 -08:00
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn iter_active(&self) -> impl Iterator<Item = &Serum3Orders> {
|
2022-03-11 08:49:40 -08:00
|
|
|
self.values.iter().filter(|p| p.is_active())
|
|
|
|
}
|
2022-03-14 05:19:50 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn find(&self, market_index: Serum3MarketIndex) -> Option<&Serum3Orders> {
|
2022-03-14 05:19:50 -07:00
|
|
|
self.values
|
|
|
|
.iter()
|
|
|
|
.find(|p| p.is_active_for_market(market_index))
|
|
|
|
}
|
2022-06-21 02:45:38 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn find_mut(&mut self, market_index: Serum3MarketIndex) -> Option<&mut Serum3Orders> {
|
2022-06-21 02:45:38 -07:00
|
|
|
self.values
|
|
|
|
.iter_mut()
|
|
|
|
.find(|p| p.is_active_for_market(market_index))
|
|
|
|
}
|
2022-03-11 08:49:40 -08:00
|
|
|
}
|
|
|
|
|
2022-03-25 01:46:38 -07:00
|
|
|
#[zero_copy]
|
2022-06-21 07:52:40 -07:00
|
|
|
pub struct PerpPositions {
|
2022-03-25 01:46:38 -07:00
|
|
|
pub market_index: PerpMarketIndex,
|
2022-03-31 05:01:08 -07:00
|
|
|
pub reserved: [u8; 6],
|
2022-03-25 01:46:38 -07:00
|
|
|
|
|
|
|
/// Active position size, measured in base lots
|
2022-04-01 06:47:12 -07:00
|
|
|
pub base_position_lots: i64,
|
2022-03-25 01:46:38 -07:00
|
|
|
/// Active position in quote (conversation rate is that of the time the order was settled)
|
|
|
|
/// measured in native quote
|
2022-04-01 06:47:12 -07:00
|
|
|
pub quote_position_native: I80F48,
|
2022-03-25 01:46:38 -07:00
|
|
|
|
|
|
|
/// Already settled funding
|
2022-05-16 06:34:56 -07:00
|
|
|
pub long_settled_funding: I80F48,
|
|
|
|
pub short_settled_funding: I80F48,
|
2022-03-25 01:46:38 -07:00
|
|
|
|
2022-04-01 06:47:12 -07:00
|
|
|
/// Base lots in bids
|
|
|
|
pub bids_base_lots: i64,
|
|
|
|
/// Base lots in asks
|
|
|
|
pub asks_base_lots: i64,
|
2022-03-25 01:46:38 -07:00
|
|
|
|
|
|
|
/// Liquidity mining rewards
|
|
|
|
// pub mngo_accrued: u64,
|
|
|
|
|
|
|
|
/// Amount that's on EventQueue waiting to be processed
|
2022-04-01 06:47:12 -07:00
|
|
|
pub taker_base_lots: i64,
|
|
|
|
pub taker_quote_lots: i64,
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
2022-05-16 01:34:22 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl std::fmt::Debug for PerpPositions {
|
2022-05-16 01:34:22 -07:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("PerpAccount")
|
|
|
|
.field("market_index", &self.market_index)
|
|
|
|
.field("base_position_lots", &self.base_position_lots)
|
|
|
|
.field("quote_position_native", &self.quote_position_native)
|
|
|
|
.field("bids_base_lots", &self.bids_base_lots)
|
|
|
|
.field("asks_base_lots", &self.asks_base_lots)
|
|
|
|
.field("taker_base_lots", &self.taker_base_lots)
|
|
|
|
.field("taker_quote_lots", &self.taker_quote_lots)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
2022-06-21 07:52:40 -07:00
|
|
|
const_assert_eq!(size_of::<PerpPositions>(), 8 + 8 * 5 + 3 * 16);
|
|
|
|
const_assert_eq!(size_of::<PerpPositions>() % 8, 0);
|
2022-03-25 01:46:38 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl Default for PerpPositions {
|
2022-03-25 01:46:38 -07:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
market_index: PerpMarketIndex::MAX,
|
2022-04-01 06:47:12 -07:00
|
|
|
base_position_lots: 0,
|
|
|
|
quote_position_native: I80F48::ZERO,
|
|
|
|
bids_base_lots: 0,
|
|
|
|
asks_base_lots: 0,
|
|
|
|
taker_base_lots: 0,
|
|
|
|
taker_quote_lots: 0,
|
2022-03-31 05:37:05 -07:00
|
|
|
reserved: Default::default(),
|
2022-05-16 06:34:56 -07:00
|
|
|
long_settled_funding: I80F48::ZERO,
|
|
|
|
short_settled_funding: I80F48::ZERO,
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl PerpPositions {
|
2022-03-25 01:46:38 -07:00
|
|
|
/// Add taker trade after it has been matched but before it has been process on EventQueue
|
2022-04-04 00:23:01 -07:00
|
|
|
pub fn add_taker_trade(&mut self, side: Side, base_lots: i64, quote_lots: i64) {
|
|
|
|
match side {
|
|
|
|
Side::Bid => {
|
|
|
|
self.taker_base_lots = cm!(self.taker_base_lots + base_lots);
|
|
|
|
self.taker_quote_lots = cm!(self.taker_quote_lots - quote_lots);
|
|
|
|
}
|
|
|
|
Side::Ask => {
|
|
|
|
self.taker_base_lots = cm!(self.taker_base_lots - base_lots);
|
|
|
|
self.taker_quote_lots = cm!(self.taker_quote_lots + quote_lots);
|
|
|
|
}
|
|
|
|
}
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
/// Remove taker trade after it has been processed on EventQueue
|
|
|
|
pub fn remove_taker_trade(&mut self, base_change: i64, quote_change: i64) {
|
2022-04-02 21:06:03 -07:00
|
|
|
self.taker_base_lots = cm!(self.taker_base_lots - base_change);
|
|
|
|
self.taker_quote_lots = cm!(self.taker_quote_lots - quote_change);
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_active(&self) -> bool {
|
|
|
|
self.market_index != PerpMarketIndex::MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_active_for_market(&self, market_index: PerpMarketIndex) -> bool {
|
|
|
|
self.market_index == market_index
|
|
|
|
}
|
2022-03-28 12:13:16 -07:00
|
|
|
|
|
|
|
/// This assumes settle_funding was already called
|
|
|
|
pub fn change_base_position(&mut self, perp_market: &mut PerpMarket, base_change: i64) {
|
2022-04-01 06:47:12 -07:00
|
|
|
let start = self.base_position_lots;
|
|
|
|
self.base_position_lots += base_change;
|
|
|
|
perp_market.open_interest += self.base_position_lots.abs() - start.abs();
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
2022-05-16 06:34:56 -07:00
|
|
|
|
|
|
|
/// Move unrealized funding payments into the quote_position
|
|
|
|
pub fn settle_funding(&mut self, perp_market: &PerpMarket) {
|
2022-05-18 08:16:14 -07:00
|
|
|
match self.base_position_lots.cmp(&0) {
|
|
|
|
Ordering::Greater => {
|
|
|
|
self.quote_position_native -= (perp_market.long_funding
|
|
|
|
- self.long_settled_funding)
|
|
|
|
* I80F48::from_num(self.base_position_lots);
|
|
|
|
}
|
|
|
|
Ordering::Less => {
|
|
|
|
self.quote_position_native -= (perp_market.short_funding
|
|
|
|
- self.short_settled_funding)
|
|
|
|
* I80F48::from_num(self.base_position_lots);
|
|
|
|
}
|
|
|
|
Ordering::Equal => (),
|
2022-05-16 06:34:56 -07:00
|
|
|
}
|
|
|
|
self.long_settled_funding = perp_market.long_funding;
|
|
|
|
self.short_settled_funding = perp_market.short_funding;
|
|
|
|
}
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[zero_copy]
|
2022-06-21 07:52:40 -07:00
|
|
|
pub struct MangoAccountPerpPositions {
|
|
|
|
pub accounts: [PerpPositions; MAX_PERP_ACCOUNTS],
|
2022-04-01 03:22:03 -07:00
|
|
|
|
|
|
|
// TODO: possibly it's more convenient to store a single list of PerpOpenOrder structs?
|
|
|
|
pub order_market: [PerpMarketIndex; MAX_PERP_OPEN_ORDERS],
|
|
|
|
pub order_side: [Side; MAX_PERP_OPEN_ORDERS], // TODO: storing enums isn't POD
|
|
|
|
pub order_id: [i128; MAX_PERP_OPEN_ORDERS],
|
2022-05-16 01:34:22 -07:00
|
|
|
pub client_order_id: [u64; MAX_PERP_OPEN_ORDERS],
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
2022-04-01 03:22:03 -07:00
|
|
|
const_assert_eq!(
|
2022-06-21 07:52:40 -07:00
|
|
|
size_of::<MangoAccountPerpPositions>(),
|
|
|
|
MAX_PERP_ACCOUNTS * size_of::<PerpPositions>() + MAX_PERP_OPEN_ORDERS * (2 + 1 + 16 + 8)
|
2022-04-01 03:22:03 -07:00
|
|
|
);
|
2022-06-21 07:52:40 -07:00
|
|
|
const_assert_eq!(size_of::<MangoAccountPerpPositions>() % 8, 0);
|
2022-03-25 01:46:38 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl std::fmt::Debug for MangoAccountPerpPositions {
|
2022-05-05 01:25:32 -07:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("MangoAccountPerps")
|
|
|
|
.field(
|
|
|
|
"accounts",
|
|
|
|
&self
|
|
|
|
.accounts
|
|
|
|
.iter()
|
|
|
|
.filter(|value| value.is_active())
|
2022-06-21 07:52:40 -07:00
|
|
|
.collect::<Vec<&PerpPositions>>(),
|
2022-05-05 01:25:32 -07:00
|
|
|
)
|
|
|
|
.field(
|
|
|
|
"order_market",
|
|
|
|
&self
|
|
|
|
.order_market
|
|
|
|
.iter()
|
|
|
|
.filter(|value| **value != PerpMarketIndex::MAX)
|
|
|
|
.collect::<Vec<&PerpMarketIndex>>(),
|
|
|
|
)
|
|
|
|
.field(
|
|
|
|
"order_side",
|
|
|
|
&self
|
|
|
|
.order_side
|
|
|
|
.iter()
|
|
|
|
.zip(self.order_id)
|
|
|
|
.filter(|value| value.1 != 0)
|
|
|
|
.map(|value| value.0)
|
|
|
|
.collect::<Vec<&Side>>(),
|
|
|
|
)
|
|
|
|
.field(
|
|
|
|
"order_id",
|
|
|
|
&self
|
|
|
|
.order_id
|
|
|
|
.iter()
|
|
|
|
.filter(|value| **value != 0)
|
|
|
|
.collect::<Vec<&i128>>(),
|
|
|
|
)
|
|
|
|
.field(
|
2022-05-16 01:34:22 -07:00
|
|
|
"client_order_id",
|
2022-05-05 01:25:32 -07:00
|
|
|
&self
|
2022-05-16 01:34:22 -07:00
|
|
|
.client_order_id
|
2022-05-05 01:25:32 -07:00
|
|
|
.iter()
|
|
|
|
.filter(|value| **value != 0)
|
|
|
|
.collect::<Vec<&u64>>(),
|
|
|
|
)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl MangoAccountPerpPositions {
|
2022-03-25 01:46:38 -07:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2022-06-21 07:52:40 -07:00
|
|
|
accounts: [PerpPositions::default(); MAX_PERP_ACCOUNTS],
|
2022-04-01 03:22:03 -07:00
|
|
|
order_market: [FREE_ORDER_SLOT; MAX_PERP_OPEN_ORDERS],
|
|
|
|
order_side: [Side::Bid; MAX_PERP_OPEN_ORDERS],
|
|
|
|
order_id: [0; MAX_PERP_OPEN_ORDERS],
|
2022-05-16 01:34:22 -07:00
|
|
|
client_order_id: [0; MAX_PERP_OPEN_ORDERS],
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-01 03:22:03 -07:00
|
|
|
pub fn get_account_mut_or_create(
|
2022-03-25 01:46:38 -07:00
|
|
|
&mut self,
|
|
|
|
perp_market_index: PerpMarketIndex,
|
2022-06-21 07:52:40 -07:00
|
|
|
) -> Result<(&mut PerpPositions, usize)> {
|
2022-03-25 01:46:38 -07:00
|
|
|
let mut pos = self
|
2022-04-01 03:22:03 -07:00
|
|
|
.accounts
|
2022-03-25 01:46:38 -07:00
|
|
|
.iter()
|
|
|
|
.position(|p| p.is_active_for_market(perp_market_index));
|
|
|
|
if pos.is_none() {
|
2022-04-01 03:22:03 -07:00
|
|
|
pos = self.accounts.iter().position(|p| !p.is_active());
|
2022-03-25 01:46:38 -07:00
|
|
|
if let Some(i) = pos {
|
2022-06-21 07:52:40 -07:00
|
|
|
self.accounts[i] = PerpPositions {
|
2022-04-04 00:23:01 -07:00
|
|
|
market_index: perp_market_index,
|
2022-03-25 01:46:38 -07:00
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(i) = pos {
|
2022-04-01 03:22:03 -07:00
|
|
|
Ok((&mut self.accounts[i], i))
|
2022-03-25 01:46:38 -07:00
|
|
|
} else {
|
|
|
|
err!(MangoError::SomeError) // TODO: No free space
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-01 03:22:03 -07:00
|
|
|
pub fn deactivate_account(&mut self, index: usize) {
|
|
|
|
self.accounts[index].market_index = PerpMarketIndex::MAX;
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn iter_active_accounts(&self) -> impl Iterator<Item = &PerpPositions> {
|
2022-04-01 03:22:03 -07:00
|
|
|
self.accounts.iter().filter(|p| p.is_active())
|
2022-03-25 01:46:38 -07:00
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub fn find_account(&self, market_index: PerpMarketIndex) -> Option<&PerpPositions> {
|
2022-04-01 03:22:03 -07:00
|
|
|
self.accounts
|
2022-03-25 01:46:38 -07:00
|
|
|
.iter()
|
|
|
|
.find(|p| p.is_active_for_market(market_index))
|
|
|
|
}
|
2022-02-10 00:07:34 -08:00
|
|
|
|
2022-03-28 12:13:16 -07:00
|
|
|
pub fn next_order_slot(&self) -> Option<usize> {
|
|
|
|
self.order_market.iter().position(|&i| i == FREE_ORDER_SLOT)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_order(
|
|
|
|
&mut self,
|
|
|
|
perp_market_index: PerpMarketIndex,
|
|
|
|
side: Side,
|
|
|
|
order: &LeafNode,
|
|
|
|
) -> Result<()> {
|
2022-04-01 03:22:03 -07:00
|
|
|
let mut perp_account = self.get_account_mut_or_create(perp_market_index).unwrap().0;
|
2022-03-28 12:13:16 -07:00
|
|
|
match side {
|
|
|
|
Side::Bid => {
|
2022-04-04 00:23:01 -07:00
|
|
|
perp_account.bids_base_lots = cm!(perp_account.bids_base_lots + order.quantity);
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
Side::Ask => {
|
2022-04-04 00:23:01 -07:00
|
|
|
perp_account.asks_base_lots = cm!(perp_account.asks_base_lots + order.quantity);
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let slot = order.owner_slot as usize;
|
|
|
|
self.order_market[slot] = perp_market_index;
|
|
|
|
self.order_side[slot] = side;
|
2022-04-01 03:22:03 -07:00
|
|
|
self.order_id[slot] = order.key;
|
2022-05-16 01:34:22 -07:00
|
|
|
self.client_order_id[slot] = order.client_order_id;
|
2022-03-28 12:13:16 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_order(&mut self, slot: usize, quantity: i64) -> Result<()> {
|
|
|
|
require!(
|
|
|
|
self.order_market[slot] != FREE_ORDER_SLOT,
|
|
|
|
MangoError::SomeError
|
|
|
|
);
|
2022-04-01 03:22:03 -07:00
|
|
|
let order_side = self.order_side[slot];
|
2022-03-28 12:13:16 -07:00
|
|
|
let perp_market_index = self.order_market[slot];
|
2022-04-01 03:22:03 -07:00
|
|
|
let perp_account = self.get_account_mut_or_create(perp_market_index).unwrap().0;
|
2022-03-28 12:13:16 -07:00
|
|
|
|
|
|
|
// accounting
|
2022-04-01 03:22:03 -07:00
|
|
|
match order_side {
|
2022-03-28 12:13:16 -07:00
|
|
|
Side::Bid => {
|
2022-04-02 21:06:03 -07:00
|
|
|
perp_account.bids_base_lots = cm!(perp_account.bids_base_lots - quantity);
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
Side::Ask => {
|
2022-04-02 21:06:03 -07:00
|
|
|
perp_account.asks_base_lots = cm!(perp_account.asks_base_lots - quantity);
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// release space
|
|
|
|
self.order_market[slot] = FREE_ORDER_SLOT;
|
|
|
|
|
|
|
|
self.order_side[slot] = Side::Bid;
|
2022-04-01 03:22:03 -07:00
|
|
|
self.order_id[slot] = 0i128;
|
2022-05-16 01:34:22 -07:00
|
|
|
self.client_order_id[slot] = 0u64;
|
2022-03-28 12:13:16 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn execute_maker(
|
|
|
|
&mut self,
|
|
|
|
perp_market_index: PerpMarketIndex,
|
|
|
|
perp_market: &mut PerpMarket,
|
|
|
|
fill: &FillEvent,
|
|
|
|
) -> Result<()> {
|
2022-04-01 03:22:03 -07:00
|
|
|
let pa = self.get_account_mut_or_create(perp_market_index).unwrap().0;
|
2022-05-16 06:34:56 -07:00
|
|
|
pa.settle_funding(perp_market);
|
2022-03-28 12:13:16 -07:00
|
|
|
|
2022-04-01 00:42:20 -07:00
|
|
|
let side = fill.taker_side.invert_side();
|
2022-03-28 12:13:16 -07:00
|
|
|
let (base_change, quote_change) = fill.base_quote_change(side);
|
|
|
|
pa.change_base_position(perp_market, base_change);
|
|
|
|
let quote = I80F48::from_num(
|
|
|
|
perp_market
|
|
|
|
.quote_lot_size
|
|
|
|
.checked_mul(quote_change)
|
|
|
|
.unwrap(),
|
|
|
|
);
|
|
|
|
let fees = quote.abs() * fill.maker_fee;
|
|
|
|
if !fill.market_fees_applied {
|
|
|
|
perp_market.fees_accrued += fees;
|
|
|
|
}
|
2022-04-01 06:47:12 -07:00
|
|
|
pa.quote_position_native = pa.quote_position_native.checked_add(quote - fees).unwrap();
|
2022-03-28 12:13:16 -07:00
|
|
|
|
|
|
|
if fill.maker_out {
|
|
|
|
self.remove_order(fill.maker_slot as usize, base_change.abs())
|
|
|
|
} else {
|
|
|
|
match side {
|
|
|
|
Side::Bid => {
|
2022-04-02 21:06:03 -07:00
|
|
|
pa.bids_base_lots = cm!(pa.bids_base_lots - base_change.abs());
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
Side::Ask => {
|
2022-04-02 21:06:03 -07:00
|
|
|
pa.asks_base_lots = cm!(pa.asks_base_lots - base_change.abs());
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn execute_taker(
|
|
|
|
&mut self,
|
|
|
|
perp_market_index: PerpMarketIndex,
|
|
|
|
perp_market: &mut PerpMarket,
|
|
|
|
fill: &FillEvent,
|
|
|
|
) -> Result<()> {
|
2022-04-01 03:22:03 -07:00
|
|
|
let pa = self.get_account_mut_or_create(perp_market_index).unwrap().0;
|
2022-05-16 06:34:56 -07:00
|
|
|
pa.settle_funding(perp_market);
|
|
|
|
|
2022-03-28 12:13:16 -07:00
|
|
|
let (base_change, quote_change) = fill.base_quote_change(fill.taker_side);
|
|
|
|
pa.remove_taker_trade(base_change, quote_change);
|
|
|
|
pa.change_base_position(perp_market, base_change);
|
|
|
|
let quote = I80F48::from_num(perp_market.quote_lot_size * quote_change);
|
|
|
|
|
|
|
|
// fees are assessed at time of trade; no need to assess fees here
|
|
|
|
|
2022-04-01 06:47:12 -07:00
|
|
|
pa.quote_position_native += quote;
|
2022-03-28 12:13:16 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-05-16 01:34:22 -07:00
|
|
|
|
|
|
|
pub fn find_order_with_client_order_id(
|
|
|
|
&self,
|
|
|
|
market_index: PerpMarketIndex,
|
|
|
|
client_order_id: u64,
|
|
|
|
) -> Option<(i128, Side)> {
|
|
|
|
for i in 0..MAX_PERP_OPEN_ORDERS {
|
|
|
|
if self.order_market[i] == market_index && self.client_order_id[i] == client_order_id {
|
|
|
|
return Some((self.order_id[i], self.order_side[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn find_order_side(&self, market_index: PerpMarketIndex, order_id: i128) -> Option<Side> {
|
|
|
|
for i in 0..MAX_PERP_OPEN_ORDERS {
|
|
|
|
if self.order_market[i] == market_index && self.order_id[i] == order_id {
|
|
|
|
return Some(self.order_side[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2022-03-28 12:13:16 -07:00
|
|
|
}
|
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
impl Default for MangoAccountPerpPositions {
|
2022-04-01 03:22:03 -07:00
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[account(zero_copy)]
|
|
|
|
pub struct MangoAccount {
|
2022-04-12 11:23:45 -07:00
|
|
|
pub name: [u8; 32],
|
2022-04-12 07:19:58 -07:00
|
|
|
|
2022-04-01 03:22:03 -07:00
|
|
|
pub group: Pubkey,
|
|
|
|
pub owner: Pubkey,
|
|
|
|
|
|
|
|
// Alternative authority/signer of transactions for a mango account
|
|
|
|
pub delegate: Pubkey,
|
|
|
|
|
|
|
|
// Maps token_index -> deposit/borrow account for each token
|
|
|
|
// that is active on this MangoAccount.
|
2022-06-21 07:52:40 -07:00
|
|
|
pub tokens: MangoAccountTokenPositions,
|
2022-04-01 03:22:03 -07:00
|
|
|
|
|
|
|
// Maps serum_market_index -> open orders for each serum market
|
|
|
|
// that is active on this MangoAccount.
|
2022-06-21 07:52:40 -07:00
|
|
|
pub serum3: MangoAccountSerum3Orders,
|
2022-04-01 03:22:03 -07:00
|
|
|
|
2022-06-21 07:52:40 -07:00
|
|
|
pub perps: MangoAccountPerpPositions,
|
2022-04-01 03:22:03 -07:00
|
|
|
|
|
|
|
/// This account cannot open new positions or borrow until `init_health >= 0`
|
|
|
|
pub being_liquidated: u8,
|
|
|
|
|
|
|
|
/// This account cannot do anything except go through `resolve_bankruptcy`
|
|
|
|
pub is_bankrupt: u8,
|
|
|
|
|
|
|
|
pub account_num: u8,
|
|
|
|
pub bump: u8,
|
|
|
|
|
|
|
|
// pub info: [u8; INFO_LEN], // TODO: Info could be in a separate PDA?
|
|
|
|
pub reserved: [u8; 4],
|
|
|
|
}
|
|
|
|
const_assert_eq!(
|
|
|
|
size_of::<MangoAccount>(),
|
2022-04-12 11:23:45 -07:00
|
|
|
32 + 3 * 32
|
2022-06-21 07:52:40 -07:00
|
|
|
+ size_of::<MangoAccountTokenPositions>()
|
|
|
|
+ size_of::<MangoAccountSerum3Orders>()
|
|
|
|
+ size_of::<MangoAccountPerpPositions>()
|
2022-04-01 03:22:03 -07:00
|
|
|
+ 4
|
|
|
|
+ 4
|
|
|
|
);
|
|
|
|
const_assert_eq!(size_of::<MangoAccount>() % 8, 0);
|
|
|
|
|
2022-05-05 01:25:32 -07:00
|
|
|
impl std::fmt::Debug for MangoAccount {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("MangoAccount")
|
2022-05-06 01:13:33 -07:00
|
|
|
.field("name", &self.name())
|
2022-05-05 01:25:32 -07:00
|
|
|
.field("group", &self.group)
|
|
|
|
.field("owner", &self.owner)
|
|
|
|
.field("delegate", &self.delegate)
|
|
|
|
.field("tokens", &self.tokens)
|
|
|
|
.field("serum3", &self.serum3)
|
|
|
|
.field("perps", &self.perps)
|
|
|
|
.field("being_liquidated", &self.being_liquidated)
|
|
|
|
.field("is_bankrupt", &self.is_bankrupt)
|
|
|
|
.field("account_num", &self.account_num)
|
|
|
|
.field("bump", &self.bump)
|
|
|
|
.field("reserved", &self.reserved)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-06 01:13:33 -07:00
|
|
|
impl MangoAccount {
|
2022-05-29 03:25:12 -07:00
|
|
|
pub fn name(&self) -> &str {
|
2022-05-06 01:13:33 -07:00
|
|
|
std::str::from_utf8(&self.name)
|
|
|
|
.unwrap()
|
|
|
|
.trim_matches(char::from(0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-23 03:13:55 -07:00
|
|
|
impl Default for MangoAccount {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
name: Default::default(),
|
|
|
|
group: Pubkey::default(),
|
|
|
|
owner: Pubkey::default(),
|
|
|
|
delegate: Pubkey::default(),
|
2022-06-21 07:52:40 -07:00
|
|
|
tokens: MangoAccountTokenPositions::new(),
|
|
|
|
serum3: MangoAccountSerum3Orders::new(),
|
|
|
|
perps: MangoAccountPerpPositions::new(),
|
2022-05-23 03:13:55 -07:00
|
|
|
being_liquidated: 0,
|
|
|
|
is_bankrupt: 0,
|
|
|
|
account_num: 0,
|
|
|
|
bump: 0,
|
|
|
|
reserved: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 06:42:14 -08:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! account_seeds {
|
|
|
|
( $account:expr ) => {
|
|
|
|
&[
|
|
|
|
$account.group.as_ref(),
|
|
|
|
b"account".as_ref(),
|
|
|
|
$account.owner.as_ref(),
|
|
|
|
&$account.account_num.to_le_bytes(),
|
|
|
|
&[$account.bump],
|
|
|
|
]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use account_seeds;
|