Clarkeni/loan fee logging (#180)
Logging for loan origination fees and token bankruptcy
This commit is contained in:
parent
6814701046
commit
704dfcaa27
2
anchor
2
anchor
|
@ -1 +1 @@
|
||||||
Subproject commit b52f23614601652a99ec6c27aec77bd327363b31
|
Subproject commit 9e546f52d967e95fea4ae105f4bc7bf3720b9464
|
|
@ -10,6 +10,11 @@ use crate::state::ScanningAccountRetriever;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
|
|
||||||
|
use crate::logs::{
|
||||||
|
LiquidateTokenBankruptcyLog, LoanOriginationFeeInstruction, TokenBalanceLog,
|
||||||
|
WithdrawLoanOriginationFeeLog,
|
||||||
|
};
|
||||||
|
|
||||||
// Remaining accounts:
|
// Remaining accounts:
|
||||||
// - all banks for liab_mint_info (writable)
|
// - all banks for liab_mint_info (writable)
|
||||||
// - merged health accounts for liqor+liqee
|
// - merged health accounts for liqor+liqee
|
||||||
|
@ -98,7 +103,8 @@ pub fn liq_token_bankruptcy(
|
||||||
|
|
||||||
let (liab_bank, liab_price, opt_quote_bank_and_price) =
|
let (liab_bank, liab_price, opt_quote_bank_and_price) =
|
||||||
account_retriever.banks_mut_and_oracles(liab_token_index, QUOTE_TOKEN_INDEX)?;
|
account_retriever.banks_mut_and_oracles(liab_token_index, QUOTE_TOKEN_INDEX)?;
|
||||||
let liab_deposit_index = liab_bank.deposit_index;
|
let mut liab_deposit_index = liab_bank.deposit_index;
|
||||||
|
let liab_borrow_index = liab_bank.borrow_index;
|
||||||
let (liqee_liab, liqee_raw_token_index) = liqee.token_position_mut(liab_token_index)?;
|
let (liqee_liab, liqee_raw_token_index) = liqee.token_position_mut(liab_token_index)?;
|
||||||
let initial_liab_native = liqee_liab.native(&liab_bank);
|
let initial_liab_native = liqee_liab.native(&liab_bank);
|
||||||
let mut remaining_liab_loss = -initial_liab_native;
|
let mut remaining_liab_loss = -initial_liab_native;
|
||||||
|
@ -151,26 +157,52 @@ pub fn liq_token_bankruptcy(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// move quote assets into liqor and withdraw liab assets
|
// move quote assets into liqor and withdraw liab assets
|
||||||
if let Some((quote_bank, _)) = opt_quote_bank_and_price {
|
if let Some((quote_bank, quote_price)) = opt_quote_bank_and_price {
|
||||||
// account constraint #2 a)
|
// account constraint #2 a)
|
||||||
require_keys_eq!(quote_bank.vault, ctx.accounts.quote_vault.key());
|
require_keys_eq!(quote_bank.vault, ctx.accounts.quote_vault.key());
|
||||||
require_keys_eq!(quote_bank.mint, ctx.accounts.insurance_vault.mint);
|
require_keys_eq!(quote_bank.mint, ctx.accounts.insurance_vault.mint);
|
||||||
|
|
||||||
|
let quote_deposit_index = quote_bank.deposit_index;
|
||||||
|
let quote_borrow_index = quote_bank.borrow_index;
|
||||||
|
|
||||||
// credit the liqor
|
// credit the liqor
|
||||||
let (liqor_quote, liqor_quote_raw_token_index, _) =
|
let (liqor_quote, liqor_quote_raw_token_index, _) =
|
||||||
liqor.ensure_token_position(QUOTE_TOKEN_INDEX)?;
|
liqor.ensure_token_position(QUOTE_TOKEN_INDEX)?;
|
||||||
let liqor_quote_active = quote_bank.deposit(liqor_quote, insurance_transfer_i80f48)?;
|
let liqor_quote_active = quote_bank.deposit(liqor_quote, insurance_transfer_i80f48)?;
|
||||||
|
let liqor_quote_indexed_position = liqor_quote.indexed_position;
|
||||||
|
|
||||||
// transfer liab from liqee to liqor
|
// transfer liab from liqee to liqor
|
||||||
let (liqor_liab, liqor_liab_raw_token_index, _) =
|
let (liqor_liab, liqor_liab_raw_token_index, _) =
|
||||||
liqor.ensure_token_position(liab_token_index)?;
|
liqor.ensure_token_position(liab_token_index)?;
|
||||||
let liqor_liab_active = liab_bank.withdraw_with_fee(liqor_liab, liab_transfer)?;
|
let (liqor_liab_active, loan_origination_fee) =
|
||||||
|
liab_bank.withdraw_with_fee(liqor_liab, liab_transfer)?;
|
||||||
|
|
||||||
// Check liqor's health
|
// Check liqor's health
|
||||||
let liqor_health =
|
let liqor_health =
|
||||||
compute_health(&liqor.borrow(), HealthType::Init, &account_retriever)?;
|
compute_health(&liqor.borrow(), HealthType::Init, &account_retriever)?;
|
||||||
require!(liqor_health >= 0, MangoError::HealthMustBePositive);
|
require!(liqor_health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
|
||||||
|
// liqor quote
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
|
token_index: QUOTE_TOKEN_INDEX,
|
||||||
|
indexed_position: liqor_quote_indexed_position.to_bits(),
|
||||||
|
deposit_index: quote_deposit_index.to_bits(),
|
||||||
|
borrow_index: quote_borrow_index.to_bits(),
|
||||||
|
price: quote_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
|
token_index: liab_token_index,
|
||||||
|
loan_origination_fee: loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::LiqTokenBankruptcy
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if !liqor_quote_active {
|
if !liqor_quote_active {
|
||||||
liqor.deactivate_token_position(liqor_quote_raw_token_index);
|
liqor.deactivate_token_position(liqor_quote_raw_token_index);
|
||||||
}
|
}
|
||||||
|
@ -191,6 +223,7 @@ pub fn liq_token_bankruptcy(
|
||||||
|
|
||||||
// Socialize loss if there's more loss and noone else could use the
|
// Socialize loss if there's more loss and noone else could use the
|
||||||
// insurance fund to cover it.
|
// insurance fund to cover it.
|
||||||
|
let mut socialized_loss = I80F48::ZERO;
|
||||||
if insurance_fund_exhausted && remaining_liab_loss.is_positive() {
|
if insurance_fund_exhausted && remaining_liab_loss.is_positive() {
|
||||||
// find the total deposits
|
// find the total deposits
|
||||||
let mut indexed_total_deposits = I80F48::ZERO;
|
let mut indexed_total_deposits = I80F48::ZERO;
|
||||||
|
@ -205,6 +238,8 @@ pub fn liq_token_bankruptcy(
|
||||||
// Probably not.
|
// Probably not.
|
||||||
let new_deposit_index =
|
let new_deposit_index =
|
||||||
cm!(liab_deposit_index - remaining_liab_loss / indexed_total_deposits);
|
cm!(liab_deposit_index - remaining_liab_loss / indexed_total_deposits);
|
||||||
|
liab_deposit_index = new_deposit_index;
|
||||||
|
socialized_loss = remaining_liab_loss;
|
||||||
|
|
||||||
let mut amount_to_credit = remaining_liab_loss;
|
let mut amount_to_credit = remaining_liab_loss;
|
||||||
for bank_ai in bank_ais.iter() {
|
for bank_ai in bank_ais.iter() {
|
||||||
|
@ -228,6 +263,28 @@ pub fn liq_token_bankruptcy(
|
||||||
require_eq!(liqee_liab.indexed_position, I80F48::ZERO);
|
require_eq!(liqee_liab.indexed_position, I80F48::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// liqor liab
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
|
token_index: liab_token_index,
|
||||||
|
indexed_position: liqee_liab.indexed_position.to_bits(),
|
||||||
|
deposit_index: liab_deposit_index.to_bits(),
|
||||||
|
borrow_index: liab_borrow_index.to_bits(),
|
||||||
|
price: liab_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// liqee liab
|
||||||
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.liqee.key(),
|
||||||
|
token_index: liab_token_index,
|
||||||
|
indexed_position: liqee_liab.indexed_position.to_bits(),
|
||||||
|
deposit_index: liab_deposit_index.to_bits(),
|
||||||
|
borrow_index: liab_borrow_index.to_bits(),
|
||||||
|
price: liab_price.to_bits(),
|
||||||
|
});
|
||||||
|
|
||||||
let liab_bank = bank_ais[0].load::<Bank>()?;
|
let liab_bank = bank_ais[0].load::<Bank>()?;
|
||||||
let end_liab_native = liqee_liab.native(&liab_bank);
|
let end_liab_native = liqee_liab.native(&liab_bank);
|
||||||
liqee_health_cache
|
liqee_health_cache
|
||||||
|
@ -243,5 +300,17 @@ pub fn liq_token_bankruptcy(
|
||||||
liqee.deactivate_token_position(liqee_raw_token_index);
|
liqee.deactivate_token_position(liqee_raw_token_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit!(LiquidateTokenBankruptcyLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
liqee: ctx.accounts.liqee.key(),
|
||||||
|
liqor: ctx.accounts.liqor.key(),
|
||||||
|
liab_token_index: liab_token_index,
|
||||||
|
initial_liab_native: initial_liab_native.to_bits(),
|
||||||
|
liab_price: liab_price.to_bits(),
|
||||||
|
insurance_token_index: QUOTE_TOKEN_INDEX,
|
||||||
|
insurance_transfer: insurance_transfer_i80f48.to_bits(),
|
||||||
|
socialized_loss: socialized_loss.to_bits()
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ 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::logs::{
|
||||||
|
LiquidateTokenAndTokenLog, LoanOriginationFeeInstruction, TokenBalanceLog,
|
||||||
|
WithdrawLoanOriginationFeeLog,
|
||||||
|
};
|
||||||
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;
|
||||||
|
@ -77,7 +80,7 @@ pub fn liq_token_with_token(
|
||||||
// Transfer some liab_token from liqor to liqee and
|
// Transfer some liab_token from liqor to liqee and
|
||||||
// transfer some asset_token from liqee to liqor.
|
// transfer some asset_token from liqee to liqor.
|
||||||
//
|
//
|
||||||
{
|
|
||||||
// Get the mut banks and oracle prices
|
// Get the mut banks and oracle prices
|
||||||
//
|
//
|
||||||
// This must happen _after_ the health computation, since immutable borrows of
|
// This must happen _after_ the health computation, since immutable borrows of
|
||||||
|
@ -117,7 +120,8 @@ pub fn liq_token_with_token(
|
||||||
// y = x * lp / ap * ff = x * lpa / ap (native asset tokens)
|
// y = x * lp / ap * ff = x * lpa / ap (native asset tokens)
|
||||||
//
|
//
|
||||||
// Result: x = -init_health / (lp * ilw - iaw * lpa)
|
// Result: x = -init_health / (lp * ilw - iaw * lpa)
|
||||||
let liab_needed = cm!(-init_health
|
let liab_needed =
|
||||||
|
cm!(-init_health
|
||||||
/ (liab_price * init_liab_weight - init_asset_weight * liab_price_adjusted));
|
/ (liab_price * init_liab_weight - init_asset_weight * liab_price_adjusted));
|
||||||
|
|
||||||
// How much liab can we get at most for the asset balance?
|
// How much liab can we get at most for the asset balance?
|
||||||
|
@ -138,13 +142,13 @@ pub fn liq_token_with_token(
|
||||||
|
|
||||||
// Apply the balance changes to the liqor and liqee accounts
|
// Apply the balance changes to the liqor and liqee accounts
|
||||||
let liqee_liab_position = liqee.token_position_mut_by_raw_index(liqee_liab_raw_index);
|
let liqee_liab_position = liqee.token_position_mut_by_raw_index(liqee_liab_raw_index);
|
||||||
let liqee_liab_active =
|
let liqee_liab_active = liab_bank.deposit_with_dusting(liqee_liab_position, liab_transfer)?;
|
||||||
liab_bank.deposit_with_dusting(liqee_liab_position, liab_transfer)?;
|
|
||||||
let liqee_liab_position_indexed = liqee_liab_position.indexed_position;
|
let liqee_liab_position_indexed = liqee_liab_position.indexed_position;
|
||||||
|
|
||||||
let (liqor_liab_position, liqor_liab_raw_index, _) =
|
let (liqor_liab_position, liqor_liab_raw_index, _) =
|
||||||
liqor.ensure_token_position(liab_token_index)?;
|
liqor.ensure_token_position(liab_token_index)?;
|
||||||
let liqor_liab_active = liab_bank.withdraw_with_fee(liqor_liab_position, liab_transfer)?;
|
let (liqor_liab_active, loan_origination_fee) =
|
||||||
|
liab_bank.withdraw_with_fee(liqor_liab_position, liab_transfer)?;
|
||||||
let liqor_liab_position_indexed = liqor_liab_position.indexed_position;
|
let liqor_liab_position_indexed = liqor_liab_position.indexed_position;
|
||||||
let liqee_liab_native_after = liqee_liab_position.native(&liab_bank);
|
let liqee_liab_native_after = liqee_liab_position.native(&liab_bank);
|
||||||
|
|
||||||
|
@ -175,19 +179,6 @@ pub fn liq_token_with_token(
|
||||||
asset_transfer
|
asset_transfer
|
||||||
);
|
);
|
||||||
|
|
||||||
emit!(LiquidateTokenAndTokenLog {
|
|
||||||
mango_group: ctx.accounts.group.key(),
|
|
||||||
liqee: ctx.accounts.liqee.key(),
|
|
||||||
liqor: ctx.accounts.liqor.key(),
|
|
||||||
asset_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
|
// liqee asset
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
mango_group: ctx.accounts.group.key(),
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
@ -229,6 +220,16 @@ pub fn liq_token_with_token(
|
||||||
price: liab_price.to_bits(),
|
price: liab_price.to_bits(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
|
token_index: liab_token_index,
|
||||||
|
loan_origination_fee: loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::LiqTokenWithToken
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Since we use a scanning account retriever, it's safe to deactivate inactive token positions
|
// Since we use a scanning account retriever, it's safe to deactivate inactive token positions
|
||||||
if !liqee_asset_active {
|
if !liqee_asset_active {
|
||||||
liqee.deactivate_token_position(liqee_asset_raw_index);
|
liqee.deactivate_token_position(liqee_asset_raw_index);
|
||||||
|
@ -242,7 +243,6 @@ pub fn liq_token_with_token(
|
||||||
if !liqor_liab_active {
|
if !liqor_liab_active {
|
||||||
liqor.deactivate_token_position(liqor_liab_raw_index)
|
liqor.deactivate_token_position(liqor_liab_raw_index)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check liqee health again
|
// Check liqee health again
|
||||||
let liqee_init_health = liqee_health_cache.health(HealthType::Init);
|
let liqee_init_health = liqee_health_cache.health(HealthType::Init);
|
||||||
|
@ -255,5 +255,18 @@ pub fn liq_token_with_token(
|
||||||
.context("compute liqor health")?;
|
.context("compute liqor health")?;
|
||||||
require!(liqor_health >= 0, MangoError::HealthMustBePositive);
|
require!(liqor_health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
|
||||||
|
emit!(LiquidateTokenAndTokenLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
liqee: ctx.accounts.liqee.key(),
|
||||||
|
liqor: ctx.accounts.liqor.key(),
|
||||||
|
asset_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_health_cache.has_liquidatable_assets() & liqee_init_health.is_negative()
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ use crate::error::*;
|
||||||
use crate::instructions::apply_vault_difference;
|
use crate::instructions::apply_vault_difference;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
|
|
||||||
|
use crate::logs::{LoanOriginationFeeInstruction, WithdrawLoanOriginationFeeLog};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct Serum3LiqForceCancelOrders<'info> {
|
pub struct Serum3LiqForceCancelOrders<'info> {
|
||||||
pub group: AccountLoader<'info, Group>,
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
@ -68,9 +70,9 @@ pub fn serum3_liq_force_cancel_orders(
|
||||||
//
|
//
|
||||||
// Validation
|
// Validation
|
||||||
//
|
//
|
||||||
|
let serum_market = ctx.accounts.serum_market.load()?;
|
||||||
{
|
{
|
||||||
let account = ctx.accounts.account.load()?;
|
let account = ctx.accounts.account.load()?;
|
||||||
let serum_market = ctx.accounts.serum_market.load()?;
|
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
require!(
|
require!(
|
||||||
|
@ -138,6 +140,7 @@ pub fn serum3_liq_force_cancel_orders(
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
||||||
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
||||||
|
let (vault_difference_result, base_loan_origination_fee, quote_loan_origination_fee) =
|
||||||
apply_vault_difference(
|
apply_vault_difference(
|
||||||
&mut account.borrow_mut(),
|
&mut account.borrow_mut(),
|
||||||
&mut base_bank,
|
&mut base_bank,
|
||||||
|
@ -146,8 +149,27 @@ pub fn serum3_liq_force_cancel_orders(
|
||||||
&mut quote_bank,
|
&mut quote_bank,
|
||||||
after_quote_vault,
|
after_quote_vault,
|
||||||
before_quote_vault,
|
before_quote_vault,
|
||||||
)?
|
)?;
|
||||||
.deactivate_inactive_token_accounts(&mut account.borrow_mut());
|
vault_difference_result.deactivate_inactive_token_accounts(&mut account.borrow_mut());
|
||||||
|
|
||||||
|
if base_loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: serum_market.base_token_index,
|
||||||
|
loan_origination_fee: base_loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::Serum3LiqForceCancelOrders
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if quote_loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: serum_market.quote_token_index,
|
||||||
|
loan_origination_fee: quote_loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::Serum3LiqForceCancelOrders
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ use serum_dex::instruction::NewOrderInstructionV3;
|
||||||
use serum_dex::matching::Side;
|
use serum_dex::matching::Side;
|
||||||
use serum_dex::state::OpenOrders;
|
use serum_dex::state::OpenOrders;
|
||||||
|
|
||||||
|
use crate::logs::{LoanOriginationFeeInstruction, WithdrawLoanOriginationFeeLog};
|
||||||
|
|
||||||
/// For loan origination fees bookkeeping purposes
|
/// For loan origination fees bookkeeping purposes
|
||||||
pub struct OpenOrdersSlim {
|
pub struct OpenOrdersSlim {
|
||||||
pub native_coin_free: u64,
|
pub native_coin_free: u64,
|
||||||
|
@ -273,9 +275,10 @@ pub fn serum3_place_order(
|
||||||
|
|
||||||
// Charge the difference in vault balances to the user's account
|
// Charge the difference in vault balances to the user's account
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
let vault_difference_result = {
|
let (vault_difference_result, base_loan_origination_fee, quote_loan_origination_fee) = {
|
||||||
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
||||||
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
||||||
|
|
||||||
apply_vault_difference(
|
apply_vault_difference(
|
||||||
&mut account.borrow_mut(),
|
&mut account.borrow_mut(),
|
||||||
&mut base_bank,
|
&mut base_bank,
|
||||||
|
@ -298,6 +301,25 @@ pub fn serum3_place_order(
|
||||||
|
|
||||||
vault_difference_result.deactivate_inactive_token_accounts(&mut account.borrow_mut());
|
vault_difference_result.deactivate_inactive_token_accounts(&mut account.borrow_mut());
|
||||||
|
|
||||||
|
if base_loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: serum_market.base_token_index,
|
||||||
|
loan_origination_fee: base_loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::Serum3PlaceOrder
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if quote_loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: serum_market.quote_token_index,
|
||||||
|
loan_origination_fee: quote_loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::Serum3PlaceOrder
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,25 +372,31 @@ pub fn apply_vault_difference(
|
||||||
quote_bank: &mut Bank,
|
quote_bank: &mut Bank,
|
||||||
after_quote_vault: u64,
|
after_quote_vault: u64,
|
||||||
before_quote_vault: u64,
|
before_quote_vault: u64,
|
||||||
) -> Result<VaultDifferenceResult> {
|
) -> Result<(VaultDifferenceResult, I80F48, I80F48)> {
|
||||||
// TODO: Applying the loan origination fee here may be too early: it should only be
|
// TODO: Applying the loan origination fee here may be too early: it should only be
|
||||||
// charged if an order executes and the loan materializes? Otherwise MMs that place
|
// charged if an order executes and the loan materializes? Otherwise MMs that place
|
||||||
// an order without having the funds will be charged for each place_order!
|
// an order without having the funds will be charged for each place_order!
|
||||||
|
|
||||||
let (base_position, base_raw_index) = account.token_position_mut(base_bank.token_index)?;
|
let (base_position, base_raw_index) = account.token_position_mut(base_bank.token_index)?;
|
||||||
let base_change = I80F48::from(after_base_vault) - I80F48::from(before_base_vault);
|
let base_change = I80F48::from(after_base_vault) - I80F48::from(before_base_vault);
|
||||||
let base_active = base_bank.change_with_fee(base_position, base_change)?;
|
let (base_active, base_loan_origination_fee) =
|
||||||
|
base_bank.change_with_fee(base_position, base_change)?;
|
||||||
|
|
||||||
let (quote_position, quote_raw_index) = account.token_position_mut(quote_bank.token_index)?;
|
let (quote_position, quote_raw_index) = account.token_position_mut(quote_bank.token_index)?;
|
||||||
let quote_change = I80F48::from(after_quote_vault) - I80F48::from(before_quote_vault);
|
let quote_change = I80F48::from(after_quote_vault) - I80F48::from(before_quote_vault);
|
||||||
let quote_active = quote_bank.change_with_fee(quote_position, quote_change)?;
|
let (quote_active, quote_loan_origination_fee) =
|
||||||
|
quote_bank.change_with_fee(quote_position, quote_change)?;
|
||||||
|
|
||||||
Ok(VaultDifferenceResult {
|
Ok((
|
||||||
|
VaultDifferenceResult {
|
||||||
base_raw_index,
|
base_raw_index,
|
||||||
base_active,
|
base_active,
|
||||||
quote_raw_index,
|
quote_raw_index,
|
||||||
quote_active,
|
quote_active,
|
||||||
})
|
},
|
||||||
|
base_loan_origination_fee,
|
||||||
|
quote_loan_origination_fee,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cpi_place_order(ctx: &Serum3PlaceOrder, order: NewOrderInstructionV3) -> Result<()> {
|
fn cpi_place_order(ctx: &Serum3PlaceOrder, order: NewOrderInstructionV3) -> Result<()> {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::serum3_cpi::load_open_orders_ref;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
|
|
||||||
use super::{apply_vault_difference, OpenOrdersReserved, OpenOrdersSlim};
|
use super::{apply_vault_difference, OpenOrdersReserved, OpenOrdersSlim};
|
||||||
|
use crate::logs::{LoanOriginationFeeInstruction, WithdrawLoanOriginationFeeLog};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct Serum3SettleFunds<'info> {
|
pub struct Serum3SettleFunds<'info> {
|
||||||
|
@ -149,6 +150,7 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
||||||
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
||||||
|
let (vault_difference_result, base_loan_origination_fee, quote_loan_origination_fee) =
|
||||||
apply_vault_difference(
|
apply_vault_difference(
|
||||||
&mut account.borrow_mut(),
|
&mut account.borrow_mut(),
|
||||||
&mut base_bank,
|
&mut base_bank,
|
||||||
|
@ -157,8 +159,27 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
|
||||||
&mut quote_bank,
|
&mut quote_bank,
|
||||||
after_quote_vault,
|
after_quote_vault,
|
||||||
before_quote_vault,
|
before_quote_vault,
|
||||||
)?
|
)?;
|
||||||
.deactivate_inactive_token_accounts(&mut account.borrow_mut());
|
vault_difference_result.deactivate_inactive_token_accounts(&mut account.borrow_mut());
|
||||||
|
|
||||||
|
if base_loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: serum_market.base_token_index,
|
||||||
|
loan_origination_fee: base_loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::Serum3SettleFunds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if quote_loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index: serum_market.quote_token_index,
|
||||||
|
loan_origination_fee: quote_loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::Serum3SettleFunds
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -117,7 +117,9 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
|
||||||
deposit_index: deposit_index.to_bits(),
|
deposit_index: deposit_index.to_bits(),
|
||||||
borrow_index: borrow_index.to_bits(),
|
borrow_index: borrow_index.to_bits(),
|
||||||
avg_utilization: new_avg_utilization.to_bits(),
|
avg_utilization: new_avg_utilization.to_bits(),
|
||||||
price: price.to_bits()
|
price: price.to_bits(),
|
||||||
|
collected_fees: some_bank.collected_fees_native.to_bits(),
|
||||||
|
loan_fee_rate: some_bank.loan_fee_rate.to_bits()
|
||||||
});
|
});
|
||||||
|
|
||||||
drop(some_bank);
|
drop(some_bank);
|
||||||
|
|
|
@ -6,7 +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::logs::{
|
||||||
|
LoanOriginationFeeInstruction, TokenBalanceLog, WithdrawLoanOriginationFeeLog, WithdrawLog,
|
||||||
|
};
|
||||||
use crate::state::new_fixed_order_account_retriever;
|
use crate::state::new_fixed_order_account_retriever;
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
|
|
||||||
// The bank will also be passed in remainingAccounts. Use an explicit scope
|
// 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.
|
||||||
let (position_is_active, amount_i80f48) = {
|
let (position_is_active, amount_i80f48, loan_origination_fee) = {
|
||||||
let mut bank = ctx.accounts.bank.load_mut()?;
|
let mut bank = ctx.accounts.bank.load_mut()?;
|
||||||
let native_position = position.native(&bank);
|
let native_position = position.native(&bank);
|
||||||
|
|
||||||
|
@ -87,7 +89,8 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
let amount_i80f48 = I80F48::from(amount);
|
let amount_i80f48 = I80F48::from(amount);
|
||||||
|
|
||||||
// Update the bank and position
|
// Update the bank and position
|
||||||
let position_is_active = bank.withdraw_with_fee(position, amount_i80f48)?;
|
let (position_is_active, loan_origination_fee) =
|
||||||
|
bank.withdraw_with_fee(position, amount_i80f48)?;
|
||||||
|
|
||||||
// Provide a readable error message in case the vault doesn't have enough tokens
|
// Provide a readable error message in case the vault doesn't have enough tokens
|
||||||
if ctx.accounts.vault.amount < amount {
|
if ctx.accounts.vault.amount < amount {
|
||||||
|
@ -106,7 +109,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
amount,
|
amount,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
(position_is_active, amount_i80f48)
|
(position_is_active, amount_i80f48, loan_origination_fee)
|
||||||
};
|
};
|
||||||
|
|
||||||
let indexed_position = position.indexed_position;
|
let indexed_position = position.indexed_position;
|
||||||
|
@ -156,5 +159,15 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
price: oracle_price.to_bits(),
|
price: oracle_price.to_bits(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if loan_origination_fee.is_positive() {
|
||||||
|
emit!(WithdrawLoanOriginationFeeLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
|
mango_account: ctx.accounts.account.key(),
|
||||||
|
token_index,
|
||||||
|
loan_origination_fee: loan_origination_fee.to_bits(),
|
||||||
|
instruction: LoanOriginationFeeInstruction::TokenWithdraw,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,8 @@ pub struct UpdateIndexLog {
|
||||||
pub borrow_index: i128, // I80F48
|
pub borrow_index: i128, // I80F48
|
||||||
pub avg_utilization: i128, // I80F48
|
pub avg_utilization: i128, // I80F48
|
||||||
pub price: i128, // I80F48
|
pub price: i128, // I80F48
|
||||||
|
pub collected_fees: i128, // I80F48
|
||||||
|
pub loan_fee_rate: i128, // I80F48
|
||||||
}
|
}
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
|
@ -159,7 +161,7 @@ pub struct LiquidateTokenAndTokenLog {
|
||||||
pub liab_transfer: i128, // I80F48
|
pub liab_transfer: i128, // I80F48
|
||||||
pub asset_price: i128, // I80F48
|
pub asset_price: i128, // I80F48
|
||||||
pub liab_price: i128, // I80F48
|
pub liab_price: i128, // I80F48
|
||||||
// pub bankruptcy: bool,
|
pub bankruptcy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
|
@ -175,3 +177,37 @@ pub struct OpenOrdersBalanceLog {
|
||||||
pub referrer_rebates_accrued: u64,
|
pub referrer_rebates_accrued: u64,
|
||||||
pub price: i128, // I80F48
|
pub price: i128, // I80F48
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone, Debug, AnchorSerialize, AnchorDeserialize)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum LoanOriginationFeeInstruction {
|
||||||
|
Unknown,
|
||||||
|
LiqTokenBankruptcy,
|
||||||
|
LiqTokenWithToken,
|
||||||
|
Serum3LiqForceCancelOrders,
|
||||||
|
Serum3PlaceOrder,
|
||||||
|
Serum3SettleFunds,
|
||||||
|
TokenWithdraw,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct WithdrawLoanOriginationFeeLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
|
pub mango_account: Pubkey,
|
||||||
|
pub token_index: u16,
|
||||||
|
pub loan_origination_fee: i128, // I80F48
|
||||||
|
pub instruction: LoanOriginationFeeInstruction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[event]
|
||||||
|
pub struct LiquidateTokenBankruptcyLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
|
pub liqee: Pubkey,
|
||||||
|
pub liqor: Pubkey,
|
||||||
|
pub liab_token_index: u16,
|
||||||
|
pub initial_liab_native: i128,
|
||||||
|
pub liab_price: i128,
|
||||||
|
pub insurance_token_index: u16,
|
||||||
|
pub insurance_transfer: i128,
|
||||||
|
pub socialized_loss: i128,
|
||||||
|
}
|
||||||
|
|
|
@ -271,7 +271,10 @@ impl Bank {
|
||||||
position: &mut TokenPosition,
|
position: &mut TokenPosition,
|
||||||
native_amount: I80F48,
|
native_amount: I80F48,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
self.withdraw_internal(position, native_amount, false, !position.is_in_use())
|
let (position_is_active, _) =
|
||||||
|
self.withdraw_internal(position, native_amount, false, !position.is_in_use())?;
|
||||||
|
|
||||||
|
return Ok(position_is_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `withdraw_without_fee()` but allows dusting of in-use token accounts.
|
/// Like `withdraw_without_fee()` but allows dusting of in-use token accounts.
|
||||||
|
@ -282,8 +285,9 @@ impl Bank {
|
||||||
position: &mut TokenPosition,
|
position: &mut TokenPosition,
|
||||||
native_amount: I80F48,
|
native_amount: I80F48,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
self.withdraw_internal(position, native_amount, false, true)
|
Ok(self
|
||||||
.map(|not_dusted| not_dusted || position.is_in_use())
|
.withdraw_internal(position, native_amount, false, true)
|
||||||
|
.map(|(not_dusted, _)| not_dusted || position.is_in_use())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Withdraws `native_amount` while applying the loan origination fee if a borrow is created.
|
/// Withdraws `native_amount` while applying the loan origination fee if a borrow is created.
|
||||||
|
@ -298,7 +302,7 @@ impl Bank {
|
||||||
&mut self,
|
&mut self,
|
||||||
position: &mut TokenPosition,
|
position: &mut TokenPosition,
|
||||||
native_amount: I80F48,
|
native_amount: I80F48,
|
||||||
) -> Result<bool> {
|
) -> Result<(bool, I80F48)> {
|
||||||
self.withdraw_internal(position, native_amount, true, !position.is_in_use())
|
self.withdraw_internal(position, native_amount, true, !position.is_in_use())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +313,7 @@ impl Bank {
|
||||||
mut native_amount: I80F48,
|
mut native_amount: I80F48,
|
||||||
with_loan_origination_fee: bool,
|
with_loan_origination_fee: bool,
|
||||||
allow_dusting: bool,
|
allow_dusting: bool,
|
||||||
) -> Result<bool> {
|
) -> Result<(bool, I80F48)> {
|
||||||
require_gte!(native_amount, 0);
|
require_gte!(native_amount, 0);
|
||||||
let native_position = position.native(self);
|
let native_position = position.native(self);
|
||||||
|
|
||||||
|
@ -322,13 +326,13 @@ impl Bank {
|
||||||
self.dust = cm!(self.dust + new_native_position);
|
self.dust = cm!(self.dust + new_native_position);
|
||||||
self.indexed_deposits = cm!(self.indexed_deposits - position.indexed_position);
|
self.indexed_deposits = cm!(self.indexed_deposits - position.indexed_position);
|
||||||
position.indexed_position = I80F48::ZERO;
|
position.indexed_position = I80F48::ZERO;
|
||||||
return Ok(false);
|
return Ok((false, I80F48::ZERO));
|
||||||
} else {
|
} else {
|
||||||
// withdraw some deposits leaving a positive balance
|
// withdraw some deposits leaving a positive balance
|
||||||
let indexed_change = cm!(native_amount / self.deposit_index);
|
let indexed_change = cm!(native_amount / self.deposit_index);
|
||||||
self.indexed_deposits = cm!(self.indexed_deposits - indexed_change);
|
self.indexed_deposits = cm!(self.indexed_deposits - indexed_change);
|
||||||
position.indexed_position = cm!(position.indexed_position - indexed_change);
|
position.indexed_position = cm!(position.indexed_position - indexed_change);
|
||||||
return Ok(true);
|
return Ok((true, I80F48::ZERO));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,8 +343,9 @@ impl Bank {
|
||||||
native_amount = -new_native_position;
|
native_amount = -new_native_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut loan_origination_fee = I80F48::ZERO;
|
||||||
if with_loan_origination_fee {
|
if with_loan_origination_fee {
|
||||||
let loan_origination_fee = cm!(self.loan_origination_fee_rate * native_amount);
|
loan_origination_fee = cm!(self.loan_origination_fee_rate * native_amount);
|
||||||
self.collected_fees_native = cm!(self.collected_fees_native + loan_origination_fee);
|
self.collected_fees_native = cm!(self.collected_fees_native + loan_origination_fee);
|
||||||
native_amount = cm!(native_amount + loan_origination_fee);
|
native_amount = cm!(native_amount + loan_origination_fee);
|
||||||
}
|
}
|
||||||
|
@ -350,7 +355,7 @@ impl Bank {
|
||||||
self.indexed_borrows = cm!(self.indexed_borrows + indexed_change);
|
self.indexed_borrows = cm!(self.indexed_borrows + indexed_change);
|
||||||
position.indexed_position = cm!(position.indexed_position - indexed_change);
|
position.indexed_position = cm!(position.indexed_position - indexed_change);
|
||||||
|
|
||||||
Ok(true)
|
Ok((true, loan_origination_fee))
|
||||||
}
|
}
|
||||||
|
|
||||||
// withdraw the loan origination fee for a borrow that happenend earlier
|
// withdraw the loan origination fee for a borrow that happenend earlier
|
||||||
|
@ -358,12 +363,15 @@ impl Bank {
|
||||||
&mut self,
|
&mut self,
|
||||||
position: &mut TokenPosition,
|
position: &mut TokenPosition,
|
||||||
already_borrowed_native_amount: I80F48,
|
already_borrowed_native_amount: I80F48,
|
||||||
) -> Result<bool> {
|
) -> Result<(bool, I80F48)> {
|
||||||
let loan_origination_fee =
|
let loan_origination_fee =
|
||||||
cm!(self.loan_origination_fee_rate * already_borrowed_native_amount);
|
cm!(self.loan_origination_fee_rate * already_borrowed_native_amount);
|
||||||
self.collected_fees_native = cm!(self.collected_fees_native + loan_origination_fee);
|
self.collected_fees_native = cm!(self.collected_fees_native + loan_origination_fee);
|
||||||
|
|
||||||
self.withdraw_internal(position, loan_origination_fee, false, !position.is_in_use())
|
let (position_is_active, _) =
|
||||||
|
self.withdraw_internal(position, loan_origination_fee, false, !position.is_in_use())?;
|
||||||
|
|
||||||
|
Ok((position_is_active, loan_origination_fee))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change a position without applying the loan origination fee
|
/// Change a position without applying the loan origination fee
|
||||||
|
@ -384,9 +392,9 @@ impl Bank {
|
||||||
&mut self,
|
&mut self,
|
||||||
position: &mut TokenPosition,
|
position: &mut TokenPosition,
|
||||||
native_amount: I80F48,
|
native_amount: I80F48,
|
||||||
) -> Result<bool> {
|
) -> Result<(bool, I80F48)> {
|
||||||
if native_amount >= 0 {
|
if native_amount >= 0 {
|
||||||
self.deposit(position, native_amount)
|
Ok((self.deposit(position, native_amount)?, I80F48::ZERO))
|
||||||
} else {
|
} else {
|
||||||
self.withdraw_with_fee(position, -native_amount)
|
self.withdraw_with_fee(position, -native_amount)
|
||||||
}
|
}
|
||||||
|
@ -635,7 +643,7 @@ mod tests {
|
||||||
//
|
//
|
||||||
|
|
||||||
let change = I80F48::from(change);
|
let change = I80F48::from(change);
|
||||||
let is_active = bank.change_with_fee(&mut account, change)?;
|
let (is_active, _) = bank.change_with_fee(&mut account, change)?;
|
||||||
|
|
||||||
let mut expected_native = start_native + change;
|
let mut expected_native = start_native + change;
|
||||||
if expected_native >= 0.0 && expected_native < 1.0 && !is_in_use {
|
if expected_native >= 0.0 && expected_native < 1.0 && !is_in_use {
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl TestContextBuilder {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// intentionally set to as tight as possible, to catch potential problems early
|
// intentionally set to as tight as possible, to catch potential problems early
|
||||||
test.set_compute_max_units(86000);
|
test.set_compute_max_units(87000);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
test,
|
test,
|
||||||
|
|
|
@ -4322,6 +4322,35 @@ export type MangoV4 = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "LoanOriginationFeeInstruction",
|
||||||
|
"type": {
|
||||||
|
"kind": "enum",
|
||||||
|
"variants": [
|
||||||
|
{
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LiqTokenBankruptcy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LiqTokenWithToken"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serum3LiqForceCancelOrders"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serum3PlaceOrder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serum3SettleFunds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TokenWithdraw"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "HealthType",
|
"name": "HealthType",
|
||||||
"docs": [
|
"docs": [
|
||||||
|
@ -4848,6 +4877,16 @@ export type MangoV4 = {
|
||||||
"name": "price",
|
"name": "price",
|
||||||
"type": "i128",
|
"type": "i128",
|
||||||
"index": false
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "collectedFees",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "loanFeeRate",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -4928,6 +4967,11 @@ export type MangoV4 = {
|
||||||
"name": "liabPrice",
|
"name": "liabPrice",
|
||||||
"type": "i128",
|
"type": "i128",
|
||||||
"index": false
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bankruptcy",
|
||||||
|
"type": "bool",
|
||||||
|
"index": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -4980,6 +5024,88 @@ export type MangoV4 = {
|
||||||
"index": false
|
"index": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WithdrawLoanOriginationFeeLog",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mangoAccount",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tokenIndex",
|
||||||
|
"type": "u16",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "loanOriginationFee",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "instruction",
|
||||||
|
"type": {
|
||||||
|
"defined": "LoanOriginationFeeInstruction"
|
||||||
|
},
|
||||||
|
"index": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LiquidateTokenBankruptcyLog",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liqee",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liqor",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liabTokenIndex",
|
||||||
|
"type": "u16",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "initialLiabNative",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liabPrice",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "insuranceTokenIndex",
|
||||||
|
"type": "u16",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "insuranceTransfer",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "socializedLoss",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"errors": [
|
"errors": [
|
||||||
|
@ -9385,6 +9511,35 @@ export const IDL: MangoV4 = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "LoanOriginationFeeInstruction",
|
||||||
|
"type": {
|
||||||
|
"kind": "enum",
|
||||||
|
"variants": [
|
||||||
|
{
|
||||||
|
"name": "Unknown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LiqTokenBankruptcy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LiqTokenWithToken"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serum3LiqForceCancelOrders"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serum3PlaceOrder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serum3SettleFunds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TokenWithdraw"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "HealthType",
|
"name": "HealthType",
|
||||||
"docs": [
|
"docs": [
|
||||||
|
@ -9911,6 +10066,16 @@ export const IDL: MangoV4 = {
|
||||||
"name": "price",
|
"name": "price",
|
||||||
"type": "i128",
|
"type": "i128",
|
||||||
"index": false
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "collectedFees",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "loanFeeRate",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -9991,6 +10156,11 @@ export const IDL: MangoV4 = {
|
||||||
"name": "liabPrice",
|
"name": "liabPrice",
|
||||||
"type": "i128",
|
"type": "i128",
|
||||||
"index": false
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bankruptcy",
|
||||||
|
"type": "bool",
|
||||||
|
"index": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -10043,6 +10213,88 @@ export const IDL: MangoV4 = {
|
||||||
"index": false
|
"index": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WithdrawLoanOriginationFeeLog",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mangoAccount",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tokenIndex",
|
||||||
|
"type": "u16",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "loanOriginationFee",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "instruction",
|
||||||
|
"type": {
|
||||||
|
"defined": "LoanOriginationFeeInstruction"
|
||||||
|
},
|
||||||
|
"index": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LiquidateTokenBankruptcyLog",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liqee",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liqor",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liabTokenIndex",
|
||||||
|
"type": "u16",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "initialLiabNative",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liabPrice",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "insuranceTokenIndex",
|
||||||
|
"type": "u16",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "insuranceTransfer",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "socializedLoss",
|
||||||
|
"type": "i128",
|
||||||
|
"index": false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"errors": [
|
"errors": [
|
||||||
|
|
Loading…
Reference in New Issue