reduce CU cost of fallback oracles
This commit is contained in:
parent
2b0a0e3a3e
commit
59001b3631
|
@ -373,7 +373,7 @@ pub async fn init(
|
||||||
{
|
{
|
||||||
if unchecked_oracle_state
|
if unchecked_oracle_state
|
||||||
.check_confidence_and_maybe_staleness(
|
.check_confidence_and_maybe_staleness(
|
||||||
&oracle_pk,
|
&mkt.1.name,
|
||||||
&oracle_config.to_oracle_config(),
|
&oracle_config.to_oracle_config(),
|
||||||
None, // force this to always return a price no matter how stale
|
None, // force this to always return a price no matter how stale
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,8 +22,8 @@ pub struct TokenEdit<'info> {
|
||||||
/// CHECK: The oracle can be one of several different account types
|
/// CHECK: The oracle can be one of several different account types
|
||||||
pub oracle: UncheckedAccount<'info>,
|
pub oracle: UncheckedAccount<'info>,
|
||||||
|
|
||||||
/// The oracle account is optional and only used when reset_stable_price is set.
|
/// The fallback oracle account is optional and only used when set_fallback_oracle is true.
|
||||||
///
|
///
|
||||||
/// CHECK: The oracle can be one of several different account types
|
/// CHECK: The fallback oracle can be one of several different account types
|
||||||
pub fallback_oracle: UncheckedAccount<'info>,
|
pub fallback_oracle: UncheckedAccount<'info>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,11 +181,12 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
|
|
||||||
// When borrowing the price has be trustworthy, so we can do a reasonable
|
// When borrowing the price has be trustworthy, so we can do a reasonable
|
||||||
// net borrow check.
|
// net borrow check.
|
||||||
|
let slot_opt = Some(Clock::get()?.slot);
|
||||||
unsafe_oracle_state.check_confidence_and_maybe_staleness(
|
unsafe_oracle_state.check_confidence_and_maybe_staleness(
|
||||||
&bank.oracle,
|
&bank.name(),
|
||||||
&bank.oracle_config,
|
&bank.oracle_config,
|
||||||
Some(Clock::get()?.slot),
|
slot_opt,
|
||||||
)?;
|
).with_context(|| oracle_log_context(&unsafe_oracle_state, &bank.oracle_config, slot_opt))?;
|
||||||
bank.check_net_borrows(unsafe_oracle_state.price)?;
|
bank.check_net_borrows(unsafe_oracle_state.price)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ use anchor_lang::prelude::*;
|
||||||
use anchor_spl::token::TokenAccount;
|
use anchor_spl::token::TokenAccount;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
use oracle::oracle_log_context;
|
||||||
use static_assertions::const_assert_eq;
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
@ -949,20 +950,10 @@ impl Bank {
|
||||||
staleness_slot: Option<u64>,
|
staleness_slot: Option<u64>,
|
||||||
) -> Result<I80F48> {
|
) -> Result<I80F48> {
|
||||||
require_keys_eq!(self.oracle, *oracle_acc.key());
|
require_keys_eq!(self.oracle, *oracle_acc.key());
|
||||||
self.oracle_price_inner(oracle_acc, staleness_slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn oracle_price_inner(
|
|
||||||
&self,
|
|
||||||
oracle_acc: &impl KeyedAccountReader,
|
|
||||||
staleness_slot: Option<u64>,
|
|
||||||
) -> Result<I80F48> {
|
|
||||||
let state = oracle::oracle_state_unchecked(oracle_acc, self.mint_decimals)?;
|
let state = oracle::oracle_state_unchecked(oracle_acc, self.mint_decimals)?;
|
||||||
state.check_confidence_and_maybe_staleness(
|
state
|
||||||
&self.oracle,
|
.check_confidence_and_maybe_staleness(&self.name(), &self.oracle_config, staleness_slot)
|
||||||
&self.oracle_config,
|
.with_context(|| oracle_log_context(&state, &self.oracle_config, staleness_slot))?;
|
||||||
staleness_slot,
|
|
||||||
)?;
|
|
||||||
Ok(state.price)
|
Ok(state.price)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,13 +964,32 @@ impl Bank {
|
||||||
fallback_oracle_acc_opt: Option<&impl KeyedAccountReader>,
|
fallback_oracle_acc_opt: Option<&impl KeyedAccountReader>,
|
||||||
staleness_slot: Option<u64>,
|
staleness_slot: Option<u64>,
|
||||||
) -> Result<I80F48> {
|
) -> Result<I80F48> {
|
||||||
let primary_price = self.oracle_price(oracle_acc, staleness_slot);
|
require_keys_eq!(self.oracle, *oracle_acc.key());
|
||||||
if primary_price.is_oracle_error() && fallback_oracle_acc_opt.is_some() {
|
let primary_state = oracle::oracle_state_unchecked(oracle_acc, self.mint_decimals)?;
|
||||||
|
let primary_ok = primary_state.check_confidence_and_maybe_staleness(
|
||||||
|
&self.name(),
|
||||||
|
&self.oracle_config,
|
||||||
|
staleness_slot,
|
||||||
|
);
|
||||||
|
if primary_ok.is_oracle_error() && fallback_oracle_acc_opt.is_some() {
|
||||||
let fallback_oracle_acc = fallback_oracle_acc_opt.unwrap();
|
let fallback_oracle_acc = fallback_oracle_acc_opt.unwrap();
|
||||||
require_keys_eq!(self.fallback_oracle, *fallback_oracle_acc.key());
|
require_keys_eq!(self.fallback_oracle, *fallback_oracle_acc.key());
|
||||||
self.oracle_price_inner(fallback_oracle_acc, staleness_slot)
|
let fallback_state =
|
||||||
|
oracle::oracle_state_unchecked(fallback_oracle_acc, self.mint_decimals)?;
|
||||||
|
let fallback_ok = fallback_state.check_confidence_and_maybe_staleness(
|
||||||
|
&self.name(),
|
||||||
|
&self.oracle_config,
|
||||||
|
staleness_slot,
|
||||||
|
);
|
||||||
|
fallback_ok.with_context(|| {
|
||||||
|
oracle_log_context(&fallback_state, &self.oracle_config, staleness_slot)
|
||||||
|
})?;
|
||||||
|
Ok(fallback_state.price)
|
||||||
} else {
|
} else {
|
||||||
primary_price
|
primary_ok.with_context(|| {
|
||||||
|
oracle_log_context(&primary_state, &self.oracle_config, staleness_slot)
|
||||||
|
})?;
|
||||||
|
Ok(primary_state.price)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,19 +105,19 @@ impl OracleState {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn check_confidence_and_maybe_staleness(
|
pub fn check_confidence_and_maybe_staleness(
|
||||||
&self,
|
&self,
|
||||||
oracle_pk: &Pubkey,
|
oracle_name: &str,
|
||||||
config: &OracleConfig,
|
config: &OracleConfig,
|
||||||
staleness_slot: Option<u64>,
|
staleness_slot: Option<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some(now_slot) = staleness_slot {
|
if let Some(now_slot) = staleness_slot {
|
||||||
self.check_staleness(oracle_pk, config, now_slot)?;
|
self.check_staleness(oracle_name, config, now_slot)?;
|
||||||
}
|
}
|
||||||
self.check_confidence(oracle_pk, config)
|
self.check_confidence(oracle_name, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_staleness(
|
pub fn check_staleness(
|
||||||
&self,
|
&self,
|
||||||
oracle_pk: &Pubkey,
|
oracle_name: &str,
|
||||||
config: &OracleConfig,
|
config: &OracleConfig,
|
||||||
now_slot: u64,
|
now_slot: u64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -127,27 +127,15 @@ impl OracleState {
|
||||||
.saturating_add(config.max_staleness_slots as u64)
|
.saturating_add(config.max_staleness_slots as u64)
|
||||||
< now_slot
|
< now_slot
|
||||||
{
|
{
|
||||||
msg!(
|
msg!("Oracle is stale: {}", oracle_name);
|
||||||
"Oracle is stale; pubkey {}, price: {}, last_update_slot: {}, now_slot: {}",
|
|
||||||
oracle_pk,
|
|
||||||
self.price.to_num::<f64>(),
|
|
||||||
self.last_update_slot,
|
|
||||||
now_slot,
|
|
||||||
);
|
|
||||||
return Err(MangoError::OracleStale.into());
|
return Err(MangoError::OracleStale.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_confidence(&self, oracle_pk: &Pubkey, config: &OracleConfig) -> Result<()> {
|
pub fn check_confidence(&self, oracle_name: &str, config: &OracleConfig) -> Result<()> {
|
||||||
if self.deviation > config.conf_filter * self.price {
|
if self.deviation > config.conf_filter * self.price {
|
||||||
msg!(
|
msg!("Oracle confidence not good enough: {}", oracle_name);
|
||||||
"Oracle confidence not good enough: pubkey {}, price: {}, deviation: {}, conf_filter: {}",
|
|
||||||
oracle_pk,
|
|
||||||
self.price.to_num::<f64>(),
|
|
||||||
self.deviation.to_num::<f64>(),
|
|
||||||
config.conf_filter.to_num::<f32>(),
|
|
||||||
);
|
|
||||||
return Err(MangoError::OracleConfidence.into());
|
return Err(MangoError::OracleConfidence.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -332,6 +320,21 @@ pub fn oracle_state_unchecked(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn oracle_log_context(
|
||||||
|
state: &OracleState,
|
||||||
|
oracle_config: &OracleConfig,
|
||||||
|
staleness_slot: Option<u64>,
|
||||||
|
) -> String {
|
||||||
|
format!(
|
||||||
|
"price: {}, deviation: {}, last_update_slot: {}, now_slot: {}, conf_filter: {:#?}",
|
||||||
|
state.price.to_num::<f64>(),
|
||||||
|
state.deviation.to_num::<f64>(),
|
||||||
|
state.last_update_slot,
|
||||||
|
staleness_slot.unwrap_or_else(|| u64::MAX),
|
||||||
|
oracle_config.conf_filter.to_num::<f32>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -4,10 +4,11 @@ use anchor_lang::prelude::*;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
|
||||||
|
use oracle::oracle_log_context;
|
||||||
use static_assertions::const_assert_eq;
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
use crate::accounts_zerocopy::KeyedAccountReader;
|
use crate::accounts_zerocopy::KeyedAccountReader;
|
||||||
use crate::error::MangoError;
|
use crate::error::{Contextable, MangoError};
|
||||||
use crate::logs::{emit_stack, PerpUpdateFundingLogV2};
|
use crate::logs::{emit_stack, PerpUpdateFundingLogV2};
|
||||||
use crate::state::orderbook::Side;
|
use crate::state::orderbook::Side;
|
||||||
use crate::state::{oracle, TokenIndex};
|
use crate::state::{oracle, TokenIndex};
|
||||||
|
@ -275,11 +276,9 @@ impl PerpMarket {
|
||||||
) -> Result<OracleState> {
|
) -> Result<OracleState> {
|
||||||
require_keys_eq!(self.oracle, *oracle_acc.key());
|
require_keys_eq!(self.oracle, *oracle_acc.key());
|
||||||
let state = oracle::oracle_state_unchecked(oracle_acc, self.base_decimals)?;
|
let state = oracle::oracle_state_unchecked(oracle_acc, self.base_decimals)?;
|
||||||
state.check_confidence_and_maybe_staleness(
|
state
|
||||||
&self.oracle,
|
.check_confidence_and_maybe_staleness(&self.name(), &self.oracle_config, staleness_slot)
|
||||||
&self.oracle_config,
|
.with_context(|| oracle_log_context(&state, &self.oracle_config, staleness_slot))?;
|
||||||
staleness_slot,
|
|
||||||
)?;
|
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ async fn test_health_compute_tokens() -> Result<(), TransportError> {
|
||||||
let avg_cu_increase = cu_measurements.windows(2).map(|p| p[1] - p[0]).sum::<u64>()
|
let avg_cu_increase = cu_measurements.windows(2).map(|p| p[1] - p[0]).sum::<u64>()
|
||||||
/ (cu_measurements.len() - 1) as u64;
|
/ (cu_measurements.len() - 1) as u64;
|
||||||
println!("average cu increase: {avg_cu_increase}");
|
println!("average cu increase: {avg_cu_increase}");
|
||||||
assert!(avg_cu_increase < 3230);
|
assert!(avg_cu_increase < 3350);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ async fn test_health_compute_tokens_during_maint_weight_shift() -> Result<(), Tr
|
||||||
let avg_cu_increase = cu_measurements.windows(2).map(|p| p[1] - p[0]).sum::<u64>()
|
let avg_cu_increase = cu_measurements.windows(2).map(|p| p[1] - p[0]).sum::<u64>()
|
||||||
/ (cu_measurements.len() - 1) as u64;
|
/ (cu_measurements.len() - 1) as u64;
|
||||||
println!("average cu increase: {avg_cu_increase}");
|
println!("average cu increase: {avg_cu_increase}");
|
||||||
assert!(avg_cu_increase < 4200);
|
assert!(avg_cu_increase < 4300);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ async fn test_health_compute_tokens_fallback_oracles() -> Result<(), TransportEr
|
||||||
for _ in 0..num_tokens {
|
for _ in 0..num_tokens {
|
||||||
fallback_oracle_kps.push(TestKeypair::new());
|
fallback_oracle_kps.push(TestKeypair::new());
|
||||||
}
|
}
|
||||||
let fallback_metas: Vec<AccountMeta> = fallback_oracle_kps
|
let success_metas: Vec<AccountMeta> = fallback_oracle_kps
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| AccountMeta {
|
.map(|x| AccountMeta {
|
||||||
pubkey: x.pubkey(),
|
pubkey: x.pubkey(),
|
||||||
|
@ -197,7 +197,7 @@ async fn test_health_compute_tokens_fallback_oracles() -> Result<(), TransportEr
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// let fallback_metas = vec![];
|
let failure_metas = vec![];
|
||||||
|
|
||||||
//
|
//
|
||||||
// SETUP: Create a group and an account
|
// SETUP: Create a group and an account
|
||||||
|
@ -215,7 +215,8 @@ async fn test_health_compute_tokens_fallback_oracles() -> Result<(), TransportEr
|
||||||
let account =
|
let account =
|
||||||
create_funded_account(&solana, group, owner, 0, &context.users[1], &[], 1000, 0).await;
|
create_funded_account(&solana, group, owner, 0, &context.users[1], &[], 1000, 0).await;
|
||||||
|
|
||||||
let mut cu_measurements = vec![];
|
let mut success_measurements = vec![];
|
||||||
|
let mut failure_measurements = vec![];
|
||||||
for token_account in &context.users[0].token_accounts[..mints.len()] {
|
for token_account in &context.users[0].token_accounts[..mints.len()] {
|
||||||
deposit_cu_datapoint(solana, account, owner, *token_account).await;
|
deposit_cu_datapoint(solana, account, owner, *token_account).await;
|
||||||
}
|
}
|
||||||
|
@ -279,19 +280,39 @@ async fn test_health_compute_tokens_fallback_oracles() -> Result<(), TransportEr
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cu_measurements.push(
|
success_measurements.push(
|
||||||
deposit_cu_fallbacks_datapoint(
|
deposit_cu_fallbacks_datapoint(
|
||||||
solana,
|
solana,
|
||||||
account,
|
account,
|
||||||
owner,
|
owner,
|
||||||
*token_account,
|
*token_account,
|
||||||
fallback_metas.clone(),
|
success_metas.clone(),
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
);
|
||||||
|
|
||||||
|
failure_measurements.push(
|
||||||
|
deposit_cu_fallbacks_datapoint(
|
||||||
|
solana,
|
||||||
|
account,
|
||||||
|
owner,
|
||||||
|
*token_account,
|
||||||
|
failure_metas.clone(),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
println!("successful fallbacks:");
|
||||||
for (i, pair) in cu_measurements.windows(2).enumerate() {
|
for (i, pair) in success_measurements.windows(2).enumerate() {
|
||||||
|
println!(
|
||||||
|
"after adding token {}: {} (+{})",
|
||||||
|
i,
|
||||||
|
pair[1],
|
||||||
|
pair[1] - pair[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!("failed fallbacks:");
|
||||||
|
for (i, pair) in failure_measurements.windows(2).enumerate() {
|
||||||
println!(
|
println!(
|
||||||
"after adding token {}: {} (+{})",
|
"after adding token {}: {} (+{})",
|
||||||
i,
|
i,
|
||||||
|
@ -300,10 +321,21 @@ async fn test_health_compute_tokens_fallback_oracles() -> Result<(), TransportEr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let avg_cu_increase = cu_measurements.windows(2).map(|p| p[1] - p[0]).sum::<u64>()
|
let avg_success_increase = success_measurements
|
||||||
/ (cu_measurements.len() - 1) as u64;
|
.windows(2)
|
||||||
println!("average cu increase: {avg_cu_increase}");
|
.map(|p| p[1] - p[0])
|
||||||
assert!(avg_cu_increase < 16_600);
|
.sum::<u64>()
|
||||||
|
/ (success_measurements.len() - 1) as u64;
|
||||||
|
|
||||||
|
let avg_failure_increase = failure_measurements
|
||||||
|
.windows(2)
|
||||||
|
.map(|p| p[1] - p[0])
|
||||||
|
.sum::<u64>()
|
||||||
|
/ (failure_measurements.len() - 1) as u64;
|
||||||
|
println!("average success increase: {avg_success_increase}");
|
||||||
|
println!("average failure increase: {avg_failure_increase}");
|
||||||
|
assert!(avg_success_increase < 2_050);
|
||||||
|
assert!(avg_success_increase < 18_500);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1491,26 +1491,27 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
|
|
||||||
let fallback_oracle_kp = TestKeypair::new();
|
let fallback_oracle_kp = TestKeypair::new();
|
||||||
let fallback_oracle = fallback_oracle_kp.pubkey();
|
let fallback_oracle = fallback_oracle_kp.pubkey();
|
||||||
let admin = TestKeypair::new();
|
|
||||||
let owner = context.users[0].key;
|
let owner = context.users[0].key;
|
||||||
let payer = context.users[1].key;
|
let payer = context.users[1].key;
|
||||||
let mints = &context.mints[0..3];
|
|
||||||
let payer_token_accounts = &context.users[1].token_accounts[0..3];
|
let payer_token_accounts = &context.users[1].token_accounts[0..3];
|
||||||
|
|
||||||
//
|
//
|
||||||
// SETUP: Create a group and an account
|
// SETUP: Create a group and an account
|
||||||
//
|
//
|
||||||
|
let deposit_amount = 1_000;
|
||||||
let GroupWithTokens { group, tokens, .. } = GroupWithTokensConfig {
|
let CommonSetup {
|
||||||
|
group_with_tokens,
|
||||||
|
quote_token,
|
||||||
|
base_token,
|
||||||
|
mut order_placer,
|
||||||
|
..
|
||||||
|
} = common_setup(&context, deposit_amount).await;
|
||||||
|
let GroupWithTokens {
|
||||||
|
group,
|
||||||
admin,
|
admin,
|
||||||
payer,
|
tokens,
|
||||||
mints: mints.to_vec(),
|
..
|
||||||
..GroupWithTokensConfig::default()
|
} = group_with_tokens;
|
||||||
}
|
|
||||||
.create(solana)
|
|
||||||
.await;
|
|
||||||
let base_token = &tokens[0];
|
|
||||||
let quote_token = &tokens[1];
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// SETUP: Create a fallback oracle
|
// SETUP: Create a fallback oracle
|
||||||
|
@ -1520,7 +1521,7 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
StubOracleCreate {
|
StubOracleCreate {
|
||||||
oracle: fallback_oracle_kp,
|
oracle: fallback_oracle_kp,
|
||||||
group,
|
group,
|
||||||
mint: mints[2].pubkey,
|
mint: tokens[2].mint.pubkey,
|
||||||
admin,
|
admin,
|
||||||
payer,
|
payer,
|
||||||
},
|
},
|
||||||
|
@ -1536,7 +1537,7 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
TokenEdit {
|
TokenEdit {
|
||||||
group,
|
group,
|
||||||
admin,
|
admin,
|
||||||
mint: mints[2].pubkey,
|
mint: tokens[2].mint.pubkey,
|
||||||
fallback_oracle,
|
fallback_oracle,
|
||||||
options: mango_v4::instruction::TokenEdit {
|
options: mango_v4::instruction::TokenEdit {
|
||||||
set_fallback_oracle: true,
|
set_fallback_oracle: true,
|
||||||
|
@ -1550,41 +1551,13 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
let bank_data: Bank = solana.get_account(tokens[2].bank).await;
|
let bank_data: Bank = solana.get_account(tokens[2].bank).await;
|
||||||
assert!(bank_data.fallback_oracle == fallback_oracle);
|
assert!(bank_data.fallback_oracle == fallback_oracle);
|
||||||
|
|
||||||
// fill vaults, so we can borrow
|
|
||||||
let _vault_account = create_funded_account(
|
|
||||||
&solana,
|
|
||||||
group,
|
|
||||||
owner,
|
|
||||||
2,
|
|
||||||
&context.users[1],
|
|
||||||
mints,
|
|
||||||
100_000,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
//
|
|
||||||
// SETUP: Create account
|
|
||||||
//
|
|
||||||
let account = create_funded_account(
|
|
||||||
&solana,
|
|
||||||
group,
|
|
||||||
owner,
|
|
||||||
0,
|
|
||||||
&context.users[1],
|
|
||||||
&[mints[1]],
|
|
||||||
1_000,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Create some token1 borrows
|
// Create some token1 borrows
|
||||||
send_tx(
|
send_tx(
|
||||||
solana,
|
solana,
|
||||||
TokenWithdrawInstruction {
|
TokenWithdrawInstruction {
|
||||||
amount: 300,
|
amount: 1_500,
|
||||||
allow_borrow: true,
|
allow_borrow: true,
|
||||||
account,
|
account: order_placer.account,
|
||||||
owner,
|
owner,
|
||||||
token_account: payer_token_accounts[2],
|
token_account: payer_token_accounts[2],
|
||||||
bank_index: 0,
|
bank_index: 0,
|
||||||
|
@ -1593,76 +1566,13 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
//
|
|
||||||
// SETUP: Create serum market
|
|
||||||
//
|
|
||||||
let serum_market_cookie = context
|
|
||||||
.serum
|
|
||||||
.list_spot_market(&base_token.mint, "e_token.mint)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
//
|
|
||||||
// TEST: Register a serum market
|
|
||||||
//
|
|
||||||
let serum_market = send_tx(
|
|
||||||
solana,
|
|
||||||
Serum3RegisterMarketInstruction {
|
|
||||||
group,
|
|
||||||
admin,
|
|
||||||
serum_program: context.serum.program_id,
|
|
||||||
serum_market_external: serum_market_cookie.market,
|
|
||||||
market_index: 0,
|
|
||||||
base_bank: base_token.bank,
|
|
||||||
quote_bank: quote_token.bank,
|
|
||||||
payer,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.serum_market;
|
|
||||||
|
|
||||||
//
|
|
||||||
// TEST: Create an open orders account
|
|
||||||
//
|
|
||||||
let open_orders = send_tx(
|
|
||||||
solana,
|
|
||||||
Serum3CreateOpenOrdersInstruction {
|
|
||||||
account,
|
|
||||||
serum_market,
|
|
||||||
owner,
|
|
||||||
payer,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.open_orders;
|
|
||||||
|
|
||||||
let account_data = get_mango_account(solana, account).await;
|
|
||||||
assert_eq!(
|
|
||||||
account_data
|
|
||||||
.active_serum3_orders()
|
|
||||||
.map(|v| (v.open_orders, v.market_index))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(open_orders, 0)]
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut order_placer = SerumOrderPlacer {
|
|
||||||
solana: solana.clone(),
|
|
||||||
serum: context.serum.clone(),
|
|
||||||
account,
|
|
||||||
owner: owner.clone(),
|
|
||||||
serum_market,
|
|
||||||
open_orders,
|
|
||||||
next_client_order_id: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make oracle invalid by increasing deviation
|
// Make oracle invalid by increasing deviation
|
||||||
send_tx(
|
send_tx(
|
||||||
solana,
|
solana,
|
||||||
StubOracleSetTestInstruction {
|
StubOracleSetTestInstruction {
|
||||||
oracle: tokens[2].oracle,
|
oracle: tokens[2].oracle,
|
||||||
group,
|
group,
|
||||||
mint: mints[2].pubkey,
|
mint: tokens[2].mint.pubkey,
|
||||||
admin,
|
admin,
|
||||||
price: 1.0,
|
price: 1.0,
|
||||||
last_update_slot: 0,
|
last_update_slot: 0,
|
||||||
|
@ -1713,7 +1623,7 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
result.result.unwrap();
|
result.result.unwrap();
|
||||||
|
|
||||||
let account_data = get_mango_account(solana, account).await;
|
let account_data = get_mango_account(solana, order_placer.account).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
account_data
|
account_data
|
||||||
.token_position_by_raw_index(0)
|
.token_position_by_raw_index(0)
|
||||||
|
@ -1726,14 +1636,14 @@ async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
||||||
.token_position_by_raw_index(1)
|
.token_position_by_raw_index(1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.in_use_count,
|
.in_use_count,
|
||||||
0
|
1
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
account_data
|
account_data
|
||||||
.token_position_by_raw_index(2)
|
.token_position_by_raw_index(2)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.in_use_count,
|
.in_use_count,
|
||||||
1
|
0
|
||||||
);
|
);
|
||||||
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
|
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
|
||||||
assert_eq!(serum_orders.base_borrows_without_fee, 0);
|
assert_eq!(serum_orders.base_borrows_without_fee, 0);
|
||||||
|
|
Loading…
Reference in New Issue