diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 43c6676..8689b9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,9 +19,11 @@ on: - "**.toml" - "**.lock" - ".github/workflows/*.yml" - + jobs: format-build-test: + env: + CARGO_DRIFT_FFI_PATH: /usr/lib runs-on: ubicloud steps: - name: Check out @@ -32,32 +34,20 @@ jobs: rustup component add clippy rustfmt - name: install libdrift_ffi_sys run: | - curl -L https://github.com/user-attachments/files/17806677/libdrift_ffi_sys.so.zip > ffi.zip + curl -L https://github.com/user-attachments/files/17849111/libdrift_ffi_sys.so.zip > ffi.zip unzip ffi.zip ldd libdrift_ffi_sys.so - sudo cp libdrift_ffi_sys.so /usr/lib + sudo cp libdrift_ffi_sys.so $CARGO_DRIFT_FFI_PATH ldconfig -p - name: Format run: cargo fmt --all -- --check - name: Build run: cargo check - env: - CARGO_DRIFT_FFI_PATH: "/usr/lib" - # - name: Clippy - # uses: giraffate/clippy-action@v1 - # with: - # reporter: 'github-pr-review' - # github_token: ${{ secrets.GITHUB_TOKEN }} - # env: - # RUST_TOOLCHAIN: stable-x86_64-linux-unknown-gnu # force clippy to build with same rust version - # CARGO_DRIFT_FFI_PATH: "/usr/lib" - name: Test run: | cargo test --no-fail-fast --lib -- --nocapture cargo test --no-fail-fast --test integration -- --nocapture --test-threads 1 env: - RUST_LOG: info TEST_DEVNET_RPC_ENDPOINT: ${{ secrets.DEVNET_RPC_ENDPOINT }} TEST_MAINNET_RPC_ENDPOINT: ${{ secrets.MAINNET_RPC_ENDPOINT }} - TEST_PRIVATE_KEY: ${{ secrets.TEST_PRIVATE_KEY }} - CARGO_DRIFT_FFI_PATH: "/usr/lib" \ No newline at end of file + TEST_PRIVATE_KEY: ${{ secrets.TEST_PRIVATE_KEY }} \ No newline at end of file diff --git a/crates/drift-ffi-sys b/crates/drift-ffi-sys index 0d5df4c..9332730 160000 --- a/crates/drift-ffi-sys +++ b/crates/drift-ffi-sys @@ -1 +1 @@ -Subproject commit 0d5df4c857466cb9132763bb925d94c9a1b96f5c +Subproject commit 9332730fb3668d938ce3f2a9e9b62d692ffedb64 diff --git a/crates/src/drift_idl.rs b/crates/src/drift_idl.rs index ae29ea4..024c170 100644 --- a/crates/src/drift_idl.rs +++ b/crates/src/drift_idl.rs @@ -2,7 +2,6 @@ #![doc = r""] #![doc = r" Auto-generated IDL types, manual edits do not persist (see `crates/drift-idl-gen`)"] #![doc = r""] -use self::traits::ToAccountMetas; use anchor_lang::{ prelude::{ account, @@ -13,6 +12,8 @@ use anchor_lang::{ }; use serde::{Deserialize, Serialize}; use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey}; + +use self::traits::ToAccountMetas; pub mod traits { use solana_sdk::instruction::AccountMeta; #[doc = r" This is distinct from the anchor_lang version of the trait"] @@ -1918,8 +1919,9 @@ pub mod instructions { } pub mod types { #![doc = r" IDL types"] - use super::*; use std::ops::Mul; + + use super::*; #[doc = ""] #[doc = " backwards compatible u128 deserializing data from rust <=1.76.0 when u/i128 was 8-byte aligned"] #[doc = " https://solana.stackexchange.com/questions/7720/using-u128-without-sacrificing-alignment-8"] diff --git a/crates/src/ffi.rs b/crates/src/ffi.rs index eaf6c2b..b44b83f 100644 --- a/crates/src/ffi.rs +++ b/crates/src/ffi.rs @@ -114,6 +114,13 @@ extern "C" { user: &accounts::User, market_index: u16, ) -> FfiResult<&types::PerpPosition>; + #[allow(improper_ctypes)] + pub fn orders_place_perp_order( + user: &accounts::User, + state: &accounts::State, + order_params: &types::OrderParams, + accounts: &mut AccountsList, + ) -> FfiResult; } // @@ -159,6 +166,20 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( to_sdk_result(res) } +/// Simulates the program's `place_perp_order` ix +/// Useful to verify an order can be placed given factors such as available margin, etc. +/// +/// Returns `true` if the order could be placed +pub fn simulate_place_perp_order( + user: &accounts::User, + accounts: &mut AccountsList, + state: &accounts::State, + order_params: &types::OrderParams, +) -> SdkResult { + let res = unsafe { orders_place_perp_order(user, state, order_params, accounts) }; + to_sdk_result(res) +} + impl types::SpotPosition { pub fn is_available(&self) -> bool { unsafe { spot_position_is_available(self) } @@ -381,14 +402,19 @@ mod tests { use solana_sdk::{account::Account, pubkey::Pubkey}; use type_layout::TypeLayout; - use super::{AccountWithKey, AccountsList, MarginCalculation, MarginContextMode}; + use super::{ + simulate_place_perp_order, AccountWithKey, AccountsList, MarginCalculation, + MarginContextMode, + }; use crate::{ - constants::{self}, + accounts::State, + constants::{self, ids::pyth_program}, + create_account_info, drift_idl::{ accounts::{PerpMarket, SpotMarket, User}, types::{ - ContractType, MarginRequirementType, OracleSource, Order, OrderType, PerpPosition, - SpotBalanceType, SpotPosition, + ContractType, MarginRequirementType, OracleSource, Order, OrderParams, OrderType, + PerpPosition, SpotBalanceType, SpotPosition, }, }, ffi::{ @@ -396,18 +422,15 @@ mod tests { calculate_margin_requirement_and_total_collateral_and_liability_info, get_oracle_price, }, math::constants::{ - BASE_PRECISION_I64, LIQUIDATION_FEE_PRECISION, MARGIN_PRECISION, PRICE_PRECISION_I64, - QUOTE_PRECISION, QUOTE_PRECISION_I64, SPOT_BALANCE_PRECISION, + BASE_PRECISION, BASE_PRECISION_I64, LIQUIDATION_FEE_PRECISION, MARGIN_PRECISION, + PRICE_PRECISION_I64, QUOTE_PRECISION, QUOTE_PRECISION_I64, SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, }, - PositionDirection, + types::MarketType, + utils::test_utils::{get_account_bytes, get_pyth_price}, + HistoricalOracleData, MarketStatus, PositionDirection, AMM, }; - const _SOL_PYTH_PRICE_STR: &str = include_str!("../../res/sol-oracle-pyth.hex"); - /// encoded pyth price account for SOL, see math/liquidation.rs tests - const SOL_PYTH_PRICE: std::cell::LazyCell> = - std::cell::LazyCell::new(|| hex::decode(_SOL_PYTH_PRICE_STR).unwrap()); - fn sol_spot_market() -> SpotMarket { SpotMarket { market_index: 1, @@ -422,15 +445,42 @@ mod tests { maintenance_liability_weight: 11 * SPOT_WEIGHT_PRECISION / 10, liquidator_fee: LIQUIDATION_FEE_PRECISION / 1000, deposit_balance: (1_000 * SPOT_BALANCE_PRECISION).into(), + order_step_size: 1_000, + order_tick_size: 1_000, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap5min: 240_000_000_000, + ..Default::default() + }, ..Default::default() } } + fn usdc_spot_market() -> SpotMarket { + SpotMarket { + market_index: 0, + oracle_source: OracleSource::QuoteAsset, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION.into(), + decimals: 6, + initial_asset_weight: SPOT_WEIGHT_PRECISION, + maintenance_asset_weight: SPOT_WEIGHT_PRECISION, + deposit_balance: (100_000 * SPOT_BALANCE_PRECISION).into(), + liquidator_fee: 0, + order_step_size: 1_000, + order_tick_size: 1_000, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap5min: 1_000_000, + ..Default::default() + }, + ..SpotMarket::default() + } + } + #[test] fn ffi_deser_1_76_0_spot_market() { // smoke test for deserializing program data (where u128/i128 alignment is 8) - let buf = hex_literal::hex!("64b1086ba84141270000000000000000000000000000000000000000000000000000000000000000fe650f0367d4a7ef9815a593ea15d36593f0643aaaf0149bb04be67ab851decd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010a5d4e800000000000000000000000000000000000000000000000000000000e40b5402000000000000000000000000e40b54020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000401f000028230000e02e0000f82a000000000000e803000000000000000000000000000000000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - let actual: &SpotMarket = bytemuck::from_bytes::(&buf.as_ref()[8..]); // ignore dscriminator + let spot_market_borsh = + hex::decode(include_str!("../../res/spot_market_1_76_0.hex")).unwrap(); + let actual: &SpotMarket = bytemuck::from_bytes::(&spot_market_borsh[8..]); // ignore dscriminator assert_eq!(actual, &sol_spot_market()); } @@ -605,8 +655,7 @@ mod tests { fn ffi_get_oracle_price() { let oracle_pubkey = Pubkey::new_unique(); let oracle_account = Account { - // encoded from pyth Price, see liquidation tests - data: SOL_PYTH_PRICE.clone(), + data: get_account_bytes(&mut get_pyth_price(240, 9)).to_vec(), owner: constants::ids::pyth_program::ID, ..Default::default() }; @@ -621,7 +670,7 @@ mod tests { let oracle_price_data = result.unwrap(); dbg!(oracle_price_data.price); - assert!(oracle_price_data.price == 60 * QUOTE_PRECISION as i64); + assert!(oracle_price_data.price == 240 * QUOTE_PRECISION as i64); } #[test] @@ -735,8 +784,7 @@ mod tests { let mut oracles = [AccountWithKey { key: Pubkey::new_unique(), account: Account { - // encoded from pyth Price, see liquidation tests - data: SOL_PYTH_PRICE.clone(), + data: get_account_bytes(&mut get_pyth_price(240, 9)).to_vec(), owner: constants::ids::pyth_program::ID, ..Default::default() }, @@ -759,6 +807,138 @@ mod tests { } } + #[test] + fn ffi_simulate_place_perp_order() { + // smoke test for ffi compatability, logic tested in `math::` module + let btc_perp_index = 1_u16; + let mut user = User::default(); + user.spot_positions[1] = SpotPosition { + market_index: 1, + scaled_balance: (1_000 * SPOT_BALANCE_PRECISION) as u64, + balance_type: SpotBalanceType::Deposit, + ..Default::default() + }; + user.perp_positions[0] = PerpPosition { + market_index: btc_perp_index, + base_asset_amount: 100 * BASE_PRECISION_I64 as i64, + quote_asset_amount: -5_000 * QUOTE_PRECISION as i64, + ..Default::default() + }; + user.perp_positions[1] = PerpPosition { + market_index: 0, + base_asset_amount: 100 * BASE_PRECISION_I64 as i64, + quote_asset_amount: -5_000 * QUOTE_PRECISION as i64, + ..Default::default() + }; + + // Create mock accounts + let mut perp_markets = vec![ + AccountWithKey { + key: Pubkey::new_unique(), + account: Account { + owner: crate::constants::PROGRAM_ID, + data: [ + PerpMarket::DISCRIMINATOR.as_slice(), + bytemuck::bytes_of(&PerpMarket { + market_index: btc_perp_index, + status: MarketStatus::Active, + amm: AMM { + order_step_size: 1_000, + order_tick_size: 1_000, + ..Default::default() + }, + ..Default::default() + }), + ] + .concat() + .to_vec(), + ..Default::default() + }, + }, + AccountWithKey { + key: Pubkey::new_unique(), + account: Account { + owner: crate::constants::PROGRAM_ID, + data: [ + PerpMarket::DISCRIMINATOR.as_slice(), + bytemuck::bytes_of(&PerpMarket { + market_index: 0, + status: MarketStatus::Active, + amm: AMM { + order_step_size: 1_000, + order_tick_size: 1_000, + ..Default::default() + }, + ..Default::default() + }), + ] + .concat() + .to_vec(), + ..Default::default() + }, + }, + ]; + let mut spot_markets = vec![ + AccountWithKey { + key: Pubkey::new_unique(), + account: Account { + owner: crate::constants::PROGRAM_ID, + data: [ + SpotMarket::DISCRIMINATOR.as_slice(), + bytemuck::bytes_of(&sol_spot_market()), + ] + .concat() + .to_vec(), + ..Default::default() + }, + }, + AccountWithKey { + key: Pubkey::new_unique(), + account: Account { + owner: crate::constants::PROGRAM_ID, + data: [ + SpotMarket::DISCRIMINATOR.as_slice(), + bytemuck::bytes_of(&usdc_spot_market()), + ] + .concat() + .to_vec(), + ..Default::default() + }, + }, + ]; + + create_account_info!( + get_pyth_price(240, 9), + &sol_spot_market().oracle, + pyth_program::ID, + sol_oracle + ); + create_account_info!( + get_pyth_price(1, 6), + &usdc_spot_market().oracle, + pyth_program::ID, + usdc_oracle + ); + + let mut oracles = [sol_oracle, usdc_oracle]; + let mut accounts = AccountsList::new(&mut perp_markets, &mut spot_markets, &mut oracles); + + let res = simulate_place_perp_order( + &user, + &mut accounts, + &State::default(), + &OrderParams { + market_index: 1, + market_type: MarketType::Perp, + direction: PositionDirection::Short, + base_asset_amount: 123 * BASE_PRECISION as u64, + order_type: OrderType::Market, + ..Default::default() + }, + ); + assert!(res.is_ok_and(|truthy| truthy)) + } + #[test] fn ffi_calculate_auction_price() { let price = calculate_auction_price( diff --git a/crates/src/math/leverage.rs b/crates/src/math/leverage.rs index 17a331f..ca0cbaa 100644 --- a/crates/src/math/leverage.rs +++ b/crates/src/math/leverage.rs @@ -182,7 +182,10 @@ impl UserMargin for DriftClient { .worst_case_base_asset_amount(oracle_price, market.contract_type)?; let margin_info = self.calculate_margin_info(user)?; - let free_collateral = margin_info.get_free_collateral() - collateral_buffer as u128; + let free_collateral = margin_info + .get_free_collateral() + .checked_sub(collateral_buffer as u128) + .ok_or(SdkError::MathError("underflow".to_string()))?; let margin_ratio = market .get_margin_ratio( diff --git a/crates/src/math/liquidation.rs b/crates/src/math/liquidation.rs index 0f1704d..4f0cf60 100644 --- a/crates/src/math/liquidation.rs +++ b/crates/src/math/liquidation.rs @@ -361,8 +361,6 @@ fn calculate_collateral_inner( #[cfg(test)] mod tests { - use anchor_lang::Discriminator; - use bytes::BytesMut; use solana_sdk::{account::Account, pubkey::Pubkey}; use super::*; @@ -374,6 +372,7 @@ mod tests { PRICE_PRECISION_I64, SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, }, + utils::test_utils::*, MarketId, }; @@ -889,146 +888,4 @@ mod tests { // entry at $80, upnl at $100 assert_eq!(unrealized_pnl, 20_i128 * QUOTE_PRECISION_I64 as i128); } - - // helpers from drift-program test_utils. - fn get_pyth_price(price: i64, expo: i32) -> pyth_test::Price { - let mut pyth_price = pyth_test::Price::default(); - let price = price * 10_i64.pow(expo as u32); - pyth_price.agg.price = price; - pyth_price.twap = price; - pyth_price.expo = expo; - pyth_price - } - - mod pyth_test { - //! helper structs for pyth oracle prices - use bytemuck::{Pod, Zeroable}; - use serde::Serialize; - - #[derive(Default, Copy, Clone, Serialize)] - #[repr(C)] - pub struct AccKey { - pub val: [u8; 32], - } - - #[derive(Copy, Clone, Default, Serialize)] - #[repr(C)] - #[allow(dead_code)] - pub enum PriceStatus { - Unknown, - #[default] - Trading, - Halted, - Auction, - } - - #[derive(Copy, Clone, Default, Serialize)] - #[repr(C)] - pub enum CorpAction { - #[default] - NoCorpAct, - } - - #[derive(Default, Copy, Clone, Serialize)] - #[repr(C)] - pub struct PriceInfo { - pub price: i64, - pub conf: u64, - pub status: PriceStatus, - pub corp_act: CorpAction, - pub pub_slot: u64, - } - #[derive(Default, Copy, Clone, Serialize)] - #[repr(C)] - pub struct PriceComp { - publisher: AccKey, - agg: PriceInfo, - latest: PriceInfo, - } - - #[derive(Copy, Clone, Default, Serialize)] - #[repr(C)] - #[allow(dead_code, clippy::upper_case_acronyms)] - pub enum PriceType { - Unknown, - #[default] - Price, - TWAP, - Volatility, - } - - #[derive(Default, Copy, Clone, Serialize)] - #[repr(C)] - pub struct Price { - pub magic: u32, // Pyth magic number. - pub ver: u32, // Program version. - pub atype: u32, // Account type. - pub size: u32, // Price account size. - pub ptype: PriceType, // Price or calculation type. - pub expo: i32, // Price exponent. - pub num: u32, // Number of component prices. - pub unused: u32, - pub curr_slot: u64, // Currently accumulating price slot. - pub valid_slot: u64, // Valid slot-time of agg. price. - pub twap: i64, // Time-weighted average price. - pub avol: u64, // Annualized price volatility. - pub drv0: i64, // Space for future derived values. - pub drv1: i64, // Space for future derived values. - pub drv2: i64, // Space for future derived values. - pub drv3: i64, // Space for future derived values. - pub drv4: i64, // Space for future derived values. - pub drv5: i64, // Space for future derived values. - pub prod: AccKey, // Product account key. - pub next: AccKey, // Next Price account in linked list. - pub agg_pub: AccKey, // Quoter who computed last aggregate price. - pub agg: PriceInfo, // Aggregate price info. - pub comp: [PriceComp; 32], // Price components one per quoter. - } - - #[cfg(target_endian = "little")] - unsafe impl Zeroable for Price {} - - #[cfg(target_endian = "little")] - unsafe impl Pod for Price {} - } - - pub fn get_account_bytes(account: &mut T) -> BytesMut { - let mut bytes = BytesMut::new(); - let data = bytemuck::bytes_of_mut(account); - bytes.extend_from_slice(data); - bytes - } - - pub fn get_anchor_account_bytes(account: &mut T) -> BytesMut { - let mut bytes = BytesMut::new(); - bytes.extend_from_slice(&T::discriminator()); - let data = bytemuck::bytes_of_mut(account); - bytes.extend_from_slice(data); - bytes - } - - #[macro_export] - macro_rules! create_account_info { - ($account:expr, $pubkey:expr, $owner:expr, $name: ident) => { - let acc = Account { - data: get_account_bytes(&mut $account).to_vec(), - owner: $owner, - ..Default::default() - }; - let $name: crate::ffi::AccountWithKey = (*$pubkey, acc).into(); - }; - } - - #[macro_export] - macro_rules! create_anchor_account_info { - ($account:expr, $pubkey:expr, $type:ident, $name: ident) => { - let owner = constants::PROGRAM_ID; - let acc = Account { - data: get_anchor_account_bytes(&mut $account).to_vec(), - owner, - ..Default::default() - }; - let $name: crate::ffi::AccountWithKey = ($pubkey, acc).into(); - }; - } } diff --git a/crates/src/oraclemap.rs b/crates/src/oraclemap.rs index 41046e0..3dd9124 100644 --- a/crates/src/oraclemap.rs +++ b/crates/src/oraclemap.rs @@ -46,7 +46,7 @@ pub struct OracleMap { } impl OracleMap { - pub const SUBSCRIPTION_ID: &str = "oraclemap"; + pub const SUBSCRIPTION_ID: &'static str = "oraclemap"; /// Create a new `OracleMap` /// diff --git a/crates/src/slot_subscriber.rs b/crates/src/slot_subscriber.rs index 0cc5b5c..ee9d73d 100644 --- a/crates/src/slot_subscriber.rs +++ b/crates/src/slot_subscriber.rs @@ -118,7 +118,7 @@ impl SlotSubscriber { handler_fn(SlotUpdate::new(new_slot)); } None => { - + break; } } } diff --git a/crates/src/utils.rs b/crates/src/utils.rs index 4042942..812f8d6 100644 --- a/crates/src/utils.rs +++ b/crates/src/utils.rs @@ -155,6 +155,155 @@ pub mod test_envs { } } +#[cfg(test)] +pub mod test_utils { + //! test utilities + + use anchor_lang::Discriminator; + use bytes::BytesMut; + // helpers from drift-program test_utils. + pub fn get_pyth_price(price: i64, expo: i32) -> pyth_test::Price { + let mut pyth_price = pyth_test::Price::default(); + let price = price * 10_i64.pow(expo as u32); + pyth_price.agg.price = price; + pyth_price.twap = price; + pyth_price.expo = expo; + pyth_price + } + + mod pyth_test { + //! helper structs for pyth oracle prices + use bytemuck::{Pod, Zeroable}; + use serde::Serialize; + + #[derive(Default, Copy, Clone, Serialize)] + #[repr(C)] + pub struct AccKey { + pub val: [u8; 32], + } + + #[derive(Copy, Clone, Default, Serialize)] + #[repr(C)] + #[allow(dead_code)] + pub enum PriceStatus { + Unknown, + #[default] + Trading, + Halted, + Auction, + } + + #[derive(Copy, Clone, Default, Serialize)] + #[repr(C)] + pub enum CorpAction { + #[default] + NoCorpAct, + } + + #[derive(Default, Copy, Clone, Serialize)] + #[repr(C)] + pub struct PriceInfo { + pub price: i64, + pub conf: u64, + pub status: PriceStatus, + pub corp_act: CorpAction, + pub pub_slot: u64, + } + #[derive(Default, Copy, Clone, Serialize)] + #[repr(C)] + pub struct PriceComp { + publisher: AccKey, + agg: PriceInfo, + latest: PriceInfo, + } + + #[derive(Copy, Clone, Default, Serialize)] + #[repr(C)] + #[allow(dead_code, clippy::upper_case_acronyms)] + pub enum PriceType { + Unknown, + #[default] + Price, + TWAP, + Volatility, + } + + #[derive(Default, Copy, Clone, Serialize)] + #[repr(C)] + pub struct Price { + pub magic: u32, // Pyth magic number. + pub ver: u32, // Program version. + pub atype: u32, // Account type. + pub size: u32, // Price account size. + pub ptype: PriceType, // Price or calculation type. + pub expo: i32, // Price exponent. + pub num: u32, // Number of component prices. + pub unused: u32, + pub curr_slot: u64, // Currently accumulating price slot. + pub valid_slot: u64, // Valid slot-time of agg. price. + pub twap: i64, // Time-weighted average price. + pub avol: u64, // Annualized price volatility. + pub drv0: i64, // Space for future derived values. + pub drv1: i64, // Space for future derived values. + pub drv2: i64, // Space for future derived values. + pub drv3: i64, // Space for future derived values. + pub drv4: i64, // Space for future derived values. + pub drv5: i64, // Space for future derived values. + pub prod: AccKey, // Product account key. + pub next: AccKey, // Next Price account in linked list. + pub agg_pub: AccKey, // Quoter who computed last aggregate price. + pub agg: PriceInfo, // Aggregate price info. + pub comp: [PriceComp; 32], // Price components one per quoter. + } + + #[cfg(target_endian = "little")] + unsafe impl Zeroable for Price {} + + #[cfg(target_endian = "little")] + unsafe impl Pod for Price {} + } + + pub fn get_account_bytes(account: &mut T) -> BytesMut { + let mut bytes = BytesMut::new(); + let data = bytemuck::bytes_of_mut(account); + bytes.extend_from_slice(data); + bytes + } + + pub fn get_anchor_account_bytes(account: &mut T) -> BytesMut { + let mut bytes = BytesMut::new(); + bytes.extend_from_slice(&T::discriminator()); + let data = bytemuck::bytes_of_mut(account); + bytes.extend_from_slice(data); + bytes + } + + #[macro_export] + macro_rules! create_account_info { + ($account:expr, $pubkey:expr, $owner:expr, $name: ident) => { + let acc = Account { + data: crate::utils::test_utils::get_account_bytes(&mut $account).to_vec(), + owner: $owner, + ..Default::default() + }; + let $name: crate::ffi::AccountWithKey = (*$pubkey, acc).into(); + }; + } + + #[macro_export] + macro_rules! create_anchor_account_info { + ($account:expr, $pubkey:expr, $type:ident, $name: ident) => { + let owner = constants::PROGRAM_ID; + let acc = Account { + data: crate::utils::test_utils::get_anchor_account_bytes(&mut $account).to_vec(), + owner, + ..Default::default() + }; + let $name: crate::ffi::AccountWithKey = ($pubkey, acc).into(); + }; + } +} + #[cfg(test)] mod tests { use solana_sdk::signer::Signer; diff --git a/res/sol-oracle-pyth.hex b/res/sol-oracle-pyth.hex deleted file mode 100644 index f7ef459..0000000 --- a/res/sol-oracle-pyth.hex +++ /dev/null @@ -1 +0,0 @@ -000000000000000000000000000000000100000006000000000000000000000000000000000000000000000000000000008793030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087930300000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000 \ No newline at end of file diff --git a/res/spot_market_1_76_0.hex b/res/spot_market_1_76_0.hex new file mode 100644 index 0000000..72b5826 --- /dev/null +++ b/res/spot_market_1_76_0.hex @@ -0,0 +1 @@ +64b1086ba84141270000000000000000000000000000000000000000000000000000000000000000fe650f0367d4a7ef9815a593ea15d36593f0643aaaf0149bb04be67ab851decd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000601de13700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010a5d4e800000000000000000000000000000000000000000000000000000000e40b5402000000000000000000000000e40b54020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e803000000000000e8030000000000000000000000000000000000000000000000000000000000000000000000000000401f000028230000e02e0000f82a000000000000e803000000000000000000000000000000000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file