<div align="center">

# noesis-rust

**Official Rust SDK for the [Noesis](https://noesisapi.dev) on-chain intelligence API.**

[![Crates.io](https://img.shields.io/crates/v/noesis-api)](https://crates.io/crates/noesis-api)
[![Docs.rs](https://img.shields.io/docsrs/noesis-api)](https://docs.rs/noesis-api)
[![License](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
[![Website](https://img.shields.io/badge/website-noesisapi.dev-orange)](https://noesisapi.dev)

</div>

---

## Install

```bash
cargo add noesis-api
```

Or add to `Cargo.toml`:

```toml
[dependencies]
noesis-api = "0.3"
tokio = { version = "1", features = ["full"] }
```

**MSRV**: Rust 1.75+.

### TLS backend

The default TLS backend is `native-tls` (OpenSSL on most platforms). For a pure-Rust build — useful for `musl` / Alpine / `scratch` Docker images, or projects that already use `rustls` — opt into `rustls-tls`:

```toml
[dependencies]
noesis-api = { version = "0.3", default-features = false, features = ["rustls-tls"] }
```

Feature flags:

- `native-tls` *(default)* — links against the system's native TLS stack
- `rustls-tls` — pure-Rust TLS via `rustls` + `webpki-roots`

## Quick start

```rust
use noesis_api::Noesis;

#[tokio::main]
async fn main() -> Result<(), noesis_api::Error> {
    let client = Noesis::new(std::env::var("NOESIS_API_KEY").unwrap());

    let preview = client.token_preview("<MINT>").await?;
    println!("{:#?}", preview);

    let wallet = client.wallet_profile("<ADDRESS>").await?;
    println!("{:#?}", wallet);

    Ok(())
}
```

Get an API key at [noesisapi.dev/keys](https://noesisapi.dev/keys).

## Methods

### Tokens

```rust
use noesis_api::{Chain, HoldersOptions};

client.token_preview(mint).await?;
client.token_scan(mint).await?;
client.token_info(mint, Chain::Sol).await?;
client.token_top_holders(mint).await?;
client.token_holders(mint, HoldersOptions { limit: Some(500), ..Default::default() }).await?;
client.token_bundles(mint).await?;
client.token_fresh_wallets(mint).await?;
client.token_team_supply(mint, Chain::Sol).await?;
client.token_entry_price(mint, Chain::Sol).await?;
client.token_dev_profile(mint).await?;
client.token_best_traders(mint).await?;
client.token_early_buyers(mint, 1.0).await?;
```

### Wallets

```rust
use noesis_api::{HistoryOptions, ConnectionsOptions, TxType};

client.wallet_profile(addr).await?;
client.wallet_history(addr, HistoryOptions {
    limit: Some(50),
    ty: Some(TxType::Swap),
    ..Default::default()
}).await?;
client.wallet_connections(addr, ConnectionsOptions::default()).await?;
client.wallets_batch_identity(&addresses).await?;
client.cross_holders(&tokens).await?;
client.cross_traders(&tokens).await?;
```

### Chain / on-chain

```rust
client.chain_status().await?;
client.account(addr).await?;
client.accounts_batch(&addresses).await?;
client.parse_transactions(&signatures).await?;
```

### Live streams (SSE)

Each stream returns an `impl Stream<Item = Result<serde_json::Value>>`.
Auth errors and transport failures surface as a single `Err` item; the
stream then ends.

```rust
use futures_util::StreamExt;
use noesis_api::Noesis;

#[tokio::main]
async fn main() -> Result<(), noesis_api::Error> {
    let client = Noesis::new(std::env::var("NOESIS_API_KEY").unwrap());
    let mut stream = Box::pin(client.stream_pumpfun_new_tokens());

    while let Some(event) = stream.next().await {
        match event {
            Ok(v) => println!("new token: {v}"),
            Err(e) => { eprintln!("stream ended: {e}"); break; }
        }
    }
    Ok(())
}
```

Available streams:

```rust
client.stream_pumpfun_new_tokens();
client.stream_pumpfun_migrations();
client.stream_raydium_new_pools();
client.stream_meteora_new_pools();
```

## Error handling

Non-2xx responses surface as typed [`noesis_api::Error`] variants:

```rust
use noesis_api::{Noesis, Error};

let client = Noesis::new("se_...");
match client.token_preview("<MINT>").await {
    Ok(data) => { /* ... */ }
    Err(Error::RateLimit { retry_after_seconds, limit, limit_type, signed_in, .. }) => {
        // limit      == Some("1 request/5 seconds")
        // limit_type == Some("Heavy")
        // signed_in  == Some(false)
        let wait = retry_after_seconds.unwrap_or(5);
        tokio::time::sleep(std::time::Duration::from_secs(wait)).await;
    }
    Err(Error::Unauthorized { .. }) => eprintln!("Invalid or missing API key"),
    Err(Error::NotFound { .. })     => eprintln!("Wallet/token/route not found"),
    Err(e) => eprintln!("Other error: {e}"),
}
```

`retry_after_seconds` reads the JSON body, then falls back to the
`Retry-After` response header.

## Custom base URL

```rust
let client = Noesis::with_base_url("se_...", "https://custom.example.com");
```

## License

MIT — see [LICENSE](./LICENSE).

## Links

- [Website](https://noesisapi.dev)
- [API docs](https://noesisapi.dev/docs)
- [OpenAPI spec](https://github.com/Rengon0x/NoesisAPI/blob/main/openapi.yaml)
- [Examples](https://github.com/Rengon0x/NoesisAPI/tree/main/examples)
