From bf334943c78c4532a6184e136a9ced45839167a0 Mon Sep 17 00:00:00 2001 From: jordy25519 Date: Thu, 21 Nov 2024 11:27:33 -0500 Subject: [PATCH] Add orders_place_perp_order for local order validation (#3) * Add orders_place_perp_order local order sim * fix OrderParams C abi --- src/exports.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/types.rs | 32 +++++++++++++++-- 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/src/exports.rs b/src/exports.rs index 18af1e1..e3c9321 100644 --- a/src/exports.rs +++ b/src/exports.rs @@ -1,6 +1,8 @@ //! //! Define FFI for subset of drift program //! +use std::time::{SystemTime, UNIX_EPOCH}; + use abi_stable::std_types::{ ROption, RResult::{RErr, ROk}, @@ -10,14 +12,21 @@ use drift_program::{ state::{ oracle::{get_oracle_price as get_oracle_price_, OracleSource}, oracle_map::OracleMap, + order_params::PlaceOrderOptions, perp_market::{ContractType, PerpMarket}, perp_market_map::PerpMarketMap, spot_market::SpotMarket, spot_market_map::SpotMarketMap, + state::State, user::{Order, PerpPosition, SpotPosition, User}, }, }; -use solana_sdk::{account::Account, account_info::IntoAccountInfo, clock::Slot, pubkey::Pubkey}; +use solana_sdk::{ + account::Account, + account_info::IntoAccountInfo, + clock::{Clock, Slot}, + pubkey::Pubkey, +}; use crate::types::{ compat::{self}, @@ -114,6 +123,86 @@ pub extern "C" fn math_calculate_margin_requirement_and_total_collateral_and_lia to_ffi_result(m) } +#[no_mangle] +pub extern "C" fn orders_place_perp_order( + user: &User, + state: &State, + order_params: &crate::types::OrderParams, + accounts: &mut AccountsList, +) -> FfiResult { + let spot_accounts = accounts + .spot_markets + .iter_mut() + .map(IntoAccountInfo::into_account_info) + .collect::>(); + let spot_map = + SpotMarketMap::load(&Default::default(), &mut spot_accounts.iter().peekable()).unwrap(); + + let perp_accounts = accounts + .perp_markets + .iter_mut() + .map(IntoAccountInfo::into_account_info) + .collect::>(); + let perp_map = + PerpMarketMap::load(&Default::default(), &mut perp_accounts.iter().peekable()).unwrap(); + + let oracle_accounts = accounts + .oracles + .iter_mut() + .map(IntoAccountInfo::into_account_info) + .collect::>(); + let mut oracle_map = OracleMap::load( + &mut oracle_accounts.iter().peekable(), + accounts.latest_slot, + accounts.oracle_guard_rails, + ) + .unwrap(); + + // has no epoch info but this is unrequired for orderplacement + let local_clock = Clock { + slot: accounts.latest_slot, + epoch_start_timestamp: 0, + epoch: 0, + leader_schedule_epoch: 0, + unix_timestamp: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as i64, + }; + + let res = drift_program::controller::orders::place_perp_order( + state, + &mut user.clone(), + user.authority, + &perp_map, + &spot_map, + &mut oracle_map, + &local_clock, + drift_program::state::order_params::OrderParams { + order_type: order_params.order_type, + market_type: order_params.market_type, + direction: order_params.direction, + user_order_id: order_params.user_order_id, + base_asset_amount: order_params.base_asset_amount, + price: order_params.price, + market_index: order_params.market_index, + reduce_only: order_params.reduce_only, + post_only: order_params.post_only, + immediate_or_cancel: order_params.immediate_or_cancel, + max_ts: order_params.max_ts, + trigger_price: order_params.trigger_price, + trigger_condition: order_params.trigger_condition, + oracle_price_offset: order_params.oracle_price_offset, + auction_duration: order_params.auction_duration, + auction_start_price: order_params.auction_start_price, + auction_end_price: order_params.auction_end_price, + }, + PlaceOrderOptions::default(), + ); + + to_ffi_result(res.map(|_| true)) +} + #[no_mangle] pub extern "C" fn order_is_limit_order(order: &Order) -> bool { order.is_limit_order() @@ -145,7 +234,7 @@ pub extern "C" fn perp_position_get_unrealized_pnl( oracle_price: i64, ) -> FfiResult { to_ffi_result(position.get_unrealized_pnl(oracle_price).map(compat::i128)) -} +} #[no_mangle] pub extern "C" fn perp_position_is_available(position: &PerpPosition) -> bool { diff --git a/src/types.rs b/src/types.rs index 2845e69..f25e2b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,8 +1,14 @@ //! cross-boundary FFI types use abi_stable::std_types::RResult; use drift_program::{ + controller::position::PositionDirection, math::margin::MarginRequirementType, - state::{margin_calculation::MarginContext, state::OracleGuardRails}, + state::{ + margin_calculation::MarginContext, + order_params::PostOnlyParam, + state::OracleGuardRails, + user::{MarketType, OrderTriggerCondition, OrderType}, + }, }; use solana_sdk::{ account::Account, @@ -76,7 +82,6 @@ impl From for MarginContext { } } - #[repr(C, align(16))] #[derive(Copy, Clone, Debug, PartialEq, TypeLayout)] pub struct MarginCalculation { @@ -100,6 +105,29 @@ impl MarginCalculation { } } +/// Same as drift program `OrderParams` but with `C` layout +#[repr(C)] +#[derive(Debug)] +pub struct OrderParams { + pub order_type: OrderType, + pub market_type: MarketType, + pub direction: PositionDirection, + pub user_order_id: u8, + pub base_asset_amount: u64, + pub price: u64, + pub market_index: u16, + pub reduce_only: bool, + pub post_only: PostOnlyParam, + pub immediate_or_cancel: bool, + pub max_ts: Option, + pub trigger_price: Option, + pub trigger_condition: OrderTriggerCondition, + pub oracle_price_offset: Option, // price offset from oracle for order (~ +/- 2147 max) + pub auction_duration: Option, // specified in slots + pub auction_start_price: Option, // specified in price or oracle_price_offset + pub auction_end_price: Option, // specified in price or oracle_price_offset +} + #[repr(C)] #[derive(Default, Clone, Copy, Debug)] pub struct OraclePriceData {