ferrox_actions/
birdeye.rs

1pub mod client;
2
3use crate::{
4    action::{ActionBuilder, ActionGroup, FunctionAction},
5    AgentState,
6};
7use client::BirdeyeClient;
8use serde::Deserialize;
9use std::sync::Arc;
10
11// Parameter structs for each action
12#[derive(Debug, Deserialize)]
13pub struct TokenPriceParams {
14    address: String,
15}
16
17#[derive(Debug, Deserialize)]
18pub struct TokenPriceHistoryParams {
19    address: String,
20    resolution: String,
21    limit: Option<i32>,
22}
23
24#[derive(Debug, Deserialize)]
25pub struct MultiTokenPriceParams {
26    addresses: String, // Comma-separated list of addresses
27}
28
29#[derive(Debug, Deserialize)]
30pub struct TokenOhlcvParams {
31    address: String,
32    resolution: String, // "1" | "3" | "5" | "15" | "30" | "60" | "120" | "240" | "360" | "480" | "720" | "1D" | "3D" | "1W" | "1M"
33}
34
35#[derive(Debug, Deserialize)]
36pub struct PairOhlcvParams {
37    pair_address: String,
38    resolution: String,
39}
40
41#[derive(Debug, Deserialize)]
42pub struct TokenTradesParams {
43    address: String,
44    limit: Option<i32>,
45    offset: Option<i32>,
46}
47
48#[derive(Debug, Deserialize)]
49pub struct PairTradesParams {
50    pair_address: String,
51    limit: Option<i32>,
52    offset: Option<i32>,
53}
54
55#[derive(Debug, Deserialize)]
56pub struct TokenOverviewParams {
57    address: String,
58}
59
60#[derive(Debug, Deserialize)]
61pub struct TokenListParams {
62    limit: Option<i32>,
63    offset: Option<i32>,
64}
65
66#[derive(Debug, Deserialize)]
67pub struct TokenSecurityParams {
68    address: String,
69}
70
71#[derive(Debug, Deserialize)]
72pub struct TokenMarketListParams {
73    address: String,
74}
75
76#[derive(Debug, Deserialize)]
77pub struct TokenNewListingParams {
78    limit: Option<i32>,
79    offset: Option<i32>,
80}
81
82#[derive(Debug, Deserialize)]
83pub struct TokenTopTradersParams {
84    address: String,
85    limit: Option<i32>,
86}
87
88#[derive(Debug, Deserialize)]
89pub struct TokenTrendingParams {
90    limit: Option<i32>,
91}
92
93#[derive(Debug, Deserialize)]
94pub struct GainersLosersParams {}
95
96#[derive(Debug, Deserialize)]
97pub struct TraderTxsByTimeParams {
98    address: String,
99    time_from: i64,
100    time_to: i64,
101    limit: Option<i32>,
102}
103
104#[derive(Debug, Deserialize)]
105pub struct SupportedChainsParams {}
106
107#[derive(Debug, Deserialize)]
108pub struct WalletPortfolioParams {
109    wallet_address: String,
110    chain_id: String,
111}
112
113#[derive(Debug, Deserialize)]
114pub struct WalletPortfolioMultichainParams {
115    wallet_address: String,
116}
117
118#[derive(Debug, Deserialize)]
119pub struct WalletTransactionHistoryParams {
120    wallet_address: String,
121    chain_id: String,
122    limit: Option<i32>,
123    offset: Option<i32>,
124}
125
126#[derive(Debug, Deserialize)]
127pub struct WalletTransactionHistoryMultichainParams {
128    wallet_address: String,
129    limit: Option<i32>,
130    offset: Option<i32>,
131}
132
133#[derive(Debug, Deserialize)]
134pub struct SimulateTransactionParams {
135    chain_id: String,
136    tx_data: String,
137}
138
139// Action group that contains all Birdeye actions
140pub struct BirdeyeActionGroup<S: Send + Sync + Clone + 'static> {
141    actions: Vec<Arc<FunctionAction<S>>>,
142}
143
144impl<S: Send + Sync + Clone + 'static> ActionGroup<S> for BirdeyeActionGroup<S> {
145    fn actions(&self) -> &[Arc<FunctionAction<S>>] {
146        &self.actions
147    }
148}
149
150impl<S: Send + Sync + Clone + 'static> BirdeyeActionGroup<S> {
151    pub fn new() -> Self {
152        let mut actions = Vec::new();
153
154        // Add token price action
155        {
156            async fn get_token_price<S: Send + Sync + Clone + 'static>(
157                params: TokenPriceParams,
158                _send_state: serde_json::Value,
159                _state: AgentState<S>,
160            ) -> Result<String, String> {
161                let api_key = std::env::var("BIRDEYE_API_KEY")
162                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
163                let client = BirdeyeClient::new(api_key);
164                client.get_token_price(params.address).await
165            }
166
167            let action = ActionBuilder::<_, _, _, _>::new("get_token_price", get_token_price, None)
168                .description("Get real-time price data for a token")
169                .parameter("address", "Token address", "string", true)
170                .build();
171
172            actions.push(Arc::new(action));
173        }
174
175        // Add token price history action
176        {
177            async fn get_token_price_history<S: Send + Sync + Clone + 'static>(
178                params: TokenPriceHistoryParams,
179                _send_state: serde_json::Value,
180                _state: AgentState<S>,
181            ) -> Result<String, String> {
182                let api_key = std::env::var("BIRDEYE_API_KEY")
183                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
184                let client = BirdeyeClient::new(api_key);
185
186                let time_to = chrono::Utc::now().timestamp();
187                let time_from = calculate_time_from(time_to, &params.resolution)?;
188
189                client
190                    .get_token_price_history(
191                        params.address,
192                        params.resolution,
193                        Some(time_from),
194                        Some(time_to),
195                        params.limit,
196                    )
197                    .await
198            }
199
200            let action = ActionBuilder::<_, _, _, _>::new(
201                "get_token_price_history",
202                get_token_price_history,
203                None,
204            )
205            .description("Get historical price data for a token")
206            .parameter("address", "Token address", "string", true)
207            .parameter(
208                "resolution",
209                "Time resolution (1, 3, 5, 15, 30, 60, 120, 240, 360, 480, 720, 1D, 3D, 1W, 1M)",
210                "string",
211                true,
212            )
213            .parameter("limit", "Number of records to return", "integer", false)
214            .build();
215
216            actions.push(Arc::new(action));
217        }
218
219        // Continue with more actions...
220        // Add multi token price action
221        {
222            async fn get_multi_token_price<S: Send + Sync + Clone + 'static>(
223                params: MultiTokenPriceParams,
224                _send_state: serde_json::Value,
225                _state: AgentState<S>,
226            ) -> Result<String, String> {
227                let api_key = std::env::var("BIRDEYE_API_KEY")
228                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
229                let client = BirdeyeClient::new(api_key);
230                client.get_multi_token_price(params.addresses).await
231            }
232
233            let action = ActionBuilder::<_, _, _, _>::new(
234                "get_multi_token_price",
235                get_multi_token_price,
236                None,
237            )
238            .description("Get price data for multiple tokens")
239            .parameter(
240                "addresses",
241                "Comma-separated list of token addresses",
242                "string",
243                true,
244            )
245            .build();
246
247            actions.push(Arc::new(action));
248        }
249
250        // Add token trending action
251        {
252            async fn get_token_trending<S: Send + Sync + Clone + 'static>(
253                params: TokenTrendingParams,
254                _send_state: serde_json::Value,
255                _state: AgentState<S>,
256            ) -> Result<String, String> {
257                let api_key = std::env::var("BIRDEYE_API_KEY")
258                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
259                let client = BirdeyeClient::new(api_key);
260                client.get_token_trending(params.limit).await
261            }
262
263            let action =
264                ActionBuilder::<_, _, _, _>::new("get_token_trending", get_token_trending, None)
265                    .description("Get trending tokens")
266                    .parameter("limit", "Number of tokens to return", "integer", false)
267                    .build();
268
269            actions.push(Arc::new(action));
270        }
271
272        // Add token OHLCV action
273        {
274            async fn get_token_ohlcv<S: Send + Sync + Clone + 'static>(
275                params: TokenOhlcvParams,
276                _send_state: serde_json::Value,
277                _state: AgentState<S>,
278            ) -> Result<String, String> {
279                let api_key = std::env::var("BIRDEYE_API_KEY")
280                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
281                let client = BirdeyeClient::new(api_key);
282
283                let time_to = chrono::Utc::now().timestamp();
284                let time_from = calculate_time_from(time_to, &params.resolution)?;
285
286                client
287                    .get_token_ohlcv(params.address, params.resolution, time_from, time_to)
288                    .await
289            }
290
291            let action = ActionBuilder::<_, _, _, _>::new(
292                "get_token_ohlcv",
293                get_token_ohlcv,
294                None,
295            )
296            .description("Get OHLCV data for a token (only solana tokens). Do not use if it is an ethereum token")
297            .parameter("address", "Token address", "string", true)
298            .parameter(
299                "resolution",
300                "Time resolution (1, 3, 5, 15, 30, 60, 120, 240, 360, 480, 720, 1D, 3D, 1W, 1M)",
301                "string",
302                true,
303            )
304            .build();
305
306            actions.push(Arc::new(action));
307        }
308
309        // Add pair OHLCV action
310        {
311            async fn get_pair_ohlcv<S: Send + Sync + Clone + 'static>(
312                params: PairOhlcvParams,
313                _send_state: serde_json::Value,
314                _state: AgentState<S>,
315            ) -> Result<String, String> {
316                let api_key = std::env::var("BIRDEYE_API_KEY")
317                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
318                let client = BirdeyeClient::new(api_key);
319
320                let time_to = chrono::Utc::now().timestamp();
321                let time_from = calculate_time_from(time_to, &params.resolution)?;
322
323                client
324                    .get_pair_ohlcv(params.pair_address, params.resolution, time_from, time_to)
325                    .await
326            }
327
328            let action = ActionBuilder::<_, _, _, _>::new(
329                "get_pair_ohlcv",
330                get_pair_ohlcv,
331                None,
332            )
333            .description("Get OHLCV data for a trading pair")
334            .parameter("pair_address", "Pair address", "string", true)
335            .parameter(
336                "resolution",
337                "Time resolution (1, 3, 5, 15, 30, 60, 120, 240, 360, 480, 720, 1D, 3D, 1W, 1M)",
338                "string",
339                true,
340            )
341            .build();
342
343            actions.push(Arc::new(action));
344        }
345
346        // Add token trades action
347        {
348            async fn get_token_trades<S: Send + Sync + Clone + 'static>(
349                params: TokenTradesParams,
350                _send_state: serde_json::Value,
351                _state: AgentState<S>,
352            ) -> Result<String, String> {
353                let api_key = std::env::var("BIRDEYE_API_KEY")
354                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
355                let client = BirdeyeClient::new(api_key);
356                client
357                    .get_token_trades(params.address, params.limit, params.offset)
358                    .await
359            }
360
361            let action =
362                ActionBuilder::<_, _, _, _>::new("get_token_trades", get_token_trades, None)
363                    .description("Get recent trades for a token")
364                    .parameter("address", "Token address", "string", true)
365                    .parameter("limit", "Number of trades to return", "integer", false)
366                    .parameter("offset", "Number of trades to skip", "integer", false)
367                    .build();
368
369            actions.push(Arc::new(action));
370        }
371
372        // Add pair trades action
373        {
374            async fn get_pair_trades<S: Send + Sync + Clone + 'static>(
375                params: PairTradesParams,
376                _send_state: serde_json::Value,
377                _state: AgentState<S>,
378            ) -> Result<String, String> {
379                let api_key = std::env::var("BIRDEYE_API_KEY")
380                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
381                let client = BirdeyeClient::new(api_key);
382                client
383                    .get_pair_trades(params.pair_address, params.limit, params.offset)
384                    .await
385            }
386
387            let action = ActionBuilder::<_, _, _, _>::new("get_pair_trades", get_pair_trades, None)
388                .description("Get recent trades for a trading pair")
389                .parameter("pair_address", "Pair address", "string", true)
390                .parameter("limit", "Number of trades to return", "integer", false)
391                .parameter("offset", "Number of trades to skip", "integer", false)
392                .build();
393
394            actions.push(Arc::new(action));
395        }
396
397        // Add token overview action
398        {
399            async fn get_token_overview<S: Send + Sync + Clone + 'static>(
400                params: TokenOverviewParams,
401                _send_state: serde_json::Value,
402                _state: AgentState<S>,
403            ) -> Result<String, String> {
404                let api_key = std::env::var("BIRDEYE_API_KEY")
405                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
406                let client = BirdeyeClient::new(api_key);
407                client.get_token_overview(params.address).await
408            }
409
410            let action =
411                ActionBuilder::<_, _, _, _>::new("get_token_overview", get_token_overview, None)
412                    .description("Get comprehensive overview data for a token")
413                    .parameter("address", "Token address", "string", true)
414                    .build();
415
416            actions.push(Arc::new(action));
417        }
418
419        // Add token list action
420        {
421            async fn get_token_list<S: Send + Sync + Clone + 'static>(
422                params: TokenListParams,
423                _send_state: serde_json::Value,
424                _state: AgentState<S>,
425            ) -> Result<String, String> {
426                let api_key = std::env::var("BIRDEYE_API_KEY")
427                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
428                let client = BirdeyeClient::new(api_key);
429                client.get_token_list(params.limit, params.offset).await
430            }
431
432            let action = ActionBuilder::<_, _, _, _>::new("get_token_list", get_token_list, None)
433                .description("Get list of tokens with market data")
434                .parameter("limit", "Number of tokens to return", "integer", false)
435                .parameter("offset", "Number of tokens to skip", "integer", false)
436                .build();
437
438            actions.push(Arc::new(action));
439        }
440
441        // Add token security action
442        {
443            async fn get_token_security<S: Send + Sync + Clone + 'static>(
444                params: TokenSecurityParams,
445                _send_state: serde_json::Value,
446                _state: AgentState<S>,
447            ) -> Result<String, String> {
448                let api_key = std::env::var("BIRDEYE_API_KEY")
449                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
450                let client = BirdeyeClient::new(api_key);
451                client.get_token_security(params.address).await
452            }
453
454            let action =
455                ActionBuilder::<_, _, _, _>::new("get_token_security", get_token_security, None)
456                    .description("Get security information for a token")
457                    .parameter("address", "Token address", "string", true)
458                    .build();
459
460            actions.push(Arc::new(action));
461        }
462
463        // Add token market list action
464        {
465            async fn get_token_market_list<S: Send + Sync + Clone + 'static>(
466                params: TokenMarketListParams,
467                _send_state: serde_json::Value,
468                _state: AgentState<S>,
469            ) -> Result<String, String> {
470                let api_key = std::env::var("BIRDEYE_API_KEY")
471                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
472                let client = BirdeyeClient::new(api_key);
473                client.get_token_market_list(params.address).await
474            }
475
476            let action = ActionBuilder::<_, _, _, _>::new(
477                "get_token_market_list",
478                get_token_market_list,
479                None,
480            )
481            .description("Get list of markets where a token is traded")
482            .parameter("address", "Token address", "string", true)
483            .build();
484
485            actions.push(Arc::new(action));
486        }
487
488        // Add token new listing action
489        {
490            async fn get_token_new_listing<S: Send + Sync + Clone + 'static>(
491                params: TokenNewListingParams,
492                _send_state: serde_json::Value,
493                _state: AgentState<S>,
494            ) -> Result<String, String> {
495                let api_key = std::env::var("BIRDEYE_API_KEY")
496                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
497                let client = BirdeyeClient::new(api_key);
498                client
499                    .get_token_new_listing(params.limit, params.offset)
500                    .await
501            }
502
503            let action = ActionBuilder::<_, _, _, _>::new(
504                "get_token_new_listing",
505                get_token_new_listing,
506                None,
507            )
508            .description("Get list of newly listed tokens")
509            .parameter("limit", "Number of tokens to return", "integer", false)
510            .parameter("offset", "Number of tokens to skip", "integer", false)
511            .build();
512
513            actions.push(Arc::new(action));
514        }
515
516        // Add token top traders action
517        {
518            async fn get_token_top_traders<S: Send + Sync + Clone + 'static>(
519                params: TokenTopTradersParams,
520                _send_state: serde_json::Value,
521                _state: AgentState<S>,
522            ) -> Result<String, String> {
523                let api_key = std::env::var("BIRDEYE_API_KEY")
524                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
525                let client = BirdeyeClient::new(api_key);
526                client
527                    .get_token_top_traders(params.address, params.limit)
528                    .await
529            }
530
531            let action = ActionBuilder::<_, _, _, _>::new(
532                "get_token_top_traders",
533                get_token_top_traders,
534                None,
535            )
536            .description("Get top traders for a token")
537            .parameter("address", "Token address", "string", true)
538            .parameter("limit", "Number of traders to return", "integer", false)
539            .build();
540
541            actions.push(Arc::new(action));
542        }
543
544        // Add gainers/losers action
545        {
546            async fn get_gainers_losers<S: Send + Sync + Clone + 'static>(
547                _params: GainersLosersParams,
548                _send_state: serde_json::Value,
549                _state: AgentState<S>,
550            ) -> Result<String, String> {
551                let api_key = std::env::var("BIRDEYE_API_KEY")
552                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
553                let client = BirdeyeClient::new(api_key);
554                client.get_gainers_losers().await
555            }
556
557            let action =
558                ActionBuilder::<_, _, _, _>::new("get_gainers_losers", get_gainers_losers, None)
559                    .description("Get gainers and losers data")
560                    .build();
561
562            actions.push(Arc::new(action));
563        }
564
565        // Add trader transactions by time action
566        {
567            async fn get_trader_txs_by_time<S: Send + Sync + Clone + 'static>(
568                params: TraderTxsByTimeParams,
569                _send_state: serde_json::Value,
570                _state: AgentState<S>,
571            ) -> Result<String, String> {
572                let api_key = std::env::var("BIRDEYE_API_KEY")
573                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
574                let client = BirdeyeClient::new(api_key);
575                client
576                    .get_trader_txs_by_time(
577                        params.address,
578                        params.time_from,
579                        params.time_to,
580                        params.limit,
581                    )
582                    .await
583            }
584
585            let action = ActionBuilder::<_, _, _, _>::new(
586                "get_trader_txs_by_time",
587                get_trader_txs_by_time,
588                None,
589            )
590            .description("Get trader transactions within a time range")
591            .parameter("address", "Token address", "string", true)
592            .parameter("time_from", "Start timestamp (Unix)", "integer", true)
593            .parameter("time_to", "End timestamp (Unix)", "integer", true)
594            .parameter(
595                "limit",
596                "Number of transactions to return",
597                "integer",
598                false,
599            )
600            .build();
601
602            actions.push(Arc::new(action));
603        }
604
605        // Add supported chains action
606        {
607            async fn list_supported_chains<S: Send + Sync + Clone + 'static>(
608                _params: SupportedChainsParams,
609                _send_state: serde_json::Value,
610                _state: AgentState<S>,
611            ) -> Result<String, String> {
612                let api_key = std::env::var("BIRDEYE_API_KEY")
613                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
614                let client = BirdeyeClient::new(api_key);
615                client.list_supported_chains().await
616            }
617
618            let action = ActionBuilder::<_, _, _, _>::new(
619                "list_supported_chains",
620                list_supported_chains,
621                None,
622            )
623            .description("List supported blockchain networks")
624            .build();
625
626            actions.push(Arc::new(action));
627        }
628
629        // Add wallet portfolio action
630        {
631            async fn get_wallet_portfolio<S: Send + Sync + Clone + 'static>(
632                params: WalletPortfolioParams,
633                _send_state: serde_json::Value,
634                _state: AgentState<S>,
635            ) -> Result<String, String> {
636                let api_key = std::env::var("BIRDEYE_API_KEY")
637                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
638                let client = BirdeyeClient::new(api_key);
639                client
640                    .get_wallet_portfolio(params.wallet_address, params.chain_id)
641                    .await
642            }
643
644            let action = ActionBuilder::<_, _, _, _>::new(
645                "get_wallet_portfolio",
646                get_wallet_portfolio,
647                None,
648            )
649            .description("Get wallet portfolio for a specific chain")
650            .parameter("wallet_address", "Wallet address", "string", true)
651            .parameter("chain_id", "Chain ID", "string", true)
652            .build();
653
654            actions.push(Arc::new(action));
655        }
656
657        // Add multichain wallet portfolio action
658        {
659            async fn get_wallet_portfolio_multichain<S: Send + Sync + Clone + 'static>(
660                params: WalletPortfolioMultichainParams,
661                _send_state: serde_json::Value,
662                _state: AgentState<S>,
663            ) -> Result<String, String> {
664                let api_key = std::env::var("BIRDEYE_API_KEY")
665                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
666                let client = BirdeyeClient::new(api_key);
667                client
668                    .get_wallet_portfolio_multichain(params.wallet_address)
669                    .await
670            }
671
672            let action = ActionBuilder::<_, _, _, _>::new(
673                "get_wallet_portfolio_multichain",
674                get_wallet_portfolio_multichain,
675                None,
676            )
677            .description("Get wallet portfolio across all chains")
678            .parameter("wallet_address", "Wallet address", "string", true)
679            .build();
680
681            actions.push(Arc::new(action));
682        }
683
684        // Add wallet transaction history action
685        {
686            async fn get_wallet_transaction_history<S: Send + Sync + Clone + 'static>(
687                params: WalletTransactionHistoryParams,
688                _send_state: serde_json::Value,
689                _state: AgentState<S>,
690            ) -> Result<String, String> {
691                let api_key = std::env::var("BIRDEYE_API_KEY")
692                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
693                let client = BirdeyeClient::new(api_key);
694                client
695                    .get_wallet_transaction_history(
696                        params.wallet_address,
697                        params.chain_id,
698                        params.limit,
699                        params.offset,
700                    )
701                    .await
702            }
703
704            let action = ActionBuilder::<_, _, _, _>::new(
705                "get_wallet_transaction_history",
706                get_wallet_transaction_history,
707                None,
708            )
709            .description("Get wallet transaction history for a specific chain")
710            .parameter("wallet_address", "Wallet address", "string", true)
711            .parameter("chain_id", "Chain ID", "string", true)
712            .parameter(
713                "limit",
714                "Number of transactions to return",
715                "integer",
716                false,
717            )
718            .parameter("offset", "Number of transactions to skip", "integer", false)
719            .build();
720
721            actions.push(Arc::new(action));
722        }
723
724        // Add multichain wallet transaction history action
725        {
726            async fn get_wallet_transaction_history_multichain<S: Send + Sync + Clone + 'static>(
727                params: WalletTransactionHistoryMultichainParams,
728                _send_state: serde_json::Value,
729                _state: AgentState<S>,
730            ) -> Result<String, String> {
731                let api_key = std::env::var("BIRDEYE_API_KEY")
732                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
733                let client = BirdeyeClient::new(api_key);
734                client
735                    .get_wallet_transaction_history_multichain(
736                        params.wallet_address,
737                        params.limit,
738                        params.offset,
739                    )
740                    .await
741            }
742
743            let action = ActionBuilder::<_, _, _, _>::new(
744                "get_wallet_transaction_history_multichain",
745                get_wallet_transaction_history_multichain,
746                None,
747            )
748            .description("Get wallet transaction history across all chains")
749            .parameter("wallet_address", "Wallet address", "string", true)
750            .parameter(
751                "limit",
752                "Number of transactions to return",
753                "integer",
754                false,
755            )
756            .parameter("offset", "Number of transactions to skip", "integer", false)
757            .build();
758
759            actions.push(Arc::new(action));
760        }
761
762        // Add transaction simulation action
763        {
764            async fn simulate_transaction<S: Send + Sync + Clone + 'static>(
765                params: SimulateTransactionParams,
766                _send_state: serde_json::Value,
767                _state: AgentState<S>,
768            ) -> Result<String, String> {
769                let api_key = std::env::var("BIRDEYE_API_KEY")
770                    .map_err(|_| "BIRDEYE_API_KEY environment variable not set".to_string())?;
771                let client = BirdeyeClient::new(api_key);
772                client
773                    .simulate_transaction(params.chain_id, params.tx_data)
774                    .await
775            }
776
777            let action = ActionBuilder::<_, _, _, _>::new(
778                "simulate_transaction",
779                simulate_transaction,
780                None,
781            )
782            .description("Simulate a transaction")
783            .parameter("chain_id", "Chain ID", "string", true)
784            .parameter("tx_data", "Transaction data", "string", true)
785            .build();
786
787            actions.push(Arc::new(action));
788        }
789
790        Self { actions }
791    }
792}
793
794// Add helper function to calculate time_from based on resolution
795fn calculate_time_from(time_to: i64, resolution: &str) -> Result<i64, String> {
796    let duration = match resolution {
797        "1" | "3" | "5" | "15" | "30" | "60" => {
798            let minutes: i64 = resolution
799                .parse()
800                .map_err(|_| "Invalid resolution format")?;
801            chrono::Duration::minutes(minutes * 100) // Get 100 data points
802        }
803        "120" | "240" | "360" | "480" | "720" => {
804            let minutes: i64 = resolution
805                .parse()
806                .map_err(|_| "Invalid resolution format")?;
807            chrono::Duration::minutes(minutes * 50) // Get 50 data points
808        }
809        "1D" => chrono::Duration::days(100),
810        "3D" => chrono::Duration::days(300),
811        "1W" => chrono::Duration::weeks(100),
812        "1M" => chrono::Duration::days(100 * 30),
813        _ => return Err("Invalid resolution".to_string()),
814    };
815
816    Ok(time_to - duration.num_seconds())
817}