PerpUpdateFunding: update automatically on PerpPlaceOrder (#287)

This commit is contained in:
Christian Kamm 2022-11-10 14:41:20 +01:00 committed by GitHub
parent efec1eb906
commit b6361bad37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 49 additions and 30 deletions

View File

@ -80,6 +80,8 @@ pub fn perp_create_market(
"settlement tokens != USDC are not fully implemented"
);
let now_ts: u64 = Clock::get()?.unix_timestamp.try_into().unwrap();
let mut perp_market = ctx.accounts.perp_market.load_init()?;
*perp_market = PerpMarket {
group: ctx.accounts.group.key(),
@ -106,14 +108,14 @@ pub fn perp_create_market(
impact_quantity,
long_funding: I80F48::ZERO,
short_funding: I80F48::ZERO,
funding_last_updated: Clock::get()?.unix_timestamp,
funding_last_updated: now_ts,
open_interest: 0,
seq_num: 0,
fees_accrued: I80F48::ZERO,
fees_settled: I80F48::ZERO,
bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?,
base_decimals,
registration_time: Clock::get()?.unix_timestamp,
registration_time: now_ts,
padding1: Default::default(),
padding2: Default::default(),
padding3: Default::default(),

View File

@ -39,6 +39,23 @@ pub fn perp_place_order(ctx: Context<PerpPlaceOrder>, order: Order, limit: u8) -
require_gte!(order.max_base_lots, 0);
require_gte!(order.max_quote_lots, 0);
let now_ts: u64 = Clock::get()?.unix_timestamp.try_into().unwrap();
let oracle_price;
// Update funding if possible.
//
// Doing this automatically here makes it impossible for attackers to add orders to the orderbook
// before triggering the funding computation.
{
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
let book = ctx.accounts.orderbook.load_mut()?;
oracle_price =
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?)?;
perp_market.update_funding(&book, oracle_price, now_ts)?;
}
let mut account = ctx.accounts.account.load_mut()?;
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
@ -79,10 +96,7 @@ pub fn perp_place_order(ctx: Context<PerpPlaceOrder>, order: Order, limit: u8) -
let mut event_queue = ctx.accounts.event_queue.load_mut()?;
let oracle_price =
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?)?;
let now_ts = Clock::get()?.unix_timestamp as u64;
let now_ts: u64 = Clock::get()?.unix_timestamp.try_into().unwrap();
// TODO apply reduce_only flag to compute final base_lots, also process event queue
require!(order.reduce_only == false, MangoError::SomeError);

View File

@ -3,8 +3,6 @@ use anchor_lang::prelude::*;
use crate::accounts_zerocopy::*;
use crate::state::{Group, OrderBook, PerpMarket};
use crate::logs::PerpUpdateFundingLog;
#[derive(Accounts)]
pub struct PerpUpdateFunding<'info> {
pub group: AccountLoader<'info, Group>, // Required for group metadata parsing
@ -23,8 +21,7 @@ pub struct PerpUpdateFunding<'info> {
pub oracle: UncheckedAccount<'info>,
}
pub fn perp_update_funding(ctx: Context<PerpUpdateFunding>) -> Result<()> {
// TODO: should we enforce a minimum window between 2 update_funding ix calls?
let now_ts = Clock::get()?.unix_timestamp;
let now_ts: u64 = Clock::get()?.unix_timestamp.try_into().unwrap();
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
let book = ctx.accounts.orderbook.load_mut()?;
@ -32,17 +29,7 @@ pub fn perp_update_funding(ctx: Context<PerpUpdateFunding>) -> Result<()> {
let oracle_price =
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?)?;
perp_market.update_funding(&book, oracle_price, now_ts as u64)?;
emit!(PerpUpdateFundingLog {
mango_group: ctx.accounts.group.key(),
market_index: perp_market.perp_market_index,
long_funding: perp_market.long_funding.to_bits(),
short_funding: perp_market.long_funding.to_bits(),
price: oracle_price.to_bits(),
fees_accrued: perp_market.fees_accrued.to_bits(),
open_interest: perp_market.open_interest,
});
perp_market.update_funding(&book, oracle_price, now_ts)?;
Ok(())
}

