Clarkeni/logging (#81)
* Add logging * Added new_fixed_order_account_retriever to allow us to more easily access oracle prices outside of health calculations for logging purposes * Add token balances logging to token and token liquidation and add logging to margin trade * rust format * fix clippy errors * Address PR requested changes * fix flash_loan * Recalculate raw_token_index in token withdraw to account for position becoming inactive * Fix retrieving oracle for logging in get_mut_or_create(), return the raw index into the account's token positions as well as the index into active positions only. The latter index is useful for indexing into banks in the health account list. * Add logging flash_loan2 and flash_loan3 * Refactoring flash loan logging Co-authored-by: Christian Kamm <mail@ckamm.de>
This commit is contained in:
parent
ecbffe499f
commit
f8da1f6a40
|
@ -41,7 +41,6 @@ switchboard-program = ">=0.2.0"
|
||||||
switchboard-utils = ">=0.1.36"
|
switchboard-utils = ">=0.1.36"
|
||||||
switchboard-v2 = "0.1.10"
|
switchboard-v2 = "0.1.10"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-sdk = { version = "~1.9.13", default-features = false }
|
solana-sdk = { version = "~1.9.13", default-features = false }
|
||||||
solana-program-test = "~1.9.13"
|
solana-program-test = "~1.9.13"
|
||||||
|
|
|
@ -14,7 +14,8 @@ pub struct ComputeHealth<'info> {
|
||||||
|
|
||||||
pub fn compute_health(ctx: Context<ComputeHealth>, health_type: HealthType) -> Result<I80F48> {
|
pub fn compute_health(ctx: Context<ComputeHealth>, health_type: HealthType) -> Result<I80F48> {
|
||||||
let account = ctx.accounts.account.load()?;
|
let account = ctx.accounts.account.load()?;
|
||||||
let health = compute_health_from_fixed_accounts(&account, health_type, ctx.remaining_accounts)?;
|
let retriever = new_fixed_order_account_retriever(ctx.remaining_accounts, &account)?;
|
||||||
|
let health = crate::state::compute_health(&account, health_type, &retriever)?;
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
|
|
||||||
Ok(health)
|
Ok(health)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use crate::accounts_zerocopy::*;
|
use crate::accounts_zerocopy::*;
|
||||||
use crate::error::MangoError;
|
use crate::error::MangoError;
|
||||||
use crate::state::{compute_health_from_fixed_accounts, Bank, Group, HealthType, MangoAccount};
|
use crate::logs::{MarginTradeLog, TokenBalanceLog};
|
||||||
|
use crate::state::{
|
||||||
|
compute_health, new_fixed_order_account_retriever, AccountRetriever, Bank, Group, HealthType,
|
||||||
|
MangoAccount,
|
||||||
|
};
|
||||||
use crate::{group_seeds, Mango};
|
use crate::{group_seeds, Mango};
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use anchor_spl::token::{self, Token, TokenAccount};
|
use anchor_spl::token::{self, Token, TokenAccount};
|
||||||
|
@ -98,7 +102,7 @@ pub fn flash_loan<'key, 'accounts, 'remaining, 'info>(
|
||||||
match ai.load::<Bank>() {
|
match ai.load::<Bank>() {
|
||||||
Ok(bank) => {
|
Ok(bank) => {
|
||||||
require!(bank.group == account.group, MangoError::SomeError);
|
require!(bank.group == account.group, MangoError::SomeError);
|
||||||
let (_, raw_token_index) = account.tokens.get_mut_or_create(bank.token_index)?;
|
let (_, raw_token_index, _) = account.tokens.get_mut_or_create(bank.token_index)?;
|
||||||
allowed_vaults.insert(bank.vault, (i, raw_token_index));
|
allowed_vaults.insert(bank.vault, (i, raw_token_index));
|
||||||
allowed_banks.insert(ai.key, bank);
|
allowed_banks.insert(ai.key, bank);
|
||||||
}
|
}
|
||||||
|
@ -115,10 +119,12 @@ pub fn flash_loan<'key, 'accounts, 'remaining, 'info>(
|
||||||
// Check pre-cpi health
|
// Check pre-cpi health
|
||||||
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
||||||
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
||||||
let pre_cpi_health =
|
{
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
let retriever = new_fixed_order_account_retriever(health_ais, &account)?;
|
||||||
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
let pre_cpi_health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
||||||
|
}
|
||||||
|
|
||||||
let all_cpi_ais = &ctx.remaining_accounts[num_health_accounts..];
|
let all_cpi_ais = &ctx.remaining_accounts[num_health_accounts..];
|
||||||
let mut all_cpi_ams = all_cpi_ais
|
let mut all_cpi_ams = all_cpi_ais
|
||||||
|
@ -173,6 +179,13 @@ pub fn flash_loan<'key, 'accounts, 'remaining, 'info>(
|
||||||
})
|
})
|
||||||
.collect::<Result<HashMap<_, _>>>()?;
|
.collect::<Result<HashMap<_, _>>>()?;
|
||||||
|
|
||||||
|
// Store the indexed value before the margin trade for logging purposes
|
||||||
|
let mut pre_indexed_positions = Vec::new();
|
||||||
|
for (_, info) in used_vaults.iter() {
|
||||||
|
let position = account.tokens.get_raw(info.raw_token_index);
|
||||||
|
pre_indexed_positions.push(position.indexed_position.to_bits());
|
||||||
|
}
|
||||||
|
|
||||||
// Find banks for used vaults in cpi_ais and collect signer seeds for them.
|
// Find banks for used vaults in cpi_ais and collect signer seeds for them.
|
||||||
// Also update withdraw_amount and loan_amount.
|
// Also update withdraw_amount and loan_amount.
|
||||||
let mut bank_signer_data = Vec::with_capacity(used_vaults.len());
|
let mut bank_signer_data = Vec::with_capacity(used_vaults.len());
|
||||||
|
@ -314,12 +327,43 @@ pub fn flash_loan<'key, 'accounts, 'remaining, 'info>(
|
||||||
adjust_for_post_cpi_vault_amounts(health_ais, all_cpi_ais, &used_vaults, &mut account)?;
|
adjust_for_post_cpi_vault_amounts(health_ais, all_cpi_ais, &used_vaults, &mut account)?;
|
||||||
|
|
||||||
// Check post-cpi health
|
// Check post-cpi health
|
||||||
let post_cpi_health =
|
let retriever = new_fixed_order_account_retriever(health_ais, &account)?;
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
let post_cpi_health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
require!(post_cpi_health >= 0, MangoError::HealthMustBePositive);
|
require!(post_cpi_health >= 0, MangoError::HealthMustBePositive);
|
||||||
msg!("post_cpi_health {:?}", post_cpi_health);
|
msg!("post_cpi_health {:?}", post_cpi_health);
|
||||||
|
|
||||||
// Deactivate inactive token accounts after health check
|
// Token balances logging
|
||||||
|
let mut token_indexes = Vec::with_capacity(used_vaults.len());
|
||||||
|
let mut post_indexed_positions = Vec::with_capacity(used_vaults.len());
|
||||||
|
for (_, info) in used_vaults.iter() {
|
||||||
|
let position = account.tokens.get_raw(info.raw_token_index);
|
||||||
|
post_indexed_positions.push(position.indexed_position.to_bits());
|
||||||
|
token_indexes.push(position.token_index as u16);
|
||||||
|
|
||||||
|
let (bank, oracle_price) = retriever.bank_and_oracle(
|
||||||
|
&ctx.accounts.group.key(),
|
||||||
|
info.bank_health_ai_index,
|
||||||
|
position.token_index,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: bank.token_index as u16,
|
||||||
|
indexed_position: position.indexed_position.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: oracle_price.to_bits(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
emit!(MarginTradeLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_indexes,
|
||||||
|
pre_indexed_positions,
|
||||||
|
post_indexed_positions,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deactivate inactive token accounts at the end
|
||||||
for raw_token_index in inactive_tokens {
|
for raw_token_index in inactive_tokens {
|
||||||
account.tokens.deactivate(raw_token_index);
|
account.tokens.deactivate(raw_token_index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use crate::accounts_zerocopy::*;
|
use crate::accounts_zerocopy::*;
|
||||||
use crate::error::MangoError;
|
use crate::error::MangoError;
|
||||||
use crate::group_seeds;
|
use crate::group_seeds;
|
||||||
use crate::state::{compute_health_from_fixed_accounts, Bank, Group, HealthType, MangoAccount};
|
use crate::logs::{FlashLoanLog, FlashLoanTokenDetail, TokenBalanceLog};
|
||||||
|
use crate::state::{
|
||||||
|
compute_health, compute_health_from_fixed_accounts, new_fixed_order_account_retriever,
|
||||||
|
AccountRetriever, Bank, Group, HealthType, MangoAccount, TokenIndex,
|
||||||
|
};
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
||||||
|
@ -208,7 +212,7 @@ pub fn flash_loan2_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
require_neq!(bank.flash_loan_vault_initial, u64::MAX);
|
require_neq!(bank.flash_loan_vault_initial, u64::MAX);
|
||||||
|
|
||||||
// Create the token position now, so we can compute the pre-health with fixed order health accounts
|
// Create the token position now, so we can compute the pre-health with fixed order health accounts
|
||||||
let (_, raw_token_index) = account.tokens.get_mut_or_create(bank.token_index)?;
|
let (_, raw_token_index, _) = account.tokens.get_mut_or_create(bank.token_index)?;
|
||||||
|
|
||||||
// Revoke delegation
|
// Revoke delegation
|
||||||
let ix = token::spl_token::instruction::revoke(
|
let ix = token::spl_token::instruction::revoke(
|
||||||
|
@ -241,14 +245,29 @@ pub fn flash_loan2_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
// Check pre-cpi health
|
// Check pre-cpi health
|
||||||
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
||||||
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
||||||
let pre_cpi_health =
|
let retriever = new_fixed_order_account_retriever(health_ais, &account)?;
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
let pre_cpi_health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
||||||
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
||||||
|
|
||||||
|
// Prices for logging
|
||||||
|
let mut prices = vec![];
|
||||||
|
for change in &changes {
|
||||||
|
let (_, oracle_price) = retriever.bank_and_oracle(
|
||||||
|
&account.group,
|
||||||
|
change.bank_index,
|
||||||
|
change.raw_token_index as TokenIndex,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
prices.push(oracle_price);
|
||||||
|
}
|
||||||
|
// Drop retriever as mut bank below uses health_ais
|
||||||
|
drop(retriever);
|
||||||
|
|
||||||
// Apply the vault diffs to the bank positions
|
// Apply the vault diffs to the bank positions
|
||||||
let mut deactivated_token_positions = vec![];
|
let mut deactivated_token_positions = vec![];
|
||||||
for change in changes {
|
let mut token_loan_details = Vec::with_capacity(changes.len());
|
||||||
|
for (change, price) in changes.iter().zip(prices.iter()) {
|
||||||
let mut bank = health_ais[change.bank_index].load_mut::<Bank>()?;
|
let mut bank = health_ais[change.bank_index].load_mut::<Bank>()?;
|
||||||
let position = account.tokens.get_mut_raw(change.raw_token_index);
|
let position = account.tokens.get_mut_raw(change.raw_token_index);
|
||||||
let native = position.native(&bank);
|
let native = position.native(&bank);
|
||||||
|
@ -271,8 +290,32 @@ pub fn flash_loan2_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
|
|
||||||
bank.flash_loan_approved_amount = 0;
|
bank.flash_loan_approved_amount = 0;
|
||||||
bank.flash_loan_vault_initial = u64::MAX;
|
bank.flash_loan_vault_initial = u64::MAX;
|
||||||
|
|
||||||
|
token_loan_details.push(FlashLoanTokenDetail {
|
||||||
|
token_index: position.token_index,
|
||||||
|
change_amount: change.amount.to_bits(),
|
||||||
|
loan: loan.to_bits(),
|
||||||
|
loan_origination_fee: loan_origination_fee.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: bank.token_index as u16,
|
||||||
|
indexed_position: position.indexed_position.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: price.to_bits(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit!(FlashLoanLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_loan_details: token_loan_details
|
||||||
|
});
|
||||||
|
|
||||||
// Check post-cpi health
|
// Check post-cpi health
|
||||||
let post_cpi_health =
|
let post_cpi_health =
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use crate::accounts_zerocopy::*;
|
use crate::accounts_zerocopy::*;
|
||||||
use crate::error::MangoError;
|
use crate::error::MangoError;
|
||||||
use crate::group_seeds;
|
use crate::group_seeds;
|
||||||
use crate::state::{compute_health_from_fixed_accounts, Bank, Group, HealthType, MangoAccount};
|
use crate::logs::{FlashLoanLog, FlashLoanTokenDetail, TokenBalanceLog};
|
||||||
|
use crate::state::{
|
||||||
|
compute_health, compute_health_from_fixed_accounts, new_fixed_order_account_retriever,
|
||||||
|
AccountRetriever, Bank, Group, HealthType, MangoAccount, TokenIndex,
|
||||||
|
};
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
||||||
|
@ -212,7 +216,7 @@ pub fn flash_loan3_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
require_neq!(bank.flash_loan_vault_initial, u64::MAX);
|
require_neq!(bank.flash_loan_vault_initial, u64::MAX);
|
||||||
|
|
||||||
// Create the token position now, so we can compute the pre-health with fixed order health accounts
|
// Create the token position now, so we can compute the pre-health with fixed order health accounts
|
||||||
let (_, raw_token_index) = account.tokens.get_mut_or_create(bank.token_index)?;
|
let (_, raw_token_index, _) = account.tokens.get_mut_or_create(bank.token_index)?;
|
||||||
|
|
||||||
// Transfer any excess over the inital balance of the token account back
|
// Transfer any excess over the inital balance of the token account back
|
||||||
// into the vault. Compute the total change in the vault balance.
|
// into the vault. Compute the total change in the vault balance.
|
||||||
|
@ -246,14 +250,29 @@ pub fn flash_loan3_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
// Check pre-cpi health
|
// Check pre-cpi health
|
||||||
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
// NOTE: This health check isn't strictly necessary. It will be, later, when
|
||||||
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
// we want to have reduce_only or be able to move an account out of bankruptcy.
|
||||||
let pre_cpi_health =
|
let retriever = new_fixed_order_account_retriever(health_ais, &account)?;
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
let pre_cpi_health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
require!(pre_cpi_health >= 0, MangoError::HealthMustBePositive);
|
||||||
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
msg!("pre_cpi_health {:?}", pre_cpi_health);
|
||||||
|
|
||||||
|
// Prices for logging
|
||||||
|
let mut prices = vec![];
|
||||||
|
for change in &changes {
|
||||||
|
let (_, oracle_price) = retriever.bank_and_oracle(
|
||||||
|
&account.group,
|
||||||
|
change.bank_index,
|
||||||
|
change.raw_token_index as TokenIndex,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
prices.push(oracle_price);
|
||||||
|
}
|
||||||
|
// Drop retriever as mut bank below uses health_ais
|
||||||
|
drop(retriever);
|
||||||
|
|
||||||
// Apply the vault diffs to the bank positions
|
// Apply the vault diffs to the bank positions
|
||||||
let mut deactivated_token_positions = vec![];
|
let mut deactivated_token_positions = vec![];
|
||||||
for change in changes {
|
let mut token_loan_details = Vec::with_capacity(changes.len());
|
||||||
|
for (change, price) in changes.iter().zip(prices.iter()) {
|
||||||
let mut bank = health_ais[change.bank_index].load_mut::<Bank>()?;
|
let mut bank = health_ais[change.bank_index].load_mut::<Bank>()?;
|
||||||
let position = account.tokens.get_mut_raw(change.raw_token_index);
|
let position = account.tokens.get_mut_raw(change.raw_token_index);
|
||||||
let native = position.native(&bank);
|
let native = position.native(&bank);
|
||||||
|
@ -276,8 +295,32 @@ pub fn flash_loan3_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
|
|
||||||
bank.flash_loan_approved_amount = 0;
|
bank.flash_loan_approved_amount = 0;
|
||||||
bank.flash_loan_vault_initial = u64::MAX;
|
bank.flash_loan_vault_initial = u64::MAX;
|
||||||
|
|
||||||
|
token_loan_details.push(FlashLoanTokenDetail {
|
||||||
|
token_index: position.token_index,
|
||||||
|
change_amount: change.amount.to_bits(),
|
||||||
|
loan: loan.to_bits(),
|
||||||
|
loan_origination_fee: loan_origination_fee.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: bank.token_index as u16,
|
||||||
|
indexed_position: position.indexed_position.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: price.to_bits(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit!(FlashLoanLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_loan_details: token_loan_details
|
||||||
|
});
|
||||||
|
|
||||||
// Check post-cpi health
|
// Check post-cpi health
|
||||||
let post_cpi_health =
|
let post_cpi_health =
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
compute_health_from_fixed_accounts(&account, HealthType::Init, health_ais)?;
|
||||||
|
|
|
@ -3,6 +3,7 @@ use fixed::types::I80F48;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
|
use crate::logs::{LiquidateTokenAndTokenLog, TokenBalanceLog};
|
||||||
use crate::state::ScanningAccountRetriever;
|
use crate::state::ScanningAccountRetriever;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
|
@ -136,6 +137,71 @@ pub fn liq_token_with_token(
|
||||||
liab_transfer,
|
liab_transfer,
|
||||||
asset_transfer
|
asset_transfer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
emit!(LiquidateTokenAndTokenLog {
|
||||||
|
liqee: ctx.accounts.liqee.key(),
|
||||||
|
liqor: ctx.accounts.liqor.key(),
|
||||||
|
asset_token_index: asset_token_index,
|
||||||
|
liab_token_index: liab_token_index,
|
||||||
|
asset_transfer: asset_transfer.to_bits(),
|
||||||
|
liab_transfer: liab_transfer.to_bits(),
|
||||||
|
asset_price: asset_price.to_bits(),
|
||||||
|
liab_price: liab_price.to_bits(),
|
||||||
|
// bankruptcy:
|
||||||
|
});
|
||||||
|
|
||||||
|
// liqee asset
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.liqee.key(),
|
||||||
|
token_index: asset_token_index,
|
||||||
|
indexed_position: liqee
|
||||||
|
.tokens
|
||||||
|
.get_mut(asset_token_index)?
|
||||||
|
.indexed_position
|
||||||
|
.to_bits(),
|
||||||
|
deposit_index: asset_bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: asset_bank.borrow_index.to_bits(),
|
||||||
|
price: asset_price.to_bits(),
|
||||||
|
});
|
||||||
|
// liqee liab
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.liqee.key(),
|
||||||
|
token_index: liab_token_index,
|
||||||
|
indexed_position: liqee
|
||||||
|
.tokens
|
||||||
|
.get_mut(liab_token_index)?
|
||||||
|
.indexed_position
|
||||||
|
.to_bits(),
|
||||||
|
deposit_index: liab_bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: liab_bank.borrow_index.to_bits(),
|
||||||
|
price: liab_price.to_bits(),
|
||||||
|
});
|
||||||
|
// liqor asset
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
|
token_index: asset_token_index,
|
||||||
|
indexed_position: liqor
|
||||||
|
.tokens
|
||||||
|
.get_mut(asset_token_index)?
|
||||||
|
.indexed_position
|
||||||
|
.to_bits(),
|
||||||
|
deposit_index: asset_bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: asset_bank.borrow_index.to_bits(),
|
||||||
|
price: asset_price.to_bits(),
|
||||||
|
});
|
||||||
|
// liqor liab
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
|
token_index: liab_token_index,
|
||||||
|
indexed_position: liqor
|
||||||
|
.tokens
|
||||||
|
.get_mut(liab_token_index)?
|
||||||
|
.indexed_position
|
||||||
|
.to_bits(),
|
||||||
|
deposit_index: liab_bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: liab_bank.borrow_index.to_bits(),
|
||||||
|
price: liab_price.to_bits(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check liqee health again
|
// Check liqee health again
|
||||||
|
|
|
@ -6,6 +6,8 @@ use crate::error::MangoError;
|
||||||
use crate::state::EventQueue;
|
use crate::state::EventQueue;
|
||||||
use crate::state::{EventType, FillEvent, Group, MangoAccount, OutEvent, PerpMarket};
|
use crate::state::{EventType, FillEvent, Group, MangoAccount, OutEvent, PerpMarket};
|
||||||
|
|
||||||
|
use crate::logs::{emit_perp_balances, FillLog};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct PerpConsumeEvents<'info> {
|
pub struct PerpConsumeEvents<'info> {
|
||||||
pub group: AccountLoader<'info, Group>,
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
@ -57,6 +59,13 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
|
||||||
&mut perp_market,
|
&mut perp_market,
|
||||||
fill,
|
fill,
|
||||||
)?;
|
)?;
|
||||||
|
emit_perp_balances(
|
||||||
|
fill.maker,
|
||||||
|
perp_market.perp_market_index as u64,
|
||||||
|
fill.price,
|
||||||
|
&ma.perps.accounts[perp_market.perp_market_index as usize],
|
||||||
|
&perp_market,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let mut maker = match mango_account_ais.iter().find(|ai| ai.key == &fill.maker)
|
let mut maker = match mango_account_ais.iter().find(|ai| ai.key == &fill.maker)
|
||||||
{
|
{
|
||||||
|
@ -85,7 +94,42 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
|
||||||
&mut perp_market,
|
&mut perp_market,
|
||||||
fill,
|
fill,
|
||||||
)?;
|
)?;
|
||||||
|
emit_perp_balances(
|
||||||
|
fill.maker,
|
||||||
|
perp_market.perp_market_index as u64,
|
||||||
|
fill.price,
|
||||||
|
&maker.perps.accounts[perp_market.perp_market_index as usize],
|
||||||
|
&perp_market,
|
||||||
|
);
|
||||||
|
emit_perp_balances(
|
||||||
|
fill.taker,
|
||||||
|
perp_market.perp_market_index as u64,
|
||||||
|
fill.price,
|
||||||
|
&taker.perps.accounts[perp_market.perp_market_index as usize],
|
||||||
|
&perp_market,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
emit!(FillLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
market_index: perp_market.perp_market_index,
|
||||||
|
taker_side: fill.taker_side as u8,
|
||||||
|
maker_slot: fill.maker_slot,
|
||||||
|
market_fees_applied: fill.market_fees_applied,
|
||||||
|
maker_out: fill.maker_out,
|
||||||
|
timestamp: fill.timestamp,
|
||||||
|
seq_num: fill.seq_num,
|
||||||
|
maker: fill.maker,
|
||||||
|
maker_order_id: fill.maker_order_id,
|
||||||
|
maker_client_order_id: fill.maker_client_order_id,
|
||||||
|
maker_fee: fill.maker_fee.to_bits(),
|
||||||
|
maker_timestamp: fill.maker_timestamp,
|
||||||
|
taker: fill.taker,
|
||||||
|
taker_order_id: fill.taker_order_id,
|
||||||
|
taker_client_order_id: fill.taker_client_order_id,
|
||||||
|
taker_fee: fill.taker_fee.to_bits(),
|
||||||
|
price: fill.price,
|
||||||
|
quantity: fill.quantity,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
EventType::Out => {
|
EventType::Out => {
|
||||||
let out: &OutEvent = cast_ref(event);
|
let out: &OutEvent = cast_ref(event);
|
||||||
|
|
|
@ -3,8 +3,8 @@ use anchor_lang::prelude::*;
|
||||||
use crate::accounts_zerocopy::*;
|
use crate::accounts_zerocopy::*;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
compute_health_from_fixed_accounts, oracle_price, Book, BookSide, EventQueue, Group,
|
compute_health, new_fixed_order_account_retriever, oracle_price, Book, BookSide, EventQueue,
|
||||||
HealthType, MangoAccount, OrderType, PerpMarket, Side,
|
Group, HealthType, MangoAccount, OrderType, PerpMarket, Side,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
|
@ -132,11 +132,8 @@ pub fn perp_place_order(
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let health = compute_health_from_fixed_accounts(
|
let retriever = new_fixed_order_account_retriever(ctx.remaining_accounts, &mango_account)?;
|
||||||
&mango_account,
|
let health = compute_health(&mango_account, HealthType::Init, &retriever)?;
|
||||||
HealthType::Init,
|
|
||||||
ctx.remaining_accounts,
|
|
||||||
)?;
|
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
require!(health >= 0, MangoError::HealthMustBePositive);
|
require!(health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,11 @@ pub fn serum3_create_open_orders(ctx: Context<Serum3CreateOpenOrders>) -> Result
|
||||||
// Make it so that the token_account_map for the base and quote currency
|
// Make it so that the token_account_map for the base and quote currency
|
||||||
// stay permanently blocked. Otherwise users may end up in situations where
|
// stay permanently blocked. Otherwise users may end up in situations where
|
||||||
// they can't settle a market because they don't have free token_account_map!
|
// they can't settle a market because they don't have free token_account_map!
|
||||||
let (quote_position, _) = account
|
let (quote_position, _, _) = account
|
||||||
.tokens
|
.tokens
|
||||||
.get_mut_or_create(serum_market.quote_token_index)?;
|
.get_mut_or_create(serum_market.quote_token_index)?;
|
||||||
quote_position.in_use_count += 1;
|
quote_position.in_use_count += 1;
|
||||||
let (base_position, _) = account
|
let (base_position, _, _) = account
|
||||||
.tokens
|
.tokens
|
||||||
.get_mut_or_create(serum_market.base_token_index)?;
|
.get_mut_or_create(serum_market.base_token_index)?;
|
||||||
base_position.in_use_count += 1;
|
base_position.in_use_count += 1;
|
||||||
|
|
|
@ -111,11 +111,9 @@ pub fn serum3_liq_force_cancel_orders(
|
||||||
// TODO: do the correct health / being_liquidated check
|
// TODO: do the correct health / being_liquidated check
|
||||||
{
|
{
|
||||||
let account = ctx.accounts.account.load()?;
|
let account = ctx.accounts.account.load()?;
|
||||||
let health = compute_health_from_fixed_accounts(
|
|
||||||
&account,
|
let retriever = new_fixed_order_account_retriever(ctx.remaining_accounts, &account)?;
|
||||||
HealthType::Maint,
|
let health = compute_health(&account, HealthType::Maint, &retriever)?;
|
||||||
ctx.remaining_accounts,
|
|
||||||
)?;
|
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
require!(health < 0, MangoError::SomeError);
|
require!(health < 0, MangoError::SomeError);
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,8 +274,8 @@ pub fn serum3_place_order(
|
||||||
// Health check
|
// Health check
|
||||||
//
|
//
|
||||||
let account = ctx.accounts.account.load()?;
|
let account = ctx.accounts.account.load()?;
|
||||||
let health =
|
let retriever = new_fixed_order_account_retriever(ctx.remaining_accounts, &account)?;
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, ctx.remaining_accounts)?;
|
let health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
require!(health >= 0, MangoError::HealthMustBePositive);
|
require!(health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ use fixed::types::I80F48;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
|
|
||||||
|
use crate::logs::{DepositLog, TokenBalanceLog};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct TokenDeposit<'info> {
|
pub struct TokenDeposit<'info> {
|
||||||
pub group: AccountLoader<'info, Group>,
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
@ -60,9 +62,9 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
require!(account.is_bankrupt == 0, MangoError::IsBankrupt);
|
require!(account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||||
|
|
||||||
let (position, position_index) = account.tokens.get_mut_or_create(token_index)?;
|
let (position, raw_token_index, active_token_index) =
|
||||||
|
account.tokens.get_mut_or_create(token_index)?;
|
||||||
|
|
||||||
// Update the bank and position
|
|
||||||
let position_is_active = {
|
let position_is_active = {
|
||||||
let mut bank = ctx.accounts.bank.load_mut()?;
|
let mut bank = ctx.accounts.bank.load_mut()?;
|
||||||
bank.deposit(position, I80F48::from(amount))?
|
bank.deposit(position, I80F48::from(amount))?
|
||||||
|
@ -71,13 +73,26 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
// Transfer the actual tokens
|
// Transfer the actual tokens
|
||||||
token::transfer(ctx.accounts.transfer_ctx(), amount)?;
|
token::transfer(ctx.accounts.transfer_ctx(), amount)?;
|
||||||
|
|
||||||
|
let indexed_position = position.indexed_position;
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: token_index,
|
||||||
|
indexed_position: indexed_position.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: oracle_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Health computation
|
// Health computation
|
||||||
// TODO: This will be used to disable is_bankrupt or being_liquidated
|
// TODO: This will be used to disable is_bankrupt or being_liquidated
|
||||||
// when health recovers sufficiently
|
// when health recovers sufficiently
|
||||||
//
|
//
|
||||||
let health =
|
let health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, ctx.remaining_accounts)?;
|
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -87,8 +102,16 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
// Deposits can deactivate a position if they cancel out a previous borrow.
|
// Deposits can deactivate a position if they cancel out a previous borrow.
|
||||||
//
|
//
|
||||||
if !position_is_active {
|
if !position_is_active {
|
||||||
account.tokens.deactivate(position_index);
|
account.tokens.deactivate(raw_token_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit!(DepositLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
signer: ctx.accounts.token_authority.key(),
|
||||||
|
token_index: token_index,
|
||||||
|
quantity: amount,
|
||||||
|
price: oracle_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ use anchor_spl::token::Token;
|
||||||
use anchor_spl::token::TokenAccount;
|
use anchor_spl::token::TokenAccount;
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
|
||||||
|
use crate::logs::{TokenBalanceLog, WithdrawLog};
|
||||||
|
use crate::state::new_fixed_order_account_retriever;
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct TokenWithdraw<'info> {
|
pub struct TokenWithdraw<'info> {
|
||||||
pub group: AccountLoader<'info, Group>,
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
@ -62,7 +65,8 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
require!(account.is_bankrupt == 0, MangoError::IsBankrupt);
|
require!(account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||||
|
|
||||||
let (position, position_index) = account.tokens.get_mut_or_create(token_index)?;
|
let (position, raw_token_index, active_token_index) =
|
||||||
|
account.tokens.get_mut_or_create(token_index)?;
|
||||||
|
|
||||||
// The bank will also be passed in remainingAccounts. Use an explicit scope
|
// The bank will also be passed in remainingAccounts. Use an explicit scope
|
||||||
// to drop the &mut before we borrow it immutably again later.
|
// to drop the &mut before we borrow it immutably again later.
|
||||||
|
@ -103,11 +107,24 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
position_is_active
|
position_is_active
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let indexed_position = position.indexed_position;
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: token_index,
|
||||||
|
indexed_position: indexed_position.to_bits(),
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
price: oracle_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Health check
|
// Health check
|
||||||
//
|
//
|
||||||
let health =
|
let health = compute_health(&account, HealthType::Init, &retriever)?;
|
||||||
compute_health_from_fixed_accounts(&account, HealthType::Init, ctx.remaining_accounts)?;
|
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
require!(health >= 0, MangoError::HealthMustBePositive);
|
require!(health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
|
||||||
|
@ -117,8 +134,16 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
// deactivated.
|
// deactivated.
|
||||||
//
|
//
|
||||||
if !position_is_active {
|
if !position_is_active {
|
||||||
account.tokens.deactivate(position_index);
|
account.tokens.deactivate(raw_token_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit!(WithdrawLog {
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
signer: ctx.accounts.owner.key(),
|
||||||
|
token_index: token_index,
|
||||||
|
quantity: amount,
|
||||||
|
price: oracle_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
use crate::logs::UpdateIndexLog;
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts_zerocopy::{LoadMutZeroCopyRef, LoadZeroCopyRef},
|
accounts_zerocopy::{LoadMutZeroCopyRef, LoadZeroCopyRef},
|
||||||
error::MangoError,
|
error::MangoError,
|
||||||
|
@ -63,6 +64,15 @@ pub fn update_index(ctx: Context<UpdateIndex>) -> Result<()> {
|
||||||
|
|
||||||
bank.deposit_index = deposit_index;
|
bank.deposit_index = deposit_index;
|
||||||
bank.borrow_index = borrow_index;
|
bank.borrow_index = borrow_index;
|
||||||
|
|
||||||
|
// clarkeni TODO: add prices
|
||||||
|
emit!(UpdateIndexLog {
|
||||||
|
mango_group: bank.group.key(),
|
||||||
|
token_index: bank.token_index,
|
||||||
|
deposit_index: bank.deposit_index.to_bits(),
|
||||||
|
borrow_index: bank.borrow_index.to_bits(),
|
||||||
|
// price: oracle_price.to_bits(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub mod accounts_zerocopy;
|
||||||
pub mod address_lookup_table;
|
pub mod address_lookup_table;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
|
pub mod logs;
|
||||||
mod serum3_cpi;
|
mod serum3_cpi;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
use crate::state::{PerpMarket, PerpPositions};
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
use borsh::{BorshDeserialize, BorshSerialize};
|
||||||
|
|
||||||
|
/// Warning: This function needs 512+ bytes free on the stack
|
||||||
|
pub fn emit_perp_balances(
|
||||||
|
mango_account: Pubkey,
|
||||||
|
market_index: u64,
|
||||||
|
price: i64,
|
||||||
|
pp: &PerpPositions,
|
||||||
|
pm: &PerpMarket,
|
||||||
|
) {
|
||||||
|
emit!(PerpBalanceLog {
|
||||||
|
mango_account: mango_account,
|
||||||
|
market_index: market_index,
|
||||||
|
base_position: pp.base_position_lots,
|
||||||
|
quote_position: pp.quote_position_native.to_bits(),
|
||||||
|
long_settled_funding: pp.long_settled_funding.to_bits(),
|
||||||
|
short_settled_funding: pp.short_settled_funding.to_bits(),
|
||||||
|
price,
|
||||||
|
long_funding: pm.long_funding.to_bits(),
|
||||||
|
short_funding: pm.short_funding.to_bits(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct PerpBalanceLog {
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub market_index: u64, // IDL doesn't support usize
|
||||||
|
pub base_position: i64,
|
||||||
|
pub quote_position: i128, // I80F48
|
||||||
|
pub long_settled_funding: i128, // I80F48
|
||||||
|
pub short_settled_funding: i128, // I80F48
|
||||||
|
pub price: i64,
|
||||||
|
pub long_funding: i128, // I80F48
|
||||||
|
pub short_funding: i128, // I80F48
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct TokenBalanceLog {
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub token_index: u16, // IDL doesn't support usize
|
||||||
|
pub indexed_position: i128, // on client convert i128 to I80F48 easily by passing in the BN to I80F48 ctor
|
||||||
|
pub deposit_index: i128, // I80F48
|
||||||
|
pub borrow_index: i128, // I80F48
|
||||||
|
pub price: i128, // I80F48
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct MarginTradeLog {
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub token_indexes: Vec<u16>,
|
||||||
|
pub pre_indexed_positions: Vec<i128>,
|
||||||
|
pub post_indexed_positions: Vec<i128>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct FlashLoanTokenDetail {
|
||||||
|
pub token_index: u16,
|
||||||
|
pub change_amount: i128,
|
||||||
|
pub loan: i128,
|
||||||
|
pub loan_origination_fee: i128,
|
||||||
|
pub deposit_index: i128,
|
||||||
|
pub borrow_index: i128,
|
||||||
|
pub price: i128,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct FlashLoanLog {
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub token_loan_details: Vec<FlashLoanTokenDetail>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct WithdrawLog {
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub signer: Pubkey,
|
||||||
|
pub token_index: u16,
|
||||||
|
pub quantity: u64,
|
||||||
|
pub price: i128, // I80F48
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct DepositLog {
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub signer: Pubkey,
|
||||||
|
pub token_index: u16,
|
||||||
|
pub quantity: u64,
|
||||||
|
pub price: i128, // I80F48
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct FillLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
|
pub market_index: u16,
|
||||||
|
pub taker_side: u8, // side from the taker's POV
|
||||||
|
pub maker_slot: u8,
|
||||||
|
pub market_fees_applied: bool,
|
||||||
|
pub maker_out: bool, // true if maker order quantity == 0
|
||||||
|
pub timestamp: u64,
|
||||||
|
pub seq_num: u64, // note: usize same as u64
|
||||||
|
|
||||||
|
pub maker: Pubkey,
|
||||||
|
pub maker_order_id: i128,
|
||||||
|
pub maker_client_order_id: u64,
|
||||||
|
pub maker_fee: i128,
|
||||||
|
|
||||||
|
// Timestamp of when the maker order was placed; copied over from the LeafNode
|
||||||
|
pub maker_timestamp: u64,
|
||||||
|
|
||||||
|
pub taker: Pubkey,
|
||||||
|
pub taker_order_id: i128,
|
||||||
|
pub taker_client_order_id: u64,
|
||||||
|
pub taker_fee: i128,
|
||||||
|
|
||||||
|
pub price: i64,
|
||||||
|
pub quantity: i64, // number of base lots
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct UpdateFundingLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
|
pub market_index: u16,
|
||||||
|
pub long_funding: i128, // I80F48
|
||||||
|
pub short_funding: i128, // I80F48
|
||||||
|
pub price: i128, // I80F48
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct UpdateIndexLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
|
pub token_index: u16,
|
||||||
|
pub deposit_index: i128, // I80F48
|
||||||
|
pub borrow_index: i128, // I80F48
|
||||||
|
// pub price: i128, // I80F48
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct LiquidateTokenAndTokenLog {
|
||||||
|
pub liqee: Pubkey,
|
||||||
|
pub liqor: Pubkey,
|
||||||
|
pub asset_token_index: u16,
|
||||||
|
pub liab_token_index: u16,
|
||||||
|
pub asset_transfer: i128, // I80F48
|
||||||
|
pub liab_transfer: i128, // I80F48
|
||||||
|
pub asset_price: i128, // I80F48
|
||||||
|
pub liab_price: i128, // I80F48
|
||||||
|
// pub bankruptcy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct OpenOrdersBalanceLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub market_index: u16,
|
||||||
|
pub base_total: u64,
|
||||||
|
pub base_free: u64,
|
||||||
|
/// this field does not include the referrer_rebates; need to add that in to get true total
|
||||||
|
pub quote_total: u64,
|
||||||
|
pub quote_free: u64,
|
||||||
|
pub referrer_rebates_accrued: u64,
|
||||||
|
pub price: i128, // I80F48
|
||||||
|
}
|
|
@ -50,6 +50,29 @@ pub struct FixedOrderAccountRetriever<T: KeyedAccountReader> {
|
||||||
pub begin_serum3: usize,
|
pub begin_serum3: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_fixed_order_account_retriever<'a, 'info>(
|
||||||
|
ais: &'a [AccountInfo<'info>],
|
||||||
|
account: &MangoAccount,
|
||||||
|
) -> Result<FixedOrderAccountRetriever<AccountInfoRef<'a, 'info>>> {
|
||||||
|
let active_token_len = account.tokens.iter_active().count();
|
||||||
|
let active_serum3_len = account.serum3.iter_active().count();
|
||||||
|
let active_perp_len = account.perps.iter_active_accounts().count();
|
||||||
|
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
||||||
|
+ active_perp_len // PerpMarkets
|
||||||
|
+ active_serum3_len); // open_orders
|
||||||
|
require!(ais.len() == expected_ais, MangoError::SomeError);
|
||||||
|
|
||||||
|
Ok(FixedOrderAccountRetriever {
|
||||||
|
ais: ais
|
||||||
|
.into_iter()
|
||||||
|
.map(|ai| AccountInfoRef::borrow(ai))
|
||||||
|
.collect::<Result<Vec<_>>>()?,
|
||||||
|
n_banks: active_token_len,
|
||||||
|
begin_perp: cm!(active_token_len * 2),
|
||||||
|
begin_serum3: cm!(active_token_len * 2 + active_perp_len),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: KeyedAccountReader> FixedOrderAccountRetriever<T> {
|
impl<T: KeyedAccountReader> FixedOrderAccountRetriever<T> {
|
||||||
fn bank(&self, group: &Pubkey, account_index: usize) -> Result<&Bank> {
|
fn bank(&self, group: &Pubkey, account_index: usize) -> Result<&Bank> {
|
||||||
let bank = self.ais[account_index].load::<Bank>()?;
|
let bank = self.ais[account_index].load::<Bank>()?;
|
||||||
|
|
|
@ -139,30 +139,44 @@ impl MangoAccountTokenPositions {
|
||||||
&mut self.values[raw_token_index]
|
&mut self.values[raw_token_index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_raw(&self, raw_token_index: usize) -> &TokenPosition {
|
||||||
|
&self.values[raw_token_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates or retrieves a TokenPosition for the token_index.
|
||||||
|
/// Returns:
|
||||||
|
/// - the position
|
||||||
|
/// - the raw index into the token positions list (for use with get_raw)
|
||||||
|
/// - the active index, for use with FixedOrderAccountRetriever
|
||||||
pub fn get_mut_or_create(
|
pub fn get_mut_or_create(
|
||||||
&mut self,
|
&mut self,
|
||||||
token_index: TokenIndex,
|
token_index: TokenIndex,
|
||||||
) -> Result<(&mut TokenPosition, usize)> {
|
) -> Result<(&mut TokenPosition, usize, usize)> {
|
||||||
// This function looks complex because of lifetimes.
|
let mut active_index = 0;
|
||||||
// Maybe there's a smart way to write it with double iter_mut()
|
let mut match_or_free = None;
|
||||||
// that doesn't confuse the borrow checker.
|
for (raw_index, position) in self.values.iter().enumerate() {
|
||||||
let mut pos = self
|
if position.is_active_for_token(token_index) {
|
||||||
.values
|
// Can't return early because of lifetimes
|
||||||
.iter()
|
match_or_free = Some((raw_index, active_index));
|
||||||
.position(|p| p.is_active_for_token(token_index));
|
break;
|
||||||
if pos.is_none() {
|
}
|
||||||
pos = self.values.iter().position(|p| !p.is_active());
|
if position.is_active() {
|
||||||
if let Some(i) = pos {
|
active_index += 1;
|
||||||
self.values[i] = TokenPosition {
|
} else if match_or_free.is_none() {
|
||||||
|
match_or_free = Some((raw_index, active_index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some((raw_index, bank_index)) = match_or_free {
|
||||||
|
let v = &mut self.values[raw_index];
|
||||||
|
if !v.is_active_for_token(token_index) {
|
||||||
|
*v = TokenPosition {
|
||||||
indexed_position: I80F48::ZERO,
|
indexed_position: I80F48::ZERO,
|
||||||
token_index,
|
token_index,
|
||||||
in_use_count: 0,
|
in_use_count: 0,
|
||||||
reserved: Default::default(),
|
reserved: Default::default(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
Ok((v, raw_index, bank_index))
|
||||||
if let Some(i) = pos {
|
|
||||||
Ok((&mut self.values[i], i))
|
|
||||||
} else {
|
} else {
|
||||||
err!(MangoError::SomeError) // TODO: No free space
|
err!(MangoError::SomeError) // TODO: No free space
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue