Skip to content

Commit 8033601

Browse files
authored
Merge pull request #21 from thrishank/bump-version-1.0.6
add the ultra token search api
2 parents f22b867 + 8fd3a7d commit 8033601

File tree

5 files changed

+202
-3
lines changed

5 files changed

+202
-3
lines changed

examples/src/ultra.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,16 @@ pub async fn ultra() {
4949

5050
println!("✅ Transaction submitted: {}", tx_signature);
5151
}
52+
53+
pub async fn ultra_token_search() {
54+
let client = JupiterClient::new("https:://lite-api.jup.ag");
55+
56+
let mints = vec![String::from("JUP")];
57+
58+
let data = client
59+
.ultra_token_search(&mints)
60+
.await
61+
.expect("failed to get token info");
62+
63+
println!("data: {:?}", data);
64+
}

jup-ag-sdk/src/client/ultra_api.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{
22
error::{JupiterClientError, handle_response},
33
types::{
4-
Router, Shield, TokenBalancesResponse, UltraExecuteOrderRequest, UltraExecuteOrderResponse,
5-
UltraOrderRequest, UltraOrderResponse,
4+
Router, Shield, TokenBalancesResponse, TokenInfo, UltraExecuteOrderRequest,
5+
UltraExecuteOrderResponse, UltraOrderRequest, UltraOrderResponse,
66
},
77
};
88

@@ -190,6 +190,57 @@ impl JupiterClient {
190190
}
191191
}
192192

