riglr_solana_tools/
lib.rs

1//! # riglr-solana-tools
2//!
3//! A comprehensive suite of rig-compatible tools for interacting with the Solana blockchain.
4//!
5//! This crate provides ready-to-use tools for building Solana-native AI agents, including:
6//!
7//! - **Balance Tools**: Check SOL and SPL token balances
8//! - **Transaction Tools**: Send SOL and token transfers
9//! - **DeFi Tools**: Interact with Jupiter for swaps and quotes
10//! - **Pump.fun Tools**: Deploy, buy, and sell tokens on Pump.fun
11//! - **Network Tools**: Query blockchain state and transaction details
12//!
13//! All tools are built with the `#[tool]` macro for seamless integration with rig agents
14//! and include comprehensive error handling and retry logic.
15//!
16//! ## Features
17//!
18//! - **Production Ready**: Built-in retry logic, timeouts, and error handling
19//! - **Type Safe**: Full Rust type safety with serde and schemars integration
20//! - **Async First**: Non-blocking operations using tokio
21//! - **Composable**: Mix and match tools as needed for your agent
22//! - **Well Documented**: Every tool includes usage examples
23//!
24//! ## Quick Start
25//!
26//! ```ignore
27//! use riglr_solana_tools::balance::get_sol_balance;
28//! use riglr_core::provider::ApplicationContext;
29//! use riglr_core::{ToolWorker, ExecutionConfig, Job, idempotency::InMemoryIdempotencyStore};
30//! use riglr_config::Config;
31//! use solana_client::rpc_client::RpcClient;
32//! use std::sync::Arc;
33//!
34//! # async fn example() -> anyhow::Result<()> {
35//! // Set up ApplicationContext with Solana RPC client
36//! let config = Config::from_env();
37//! let context = ApplicationContext::from_config(&config);
38//! let solana_client = Arc::new(RpcClient::new("https://api.mainnet-beta.solana.com"));
39//! context.set_extension(solana_client);
40//!
41//! // Create and register tools with worker
42//! let worker = ToolWorker::<InMemoryIdempotencyStore>::new(
43//!     ExecutionConfig::default(),
44//!     context
45//! );
46//!
47//! worker.register_tool(Arc::new(get_sol_balance)).await;
48//!
49//! // Execute the tool
50//! let job = Job::new(
51//!     "get_sol_balance",
52//!     &serde_json::json!({"address": "So11111111111111111111111111111111111111112"}),
53//!     3
54//! )?;
55//!
56//! let result = worker.process_job(job).await?;
57//! println!("Balance result: {:?}", result);
58//! # Ok(())
59//! # }
60//! ```
61//!
62//! ## Tool Categories
63//!
64//! - [`balance`] - Balance checking tools for SOL and SPL tokens
65//! - [`transaction`] - Transaction creation and execution tools
66//! - [`swap`] - Jupiter DEX integration for token swaps
67//! - [`pump`] - Pump.fun integration for meme token deployment and trading
68//! - [`network`] - Network state and blockchain query tools
69
70pub mod balance;
71pub mod clients;
72pub mod common;
73pub mod error;
74pub mod network;
75pub mod pump;
76pub mod signer;
77pub mod swap;
78pub mod transaction;
79pub mod utils;
80
81// Re-export commonly used tools
82pub use balance::*;
83pub use network::*;
84pub use pump::*;
85pub use signer::*;
86pub use swap::*;
87pub use transaction::*;
88
89// Re-export specific utilities to avoid ambiguous glob conflicts
90pub use utils::{
91    execute_solana_transaction, send_transaction, send_transaction_with_retry, validate_address,
92    TransactionConfig, TransactionSubmissionResult,
93};
94// Note: generate_mint_keypair and create_token_with_mint_keypair are not re-exported
95// at the top level to avoid conflicts. Use utils::generate_mint_keypair directly.
96
97// Re-export error types
98pub use error::SolanaToolError;
99
100// Re-export signer types for convenience
101pub use riglr_core::{signer::UnifiedSigner, SignerContext};
102
103/// Current version of riglr-solana-tools
104pub const VERSION: &str = env!("CARGO_PKG_VERSION");
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_version_is_valid() {
112        // VERSION should be a valid semantic version string
113        assert!(!VERSION.is_empty(), "VERSION should not be empty");
114        assert!(
115            VERSION.contains('.'),
116            "VERSION should contain dots (semantic versioning)"
117        );
118        // Verify it's a proper version format (at least X.Y.Z)
119        let parts: Vec<&str> = VERSION.split('.').collect();
120        assert!(
121            parts.len() >= 3,
122            "VERSION should have at least 3 parts (major.minor.patch)"
123        );
124        // Each part should be numeric (for basic semantic versioning)
125        for (i, part) in parts.iter().take(3).enumerate() {
126            // Remove any pre-release or build metadata for the first 3 parts
127            let clean_part = part.split('-').next().unwrap().split('+').next().unwrap();
128            assert!(
129                clean_part.chars().all(|c| c.is_ascii_digit()),
130                "VERSION part {} should be numeric, got: {}",
131                i,
132                clean_part
133            );
134        }
135    }
136
137    #[test]
138    fn test_version_constant_accessible() {
139        // Test that VERSION constant can be accessed and assigned
140        let version_copy = VERSION;
141        assert_eq!(version_copy, VERSION);
142    }
143
144    #[test]
145    fn test_module_declarations_exist() {
146        // Verify that all declared modules can be referenced
147        // This test ensures the module declarations are valid and the modules exist
148
149        // Test that we can access module paths (compilation test)
150        let _balance_module = std::any::type_name::<balance::BalanceResult>();
151        let _error_module = std::any::type_name::<error::SolanaToolError>();
152        // Note: Tool-generated Args types are in different namespace after macro changes
153        let _signer_module = std::any::type_name::<signer::LocalSolanaSigner>();
154
155        // These should compile without errors if modules exist
156    }
157
158    #[test]
159    fn test_re_exported_types_accessible() {
160        // Test that re-exported types from balance module are accessible
161        // Note: Tool-generated Args types are in different namespace after macro changes
162        // We can still verify that the tool functions themselves are accessible
163        let _balance_result = std::any::type_name::<balance::BalanceResult>();
164
165        // Test that re-exported types from transaction module are accessible
166        let _transaction_result = std::any::type_name::<utils::TransactionSubmissionResult>();
167        let _transaction_config = std::any::type_name::<utils::TransactionConfig>();
168
169        // Test that re-exported types from signer module are accessible
170        let _local_solana_signer = std::any::type_name::<signer::LocalSolanaSigner>();
171    }
172
173    #[test]
174    fn test_utils_re_exports_accessible() {
175        // Test that specific utils re-exports are accessible by type name
176        let _transaction_config = std::any::type_name::<TransactionConfig>();
177        let _transaction_result = std::any::type_name::<TransactionSubmissionResult>();
178    }
179
180    #[test]
181    fn test_error_re_exports() {
182        // Test that error re-exports work
183        let _solana_tool_error = std::any::type_name::<SolanaToolError>();
184    }
185
186    #[test]
187    fn test_riglr_core_re_exports() {
188        // Test that riglr-core re-exports are accessible
189        let _unified_signer = std::any::type_name::<dyn UnifiedSigner>();
190        let _signer_context = std::any::type_name::<SignerContext>();
191    }
192
193    #[test]
194    fn test_version_matches_cargo_pkg_version() {
195        // VERSION should match the CARGO_PKG_VERSION environment variable
196        // This is a compile-time guarantee, but we test the behavior
197        let version = VERSION;
198
199        // Basic validation that it looks like a version
200        assert!(
201            !version.is_empty() && version.contains('.'),
202            "VERSION should be a non-empty version string with dots"
203        );
204
205        // Test that VERSION is a static string
206        let version_ref: &'static str = VERSION;
207        assert_eq!(version_ref, VERSION);
208    }
209
210    #[test]
211    fn test_crate_documentation_constants() {
212        // Test that the crate has the expected structure based on documentation
213        // This validates that the public API matches what's documented
214
215        // These should be accessible as documented in the crate docs
216        let _version: &'static str = VERSION;
217
218        // Verify the main re-exported modules exist by checking their types
219        let _balance_exists = std::any::type_name::<balance::BalanceResult>();
220        // Note: Tool-generated Args types are in different namespace after macro changes
221        let _signer_exists = std::any::type_name::<signer::LocalSolanaSigner>();
222        let _error_exists = std::any::type_name::<error::SolanaToolError>();
223    }
224}