ferrox_actions/
gmgn.rs

1use crate::{
2    action::{ActionBuilder, ActionGroup, FunctionAction},
3    AgentState,
4};
5use serde::{Deserialize, Serialize};
6use std::sync::Arc;
7
8#[derive(Debug, Deserialize, Serialize)]
9pub struct GmgnKlineResponse {
10    pub code: i32,
11    pub msg: String,
12    pub data: Vec<KlineData>,
13}
14
15#[derive(Debug, Deserialize, Serialize)]
16pub struct KlineData {
17    pub open: String,
18    pub high: String,
19    pub low: String,
20    pub close: String,
21    pub volume: String,
22    pub time: String,
23}
24
25pub async fn fetch_k_line_data_from_gmgn(
26    token_address: String,
27    time_from: i64,
28    time_to: i64,
29) -> Result<GmgnKlineResponse, String> {
30    let client = reqwest::Client::new();
31
32    let url = format!(
33        "https://www.gmgn.cc/defi/quotation/v1/tokens/kline/sol/{}?resolution=1h&from={}&to={}",
34        token_address, time_from, time_to
35    );
36    println!("Fetching kline data from GMGN: {}", url);
37
38    match client.get(&url).send().await {
39        Ok(response) => match response.json::<GmgnKlineResponse>().await {
40            Ok(kline_data) => Ok(kline_data),
41            Err(e) => {
42                println!("Failed to parse GMGN response: {}", e);
43                Err("Error parsing kline data".to_string())
44            }
45        },
46        Err(e) => {
47            println!("Failed to fetch from GMGN: {}", e);
48            Err("Failed to fetch kline data".to_string())
49        }
50    }
51}
52
53#[derive(Debug, Deserialize)]
54pub struct KlineDataParams {
55    token_address: String,
56    time_from: i64,
57    time_to: i64,
58}
59
60pub struct GmgnActionGroup<S: Send + Sync + Clone + 'static> {
61    actions: Vec<Arc<FunctionAction<S>>>,
62}
63
64impl<S: Send + Sync + Clone + 'static> ActionGroup<S> for GmgnActionGroup<S> {
65    fn actions(&self) -> &[Arc<FunctionAction<S>>] {
66        &self.actions
67    }
68}
69
70impl<S: Send + Sync + Clone + 'static> GmgnActionGroup<S> {
71    pub fn new() -> Self {
72        let mut actions = Vec::new();
73        // Add kline data action
74        {
75            async fn get_kline_data<S: Send + Sync + Clone + 'static>(
76                params: KlineDataParams,
77                _send_state: serde_json::Value,
78                _state: AgentState<S>,
79            ) -> Result<String, String> {
80                let kline_data = fetch_k_line_data_from_gmgn(
81                    params.token_address,
82                    params.time_from,
83                    params.time_to,
84                )
85                .await?;
86
87                serde_json::to_string(&kline_data)
88                    .map_err(|e| format!("Failed to serialize GMGN response: {}", e))
89            }
90
91            let action = ActionBuilder::<_, _, _, _>::new(
92                "get_gmgn_kline_data",
93                get_kline_data,
94                None,
95            )
96            .description(
97                "Get OHLCV kline data for a Solana token from GMGN (alternative to Birdeye). Use this if the birdeye response is empty or errored",
98            )
99            .parameter("token_address", "Solana token address", "string", true)
100            .parameter("time_from", "Start timestamp (Unix)", "integer", true)
101            .parameter("time_to", "End timestamp (Unix)", "integer", true)
102            .build();
103
104            actions.push(Arc::new(action));
105        }
106
107        Self { actions }
108    }
109}