bad code; WILL THROW STACK FRAME ERROR IN PROD

This commit is contained in:
dd 2021-06-01 14:08:49 -04:00
parent 43e5e83f13
commit d4fe5c879e
3 changed files with 116 additions and 72 deletions

View File

@ -20,7 +20,8 @@ cargo build-bpf --features devnet --bpf-out-dir target/devnet
# this will give a separate program id for devnet
#solana-keygen new --outfile target/devnet/mango-dev.json
#MANGO_PROGRAM_ID="$(solana program deploy target/devnet/mango.so --program-id target/devnet/mango-dev.json | jq .programId -r)"
MANGO_PROGRAM_ID="$(solana program deploy target/devnet/mango.so --program-id $MANGO_PROGRAM_ID --keypair $KEYPAIR --output json-compact | jq .programId -r)"
#MANGO_PROGRAM_ID="$(solana program deploy target/devnet/mango.so --program-id $MANGO_PROGRAM_ID --keypair $KEYPAIR --output json-compact | jq .programId -r)"
solana program deploy target/devnet/mango.so --program-id $MANGO_PROGRAM_ID --keypair $KEYPAIR --output json-compact
popd
cd cli

View File

@ -1246,11 +1246,14 @@ impl Processor {
check_open_orders(&open_orders_accs[i], &mango_group.signer_key)?;
}
sol_log_compute_units();
let clock = Clock::from_account_info(clock_acc)?;
mango_group.update_indexes(&clock)?; // TODO consider removing for compute limit space
sol_log_compute_units();
let prices = get_prices(&mango_group, oracle_accs)?;
let start_assets = liqee_margin_account.get_total_assets(&mango_group, open_orders_accs)?;
let start_liabs = liqee_margin_account.get_total_liabs(&mango_group)?;
let start_assets = liqee_margin_account.get_assets(&mango_group, open_orders_accs)?;
let start_liabs = liqee_margin_account.get_liabs(&mango_group)?;
let coll_ratio = liqee_margin_account.coll_ratio_from_assets_liabs(
&prices, &start_assets, &start_liabs)?;
// let coll_ratio = liqee_margin_account.get_collateral_ratio(
@ -1300,12 +1303,11 @@ impl Processor {
&[&signers_seeds], out_quantity)?;
// Check if account valid now
sol_log_compute_units();
let end_assets = liqee_margin_account.get_total_assets(&mango_group, open_orders_accs)?;
let end_liabs = liqee_margin_account.get_total_liabs(&mango_group)?;
let end_assets = liqee_margin_account.get_assets(&mango_group, open_orders_accs)?;
let end_liabs = liqee_margin_account.get_liabs(&mango_group)?;
let coll_ratio = liqee_margin_account.coll_ratio_from_assets_liabs(
&prices, &end_assets, &end_liabs)?;
let mut total_deposits = [0u64; NUM_TOKENS];
let mut total_deposits = [ZERO_U64F64; NUM_TOKENS];
let mut socialized_losses = false;
if coll_ratio >= mango_group.init_coll_ratio {
@ -1313,17 +1315,18 @@ impl Processor {
liqee_margin_account.being_liquidated = false;
} else {
// if all asset vals is dust (less than 1 cent?) socialize loss on lenders
let assets_val = liqee_margin_account.get_assets_val(&mango_group, &prices, open_orders_accs)?;
let assets_val = dot_product(&end_assets, &prices);
if assets_val < DUST_THRESHOLD {
for i in 0..NUM_TOKENS {
let native_borrow: U64F64 = liqee_margin_account.borrows[i] * mango_group.indexes[i].borrow;
let total_deposits_native: U64F64 = mango_group.total_deposits[i] * mango_group.indexes[i].deposit;
total_deposits[i] = total_deposits_native.to_num();
total_deposits[i] = total_deposits_native;
if native_borrow > 0 {
socialized_losses = true;
socialize_loss(
&mut mango_group,
&mut liqee_margin_account,
@ -1336,19 +1339,8 @@ impl Processor {
}
}
let mut prices_f64 = [0_f64; NUM_TOKENS];
for i in 0..NUM_TOKENS {
prices_f64[i] = prices[i].to_num::<f64>();
}
// Note total_deposits is only logged with reasonable values if assets_val < DUST_THRESHOLD
msg!("liquidation details: {{ \
\"start\": {{ \"assets\": {:?}, \"liabs\": {:?} }}, \
\"end\": {{ \"assets\": {:?}, \"liabs\": {:?} }}, \
\"prices\": {:?}, \
\"socialized_losses\": {}, \
\"total_deposits\": {:?} \
}}", start_assets, start_liabs, end_assets, end_liabs, prices_f64, socialized_losses, total_deposits);
sol_log_compute_units();
log_liquidation_details(&start_assets, &start_liabs, &end_assets, &end_liabs, &prices, socialized_losses, &total_deposits);
// TODO do I need to check total deposits and total borrows?
// TODO log deposit indexes before and after liquidation as a way to measure socialize of losses
@ -1471,7 +1463,50 @@ impl Processor {
}
}
#[inline(always)]
fn dot_product(a: &[U64F64; NUM_TOKENS], b: &[U64F64; NUM_TOKENS]) -> U64F64 {
let mut val = ZERO_U64F64;
for i in 0..NUM_TOKENS {
val = a[i].checked_mul(b[i]).unwrap().checked_add(val).unwrap()
}
val
}
#[inline(always)]
fn log_liquidation_details(
start_assets: &[U64F64; NUM_TOKENS],
start_liabs: &[U64F64; NUM_TOKENS],
end_assets: &[U64F64; NUM_TOKENS],
end_liabs: &[U64F64; NUM_TOKENS],
prices: &[U64F64; NUM_TOKENS],
socialized_losses: bool,
total_deposits: &[U64F64; NUM_TOKENS]
) {
let mut prices_f64 = [0_f64; NUM_TOKENS];
let mut start_assets_u64 = [0u64; NUM_TOKENS];
let mut start_liabs_u64 = [0u64; NUM_TOKENS];
let mut end_assets_u64 = [0u64; NUM_TOKENS];
let mut end_liabs_u64 = [0u64; NUM_TOKENS];
let mut total_deposits_u64 = [0u64; NUM_TOKENS];
for i in 0..NUM_TOKENS {
prices_f64[i] = prices[i].to_num::<f64>();
start_assets_u64[i] = start_assets[i].to_num();
start_liabs_u64[i] = start_liabs[i].to_num();
end_assets_u64[i] = end_assets[i].to_num();
end_liabs_u64[i] = end_liabs[i].to_num();
total_deposits_u64[i] = total_deposits[i].to_num();
}
// Note total_deposits is only logged with reasonable values if assets_val < DUST_THRESHOLD
msg!("liquidation details: {{ \
\"start\": {{ \"assets\": {:?}, \"liabs\": {:?} }}, \
\"end\": {{ \"assets\": {:?}, \"liabs\": {:?} }}, \
\"prices\": {:?}, \
\"socialized_losses\": {}, \
\"total_deposits\": {:?} \
}}", start_assets_u64, start_liabs_u64, end_assets_u64, end_liabs_u64, prices_f64, socialized_losses, total_deposits_u64);
}
#[inline(always)]
fn settle_borrow_unchecked(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1499,6 +1534,7 @@ fn settle_borrow_unchecked(
}
#[inline(always)]
fn settle_borrow_full_unchecked(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1523,6 +1559,7 @@ fn settle_borrow_full_unchecked(
}
#[inline(always)]
fn socialize_loss(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1546,6 +1583,7 @@ fn socialize_loss(
Ok(())
}
#[inline(always)]
fn checked_sub_deposit(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1556,6 +1594,7 @@ fn checked_sub_deposit(
mango_group.checked_sub_deposit(token_index, quantity)
}
#[inline(always)]
fn checked_sub_borrow(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1576,6 +1615,7 @@ fn checked_sub_borrow(
Ok(())
}
#[inline(always)]
fn checked_add_deposit(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1586,6 +1626,7 @@ fn checked_add_deposit(
mango_group.checked_add_deposit(token_index, quantity)
}
#[inline(always)]
fn checked_add_borrow(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,
@ -1602,6 +1643,7 @@ fn checked_add_borrow(
Ok(())
}
#[inline(always)]
pub fn get_prices(
mango_group: &MangoGroup,
oracle_accs: &[AccountInfo]
@ -1630,6 +1672,7 @@ pub fn get_prices(
Ok(prices)
}
#[inline(never)]
fn invoke_settle_funds<'a>(
dex_prog_acc: &AccountInfo<'a>,
spot_market_acc: &AccountInfo<'a>,
@ -1675,6 +1718,7 @@ fn invoke_settle_funds<'a>(
solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
}
#[inline(always)]
fn invoke_cancel_order<'a>(
dex_prog_acc: &AccountInfo<'a>,
spot_market_acc: &AccountInfo<'a>,
@ -1712,6 +1756,7 @@ fn invoke_cancel_order<'a>(
solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
}
#[inline(always)]
fn invoke_cancel_orders<'a>(
open_orders_acc: &AccountInfo<'a>,
dex_prog_acc: &AccountInfo<'a>,
@ -1798,6 +1843,7 @@ fn invoke_cancel_orders<'a>(
Ok(())
}
#[inline(always)]
fn invoke_transfer<'a>(
token_prog_acc: &AccountInfo<'a>,
source_acc: &AccountInfo<'a>,
@ -1825,6 +1871,7 @@ fn invoke_transfer<'a>(
solana_program::program::invoke_signed(&transfer_instruction, &accs, signers_seeds)
}
#[inline(always)]
fn get_in_out_quantities(
mango_group: &mut MangoGroup,
margin_account: &mut MarginAccount,

View File

@ -10,7 +10,6 @@ use solana_program::account_info::AccountInfo;
use solana_program::clock::Clock;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;
use solana_program::msg;
use fixed_macro::types::U64F64;
@ -31,7 +30,7 @@ const MAX_R: U64F64 = U64F64!(9.5129375951293759512937e-08); // max 300% APY ->
pub const ONE_U64F64: U64F64 = U64F64!(1);
pub const ZERO_U64F64: U64F64 = U64F64!(0);
pub const PARTIAL_LIQ_INCENTIVE: U64F64 = U64F64!(1.05);
pub const DUST_THRESHOLD: U64F64 = U64F64!(0.01); // TODO make this part of MangoGroup state
pub const DUST_THRESHOLD: U64F64 = U64F64!(100000); // TODO make this part of MangoGroup state
pub const EPSILON: U64F64 = U64F64!(1.0e-17);
macro_rules! check_default {
@ -173,6 +172,7 @@ impl MangoGroup {
self.vaults.iter().position(|pk| pk == vault)
}
/// interest is in units per second (e.g. 0.01 => 1% interest per second)
#[inline(always)]
pub fn get_interest_rate(&self, token_index: usize) -> U64F64 {
let index: &MangoIndex = &self.indexes[token_index];
let native_deposits = index.deposit.checked_mul(self.total_deposits[token_index]).unwrap();
@ -191,6 +191,7 @@ impl MangoGroup {
slope * utilization
}
}
#[inline(always)]
pub fn update_indexes(&mut self, clock: &Clock) -> MangoResult<()> {
// TODO verify what happens if total_deposits < total_borrows
// TODO verify what happens if total_deposits == 0 && total_borrows > 0
@ -228,30 +229,37 @@ impl MangoGroup {
}
Ok(())
}
#[inline(always)]
pub fn has_valid_deposits_borrows(&self, token_i: usize) -> bool {
self.get_total_native_deposit(token_i) >= self.get_total_native_borrow(token_i)
}
#[inline(always)]
pub fn get_total_native_borrow(&self, token_i: usize) -> u64 {
let native: U64F64 = self.total_borrows[token_i] * self.indexes[token_i].borrow;
native.checked_ceil().unwrap().to_num() // rounds toward +inf
}
#[inline(always)]
pub fn get_total_native_deposit(&self, token_i: usize) -> u64 {
let native: U64F64 = self.total_deposits[token_i] * self.indexes[token_i].deposit;
native.checked_floor().unwrap().to_num() // rounds toward -inf
}
#[inline(always)]
pub fn get_market_index(&self, spot_market_pk: &Pubkey) -> Option<usize> {
self.spot_markets.iter().position(|market| market == spot_market_pk)
}
#[inline(always)]
pub fn checked_add_borrow(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.total_borrows[token_i] = self.total_borrows[token_i].checked_add(v).ok_or(throw!())?)
}
#[inline(always)]
pub fn checked_sub_borrow(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.total_borrows[token_i] = self.total_borrows[token_i].checked_sub(v).ok_or(throw!())?)
}
#[inline(always)]
pub fn checked_add_deposit(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.total_deposits[token_i] = self.total_deposits[token_i].checked_add(v).ok_or(throw!())?)
}
#[inline(always)]
pub fn checked_sub_deposit(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.total_deposits[token_i] = self.total_deposits[token_i].checked_sub(v).ok_or(throw!())?)
}
@ -328,6 +336,7 @@ impl MarginAccount {
}
}
#[inline(always)]
pub fn get_collateral_ratio(
&self,
mango_group: &MangoGroup,
@ -344,17 +353,18 @@ impl MarginAccount {
}
}
#[inline(always)]
pub fn coll_ratio_from_assets_liabs(
&self,
prices: &[U64F64; NUM_TOKENS],
assets: &[u64; NUM_TOKENS],
liabs: &[u64; NUM_TOKENS]
assets: &[U64F64; NUM_TOKENS],
liabs: &[U64F64; NUM_TOKENS]
) -> MangoResult<U64F64> {
let mut assets_val: U64F64 = ZERO_U64F64;
let mut liabs_val: U64F64 = ZERO_U64F64;
for i in 0..NUM_TOKENS {
liabs_val += U64F64::from_num(liabs[i]).checked_mul(prices[i]).unwrap();
assets_val += U64F64::from_num(assets[i]).checked_mul(prices[i]).unwrap();
liabs_val = liabs[i].checked_mul(prices[i]).unwrap().checked_add(liabs_val).unwrap();
assets_val = assets[i].checked_mul(prices[i]).unwrap().checked_add(assets_val).unwrap();
}
if liabs_val == ZERO_U64F64 {
@ -363,71 +373,48 @@ impl MarginAccount {
Ok(assets_val.checked_div(liabs_val).unwrap())
}
}
pub fn logged_get_coll_ratio(
&self,
mango_group: &MangoGroup,
prices: &[U64F64; NUM_TOKENS],
open_orders_accs: &[AccountInfo; NUM_MARKETS]
) -> MangoResult<U64F64> {
let assets = self.get_total_assets(&mango_group, open_orders_accs)?;
let liabs = self.get_total_liabs(&mango_group)?;
let mut assets_val: U64F64 = ZERO_U64F64;
let mut liabs_val: U64F64 = ZERO_U64F64;
for i in 0..NUM_TOKENS {
liabs_val += U64F64::from_num(liabs[i]).checked_mul(prices[i]).unwrap();
assets_val += U64F64::from_num(assets[i]).checked_mul(prices[i]).unwrap();
}
let coll_ratio = if liabs_val == ZERO_U64F64 {
U64F64::MAX
} else {
assets_val.checked_div(liabs_val).unwrap()
};
// Too expensive to call msg! with an argument of type U64F64 - convert to f64 first
let mut prices_f64 = [0_f64; NUM_TOKENS];
for i in 0..NUM_TOKENS {
prices_f64[i] = prices[i].to_num::<f64>();
}
msg!("account details: {{ \"assets\": {:?}, \"liabs\": {:?}, \"prices\": {:?}, \"coll_ratio\": {}, \"unused\": {} }}", assets, liabs, prices_f64, coll_ratio.to_num::<f64>(), 0);
Ok(coll_ratio)
}
pub fn get_total_assets(
#[inline(always)]
pub fn get_assets(
&self,
mango_group: &MangoGroup,
open_orders_accs: &[AccountInfo; NUM_MARKETS]
) -> MangoResult<[u64; NUM_TOKENS]> {
let mut assets = [0u64; NUM_TOKENS];
) -> MangoResult<[U64F64; NUM_TOKENS]> {
let mut assets = [ZERO_U64F64; NUM_TOKENS];
for i in 0..NUM_TOKENS {
assets[i] = self.get_native_deposit(&mango_group.indexes[i], i)
assets[i] = mango_group.indexes[i].deposit.checked_mul(self.deposits[i]).unwrap()
.checked_add(assets[i]).unwrap();
}
for i in 0..NUM_MARKETS {
if *open_orders_accs[i].key == Pubkey::default() {
continue;
}
let open_orders = load_open_orders(&open_orders_accs[i])?;
assets[i] = open_orders.native_coin_total.checked_add(assets[i]).unwrap();
assets[NUM_TOKENS-1] = open_orders.native_pc_total.checked_add(assets[NUM_TOKENS-1]).unwrap();
assets[i] = U64F64::from_num(open_orders.native_coin_total).checked_add(assets[i]).unwrap();
assets[NUM_TOKENS-1] = U64F64::from_num(open_orders.native_pc_total).checked_add(assets[NUM_TOKENS-1]).unwrap();
}
Ok(assets)
}
pub fn get_total_liabs(
#[inline(always)]
pub fn get_liabs(
&self,
mango_group: &MangoGroup
) -> MangoResult<[u64; NUM_TOKENS]> {
let mut liabs = [0u64; NUM_TOKENS];
for i in 0.. NUM_TOKENS {
liabs[i] = self.get_native_borrow(&mango_group.indexes[i], i);
mango_group: &MangoGroup,
) -> MangoResult<[U64F64; NUM_TOKENS]> {
let mut liabs = [ZERO_U64F64; NUM_TOKENS];
for i in 0..NUM_TOKENS {
liabs[i] = mango_group.indexes[i].borrow.checked_mul(self.borrows[i]).unwrap()
.checked_add(liabs[i]).unwrap();
}
Ok(liabs)
}
#[inline(always)]
pub fn get_assets_val(
&self,
mango_group: &MangoGroup,
@ -448,7 +435,6 @@ impl MarginAccount {
.checked_mul(prices[i]).unwrap()
.checked_add(U64F64::from_num(open_orders.native_pc_total)).unwrap()
.checked_add(assets).unwrap();
}
for i in 0..NUM_TOKENS { // add up the value in margin account deposits and positions
let index: &MangoIndex = &mango_group.indexes[i];
@ -460,6 +446,8 @@ impl MarginAccount {
Ok(assets)
}
#[inline(always)]
pub fn get_liabs_val(
&self,
mango_group: &MangoGroup,
@ -474,6 +462,7 @@ impl MarginAccount {
Ok(liabs)
}
/// Return amount of quote currency to deposit to get account above init_coll_ratio
#[inline(always)]
pub fn get_collateral_deficit(
&self,
mango_group: &MangoGroup,
@ -490,6 +479,7 @@ impl MarginAccount {
}
}
#[inline(always)]
pub fn get_partial_liq_deficit(
&self,
mango_group: &MangoGroup,
@ -505,23 +495,28 @@ impl MarginAccount {
// TODO make this checked
Ok((liabs * mango_group.init_coll_ratio - assets) / (mango_group.init_coll_ratio - PARTIAL_LIQ_INCENTIVE))
}
}
#[inline(always)]
pub fn get_native_borrow(&self, index: &MangoIndex, token_i: usize) -> u64 {
(self.borrows[token_i] * index.borrow).to_num()
}
#[inline(always)]
pub fn get_native_deposit(&self, index: &MangoIndex, token_i: usize) -> u64 {
(self.deposits[token_i] * index.deposit).to_num()
}
#[inline(always)]
pub fn checked_add_borrow(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.borrows[token_i] = self.borrows[token_i].checked_add(v).ok_or(throw!())?)
}
#[inline(always)]
pub fn checked_sub_borrow(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.borrows[token_i] = self.borrows[token_i].checked_sub(v).ok_or(throw!())?)
}
#[inline(always)]
pub fn checked_add_deposit(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.deposits[token_i] = self.deposits[token_i].checked_add(v).ok_or(throw!())?)
}
#[inline(always)]
pub fn checked_sub_deposit(&mut self, token_i: usize, v: U64F64) -> MangoResult<()> {
Ok(self.deposits[token_i] = self.deposits[token_i].checked_sub(v).ok_or(throw!())?)
}
@ -717,6 +712,7 @@ pub fn load_open_orders<'a>(
Ok(Ref::map(strip_dex_padding(acc)?, from_bytes))
}
#[inline(always)]
pub fn check_open_orders(
acc: &AccountInfo,
owner: &Pubkey