Improve listing with invalid oracles (#620)

* Stable Price: Reset on first price != 0

This helps when listing tokens or perp markets with an upcoming
oracle. Currently the stable price would be 0 and would need to be
manually reset by DAO proposal.

With this change, the first non-zero value will be used as the starting
point for the stable price instead.

* Listing instructions: Succeed even if oracle is invalid

But require the stable price to reset once it becomes valid.
This commit is contained in:
Christian Kamm 2023-06-21 15:25:24 +02:00 committed by GitHub
parent c38d2ec225
commit a77515acbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 15 deletions

View File

@ -94,11 +94,15 @@ pub fn perp_create_market(
reserved: [0; 1888],
};
let oracle_price =
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)?;
perp_market
.stable_price_model
.reset_to_price(oracle_price.to_num(), now_ts);
if let Ok(oracle_price) =
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)
{
perp_market
.stable_price_model
.reset_to_price(oracle_price.to_num(), now_ts);
} else {
perp_market.stable_price_model.reset_on_nonzero_price = 1;
}
let mut orderbook = Orderbook {
bids: ctx.accounts.bids.load_init()?,

View File

@ -93,10 +93,14 @@ pub fn token_register(
};
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
let oracle_price =
bank.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)?;
bank.stable_price_model
.reset_to_price(oracle_price.to_num(), now_ts);
if let Ok(oracle_price) =
bank.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)
{
bank.stable_price_model
.reset_to_price(oracle_price.to_num(), now_ts);
} else {
bank.stable_price_model.reset_on_nonzero_price = 1;
}
let mut mint_info = ctx.accounts.mint_info.load_init()?;
*mint_info = MintInfo {

View File

@ -79,10 +79,14 @@ pub fn token_register_trustless(
};
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
let oracle_price =
bank.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)?;
bank.stable_price_model
.reset_to_price(oracle_price.to_num(), now_ts);
if let Ok(oracle_price) =
bank.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, None)
{
bank.stable_price_model
.reset_to_price(oracle_price.to_num(), now_ts);
} else {
bank.stable_price_model.reset_on_nonzero_price = 1;
}
let mut mint_info = ctx.accounts.mint_info.load_init()?;
*mint_info = MintInfo {

View File

@ -49,15 +49,18 @@ pub struct StablePriceModel {
/// The delay_interval_index that update() was last called on.
pub last_delay_interval_index: u8,
/// If set to 1, the stable price will reset on the next non-zero price it sees.
pub reset_on_nonzero_price: u8,
#[derivative(Debug = "ignore")]
pub padding: [u8; 7],
pub padding: [u8; 6],
#[derivative(Debug = "ignore")]
pub reserved: [u8; 48],
}
const_assert_eq!(
size_of::<StablePriceModel>(),
8 + 8 + 8 * 24 + 8 + 4 + 4 + 4 + 4 + 1 + 7 + 48
8 + 8 + 8 * 24 + 8 + 4 + 4 + 4 + 4 + 1 * 2 + 6 + 48
);
const_assert_eq!(size_of::<StablePriceModel>(), 288);
const_assert_eq!(size_of::<StablePriceModel>() % 8, 0);
@ -74,6 +77,7 @@ impl Default for StablePriceModel {
delay_growth_limit: 0.06, // 6% per hour, 400% per day
stable_growth_limit: 0.0003, // 0.03% per second, 293% in 1h if updated every 10s, 281% in 1h if updated every 5min
last_delay_interval_index: 0,
reset_on_nonzero_price: 0,
padding: Default::default(),
reserved: [0; 48],
}
@ -87,6 +91,7 @@ impl StablePriceModel {
self.delay_accumulator_price = 0.0;
self.delay_accumulator_time = 0;
self.last_update_timestamp = now_ts;
self.reset_on_nonzero_price = if oracle_price > 0.0 { 0 } else { 1 };
}
pub fn delay_interval_index(&self, timestamp: u64) -> u8 {
@ -103,6 +108,11 @@ impl StablePriceModel {
}
pub fn update(&mut self, now_ts: u64, oracle_price: f64) {
// If a reset is requested (maybe there never was a non-zero price), jump to the current value
if self.reset_on_nonzero_price == 1 && oracle_price > 0.0 {
self.reset_to_price(oracle_price, now_ts);
}
let dt = now_ts.saturating_sub(self.last_update_timestamp);
// Hardcoded. Requiring a minimum time between updates reduces the possible difference
// between frequent updates and infrequent ones.