View File

@ -88,7 +88,7 @@ pub fn serum3_register_market(
bump: *ctx.bumps.get("serum_market").ok_or(MangoError::SomeError)?,
padding1: Default::default(),
padding2: Default::default(),
registration_time: Clock::get()?.unix_timestamp,
registration_time: Clock::get()?.unix_timestamp.try_into().unwrap(),
reserved: [0; 128],
};

View File

@ -151,7 +151,7 @@ pub fn token_register(
banks: Default::default(),
vaults: Default::default(),
oracle: ctx.accounts.oracle.key(),
registration_time: Clock::get()?.unix_timestamp,
registration_time: Clock::get()?.unix_timestamp.try_into().unwrap(),
reserved: [0; 2560],
};

View File

@ -126,7 +126,7 @@ pub fn token_register_trustless(
banks: Default::default(),
vaults: Default::default(),
oracle: ctx.accounts.oracle.key(),
registration_time: Clock::get()?.unix_timestamp,
registration_time: Clock::get()?.unix_timestamp.try_into().unwrap(),
reserved: [0; 2560],
};

View File

@ -28,7 +28,7 @@ pub struct MintInfo {
pub vaults: [Pubkey; MAX_BANKS],
pub oracle: Pubkey,
pub registration_time: i64,
pub registration_time: u64,
pub reserved: [u8; 2560],
}

View File

@ -46,7 +46,7 @@ pub enum OrderParams {
impl Order {
/// Convert an input expiry timestamp to a time_in_force value
pub fn tif_from_expiry(expiry_timestamp: u64) -> Option<u8> {
let now_ts = Clock::get().unwrap().unix_timestamp as u64;
let now_ts: u64 = Clock::get().unwrap().unix_timestamp.try_into().unwrap();
if expiry_timestamp != 0 {
// If expiry is far in the future, clamp to 255 seconds
let tif = expiry_timestamp.saturating_sub(now_ts).min(255);

View File

@ -11,6 +11,7 @@ use crate::state::{oracle, TokenIndex};
use crate::util::checked_math as cm;
use super::{orderbook, OracleConfig, OrderBook, DAY_I80F48};
use crate::logs::PerpUpdateFundingLog;
pub type PerpMarketIndex = u16;
@ -69,7 +70,8 @@ pub struct PerpMarket {
pub impact_quantity: i64,
pub long_funding: I80F48,
pub short_funding: I80F48,
pub funding_last_updated: i64,
/// timestamp that funding was last updated in
pub funding_last_updated: u64,
///
pub open_interest: i64,
@ -93,7 +95,7 @@ pub struct PerpMarket {
pub padding2: [u8; 6],
pub registration_time: i64,
pub registration_time: u64,
/// Fees settled in native quote currency
pub fees_settled: I80F48,
@ -154,6 +156,10 @@ impl PerpMarket {
oracle_price: I80F48,
now_ts: u64,
) -> Result<()> {
if now_ts <= self.funding_last_updated {
return Ok(());
}
let index_price = oracle_price;
let oracle_price_lots = self.native_price_to_lot(oracle_price);
@ -181,7 +187,17 @@ impl PerpMarket {
self.long_funding += funding_delta;
self.short_funding += funding_delta;
self.funding_last_updated = now_ts as i64;
self.funding_last_updated = now_ts;
emit!(PerpUpdateFundingLog {
mango_group: self.group,
market_index: self.perp_market_index,
long_funding: self.long_funding.to_bits(),
short_funding: self.long_funding.to_bits(),
price: oracle_price.to_bits(),
fees_accrued: self.fees_accrued.to_bits(),
open_interest: self.open_interest,
});
Ok(())
}

View File

@ -26,7 +26,7 @@ pub struct Serum3Market {
pub padding2: [u8; 5],
pub registration_time: i64,
pub registration_time: u64,
pub reserved: [u8; 128],
}