# madeonsol

[![Crates.io](https://img.shields.io/crates/v/madeonsol?style=flat-square)](https://crates.io/crates/madeonsol)
[![docs.rs](https://img.shields.io/docsrs/madeonsol?style=flat-square)](https://docs.rs/madeonsol)
[![Crates.io downloads](https://img.shields.io/crates/d/madeonsol?style=flat-square)](https://crates.io/crates/madeonsol)
[![GitHub stars](https://img.shields.io/github/stars/LamboPoewert/madeonsol-rs?style=flat-square&logo=github)](https://github.com/LamboPoewert/madeonsol-rs)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE)

> ⭐ **[Star on GitHub](https://github.com/LamboPoewert/madeonsol-rs)** · 📂 **[Examples](./examples/)** · 📚 **[docs.rs](https://docs.rs/madeonsol)** · 🌐 **[API docs](https://madeonsol.com/api-docs)**

Official Rust SDK for the **[MadeOnSol](https://madeonsol.com) Solana API** — typed,
async, `tokio`-based, `rustls`-only.

> Real-time Solana trading intelligence: track 1,000+ KOL wallets with <3s latency,
> score 6,700+ Pump.fun deployers by reputation, detect multi-KOL coordination
> signals, and stream every DEX trade across 9+ programs.
>
> **Free tier: 200 requests/day at <https://madeonsol.com/pricing> — no credit card required.**

> **New in 0.10.0** *(2026-05-25)* — **Price alerts, scout leaderboard, KOL consensus, peak history, coordination history, wallet derived stats, trajectory snapshots.**
> `client.price_alerts` — full CRUD for MC-drop / recovery alert rules (PRO/ULTRA). `client.kol.scout_leaderboard()` — ranked scouts by swarm attraction rate. `client.token.kol_consensus(mint)` — per-token KOL buyer/seller breakdown. `client.token.peak_history(mint)` — ATH, decline from peak, MC snapshots post-bond. `client.kol.coordination_history()` — past coordination fires. `client.deployer.trajectory(wallet, params)` now accepts `include: Some("daily_snapshots")` for 90d snapshots. `WalletStatsResponse.derived` — win rate, ROI, best/worst trade, biggest miss, AI verdict.

## Get an API key

1. Visit **<https://madeonsol.com/developer>**
2. Sign in with email or Solana wallet
3. Copy your `msk_…` key (free tier is unlocked instantly — 200 req/day, 10/min)

Paid tiers unlock higher rate limits, sub-hour windows, WebSocket streaming,
webhooks, and the all-DEX firehose:

| Tier | Price | Daily req | KOL trending sub-hour | Stream | Webhooks | DEX firehose |
|---|---|---|---|---|---|---|
| **Free** | $0 | 200 | — | — | — | — |
| **PRO** | $49/mo | 10,000 | ✅ | ✅ | 3 | — |
| **ULTRA** | $149/mo | 100,000 | ✅ | ✅ | 10 | ✅ |

Annual: PRO $490/yr, ULTRA $1,490/yr (2 months free).

## Install

```toml
[dependencies]
madeonsol = "0.10"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

Requires Rust 1.75+. Uses `reqwest` with `rustls-tls` (no OpenSSL dependency).

## Quick start

```rust
use madeonsol::{MadeOnSol, types::{KolFeedParams, KolAction}};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Free key — get one at https://madeonsol.com/pricing
    let client = MadeOnSol::new(std::env::var("MADEONSOL_API_KEY")?)?;

    let feed = client.kol.feed(&KolFeedParams {
        limit: Some(10),
        action: Some(KolAction::Buy),
        ..Default::default()
    }).await?;

    for trade in feed.trades {
        let mc = trade.market_cap_usd_at_trade
            .map(|m| format!(" @ MC ${:.0}", m))
            .unwrap_or_default();
        println!("{:?} bought {:?} for {} SOL{}",
                 trade.kol_name, trade.token_symbol, trade.sol_amount, mc);
    }
    Ok(())
}
```

Run the bundled examples:

```sh
export MADEONSOL_API_KEY=msk_...
cargo run --example kol_feed
cargo run --example deployer_alerts
```

## Namespaces

The `MadeOnSol` client exposes namespaced sub-clients:

| Namespace | Purpose |
|---|---|
| `client.kol` | KOL feed, leaderboard, coordination, PnL, trending tokens, alerts, compare, **first_touches**, **scout_leaderboard**, **coordination_history** |
| `client.deployer` | Pump.fun deployer leaderboard, alerts, trajectory (+ daily snapshots), bonded tokens |
| `client.alpha` | Alpha-wallet leaderboard, profiles, cap tables, buyer quality |
| `client.token` | Per-mint snapshot, batch lookup, buyer quality, **kol_consensus**, **peak_history**, directory list |
| `client.wallet_tracker` | Track arbitrary Solana wallets — watchlist CRUD, swap/transfer history |
| `client.wallet` | Universal wallet endpoints — stats + cross-product flags + derived analytics, FIFO PnL, open positions, paginated trades (PRO+) |
| `client.coordination_alerts` | Push alerts on coordinated buying (PRO/ULTRA) |
| `client.first_touch_subscriptions` | Push alerts on first-KOL-touch events (ULTRA) |
| `client.price_alerts` *(new 0.10)* | MC-drop / recovery price alert rules CRUD + event history (PRO/ULTRA) |
| `client.tools` | Solana tool directory search |
| `client.stream` | Issue 24h WebSocket streaming tokens |
| `client.webhooks` | Webhook CRUD (PRO/ULTRA) |

Full reference: <https://docs.rs/madeonsol> · Interactive API docs: <https://madeonsol.com/api-docs>.

## Use cases

- **Copy-trading bot** — stream KOL buys via `client.kol.feed()` and mirror trades
- **DEX trade sniping** — subscribe to the all-DEX stream filtered by token / wallet
- **Deployer sniper** — monitor `client.deployer.alerts()` for elite-tier launches
- **Coordination detector** — flag tokens with `client.kol.coordination()` or push alerts
- **Scout signal** — track first-KOL-touch events filtered to S/A-tier scouts via `client.kol.first_touches()` (backtested: ~50% swarm rate vs 14% baseline)
- **Analytics dashboard** — combine leaderboard, PnL, and tool data
- **Telegram/Discord bot** — pipe alerts via webhooks into chat
- **Portfolio tracker** — use `client.kol.wallet()` to follow specific KOL positions

## Error handling

All methods return `Result<T, madeonsol::Error>`. The `Error::Api` variant exposes
HTTP status, server message, and the raw JSON body:

```rust
use madeonsol::{MadeOnSol, Error};

# async fn run(client: MadeOnSol) -> Result<(), Error> {
match client.kol.token("invalid-mint").await {
    Ok(activity) => println!("{:?}", activity),
    Err(Error::Api { status, message, .. }) => {
        eprintln!("API error {}: {}", status, message);
    }
    Err(other) => return Err(other),
}
# Ok(())
# }
```

`Error::MissingApiKey` is returned by `MadeOnSol::new` if the key is empty or
doesn't start with `msk_` — the error message and a stderr hint both link to
<https://madeonsol.com/pricing>.

## First-touch signal *(new in 0.4)*

Every "first KOL buy on a token mint" event — when a tracked KOL is the first of the cohort to touch a token. Filterable by **scout tier** (S/A/B/C from `mv_kol_scout_score`), KOL winrate, token age, mint suffix.

**Backtest:** S-tier scouts attract ≥3 follow-on KOLs within 4h ~50% of the time vs ~14% baseline (38d / 491k buys / 72,549 events). Public leaderboard at <https://madeonsol.com/kol/scouts>.

```rust
use madeonsol::{FirstTouchPreset, FirstTouchesParams, ScoutTier};

let res = client
    .kol
    .first_touches(&FirstTouchesParams {
        preset: Some(FirstTouchPreset::Scout),
        min_scout_tier: Some(ScoutTier::S),
        limit: Some(20),
        ..Default::default()
    })
    .await?;

for e in res.events {
    println!(
        "{} scouted {} (scout_score={:?}%)",
        e.first_kol.name.unwrap_or_default(),
        e.token_symbol.unwrap_or_default(),
        e.first_kol.scout_score
    );
}
```

**Webhook subscriptions** (Ultra, up to 10 active per user) — push delivery, HMAC-SHA256 signed:

```rust
use madeonsol::{FirstTouchSubscriptionCreateParams, FirstTouchSubscriptionFilters, ScoutTier, CoordinationDeliveryMode};

let res = client
    .first_touch_subscriptions
    .create(&FirstTouchSubscriptionCreateParams {
        name: Some("S-tier scouts on pump tokens".into()),
        filters: Some(FirstTouchSubscriptionFilters {
            min_scout_tier: Some(ScoutTier::S),
            mint_suffix: Some("pump".into()),
            ..Default::default()
        }),
        delivery_mode: Some(CoordinationDeliveryMode::Webhook),
        webhook_url: Some("https://you.com/hooks/scout".into()),
    })
    .await?;
// store res.webhook_secret — shown ONCE
```

> **Don't poll — push.** Median lead time before the second KOL is 12 seconds. WebSocket channel: `kol:first_touches` (PRO+).

## Universal wallet endpoints *(new in 0.9)*

Per-wallet profile data for **any** Solana wallet — not just curated KOLs. FIFO cost-basis PnL over the last 90 days, cached server-side with dynamic TTL. Cache hits don't count against your daily quota. PRO+.

```rust
# async fn run(client: madeonsol::MadeOnSol) -> Result<(), Box<dyn std::error::Error>> {
use madeonsol::types::WalletTradesParams;

// 1. Profile any wallet — works on KOLs, alpha traders, deployers, randoms.
let stats = client.wallet.stats("ASVzakePP6GNg9r95d4LPZHJDMXun6L6E4um4pu5ybJk").await?;
if let Some(s) = stats.stats {
    println!("{}: {} trades, {} unique tokens", stats.address, s.total_trades, s.unique_tokens);
}
println!("KOL: {} · alpha: {} · deployer: {}",
    stats.flags.is_kol, stats.flags.is_alpha_tracked, stats.flags.is_deployer);

// 2. Full FIFO PnL — realized + unrealized SOL, profit factor, drawdown,
//    daily curve, closed/open positions.
let pnl = client.wallet.pnl(&stats.address).await?;
println!("Realized: {:+.2} SOL · Unrealized: {:+.2} SOL", pnl.summary.realized_sol, pnl.summary.unrealized_sol);
if let Some(pf) = pnl.summary.profit_factor {
    println!("Win rate: {:.0}% · Profit factor: {:.2}", pnl.summary.win_rate.unwrap_or(0.0) * 100.0, pf);
}
for c in pnl.closed_positions.iter().take(5) {
    println!("  {}…  {:+.2} SOL ({:?}% ROI, {} min hold)",
        &c.token_mint[..8], c.pnl_sol, c.roi_pct, c.hold_minutes.unwrap_or(0));
}

// 3. Paginated raw trades — keep paging with next_cursor.
let mut params = WalletTradesParams { limit: Some(200), ..Default::default() };
loop {
    let page = client.wallet.trades(&stats.address, &params).await?;
    for t in &page.trades { /* … */ }
    if !page.has_more { break; }
    params.cursor = page.next_cursor;
}
# Ok(())
# }
```

**Cost-basis honesty.** Observable only inside the 90-day window. Overflow sells (no matching buy in window) are silently discarded rather than fabricated. `notes.cost_basis_observable_from` makes the cutoff visible.

## Price alerts *(new in 0.10)*

Get notified when a token's market cap drops below a threshold (and optionally on recovery). PRO: 5 rules, ULTRA: 20 rules. Delivered via WebSocket channel `price:alerts` and/or HMAC-signed webhook.

```rust
# async fn run(client: madeonsol::MadeOnSol) -> Result<(), Box<dyn std::error::Error>> {
use madeonsol::types::{PriceAlertCreateParams, PriceAlertDeliveryMode, PriceAlertEventsParams};

// Create an alert: fire when MC drops 30%, then again on 50% recovery.
let res = client.price_alerts.create(&PriceAlertCreateParams {
    token_mint: "So11111111111111111111111111111111111111112".into(),
    drop_pct: 30.0,
    recovery_pct: Some(50.0),
    name: Some("SOL 30% dip".into()),
    delivery_mode: Some(PriceAlertDeliveryMode::Webhook),
    webhook_url: Some("https://you.com/hooks/price".into()),
}).await?;
// store res.webhook_secret — shown ONCE
println!("Alert {} created, status: {:?}", res.alert.id, res.alert.status);

// List active alerts
let alerts = client.price_alerts.list().await?;
for a in alerts.alerts {
    println!("{}: {} drop={}% status={:?}", a.id, a.token_mint, a.drop_pct, a.status);
}

// Check fired events
let events = client.price_alerts.events(&PriceAlertEventsParams {
    limit: Some(20),
    ..Default::default()
}).await?;
for e in events.events {
    println!("{} {} at MC ${:.0}", e.event_type, e.token_mint, e.current_mc_usd);
}
# Ok(())
# }
```

## New in 0.10: scout leaderboard, KOL consensus, peak history

```rust
# async fn run(client: madeonsol::MadeOnSol) -> Result<(), Box<dyn std::error::Error>> {
use madeonsol::types::{ScoutLeaderboardParams, ScoutTier, ScoutLeaderboardSort};

// Top scouts by swarm attraction rate
let scouts = client.kol.scout_leaderboard(&ScoutLeaderboardParams {
    limit: Some(10),
    scout_tier: Some(ScoutTier::S),
    sort: Some(ScoutLeaderboardSort::Swarm3PlusPct),
}).await?;
println!("{}", scouts);

// KOL consensus on a token
let consensus = client.token.kol_consensus("So11111111111111111111111111111111111111112").await?;
println!("{} buyers, {} sellers, exit rate {:?}%",
    consensus.total_kol_buyers, consensus.total_kol_sellers, consensus.kol_exit_rate);

// Peak MC history
let peak = client.token.peak_history("So11111111111111111111111111111111111111112").await?;
println!("ATH: {:?}, decline: {:?}%", peak.peak_mc_usd, peak.decline_from_peak_pct);
# Ok(())
# }
```

## WebSocket streams (PRO/ULTRA)

This crate does **not** ship a WebSocket client — `client.stream.get_token()`
returns the URL + token, and you connect with any WS library
(`tokio-tungstenite` recommended):

```rust
# async fn run(client: madeonsol::MadeOnSol) -> Result<(), Box<dyn std::error::Error>> {
let token = client.stream.get_token().await?;
let ws_url = format!("{}?token={}", token.ws_url, token.token);
// then: tokio_tungstenite::connect_async(&ws_url).await
# Ok(())
# }
```

The DEX firehose URL (`token.dex_ws_url`) is only present for ULTRA subscribers.
See <https://madeonsol.com/api-docs> for the full subscribe/unsubscribe protocol.

## Also available

| Platform | Package |
|---|---|
| TypeScript / Node | [`madeonsol`](https://www.npmjs.com/package/madeonsol) on npm |
| Python (LangChain, CrewAI) | [`madeonsol-x402`](https://pypi.org/project/madeonsol-x402/) on PyPI |
| MCP Server (Claude, Cursor) | [`mcp-server-madeonsol`](https://www.npmjs.com/package/mcp-server-madeonsol) · [Smithery](https://smithery.ai/servers/madeonsol/solana-kol-intelligence) · [Glama](https://glama.ai/mcp/servers/LamboPoewert/mcp-server-madeonsol) |
| ElizaOS | [`@madeonsol/plugin-madeonsol`](https://www.npmjs.com/package/@madeonsol/plugin-madeonsol) |
| Solana Agent Kit | [`solana-agent-kit-plugin-madeonsol`](https://www.npmjs.com/package/solana-agent-kit-plugin-madeonsol) |

## Links

- API docs: <https://madeonsol.com/api-docs> (interactive try-it-now)
- OpenAPI 3.1 spec: <https://madeonsol.com/api/v1/openapi.json>
- Postman collection: <https://madeonsol.com/api/v1/postman.json>
- Pricing & free key: <https://madeonsol.com/pricing>
- Issues: <https://github.com/LamboPoewert/madeonsol-rs/issues>

## License

MIT © [MadeOnSol](https://madeonsol.com)
