ferrox_actions/
coingecko.rs

1pub mod pro;
2
3use crate::{
4    action::{ActionBuilder, ActionGroup, FunctionAction},
5    AgentState,
6};
7use pro::CoinGeckoProClient;
8use serde::Deserialize;
9use std::sync::Arc;
10
11// Parameter structs for each action
12#[derive(Debug, Deserialize)]
13pub struct CoinContractMarketChartRangeParams {
14    id: String,
15    contract_address: String,
16    vs_currency: String,
17    days: String,
18}
19
20#[derive(Debug, Deserialize)]
21pub struct CoinMarketChartParams {
22    id: String,
23    vs_currency: String,
24    days: String,
25    interval: Option<String>,
26}
27
28// Add these parameter structs at the top with the other parameter structs
29#[derive(Debug, Deserialize)]
30pub struct NetworkStatusParams {} // Empty struct for endpoints with no parameters
31
32#[derive(Debug, Deserialize)]
33pub struct GlobalDataParams {}
34
35#[derive(Debug, Deserialize)]
36pub struct GlobalDefiDataParams {}
37
38#[derive(Debug, Deserialize)]
39pub struct ExchangesParams {
40    per_page: Option<u32>,
41    page: Option<u32>,
42}
43
44#[derive(Debug, Deserialize)]
45pub struct ExchangeParams {
46    id: String,
47}
48
49#[derive(Debug, Deserialize)]
50pub struct ExchangeTickersParams {
51    id: String,
52    coin_ids: Option<Vec<String>>,
53    include_exchange_logo: Option<bool>,
54    page: Option<u32>,
55    depth: Option<bool>,
56    order: Option<String>,
57}
58
59#[derive(Debug, Deserialize)]
60pub struct ExchangeVolumeChartParams {
61    id: String,
62    days: u32,
63}
64
65#[derive(Debug, Deserialize)]
66pub struct CoinsListParams {
67    include_platform: Option<bool>,
68}
69
70#[derive(Debug, Deserialize)]
71pub struct CoinTickersParams {
72    id: String,
73    exchange_ids: Option<Vec<String>>,
74    include_exchange_logo: Option<bool>,
75    page: Option<u32>,
76    order: Option<String>,
77    depth: Option<bool>,
78}
79
80#[derive(Debug, Deserialize)]
81pub struct CoinHistoryParams {
82    id: String,
83    date: String,
84    localization: Option<bool>,
85}
86
87#[derive(Debug, Deserialize)]
88pub struct CoinOhlcParams {
89    id: String,
90    vs_currency: String,
91    days: String,
92}
93
94#[derive(Debug, Deserialize)]
95pub struct CoinContractParams {
96    id: String,
97    contract_address: String,
98}
99
100#[derive(Debug, Deserialize)]
101pub struct CoinContractMarketChartParams {
102    id: String,
103    contract_address: String,
104    vs_currency: String,
105    days: String,
106}
107
108// Add these parameter structs at the top with the other parameter structs
109#[derive(Debug, Deserialize)]
110pub struct AssetPlatformsParams {} // Empty struct for endpoints with no parameters
111
112#[derive(Debug, Deserialize)]
113pub struct CoinsCategoriesListParams {}
114
115#[derive(Debug, Deserialize)]
116pub struct CoinsCategoriesParams {
117    order: Option<String>,
118}
119
120#[derive(Debug, Deserialize)]
121pub struct IndexesParams {}
122
123#[derive(Debug, Deserialize)]
124pub struct IndexesListParams {}
125
126#[derive(Debug, Deserialize)]
127pub struct DerivativesParams {}
128
129#[derive(Debug, Deserialize)]
130pub struct DerivativesExchangesParams {
131    order: Option<String>,
132    per_page: Option<u32>,
133    page: Option<u32>,
134}
135
136#[derive(Debug, Deserialize)]
137pub struct DerivativesExchangeParams {
138    id: String,
139    include_tickers: Option<String>,
140}
141
142#[derive(Debug, Deserialize)]
143pub struct ExchangeRatesParams {}
144
145#[derive(Debug, Deserialize)]
146pub struct SearchParams {
147    query: String,
148}
149
150#[derive(Debug, Deserialize)]
151pub struct TrendingParams {}
152
153#[derive(Debug, Deserialize)]
154pub struct CompaniesPublicTreasuryParams {
155    coin_id: String,
156}
157
158// Action group that contains all CoinGecko actions
159pub struct CoinGeckoActionGroup<S: Send + Sync + Clone + 'static> {
160    actions: Vec<Arc<FunctionAction<S>>>,
161}
162
163impl<S: Send + Sync + Clone + 'static> ActionGroup<S> for CoinGeckoActionGroup<S> {
164    fn actions(&self) -> &[Arc<FunctionAction<S>>] {
165        &self.actions
166    }
167}
168
169impl<S: Send + Sync + Clone + 'static> CoinGeckoActionGroup<S> {
170    pub fn new() -> Self {
171        let mut actions = Vec::new();
172
173        // Update coin contract market chart range action
174        {
175            async fn get_coin_contract_market_chart_range<S: Send + Sync + Clone + 'static>(
176                params: CoinContractMarketChartRangeParams,
177                _send_state: serde_json::Value,
178                _state: AgentState<S>,
179            ) -> Result<String, String> {
180                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
181                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
182                })?;
183
184                let client = CoinGeckoProClient::new(api_key);
185
186                let to = chrono::Utc::now().timestamp() as u64;
187                let from = calculate_from_timestamp(to, &params.days)?;
188
189                client
190                    .get_coin_contract_market_chart_range(
191                        params.id,
192                        params.contract_address,
193                        params.vs_currency,
194                        from,
195                        to,
196                    )
197                    .await
198            }
199
200            let action = ActionBuilder::<_, _, _, _>::new(
201                "get_coin_contract_market_chart_range",
202                get_coin_contract_market_chart_range,
203                None,
204            )
205            .description("Get historical market data for a token contract address")
206            .parameter("id", "The coin id (e.g. ethereum)", "string", true)
207            .parameter(
208                "contract_address",
209                "Token's contract address",
210                "string",
211                true,
212            )
213            .parameter(
214                "vs_currency",
215                "The target currency (e.g. usd)",
216                "string",
217                true,
218            )
219            .parameter(
220                "days",
221                "Number of days of data to return (1, 7, 14, 30, 90, 180, 365, max)",
222                "string",
223                true,
224            )
225            .build();
226
227            actions.push(Arc::new(action));
228        }
229
230        // Add coin market chart action
231        {
232            async fn get_coin_market_chart<S: Send + Sync + Clone + 'static>(
233                params: CoinMarketChartParams,
234                _send_state: serde_json::Value,
235                _state: AgentState<S>,
236            ) -> Result<String, String> {
237                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
238                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
239                })?;
240
241                let client = CoinGeckoProClient::new(api_key);
242                client
243                    .get_coin_market_chart(
244                        params.id,
245                        params.vs_currency,
246                        params.days,
247                        params.interval,
248                    )
249                    .await
250            }
251
252            let action = ActionBuilder::<_, _, _, _>::new(
253                "get_coin_market_chart",
254                get_coin_market_chart,
255                None,
256            )
257            .description("Get historical market data include price, market cap, and 24h volume")
258            .parameter("id", "The coin id (e.g. bitcoin)", "string", true)
259            .parameter(
260                "vs_currency",
261                "The target currency (e.g. usd)",
262                "string",
263                true,
264            )
265            .parameter("days", "Data up to number of days ago", "string", true)
266            .parameter("interval", "Data interval (e.g. daily)", "string", false)
267            .build();
268
269            actions.push(Arc::new(action));
270        }
271
272        // Add network status action
273        {
274            async fn get_network_status<S: Send + Sync + Clone + 'static>(
275                _params: NetworkStatusParams,
276                _send_state: serde_json::Value,
277                _state: AgentState<S>,
278            ) -> Result<String, String> {
279                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
280                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
281                })?;
282
283                let client = CoinGeckoProClient::new(api_key);
284                client.get_network_status().await
285            }
286
287            let action =
288                ActionBuilder::<_, _, _, _>::new("get_network_status", get_network_status, None)
289                    .description("Check API server status")
290                    .build();
291
292            actions.push(Arc::new(action));
293        }
294
295        // Add global data action
296        {
297            async fn get_global_data<S: Send + Sync + Clone + 'static>(
298                _params: GlobalDataParams,
299                _send_state: serde_json::Value,
300                _state: AgentState<S>,
301            ) -> Result<String, String> {
302                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
303                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
304                })?;
305
306                let client = CoinGeckoProClient::new(api_key);
307                client.get_global_data().await
308            }
309
310            let action = ActionBuilder::<_, _, _, _>::new("get_global_data", get_global_data, None)
311                .description("Get cryptocurrency global data")
312                .build();
313
314            actions.push(Arc::new(action));
315        }
316
317        // Add global defi data action
318        {
319            async fn get_global_defi_data<S: Send + Sync + Clone + 'static>(
320                _params: GlobalDefiDataParams,
321                _send_state: serde_json::Value,
322                _state: AgentState<S>,
323            ) -> Result<String, String> {
324                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
325                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
326                })?;
327
328                let client = CoinGeckoProClient::new(api_key);
329                client.get_global_defi_data().await
330            }
331
332            let action = ActionBuilder::<_, _, _, _>::new(
333                "get_global_defi_data",
334                get_global_defi_data,
335                None,
336            )
337            .description("Get cryptocurrency global decentralized finance (defi) data")
338            .build();
339
340            actions.push(Arc::new(action));
341        }
342
343        // Add exchanges action
344        {
345            async fn get_exchanges<S: Send + Sync + Clone + 'static>(
346                params: ExchangesParams,
347                _send_state: serde_json::Value,
348                _state: AgentState<S>,
349            ) -> Result<String, String> {
350                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
351                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
352                })?;
353
354                let client = CoinGeckoProClient::new(api_key);
355                client.get_exchanges(params.per_page, params.page).await
356            }
357
358            let action = ActionBuilder::<_, _, _, _>::new("get_exchanges", get_exchanges, None)
359                .description("List all exchanges")
360                .parameter("per_page", "Total results per page", "integer", false)
361                .parameter("page", "Page number", "integer", false)
362                .build();
363
364            actions.push(Arc::new(action));
365        }
366
367        // Add exchange action
368        {
369            async fn get_exchange<S: Send + Sync + Clone + 'static>(
370                params: ExchangeParams,
371                _send_state: serde_json::Value,
372                _state: AgentState<S>,
373            ) -> Result<String, String> {
374                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
375                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
376                })?;
377
378                let client = CoinGeckoProClient::new(api_key);
379                client.get_exchange(params.id).await
380            }
381
382            let action = ActionBuilder::<_, _, _, _>::new("get_exchange", get_exchange, None)
383                .description("Get exchange volume in BTC and top 100 tickers only")
384                .parameter("id", "Exchange id", "string", true)
385                .build();
386
387            actions.push(Arc::new(action));
388        }
389
390        // Add exchange tickers action
391        {
392            async fn get_exchange_tickers<S: Send + Sync + Clone + 'static>(
393                params: ExchangeTickersParams,
394                _send_state: serde_json::Value,
395                _state: AgentState<S>,
396            ) -> Result<String, String> {
397                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
398                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
399                })?;
400
401                let client = CoinGeckoProClient::new(api_key);
402                client
403                    .get_exchange_tickers(
404                        params.id,
405                        params.coin_ids,
406                        params.include_exchange_logo,
407                        params.page,
408                        params.depth,
409                        params.order,
410                    )
411                    .await
412            }
413
414            let action = ActionBuilder::<_, _, _, _>::new(
415                "get_exchange_tickers",
416                get_exchange_tickers,
417                None,
418            )
419            .description("Get exchange tickers (paginated)")
420            .parameter("id", "Exchange id", "string", true)
421            .parameter("coin_ids", "Filter tickers by coin ids", "array", false)
422            .parameter(
423                "include_exchange_logo",
424                "Include exchange logo",
425                "boolean",
426                false,
427            )
428            .parameter("page", "Page number", "integer", false)
429            .parameter("depth", "Include 2% orderbook depth", "boolean", false)
430            .parameter("order", "Sort by order", "string", false)
431            .build();
432
433            actions.push(Arc::new(action));
434        }
435
436        // Add exchange volume chart action
437        {
438            async fn get_exchange_volume_chart<S: Send + Sync + Clone + 'static>(
439                params: ExchangeVolumeChartParams,
440                _send_state: serde_json::Value,
441                _state: AgentState<S>,
442            ) -> Result<String, String> {
443                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
444                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
445                })?;
446
447                let client = CoinGeckoProClient::new(api_key);
448                client
449                    .get_exchange_volume_chart(params.id, params.days)
450                    .await
451            }
452
453            let action = ActionBuilder::<_, _, _, _>::new(
454                "get_exchange_volume_chart",
455                get_exchange_volume_chart,
456                None,
457            )
458            .description("Get volume chart data for a given exchange")
459            .parameter("id", "Exchange id", "string", true)
460            .parameter("days", "Data up to number of days ago", "integer", true)
461            .build();
462
463            actions.push(Arc::new(action));
464        }
465
466        // Add coins list action
467        {
468            async fn get_coins_list<S: Send + Sync + Clone + 'static>(
469                params: CoinsListParams,
470                _send_state: serde_json::Value,
471                _state: AgentState<S>,
472            ) -> Result<String, String> {
473                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
474                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
475                })?;
476
477                let client = CoinGeckoProClient::new(api_key);
478                client.get_coins_list(params.include_platform).await
479            }
480
481            let action = ActionBuilder::<_, _, _, _>::new("get_coins_list", get_coins_list, None)
482                .description("List all supported coins with id and name")
483                .parameter(
484                    "include_platform",
485                    "Include platform contract addresses",
486                    "boolean",
487                    false,
488                )
489                .build();
490
491            actions.push(Arc::new(action));
492        }
493
494        // Add coin tickers action
495        {
496            async fn get_coin_tickers<S: Send + Sync + Clone + 'static>(
497                params: CoinTickersParams,
498                _send_state: serde_json::Value,
499                _state: AgentState<S>,
500            ) -> Result<String, String> {
501                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
502                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
503                })?;
504
505                let client = CoinGeckoProClient::new(api_key);
506                client
507                    .get_coin_tickers(
508                        params.id,
509                        params.exchange_ids,
510                        params.include_exchange_logo,
511                        params.page,
512                        params.order,
513                        params.depth,
514                    )
515                    .await
516            }
517
518            let action =
519                ActionBuilder::<_, _, _, _>::new("get_coin_tickers", get_coin_tickers, None)
520                    .description("Get coin tickers (paginated to 100 items)")
521                    .parameter("id", "The coin id", "string", true)
522                    .parameter(
523                        "exchange_ids",
524                        "Filter results by exchange ids",
525                        "array",
526                        false,
527                    )
528                    .parameter(
529                        "include_exchange_logo",
530                        "Include exchange logo",
531                        "boolean",
532                        false,
533                    )
534                    .parameter("page", "Page through results", "integer", false)
535                    .parameter("order", "Sort results by order", "string", false)
536                    .parameter("depth", "Include 2% orderbook depth", "boolean", false)
537                    .build();
538
539            actions.push(Arc::new(action));
540        }
541
542        // Add coin history action
543        {
544            async fn get_coin_history<S: Send + Sync + Clone + 'static>(
545                params: CoinHistoryParams,
546                _send_state: serde_json::Value,
547                _state: AgentState<S>,
548            ) -> Result<String, String> {
549                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
550                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
551                })?;
552
553                let client = CoinGeckoProClient::new(api_key);
554                client
555                    .get_coin_history(params.id, params.date, params.localization)
556                    .await
557            }
558
559            let action = ActionBuilder::<_, _, _, _>::new(
560                "get_coin_history",
561                get_coin_history,
562                None,
563            )
564            .description(
565                "Get historical data (name, price, market, stats) at a given date for a coin",
566            )
567            .parameter("id", "The coin id", "string", true)
568            .parameter(
569                "date",
570                "The date of data snapshot in dd-mm-yyyy",
571                "string",
572                true,
573            )
574            .parameter(
575                "localization",
576                "Set to false to exclude localized languages",
577                "boolean",
578                false,
579            )
580            .build();
581
582            actions.push(Arc::new(action));
583        }
584
585        // Add coin OHLC action
586        {
587            async fn get_coin_ohlc<S: Send + Sync + Clone + 'static>(
588                params: CoinOhlcParams,
589                _send_state: serde_json::Value,
590                _state: AgentState<S>,
591            ) -> Result<String, String> {
592                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
593                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
594                })?;
595
596                let client = CoinGeckoProClient::new(api_key);
597                client
598                    .get_coin_ohlc(params.id, params.vs_currency, params.days)
599                    .await
600            }
601
602            let action = ActionBuilder::<_, _, _, _>::new("get_coin_ohlc", get_coin_ohlc, None)
603                .description("Get coin's OHLC (Open, High, Low, Close) data")
604                .parameter("id", "The coin id", "string", true)
605                .parameter(
606                    "vs_currency",
607                    "The target currency of market data (usd, eur, jpy, etc.)",
608                    "string",
609                    true,
610                )
611                .parameter("days", "Data up to number of days ago", "string", true)
612                .build();
613
614            actions.push(Arc::new(action));
615        }
616
617        // Add coin contract action
618        {
619            async fn get_coin_contract<S: Send + Sync + Clone + 'static>(
620                params: CoinContractParams,
621                _send_state: serde_json::Value,
622                _state: AgentState<S>,
623            ) -> Result<String, String> {
624                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
625                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
626                })?;
627
628                let client = CoinGeckoProClient::new(api_key);
629                client
630                    .get_coin_contract(params.id, params.contract_address)
631                    .await
632            }
633
634            let action =
635                ActionBuilder::<_, _, _, _>::new("get_coin_contract", get_coin_contract, None)
636                    .description("Get coin info from contract address")
637                    .parameter("id", "Asset platform (e.g. ethereum)", "string", true)
638                    .parameter(
639                        "contract_address",
640                        "Token's contract address",
641                        "string",
642                        true,
643                    )
644                    .build();
645
646            actions.push(Arc::new(action));
647        }
648
649        // Add coin contract market chart action
650        {
651            async fn get_coin_contract_market_chart<S: Send + Sync + Clone + 'static>(
652                params: CoinContractMarketChartParams,
653                _send_state: serde_json::Value,
654                _state: AgentState<S>,
655            ) -> Result<String, String> {
656                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
657                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
658                })?;
659
660                let client = CoinGeckoProClient::new(api_key);
661                client
662                    .get_coin_contract_market_chart(
663                        params.id,
664                        params.contract_address,
665                        params.vs_currency,
666                        params.days,
667                    )
668                    .await
669            }
670
671            let action = ActionBuilder::<_, _, _, _>::new(
672                "get_coin_contract_market_chart",
673                get_coin_contract_market_chart,
674                None,
675            )
676            .description("Get historical market data for a contract address")
677            .parameter("id", "The platform id (e.g. ethereum)", "string", true)
678            .parameter(
679                "contract_address",
680                "Token's contract address",
681                "string",
682                true,
683            )
684            .parameter(
685                "vs_currency",
686                "The target currency (e.g. usd)",
687                "string",
688                true,
689            )
690            .parameter("days", "Data up to number of days ago", "string", true)
691            .build();
692
693            actions.push(Arc::new(action));
694        }
695
696        // Add asset platforms action
697        {
698            async fn get_asset_platforms<S: Send + Sync + Clone + 'static>(
699                _params: AssetPlatformsParams,
700                _send_state: serde_json::Value,
701                _state: AgentState<S>,
702            ) -> Result<String, String> {
703                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
704                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
705                })?;
706
707                let client = CoinGeckoProClient::new(api_key);
708                client.get_asset_platforms().await
709            }
710
711            let action =
712                ActionBuilder::<_, _, _, _>::new("get_asset_platforms", get_asset_platforms, None)
713                    .description("List all asset platforms (blockchain networks)")
714                    .build();
715
716            actions.push(Arc::new(action));
717        }
718
719        // Add coins categories list action
720        {
721            async fn get_coins_categories_list<S: Send + Sync + Clone + 'static>(
722                _params: CoinsCategoriesListParams,
723                _send_state: serde_json::Value,
724                _state: AgentState<S>,
725            ) -> Result<String, String> {
726                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
727                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
728                })?;
729
730                let client = CoinGeckoProClient::new(api_key);
731                client.get_coins_categories_list().await
732            }
733
734            let action = ActionBuilder::<_, _, _, _>::new(
735                "get_coins_categories_list",
736                get_coins_categories_list,
737                None,
738            )
739            .description("List all categories")
740            .build();
741
742            actions.push(Arc::new(action));
743        }
744
745        // Add coins categories action
746        {
747            async fn get_coins_categories<S: Send + Sync + Clone + 'static>(
748                params: CoinsCategoriesParams,
749                _send_state: serde_json::Value,
750                _state: AgentState<S>,
751            ) -> Result<String, String> {
752                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
753                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
754                })?;
755
756                let client = CoinGeckoProClient::new(api_key);
757                client.get_coins_categories(params.order).await
758            }
759
760            let action = ActionBuilder::<_, _, _, _>::new(
761                "get_coins_categories",
762                get_coins_categories,
763                None,
764            )
765            .description("List all categories with market data")
766            .parameter("order", "Sort by market_cap or name", "string", false)
767            .build();
768
769            actions.push(Arc::new(action));
770        }
771
772        // Add indexes action
773        {
774            async fn get_indexes<S: Send + Sync + Clone + 'static>(
775                _params: IndexesParams,
776                _send_state: serde_json::Value,
777                _state: AgentState<S>,
778            ) -> Result<String, String> {
779                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
780                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
781                })?;
782
783                let client = CoinGeckoProClient::new(api_key);
784                client.get_indexes().await
785            }
786
787            let action = ActionBuilder::<_, _, _, _>::new("get_indexes", get_indexes, None)
788                .description("List all market indexes")
789                .build();
790
791            actions.push(Arc::new(action));
792        }
793
794        // Add indexes list action
795        {
796            async fn get_indexes_list<S: Send + Sync + Clone + 'static>(
797                _params: IndexesListParams,
798                _send_state: serde_json::Value,
799                _state: AgentState<S>,
800            ) -> Result<String, String> {
801                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
802                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
803                })?;
804
805                let client = CoinGeckoProClient::new(api_key);
806                client.get_indexes_list().await
807            }
808
809            let action =
810                ActionBuilder::<_, _, _, _>::new("get_indexes_list", get_indexes_list, None)
811                    .description("List market indexes id and name")
812                    .build();
813
814            actions.push(Arc::new(action));
815        }
816
817        // Add derivatives action
818        {
819            async fn get_derivatives<S: Send + Sync + Clone + 'static>(
820                _params: DerivativesParams,
821                _send_state: serde_json::Value,
822                _state: AgentState<S>,
823            ) -> Result<String, String> {
824                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
825                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
826                })?;
827
828                let client = CoinGeckoProClient::new(api_key);
829                client.get_derivatives().await
830            }
831
832            let action = ActionBuilder::<_, _, _, _>::new("get_derivatives", get_derivatives, None)
833                .description("List all derivative tickers")
834                .build();
835
836            actions.push(Arc::new(action));
837        }
838
839        // Add derivatives exchanges action
840        {
841            async fn get_derivatives_exchanges<S: Send + Sync + Clone + 'static>(
842                params: DerivativesExchangesParams,
843                _send_state: serde_json::Value,
844                _state: AgentState<S>,
845            ) -> Result<String, String> {
846                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
847                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
848                })?;
849
850                let client = CoinGeckoProClient::new(api_key);
851                client
852                    .get_derivatives_exchanges(params.order, params.per_page, params.page)
853                    .await
854            }
855
856            let action = ActionBuilder::<_, _, _, _>::new(
857                "get_derivatives_exchanges",
858                get_derivatives_exchanges,
859                None,
860            )
861            .description("List all derivative exchanges")
862            .parameter("order", "Order results by specified field", "string", false)
863            .parameter("per_page", "Total results per page", "integer", false)
864            .parameter("page", "Page through results", "integer", false)
865            .build();
866
867            actions.push(Arc::new(action));
868        }
869
870        // Add derivatives exchange action
871        {
872            async fn get_derivatives_exchange<S: Send + Sync + Clone + 'static>(
873                params: DerivativesExchangeParams,
874                _send_state: serde_json::Value,
875                _state: AgentState<S>,
876            ) -> Result<String, String> {
877                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
878                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
879                })?;
880
881                let client = CoinGeckoProClient::new(api_key);
882                client
883                    .get_derivatives_exchange(params.id, params.include_tickers)
884                    .await
885            }
886
887            let action = ActionBuilder::<_, _, _, _>::new(
888                "get_derivatives_exchange",
889                get_derivatives_exchange,
890                None,
891            )
892            .description("Show derivative exchange data")
893            .parameter("id", "The exchange id", "string", true)
894            .parameter(
895                "include_tickers",
896                "Include tickers data (all/unexpired)",
897                "string",
898                false,
899            )
900            .build();
901
902            actions.push(Arc::new(action));
903        }
904
905        // Add exchange rates action
906        {
907            async fn get_exchange_rates<S: Send + Sync + Clone + 'static>(
908                _params: ExchangeRatesParams,
909                _send_state: serde_json::Value,
910                _state: AgentState<S>,
911            ) -> Result<String, String> {
912                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
913                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
914                })?;
915
916                let client = CoinGeckoProClient::new(api_key);
917                client.get_exchange_rates().await
918            }
919
920            let action =
921                ActionBuilder::<_, _, _, _>::new("get_exchange_rates", get_exchange_rates, None)
922                    .description("Get BTC-to-Currency exchange rates")
923                    .build();
924
925            actions.push(Arc::new(action));
926        }
927
928        // Add search action
929        {
930            async fn search<S: Send + Sync + Clone + 'static>(
931                params: SearchParams,
932                _send_state: serde_json::Value,
933                _state: AgentState<S>,
934            ) -> Result<String, String> {
935                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
936                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
937                })?;
938
939                let client = CoinGeckoProClient::new(api_key);
940                client.search(params.query).await
941            }
942
943            let action = ActionBuilder::<_, _, _, _>::new("search", search, None)
944                .description("Search for coins, categories and markets")
945                .parameter("query", "Search string", "string", true)
946                .build();
947
948            actions.push(Arc::new(action));
949        }
950
951        // Add trending action
952        {
953            async fn get_trending<S: Send + Sync + Clone + 'static>(
954                _params: TrendingParams,
955                _send_state: serde_json::Value,
956                _state: AgentState<S>,
957            ) -> Result<String, String> {
958                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
959                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
960                })?;
961
962                let client = CoinGeckoProClient::new(api_key);
963                client.get_trending().await
964            }
965
966            let action = ActionBuilder::<_, _, _, _>::new("get_trending", get_trending, None)
967                .description("Get trending search coins (Top-7) on CoinGecko")
968                .build();
969
970            actions.push(Arc::new(action));
971        }
972
973        // Add companies public treasury action
974        {
975            async fn get_companies_public_treasury<S: Send + Sync + Clone + 'static>(
976                params: CompaniesPublicTreasuryParams,
977                _send_state: serde_json::Value,
978                _state: AgentState<S>,
979            ) -> Result<String, String> {
980                let api_key = std::env::var("COINGECKO_PRO_API_KEY").map_err(|_| {
981                    "COINGECKO_PRO_API_KEY environment variable not set".to_string()
982                })?;
983
984                let client = CoinGeckoProClient::new(api_key);
985                client.get_companies_public_treasury(params.coin_id).await
986            }
987
988            let action = ActionBuilder::<_, _, _, _>::new(
989                "get_companies_public_treasury",
990                get_companies_public_treasury,
991                None,
992            )
993            .description("Get public companies bitcoin or ethereum holdings")
994            .parameter(
995                "coin_id",
996                "The coin id (bitcoin or ethereum)",
997                "string",
998                true,
999            )
1000            .build();
1001
1002            actions.push(Arc::new(action));
1003        }
1004
1005        Self { actions }
1006    }
1007
1008    pub fn actions(&self) -> &[Arc<FunctionAction<S>>] {
1009        &self.actions
1010    }
1011}
1012
1013// Add helper function to calculate from timestamp based on days
1014fn calculate_from_timestamp(to: u64, days: &str) -> Result<u64, String> {
1015    let duration = match days {
1016        "1" => chrono::Duration::days(1),
1017        "7" => chrono::Duration::days(7),
1018        "14" => chrono::Duration::days(14),
1019        "30" => chrono::Duration::days(30),
1020        "90" => chrono::Duration::days(90),
1021        "180" => chrono::Duration::days(180),
1022        "365" => chrono::Duration::days(365),
1023        "max" => chrono::Duration::days(365 * 8), // Maximum historical data
1024        _ => {
1025            return Err(
1026                "Invalid days parameter. Use: 1, 7, 14, 30, 90, 180, 365, or max".to_string(),
1027            )
1028        }
1029    };
1030
1031    let from = to
1032        .checked_sub(duration.num_seconds() as u64)
1033        .ok_or("Error calculating from timestamp")?;
1034
1035    Ok(from)
1036}