-
Notifications
You must be signed in to change notification settings - Fork 6
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
feat(api): add Raydium price fetcher cache warmup for symbols #390
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,43 @@ impl RaydiumTokenPriceFetcher { | |
} | ||
} | ||
|
||
pub async fn warmup(&self) -> Result<(), IngesterError> { | ||
#[derive(serde::Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct MintListItem { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we want a declaration of structs inside a function? Does it do something useful? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the struct is single-use and used to deserialize only one particular response for one particular function, to me it looks logical & so much better than |
||
address: String, | ||
symbol: String, | ||
} | ||
#[derive(serde::Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct MintListResponse { | ||
mint_list: Vec<MintListItem>, | ||
} | ||
// returns well-known token infos | ||
let req = "mint/list"; | ||
let response = self.get(req).await.map_err(|e| UsecaseError::Reqwest(e.to_string()))?; | ||
|
||
let tokens_data = response | ||
.get("data") | ||
.and_then(|mint_list| { | ||
serde_json::from_value::<MintListResponse>(mint_list.clone()).ok() | ||
}) | ||
.ok_or_else(|| { | ||
UsecaseError::Reqwest(format!( | ||
"No 'data' field in RaydiumTokenPriceFetcher ids response. Full response: {:#?}", | ||
response | ||
)) | ||
})?; | ||
|
||
for MintListItem { address, symbol } in tokens_data.mint_list { | ||
self.symbol_cache.insert(address.clone(), symbol.clone()).await; | ||
} | ||
|
||
self.symbol_cache.run_pending_tasks().await; | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn get(&self, endpoint: &str) -> Result<serde_json::Value, IngesterError> { | ||
let start_time = chrono::Utc::now(); | ||
let response = reqwest::get(format!("{host}/{ep}", host = self.host, ep = endpoint)) | ||
|
@@ -53,6 +90,13 @@ impl RaydiumTokenPriceFetcher { | |
} | ||
response | ||
} | ||
|
||
/// Returns the approximate sizes of the symbol and the price caches. | ||
/// | ||
/// The return format is (symbol_cache_size, price_cache_size). | ||
pub fn get_cache_sizes(&self) -> (u64, u64) { | ||
(self.symbol_cache.weighted_size(), self.price_cache.weighted_size()) | ||
} | ||
} | ||
|
||
#[async_trait] | ||
|
@@ -61,6 +105,12 @@ impl TokenPriceFetcher for RaydiumTokenPriceFetcher { | |
&self, | ||
token_ids: &[String], | ||
) -> Result<HashMap<String, String>, UsecaseError> { | ||
#[derive(serde::Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct MintIdsItem { | ||
address: String, | ||
symbol: String, | ||
} | ||
let token_ids_str: Vec<String> = token_ids.iter().map(ToString::to_string).collect(); | ||
let mut result = HashMap::with_capacity(token_ids.len()); | ||
let mut missing_token_ids = Vec::new(); | ||
|
@@ -80,21 +130,16 @@ impl TokenPriceFetcher for RaydiumTokenPriceFetcher { | |
|
||
let tokens_data = response | ||
.get("data") | ||
.and_then(|td| td.as_array()) | ||
.and_then(|item| serde_json::from_value::<Vec<Option<MintIdsItem>>>(item.clone()).ok()) | ||
.ok_or_else(|| { | ||
UsecaseError::Reqwest(format!( | ||
"No 'data' field in RaydiumTokenPriceFetcher ids response. Full response: {:#?}", | ||
response | ||
)) | ||
})?; | ||
|
||
for data in tokens_data { | ||
if let (Some(address), Some(symbol)) = ( | ||
data.get("address").and_then(|a| a.as_str()), | ||
data.get("symbol").and_then(|s| s.as_str()), | ||
) { | ||
let address = address.to_string(); | ||
let symbol = symbol.to_string(); | ||
for maybe_token_data in tokens_data { | ||
if let Some(MintIdsItem { address, symbol }) = maybe_token_data { | ||
self.symbol_cache.insert(address.clone(), symbol.clone()).await; | ||
result.insert(address, symbol); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd like to see it as a constant