Skip to main content

madeonsol/
lib.rs

1//! # MadeOnSol — official Rust SDK
2//!
3//! Solana KOL wallet tracking, Pump.fun deployer intelligence, alpha-wallet scoring,
4//! and an all-DEX trade firehose.
5//!
6//! ## Get an API key
7//!
8//! Free tier: **200 requests/day, no credit card** at <https://madeonsol.com/pricing>.
9//! Paid tiers (PRO $49/mo, ULTRA $149/mo) unlock higher rate limits, sub-hour windows,
10//! WebSocket streaming, webhooks, and the all-DEX firehose.
11//!
12//! All keys start with `msk_`.
13//!
14//! ## Quick start
15//!
16//! ```no_run
17//! use madeonsol::{MadeOnSol, types::KolFeedParams};
18//!
19//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
20//! let api_key = std::env::var("MADEONSOL_API_KEY")?;
21//! let client = MadeOnSol::new(api_key)?;
22//!
23//! let feed = client
24//!     .kol
25//!     .feed(&KolFeedParams { limit: Some(10), ..Default::default() })
26//!     .await?;
27//!
28//! for trade in feed.trades {
29//!     println!("{:?} bought {:?}", trade.kol_name, trade.token_symbol);
30//! }
31//! # Ok(())
32//! # }
33//! ```
34//!
35//! ## Namespaces
36//!
37//! - [`MadeOnSol::kol`] — KOL feed, leaderboard, coordination, PnL, trending tokens, alerts
38//! - [`MadeOnSol::deployer`] — Pump.fun deployer leaderboard, alerts, trajectory
39//! - [`MadeOnSol::alpha`] — alpha-wallet leaderboard, profiles, cap tables, buyer quality
40//! - [`MadeOnSol::wallet_tracker`] — track arbitrary Solana wallets
41//! - [`MadeOnSol::coordination_alerts`] — push alerts on coordinated buying (PRO/ULTRA)
42//! - [`MadeOnSol::tools`] — Solana tool directory search
43//! - [`MadeOnSol::stream`] — WebSocket streaming token issuance
44//! - [`MadeOnSol::webhooks`] — webhook CRUD (PRO/ULTRA)
45//!
46//! Full API reference: <https://madeonsol.com/api-docs>
47
48#![warn(missing_debug_implementations)]
49#![warn(rust_2018_idioms)]
50
51mod client;
52pub mod api;
53pub mod error;
54pub mod types;
55
56use std::sync::Arc;
57
58use crate::api::{
59    alpha::Alpha, coordination_alerts::CoordinationAlerts, deployer::Deployer,
60    first_touch_subscriptions::FirstTouchSubscriptions, kol::Kol, me::Me, stream::Stream, token::Token,
61    tools::Tools, wallet_tracker::WalletTracker, webhooks::Webhooks,
62};
63use crate::client::HttpCore;
64use crate::error::{MadeOnSolError, Result};
65
66pub use crate::error::MadeOnSolError as Error;
67
68/// MadeOnSol API client.
69///
70/// Construct with [`MadeOnSol::new`] and a `msk_…` API key, then access the
71/// namespaced sub-clients ([`kol`](Self::kol), [`deployer`](Self::deployer), etc.).
72///
73/// Cheap to clone — internal HTTP state is reference-counted.
74///
75/// # Example
76///
77/// ```no_run
78/// use madeonsol::MadeOnSol;
79///
80/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
81/// let client = MadeOnSol::new(std::env::var("MADEONSOL_API_KEY")?)?;
82/// let stats = client.deployer.stats().await?;
83/// println!("{} elite deployers tracked", stats.elite_count);
84/// # Ok(())
85/// # }
86/// ```
87#[derive(Debug, Clone)]
88pub struct MadeOnSol {
89    /// KOL wallet tracking endpoints.
90    pub kol: Kol,
91    /// Pump.fun deployer intelligence endpoints.
92    pub deployer: Deployer,
93    /// Alpha wallet intelligence: leaderboard, profiles, cap tables, buyer quality.
94    pub alpha: Alpha,
95    /// Token intelligence — comprehensive per-mint snapshot + batch lookups.
96    pub token: Token,
97    /// Account self-inspection — tier, quota, feature usage (v0.8).
98    pub me: Me,
99    /// Wallet tracker: watchlist CRUD, trades, summary.
100    pub wallet_tracker: WalletTracker,
101    /// Coordination alert rules CRUD (v1.1) — PRO/ULTRA.
102    pub coordination_alerts: CoordinationAlerts,
103    /// First-touch webhook subscriptions CRUD — ULTRA only. Use `kol.first_touches()` for read-only queries.
104    pub first_touch_subscriptions: FirstTouchSubscriptions,
105    /// Solana tool directory search.
106    pub tools: Tools,
107    /// WebSocket streaming token issuance.
108    pub stream: Stream,
109    /// Webhook management (PRO/ULTRA).
110    pub webhooks: Webhooks,
111}
112
113impl MadeOnSol {
114    /// Construct a new client.
115    ///
116    /// `api_key` must start with `msk_`. Get a free key (200 req/day, no card)
117    /// at <https://madeonsol.com/pricing>.
118    ///
119    /// # Errors
120    ///
121    /// Returns [`MadeOnSolError::MissingApiKey`] if the key is empty or missing the
122    /// `msk_` prefix. The error message includes the signup URL so end users know
123    /// where to go.
124    pub fn new(api_key: impl Into<String>) -> Result<Self> {
125        let api_key = api_key.into();
126        if !api_key.starts_with("msk_") {
127            // Print to stderr too — a bare Err can be swallowed and the user
128            // never sees the link to /pricing.
129            eprintln!(
130                "\n[madeonsol] Missing or invalid API key.\n\
131                 → Get a free key (200 req/day, no card) at https://madeonsol.com/pricing\n\
132                 → Then: madeonsol::MadeOnSol::new(std::env::var(\"MADEONSOL_API_KEY\")?)?\n"
133            );
134            return Err(MadeOnSolError::MissingApiKey);
135        }
136
137        let core = Arc::new(HttpCore::new(api_key));
138        Ok(Self {
139            kol: Kol { core: Arc::clone(&core) },
140            deployer: Deployer { core: Arc::clone(&core) },
141            alpha: Alpha { core: Arc::clone(&core) },
142            token: Token { core: Arc::clone(&core) },
143            me: Me { core: Arc::clone(&core) },
144            wallet_tracker: WalletTracker { core: Arc::clone(&core) },
145            coordination_alerts: CoordinationAlerts { core: Arc::clone(&core) },
146            first_touch_subscriptions: FirstTouchSubscriptions { core: Arc::clone(&core) },
147            tools: Tools { core: Arc::clone(&core) },
148            stream: Stream { core: Arc::clone(&core) },
149            webhooks: Webhooks { core },
150        })
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn rejects_missing_api_key() {
160        let err = MadeOnSol::new("").unwrap_err();
161        assert!(matches!(err, MadeOnSolError::MissingApiKey));
162    }
163
164    #[test]
165    fn rejects_wrong_prefix() {
166        let err = MadeOnSol::new("sk_live_abc").unwrap_err();
167        assert!(matches!(err, MadeOnSolError::MissingApiKey));
168    }
169
170    #[test]
171    fn accepts_valid_prefix() {
172        let client = MadeOnSol::new("msk_test_abcdef").unwrap();
173        // Smoke test — namespaces exist and the client clones cheaply.
174        let _cloned = client.clone();
175    }
176}