Skip to content

ffi get_oracle_price: add empty data check #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,17 @@ jobs:
uses: actions/checkout@v4
with:
submodules: true
# - name: Cache Rust toolchain
# uses: actions/cache@v4
# with:
# path: |
# ~/.rustup
# ~/.cargo/bin
# ~/.cargo/registry
# ~/.cargo/git
# target
# key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }}
# restore-keys: |
# ${{ runner.os }}-rust-
- name: Cache Rust toolchain
uses: actions/cache@v4
with:
path: |
~/.rustup
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }}
- name: Config rust toolchain
run: |
rustup show active-toolchain
Expand Down
10 changes: 7 additions & 3 deletions crates/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
errors::ErrorCode,
types::{self, ContractType, MarginRequirementType, OracleSource},
},
types::SdkError,
SdkResult,
};

Expand Down Expand Up @@ -41,7 +42,7 @@ extern "C" {

#[allow(improper_ctypes)]
pub fn oracle_get_oracle_price(
orace_source: OracleSource,
oracle_source: OracleSource,
oracle_account: &mut (Pubkey, Account),
slot: Slot,
) -> FfiResult<OraclePriceData>;
Expand Down Expand Up @@ -142,11 +143,14 @@ pub fn check_ffi_version() -> String {
}

pub fn get_oracle_price(
orace_source: OracleSource,
oracle_source: OracleSource,
oracle_account: &mut (Pubkey, Account),
slot: Slot,
) -> SdkResult<OraclePriceData> {
to_sdk_result(unsafe { oracle_get_oracle_price(orace_source, oracle_account, slot) })
if oracle_account.1.data.is_empty() {
return Err(SdkError::NoAccountData(oracle_account.0));
}
to_sdk_result(unsafe { oracle_get_oracle_price(oracle_source, oracle_account, slot) })
}

pub fn calculate_auction_price(
Expand Down
100 changes: 41 additions & 59 deletions crates/src/math/account_list_builder.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use ahash::HashMap;
use ahash::{HashMap, HashMapExt};
use arrayvec::ArrayVec;
use solana_sdk::{account::Account, pubkey::Pubkey};

use crate::{
accounts::State,
constants::{self, oracle_source_to_owner, state_account},
ffi::{AccountWithKey, AccountsList},
types::accounts::{PerpMarket, SpotMarket, User},
types::accounts::User,
utils::zero_account_to_bytes,
DriftClient, MarketId, SdkError, SdkResult,
};
Expand All @@ -19,9 +20,9 @@ use crate::{
#[derive(Default)]
pub struct AccountsListBuilder {
/// placeholder account values populated with real market & oracle account data
perp_accounts: Vec<AccountWithKey>,
spot_accounts: Vec<AccountWithKey>,
oracle_accounts: Vec<AccountWithKey>,
perp_accounts: ArrayVec<AccountWithKey, 16>,
spot_accounts: ArrayVec<AccountWithKey, 16>,
oracle_accounts: ArrayVec<AccountWithKey, 16>,
}

impl AccountsListBuilder {
Expand All @@ -38,29 +39,37 @@ impl AccountsListBuilder {
user: &User,
force_markets: &[MarketId],
) -> SdkResult<AccountsList> {
let mut oracle_markets =
HashMap::<Pubkey, MarketId>::with_capacity_and_hasher(16, Default::default());
let mut spot_markets = Vec::<SpotMarket>::new();
let mut perp_markets = Vec::<PerpMarket>::new();
let mut oracle_markets = HashMap::<Pubkey, MarketId>::with_capacity(16);
let drift_state_account = client.try_get_account::<State>(state_account())?;

let force_spot_iter = force_markets
.iter()
.filter(|m| m.is_spot())
.map(|m| m.index());
let mut spot_market_idxs = ahash::HashSet::from_iter(

let spot_market_idxs = ahash::HashSet::from_iter(
user.spot_positions
.iter()
.filter(|p| !p.is_available())
.map(|p| p.market_index)
.chain(force_spot_iter),
.chain(force_spot_iter)
.chain(std::iter::once(MarketId::QUOTE_SPOT.index())),
);
spot_market_idxs.insert(MarketId::QUOTE_SPOT.index());

for idx in spot_market_idxs {
let market = client.try_get_spot_market_account(idx)?;
oracle_markets.insert(market.oracle, MarketId::spot(market.market_index));
spot_markets.push(market);
self.spot_accounts.push(
(
market.pubkey,
Account {
data: zero_account_to_bytes(market),
owner: constants::PROGRAM_ID,
..Default::default()
},
)
.into(),
);
}

let force_perp_iter = force_markets
Expand All @@ -78,24 +87,6 @@ impl AccountsListBuilder {
for idx in perp_market_idxs {
let market = client.try_get_perp_market_account(idx)?;
oracle_markets.insert(market.amm.oracle, MarketId::perp(market.market_index));
perp_markets.push(market);
}

for market in spot_markets {
self.spot_accounts.push(
(
market.pubkey,
Account {
data: zero_account_to_bytes(market),
owner: constants::PROGRAM_ID,
..Default::default()
},
)
.into(),
);
}

for market in perp_markets {
self.perp_accounts.push(
(
market.pubkey,
Expand All @@ -115,7 +106,7 @@ impl AccountsListBuilder {
.try_get_oracle_price_data_and_slot(*market)
.ok_or(SdkError::NoMarketData(*market))?;

latest_oracle_slot = oracle.slot.max(oracle.slot);
latest_oracle_slot = oracle.slot.max(latest_oracle_slot);
let oracle_owner = oracle_source_to_owner(client.context, oracle.source);
self.oracle_accounts.push(
(
Expand Down Expand Up @@ -154,30 +145,38 @@ impl AccountsListBuilder {
user: &User,
force_markets: &[MarketId],
) -> SdkResult<AccountsList> {
let mut oracle_markets =
HashMap::<Pubkey, MarketId>::with_capacity_and_hasher(16, Default::default());
let mut spot_markets = Vec::<SpotMarket>::new();
let mut perp_markets = Vec::<PerpMarket>::new();
let mut oracle_markets = HashMap::<Pubkey, MarketId>::with_capacity(16);
let drift_state_account = client.try_get_account::<State>(state_account())?;

// TODO: could batch the requests
let force_spot_iter = force_markets
.iter()
.filter(|m| m.is_spot())
.map(|m| m.index());
let mut spot_market_idxs = ahash::HashSet::from_iter(
let spot_market_idxs = ahash::HashSet::from_iter(
user.spot_positions
.iter()
.filter(|p| !p.is_available())
.map(|p| p.market_index)
.chain(force_spot_iter),
.chain(force_spot_iter)
.chain(std::iter::once(MarketId::QUOTE_SPOT.index())),
);
spot_market_idxs.insert(MarketId::QUOTE_SPOT.index());

for market_idx in spot_market_idxs.iter() {
let market = client.get_spot_market_account(*market_idx).await?;
oracle_markets.insert(market.oracle, MarketId::spot(market.market_index));
spot_markets.push(market);

self.spot_accounts.push(
(
market.pubkey,
Account {
data: zero_account_to_bytes(market),
owner: constants::PROGRAM_ID,
..Default::default()
},
)
.into(),
);
}

let force_perp_iter = force_markets
Expand All @@ -195,29 +194,12 @@ impl AccountsListBuilder {
for market_idx in perp_market_idxs.iter() {
let market = client.get_perp_market_account(*market_idx).await?;
oracle_markets.insert(market.amm.oracle, MarketId::perp(market.market_index));
perp_markets.push(market);
}

for market in spot_markets.iter() {
self.spot_accounts.push(
(
market.pubkey,
Account {
data: zero_account_to_bytes(*market),
owner: constants::PROGRAM_ID,
..Default::default()
},
)
.into(),
);
}

for market in perp_markets.iter() {
self.perp_accounts.push(
(
market.pubkey,
Account {
data: zero_account_to_bytes(*market),
data: zero_account_to_bytes(market),
owner: constants::PROGRAM_ID,
..Default::default()
},
Expand All @@ -230,7 +212,7 @@ impl AccountsListBuilder {
for (oracle_key, market) in oracle_markets.iter() {
let oracle = client.get_oracle_price_data_and_slot(*market).await?;

latest_oracle_slot = oracle.slot.max(oracle.slot);
latest_oracle_slot = oracle.slot.max(latest_oracle_slot);
let oracle_owner = oracle_source_to_owner(client.context, oracle.source);
self.oracle_accounts.push(
(
Expand Down
1 change: 1 addition & 0 deletions crates/src/websocket_account_subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl WebsocketAccountSubscriber {
slot: response.context.slot,
});
} else {
warn!("seeding account failed: {response:?}");
return Err(SdkError::InvalidAccount);
}
}
Expand Down