193+
/// search for a token and its information by its symbol, name or mint address
194+
///
195+
/// Limit to 100 mint addresses in query
196+
///
197+
/// # Arguments
198+
///
199+
/// * `mints` - A slice of mint addresses (`&[String]`) to inspect.
200+
///
201+
/// # Returns
202+
///
203+
/// * `Ok(Vec<TokenInfo>)` containing token safety metadata.
204+
/// * `Err` if the request or deserialization fails.
205+
///
206+
/// # Jupiter API Reference
207+
///
208+
/// - [Search Endpoint](https://dev.jup.ag/docs/api/ultra-api/search)
209+
///
210+
/// # Example
211+
///
212+
/// ```
213+
/// let mints = vec![
214+
/// String::from("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
215+
/// String::from("JUP")
216+
/// ];
217+
/// let token_info = client.ultra_token_search(&mints).await?;
218+
/// ```
219+
pub async fn ultra_token_search(
220+
&self,
221+
mints: &[String],
222+
) -> Result<Vec<TokenInfo>, JupiterClientError> {
223+
let query_params = vec![("query", mints.join(","))];
224+
225+
let response = match self
226+
.client
227+
.get(format!("{}/ultra/v1/search", self.base_url))
228+
.query(&query_params)
229+
.send()
230+
.await
231+
{
232+
Ok(resp) => resp,
233+
Err(e) => return Err(JupiterClientError::RequestError(e)),
234+
};
235+
236+
let response = handle_response(response).await?;
237+
238+
match response.json::<Vec<TokenInfo>>().await {
239+
Ok(data) => Ok(data),
240+
Err(e) => Err(JupiterClientError::DeserializationError(e.to_string())),
241+
}
242+
}
243+
193244
/// Request for the list of routers available in the routing engine of Ultra, which is Juno
194245
pub async fn routers(&self) -> Result<Vec<Router>, JupiterClientError> {
195246
let response = match self

jup-ag-sdk/src/types/ultra.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,112 @@ pub struct Router {
324324
pub name: String,
325325
pub icon: String,
326326
}
327+
328+
#[derive(Debug, Serialize, Deserialize)]
329+
#[serde(rename_all = "camelCase")]
330+
pub struct TokenStats {
331+
pub price_change: Option<f64>,
332+
pub holder_change: Option<f64>,
333+
pub liquidity_change: Option<f64>,
334+
pub volume_change: Option<f64>,
335+
pub buy_volume: Option<f64>,
336+
pub sell_volume: Option<f64>,
337+
pub buy_organic_volume: Option<f64>,
338+
pub sell_organic_volume: Option<f64>,
339+
pub num_buys: Option<u64>,
340+
pub num_sells: Option<u64>,
341+
pub num_traders: Option<u64>,
342+
pub num_organic_buyers: Option<u64>,
343+
pub num_net_buyers: Option<u64>,
344+
}
345+
346+
#[derive(Debug, Serialize, Deserialize)]
347+
#[serde(rename_all = "camelCase")]
348+
pub struct FirstPool {
349+
pub id: String,
350+
pub created_at: String,
351+
}
352+
353+
#[derive(Debug, Serialize, Deserialize)]
354+
#[serde(rename_all = "camelCase")]
355+
pub struct Audit {
356+
#[serde(default)]
357+
pub is_sus: Option<bool>,
358+
#[serde(default)]
359+
pub mint_authority_disabled: Option<bool>,
360+
#[serde(default)]
361+
pub freeze_authority_disabled: Option<bool>,
362+
#[serde(default)]
363+
pub top_holders_percentage: Option<f64>,
364+
#[serde(default)]
365+
pub dev_balance_percentage: Option<f64>,
366+
#[serde(default)]
367+
pub dev_migrations: Option<u64>,
368+
}
369+
370+
#[derive(Debug, Serialize, Deserialize)]
371+
#[serde(rename_all = "camelCase")]
372+
pub struct TokenInfo {
373+
pub id: String,
374+
pub name: String,
375+
pub symbol: String,
376+
#[serde(default)]
377+
pub icon: Option<String>,
378+
pub decimals: u8,
379+
#[serde(default)]
380+
pub twitter: Option<String>,
381+
#[serde(default)]
382+
pub telegram: Option<String>,
383+
#[serde(default)]
384+
pub website: Option<String>,
385+
#[serde(default)]
386+
pub dev: Option<String>,
387+
pub circ_supply: f64,
388+
pub total_supply: f64,
389+
pub token_program: String,
390+
391+
#[serde(default)]
392+
pub launchpad: Option<String>,
393+
#[serde(default)]
394+
pub partner_config: Option<String>,
395+
#[serde(default)]
396+
pub graduated_pool: Option<String>,
397+
#[serde(default)]
398+
pub graduated_at: Option<String>,
399+
#[serde(default)]
400+
pub mint_authority: Option<String>,
401+
#[serde(default)]
402+
pub freeze_authority: Option<String>,
403+
404+
pub first_pool: FirstPool,
405+
pub holder_count: u64,
406+
#[serde(default)]
407+
pub audit: Option<Audit>,
408+
pub organic_score: f64,
409+
pub organic_score_label: String,
410+
#[serde(default)]
411+
pub is_verified: Option<bool>,
412+
#[serde(default)]
413+
pub cexes: Vec<String>,
414+
#[serde(default)]
415+
pub tags: Vec<String>,
416+
pub fdv: f64,
417+
pub mcap: f64,
418+
pub usd_price: f64,
419+
pub price_block_id: u64,
420+
pub liquidity: f64,
421+
422+
#[serde(default)]
423+
pub stats5m: Option<TokenStats>,
424+
#[serde(default)]
425+
pub stats1h: Option<TokenStats>,
426+
#[serde(default)]
427+
pub stats6h: Option<TokenStats>,
428+
#[serde(default)]
429+
pub stats24h: Option<TokenStats>,
430+
#[serde(default)]
431+
pub ct_likes: Option<u64>,
432+
#[serde(default)]
433+
pub smart_ct_likes: Option<u64>,
434+
pub updated_at: String,
435+
}

tests/src/trigger.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#[cfg(test)]
2-
mod ultra_tests {
2+
mod trigger_tests {
33
use jup_ag_sdk::types::{
44
CreateTriggerOrder, ExecuteTriggerOrder, GetTriggerOrders, OrderStatus,
55
};

tests/src/ultra.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,32 @@ mod ultra_tests {
131131
);
132132
}
133133

134+
#[tokio::test]
135+
async fn test_ultra_token_search() {
136+
let client = create_test_client();
137+
138+
let mints = vec!["JUP".to_string()];
139+
140+
let tokens = client
141+
.ultra_token_search(&mints)
142+
.await
143+
.expect("Failed to search tokens");
144+
145+
let token = &tokens[0];
146+
assert_eq!(token.id, JUP_MINT, "Token ID should match JUP");
147+
assert!(!token.id.is_empty());
148+
assert!(!token.name.is_empty());
149+
assert!(!token.symbol.is_empty());
150+
assert!(token.decimals == 6);
151+
assert!(token.circ_supply > 0.0);
152+
assert!(token.total_supply > 0.0);
153+
assert!(token.holder_count > 0);
154+
assert!(token.fdv > 0.0);
155+
assert!(token.mcap > 0.0);
156+
assert!(token.usd_price > 0.0);
157+
assert!(token.liquidity > 0.0);
158+
}
159+
134160
#[tokio::test]
135161
async fn test_routers() {
136162
let client = create_test_client();

0 commit comments

Comments
 (0)