[](https://allenhark.com)
# Slipstream Rust SDK
The official Rust client for **AllenHark Slipstream**, the high-performance Solana transaction relay and intelligence network.
[](https://crates.io/crates/allenhark-slipstream)
[](https://docs.rs/allenhark-slipstream)
[](LICENSE)
## Features
- **Discovery-based connection** -- auto-discovers workers, no manual endpoint configuration
- **Leader-proximity routing** -- real-time leader hints route transactions to the lowest-latency region
- **Multi-region support** -- connect to workers across regions, auto-route based on leader schedule
- **Multi-protocol** -- QUIC (primary), gRPC, WebSocket, HTTP with automatic fallback
- **6 real-time streams** -- leader hints, tip instructions, priority fees, latest blockhash, latest slot, transaction updates
- **Stream billing** -- each stream costs 1 token; 1-hour reconnect grace period
- **Billing tiers** -- Free (100 tx/day), Standard, Pro, Enterprise with tier-specific rate limits
- **Token billing** -- check balance, deposit SOL, view usage and deposit history
- **Keep-alive & time sync** -- background ping with RTT measurement and NTP-style clock synchronization
- **Atomic bundles** -- submit 2-5 transactions as a Jito-style atomic bundle
- **Solana RPC proxy** -- 22 Solana JSON-RPC methods proxied through Slipstream (accounts, transactions, tokens, fees, cluster info)
- **Deduplication** -- prevent duplicate submissions with custom dedup IDs
- **TPU submission** -- send transactions directly to validator TPU ports via UDP, bypassing senders (0.0001 SOL per TX)
## Installation
```toml
[dependencies]
allenhark-slipstream = "0.1"
tokio = { version = "1.0", features = ["full"] }
```
## Quick Start
```rust
use allenhark_slipstream::{Config, SlipstreamClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect with just an API key -- discovery handles the rest
let config = Config::builder()
.api_key("sk_live_12345678")
.build()?;
let client = SlipstreamClient::connect(config).await?;
// Submit a signed transaction
let result = client.submit_transaction(&tx_bytes).await?;
println!("TX: {} ({})", result.transaction_id, result.status);
// Check balance
let balance = client.get_balance().await?;
println!("Balance: {} tokens", balance.balance_tokens);
Ok(())
}
```
---
## Configuration
### ConfigBuilder Reference
Use `Config::builder()` for fluent configuration. Only `api_key` is required.
```rust
use allenhark_slipstream::{Config, PriorityFeeConfig, PriorityFeeSpeed, BackoffStrategy};
use std::time::Duration;
let config = Config::builder()
.api_key("sk_live_12345678")
.region("us-east")
.tier("pro")
.min_confidence(80)
.build()?;
```
| `api_key(key)` | `&str` | **required** | API key (must start with `sk_`) |
| `region(region)` | `&str` | auto | Preferred region (e.g., `"us-east"`, `"eu-central"`) |
| `endpoint(url)` | `&str` | auto | Override discovery with explicit worker endpoint |
| `discovery_url(url)` | `&str` | `https://discovery.allenhark.network` | Custom discovery service URL |
| `tier(tier)` | `&str` | `"pro"` | Billing tier: `"free"`, `"standard"`, `"pro"`, `"enterprise"` |
| `connection_timeout(dur)` | `Duration` | 10s | Connection timeout |
| `max_retries(n)` | `u32` | `3` | Maximum retry attempts for failed requests |
| `leader_hints(bool)` | `bool` | `true` | Auto-subscribe to leader hint stream on connect |
| `stream_tip_instructions(bool)` | `bool` | `false` | Auto-subscribe to tip instruction stream on connect |
| `stream_priority_fees(bool)` | `bool` | `false` | Auto-subscribe to priority fee stream on connect |
| `stream_latest_blockhash(bool)` | `bool` | `false` | Auto-subscribe to latest blockhash stream on connect |
| `stream_latest_slot(bool)` | `bool` | `false` | Auto-subscribe to latest slot stream on connect |
| `protocol_timeouts(timeouts)` | `ProtocolTimeouts` | QUIC=2s, gRPC=3s, WS=3s, HTTP=5s | Per-protocol connection timeouts |
| `preferred_protocol(proto)` | `Protocol` | auto | Force a specific protocol (skip fallback chain) |
| `priority_fee(config)` | `PriorityFeeConfig` | disabled | Priority fee optimization settings (see below) |
| `retry_backoff(strategy)` | `BackoffStrategy` | `Exponential` | Retry backoff: `Linear` or `Exponential` |
| `min_confidence(n)` | `u32` | `70` | Minimum confidence (0-100) for leader hint routing |
| `keepalive(bool)` | `bool` | `true` | Enable background keep-alive ping loop |
| `keepalive_interval(secs)` | `u64` | `5` | Keep-alive ping interval in seconds |
| `idle_timeout(dur)` | `Duration` | none | Disconnect after idle period |
| `webhook_url(url)` | `&str` | none | HTTPS endpoint to receive webhook POST deliveries |
| `webhook_events(events)` | `Vec<String>` | `["transaction.confirmed"]` | Webhook event types to subscribe to |
| `webhook_notification_level(level)` | `&str` | `"final"` | Transaction notification level: `"all"`, `"final"`, or `"confirmed"` |
### Billing Tiers
Each API key has a billing tier that determines transaction cost, rate limits, and priority queuing weight. Set the tier to match your API key's assigned tier:
```rust
let config = Config::builder()
.api_key("sk_live_12345678")
.tier("pro")
.build()?;
```
| **Free** | 0 (counter) | 0 (counter) | 5 rps | 10 | 5 concurrent | 100 tx/day |
| **Standard** | 50,000 lamports (0.00005 SOL) | 50,000 lamports | 5 rps | 10 | 10 concurrent | Unlimited |
| **Pro** | 100,000 lamports (0.0001 SOL) | 50,000 lamports | 20 rps | 50 | 50 concurrent | Unlimited |
| **Enterprise** | 1,000,000 lamports (0.001 SOL) | 50,000 lamports | 100 rps | 200 | 200 concurrent | Unlimited |
- **Free tier**: Uses a daily counter instead of token billing. Transactions and stream subscriptions both decrement the counter. Resets at UTC midnight.
- **Standard/Pro/Enterprise**: Deducted from token balance per transaction. Stream subscriptions cost 1 token each with a 1-hour reconnect grace period.
### PriorityFeeConfig
Controls automatic priority fee optimization for transactions.
```rust
use allenhark_slipstream::{PriorityFeeConfig, PriorityFeeSpeed};
let config = Config::builder()
.api_key("sk_live_12345678")
.priority_fee(PriorityFeeConfig {
enabled: true,
speed: PriorityFeeSpeed::UltraFast,
max_tip: Some(0.01),
})
.build()?;
```
| `enabled` | `bool` | `false` | Enable automatic priority fee optimization |
| `speed` | `PriorityFeeSpeed` | `Fast` | Fee tier: `Slow`, `Fast`, or `UltraFast` |
| `max_tip` | `Option<f64>` | `None` | Maximum tip in SOL (caps the priority fee) |
**PriorityFeeSpeed tiers:**
| `Slow` | Low | ~60-70% | Cost-sensitive, non-urgent transactions |
| `Fast` | Medium | ~85-90% | Default balance of cost and speed |
| `UltraFast` | High | ~95-99% | Time-critical trading, MEV protection |
### ProtocolTimeouts
```rust
use allenhark_slipstream::ProtocolTimeouts;
use std::time::Duration;
let config = Config::builder()
.api_key("sk_live_12345678")
.protocol_timeouts(ProtocolTimeouts {
quic: Duration::from_millis(2000),
grpc: Duration::from_millis(3000),
websocket: Duration::from_millis(3000),
http: Duration::from_millis(5000),
})
.build()?;
```
| `quic` | `Duration` | 2s | QUIC connection timeout |
| `grpc` | `Duration` | 3s | gRPC connection timeout |
| `websocket` | `Duration` | 3s | WebSocket connection timeout |
| `http` | `Duration` | 5s | HTTP request timeout |
### Protocol Fallback Chain
The SDK tries protocols in order until one succeeds:
**QUIC** (2s) -> **gRPC** (3s) -> **WebSocket** (3s) -> **HTTP** (5s)
Override with `preferred_protocol()` to skip the fallback chain:
```rust
use allenhark_slipstream::Protocol;
let config = Config::builder()
.api_key("sk_live_12345678")
.preferred_protocol(Protocol::WebSocket)
.build()?;
```
---
## Connecting
### Auto-Discovery (Recommended)
```rust
use allenhark_slipstream::{Config, SlipstreamClient};
// Minimal -- discovery finds the best worker
let config = Config::builder().api_key("sk_live_xxx").build()?;
let client = SlipstreamClient::connect(config).await?;
// With region preference
let config = Config::builder()
.api_key("sk_live_xxx")
.region("us-east")
.build()?;
let client = SlipstreamClient::connect(config).await?;
```
### Direct Endpoint (Advanced)
Bypass discovery and connect to a specific worker:
```rust
let config = Config::builder()
.api_key("sk_live_xxx")
.endpoint("http://worker-ip:9000")
.build()?;
let client = SlipstreamClient::connect(config).await?;
```
### Connection Info
```rust
let info = client.connection_info();
println!("Session: {}", info.session_id);
println!("Protocol: {}", info.protocol); // "quic", "grpc", "ws", "http"
println!("Region: {:?}", info.region);
println!("Rate limit: {} rps (burst: {})", info.rate_limit.rps, info.rate_limit.burst);
```
---
## Transaction Submission
### Basic Submit
```rust
let result = client.submit_transaction(&tx_bytes).await?;
println!("TX ID: {}", result.transaction_id);
println!("Status: {:?}", result.status);
if let Some(sig) = &result.signature {
println!("Signature: {}", sig);
}
```
### Submit with Options
```rust
use allenhark_slipstream::SubmitOptions;
let result = client.submit_transaction_with_options(&tx_bytes, &SubmitOptions {
broadcast_mode: true,
preferred_sender: Some("nozomi".to_string()),
max_retries: 5,
timeout_ms: 10_000,
dedup_id: Some("my-unique-id".to_string()),
}).await?;
```
#### SubmitOptions Fields
| `broadcast_mode` | `bool` | `false` | Fan-out to multiple regions simultaneously |
| `preferred_sender` | `Option<String>` | `None` | Prefer a specific sender (e.g., `"nozomi"`, `"0slot"`) |
| `max_retries` | `u32` | `2` | Retry attempts on failure |
| `timeout_ms` | `u64` | `30000` | Timeout per attempt in milliseconds |
| `dedup_id` | `Option<String>` | `None` | Custom deduplication ID (prevents double-submit) |
| `tpu_submission` | `bool` | `false` | Send directly to validator TPU ports via UDP (bypasses senders, 0.0001 SOL per TX) |
### TransactionResult Fields
| `request_id` | `String` | Internal request ID |
| `transaction_id` | `String` | Slipstream transaction ID |
| `signature` | `Option<String>` | Solana transaction signature (base58, when confirmed) |
| `status` | `TransactionStatus` | Current status (see table below) |
| `slot` | `Option<u64>` | Solana slot (when confirmed) |
| `timestamp` | `u64` | Unix timestamp in milliseconds |
| `routing` | `Option<RoutingInfo>` | Routing details (region, sender, latencies) |
| `error` | `Option<TransactionError>` | Error details (on failure) |
### TransactionStatus Values
| `Pending` | Received, not yet processed |
| `Processing` | Being validated and routed |
| `Sent` | Forwarded to sender |
| `Confirmed` | Confirmed on Solana |
| `Failed` | Failed permanently |
| `Duplicate` | Deduplicated (already submitted) |
| `RateLimited` | Rate limit exceeded for your tier |
| `InsufficientTokens` | Token balance too low (or free tier daily limit reached) |
### RoutingInfo Fields
| `region` | `String` | Region that handled the transaction |
| `sender` | `String` | Sender service used |
| `routing_latency_ms` | `u32` | Time spent in routing logic (ms) |
| `sender_latency_ms` | `u32` | Time spent in sender submission (ms) |
| `total_latency_ms` | `u32` | Total end-to-end latency (ms) |
### TPU Submission
Send transactions directly to Solana validator TPU ports via UDP, bypassing external sender services. Targets the current leader + next 3 leaders for redundancy. Fire-and-forget -- no sender acknowledgment, but transactions are still tracked by signature for confirmation polling.
```rust
use allenhark_slipstream::SubmitOptions;
let result = client.submit_transaction_with_options(&tx_bytes, SubmitOptions {
tpu_submission: true,
..Default::default()
}).await?;
println!("Signature: {:?}", result.signature);
// Use standard confirmation polling to check landing
```
**TPU submission billing:** 0.0001 SOL (100,000 lamports) per transaction -- separate from standard sender-based billing.
---
## Sender Discovery
Fetch the list of configured senders with their tip wallets and pricing tiers.
This is essential for building transactions in both broadcast and streaming modes.
### Get Available Senders
```rust
let senders = client.get_senders().await?;
for sender in &senders {
println!("{} ({})", sender.display_name, sender.sender_id);
println!(" Tip wallets: {:?}", sender.tip_wallets);
for tier in &sender.tip_tiers {
println!(" {}: {} SOL (~{}ms)", tier.name, tier.amount_sol, tier.expected_latency_ms);
}
}
```
### SenderInfo Fields
| `sender_id` | `String` | Sender identifier (e.g., `"nozomi"`, `"0slot"`) |
| `display_name` | `String` | Human-readable name |
| `tip_wallets` | `Vec<String>` | Solana wallet addresses for tips |
| `tip_tiers` | `Vec<TipTier>` | Pricing tiers with speed/cost tradeoffs |
### TipTier Fields
| `name` | `String` | Tier name (e.g., `"standard"`, `"fast"`, `"ultra_fast"`) |
| `amount_sol` | `f64` | Tip amount in SOL |
| `expected_latency_ms` | `u32` | Expected submission latency in milliseconds |
---
## Atomic Bundles
Submit 2-5 transactions as a Jito-style atomic bundle. All transactions execute sequentially and atomically -- either all land or none do.
### Basic Bundle
```rust
let txs = vec![tx1_bytes, tx2_bytes, tx3_bytes];
let result = client.submit_bundle(&txs).await?;
println!("Bundle ID: {}", result.bundle_id);
println!("Accepted: {}", result.accepted);
for sig in &result.signatures {
println!(" Signature: {}", sig);
}
```
### Bundle with Tip
```rust
// Explicit tip amount in lamports
let result = client.submit_bundle_with_tip(&txs, Some(100_000)).await?;
println!("Sender: {:?}", result.sender_id);
```
### BundleResult Fields
| `bundle_id` | `String` | Unique bundle identifier |
| `accepted` | `bool` | Whether the bundle was accepted by the sender |
| `signatures` | `Vec<String>` | Transaction signatures (base58) |
| `sender_id` | `Option<String>` | Sender that processed the bundle |
| `error` | `Option<String>` | Error message if rejected |
### Bundle Constraints
| Min transactions | 2 |
| Max transactions | 5 |
| Cost | 5 tokens (0.00025 SOL) flat rate per bundle |
| Execution | Atomic -- all-or-nothing sequential execution |
| Sender requirement | Must have `supports_bundles` enabled |
---
## Streaming
Real-time data feeds over QUIC (binary) or WebSocket (JSON).
**Billing:** Each stream subscription costs **1 token (0.00005 SOL)**. If the SDK reconnects within 1 hour for the same stream, no re-billing occurs (reconnect grace period). Free-tier keys deduct from the daily counter instead of tokens.
### Leader Hints
Which region is closest to the current Solana leader. Emitted every 250ms when confidence >= threshold.
```rust
let mut hints = client.subscribe_leader_hints().await?;
while let Some(hint) = hints.recv().await {
println!("Slot {}: route to {}", hint.slot, hint.preferred_region);
println!(" Leader: {}", hint.leader_pubkey);
println!(" Confidence: {}%", hint.confidence);
println!(" TPU RTT: {}ms", hint.metadata.tpu_rtt_ms);
println!(" Backups: {:?}", hint.backup_regions);
}
```
#### LeaderHint Fields
| `timestamp` | `u64` | Unix millis |
| `slot` | `u64` | Current slot |
| `expires_at_slot` | `u64` | Slot when this hint expires |
| `preferred_region` | `String` | Best region for current leader |
| `backup_regions` | `Vec<String>` | Fallback regions in priority order |
| `confidence` | `u32` | Confidence score (0-100) |
| `leader_pubkey` | `String` | Current leader validator pubkey |
| `metadata.tpu_rtt_ms` | `u32` | RTT to leader's TPU from preferred region |
| `metadata.region_score` | `f64` | Region quality score |
| `metadata.leader_tpu_address` | `Option<String>` | Leader's TPU address (ip:port) |
| `metadata.region_rtt_ms` | `Option<HashMap<String, u32>>` | Per-region RTT to leader |
### Tip Instructions
Wallet address and tip amount for building transactions in streaming tip mode.
```rust
let mut tips = client.subscribe_tip_instructions().await?;
while let Some(tip) = tips.recv().await {
println!("Sender: {} ({})", tip.sender_name, tip.sender);
println!(" Wallet: {}", tip.tip_wallet_address);
println!(" Amount: {} SOL (tier: {})", tip.tip_amount_sol, tip.tip_tier);
println!(" Latency: {}ms, Confidence: {}%", tip.expected_latency_ms, tip.confidence);
for alt in &tip.alternative_senders {
println!(" Alt: {} @ {} SOL", alt.sender, alt.tip_amount_sol);
}
}
// Latest cached tip (no subscription required)
if let Some(tip) = client.get_latest_tip().await {
println!("Tip {} SOL to {}", tip.tip_amount_sol, tip.tip_wallet_address);
}
```
#### TipInstruction Fields
| `timestamp` | `u64` | Unix millis |
| `sender` | `String` | Sender ID |
| `sender_name` | `String` | Human-readable sender name |
| `tip_wallet_address` | `String` | Tip wallet address (base58) |
| `tip_amount_sol` | `f64` | Required tip amount in SOL |
| `tip_tier` | `String` | Tip tier name |
| `expected_latency_ms` | `u32` | Expected submission latency (ms) |
| `confidence` | `u32` | Confidence score (0-100) |
| `valid_until_slot` | `u64` | Slot until which this tip is valid |
| `alternative_senders` | `Vec<AlternativeSender>` | Alternative sender options |
### Priority Fees
Network-condition-based fee recommendations, updated every second.
```rust
let mut fees = client.subscribe_priority_fees().await?;
while let Some(fee) = fees.recv().await {
println!("Speed: {}", fee.speed);
println!(" CU price: {} micro-lamports", fee.compute_unit_price);
println!(" CU limit: {}", fee.compute_unit_limit);
println!(" Est cost: {} SOL", fee.estimated_cost_sol);
println!(" Landing probability: {}%", fee.landing_probability);
println!(" Congestion: {}", fee.network_congestion);
println!(" Recent success rate: {:.1}%", fee.recent_success_rate * 100.0);
}
```
#### PriorityFee Fields
| `timestamp` | `u64` | Unix millis |
| `speed` | `String` | Fee speed tier (`"slow"`, `"fast"`, `"ultra_fast"`) |
| `compute_unit_price` | `u64` | Compute unit price in micro-lamports |
| `compute_unit_limit` | `u32` | Recommended compute unit limit |
| `estimated_cost_sol` | `f64` | Estimated total priority fee in SOL |
| `landing_probability` | `u32` | Estimated landing probability (0-100) |
| `network_congestion` | `String` | Network congestion level (`"low"`, `"medium"`, `"high"`) |
| `recent_success_rate` | `f64` | Recent success rate (0.0-1.0) |
### Latest Blockhash
Streams the latest blockhash every 2 seconds. Build transactions without a separate RPC call.
```rust
let mut bh_stream = client.subscribe_latest_blockhash().await?;
while let Some(bh) = bh_stream.recv().await {
println!("Blockhash: {}", bh.blockhash);
println!(" Valid until block height: {}", bh.last_valid_block_height);
}
```
#### LatestBlockhash Fields
| `blockhash` | `String` | Latest blockhash (base58) |
| `last_valid_block_height` | `u64` | Last valid block height for this blockhash |
| `timestamp` | `u64` | Unix millis when fetched |
### Latest Slot
Streams the current confirmed slot on every slot change (~400ms).
```rust
let mut slot_stream = client.subscribe_latest_slot().await?;
while let Some(s) = slot_stream.recv().await {
println!("Current slot: {}", s.slot);
}
```
#### LatestSlot Fields
| `slot` | `u64` | Current confirmed slot number |
| `timestamp` | `u64` | Unix millis |
### Transaction Updates
Real-time status updates for submitted transactions.
```rust
// Transaction updates arrive automatically after submission
// Access via the returned TransactionResult or stream
```
### Auto-Subscribe on Connect
Enable streams at configuration time so they activate immediately:
```rust
let config = Config::builder()
.api_key("sk_live_12345678")
.leader_hints(true) // default: true
.stream_tip_instructions(true) // default: false
.stream_priority_fees(true) // default: false
.stream_latest_blockhash(true) // default: false
.stream_latest_slot(true) // default: false
.build()?;
let client = SlipstreamClient::connect(config).await?;
// All 5 streams are active immediately -- just consume channels
```
### Handling Multiple Streams
Use `tokio::select!` to handle all streams concurrently:
```rust
let mut hints = client.subscribe_leader_hints().await?;
let mut tips = client.subscribe_tip_instructions().await?;
let mut fees = client.subscribe_priority_fees().await?;
let mut bh = client.subscribe_latest_blockhash().await?;
let mut slots = client.subscribe_latest_slot().await?;
loop {
tokio::select! {
Some(hint) = hints.recv() => {
println!("Leader in {} ({}%)", hint.preferred_region, hint.confidence);
}
Some(tip) = tips.recv() => {
println!("Tip {} SOL to {}", tip.tip_amount_sol, tip.tip_wallet_address);
}
Some(fee) = fees.recv() => {
println!("Fee {} µL/CU ({}% landing)", fee.compute_unit_price, fee.landing_probability);
}
Some(blockhash) = bh.recv() => {
println!("Blockhash: {}", blockhash.blockhash);
}
Some(slot) = slots.recv() => {
println!("Slot: {}", slot.slot);
}
}
}
```
---
## Keep-Alive & Time Sync
Background keep-alive mechanism providing latency measurement and NTP-style clock synchronization.
```rust
// Enabled by default (5s interval)
let config = Config::builder()
.api_key("sk_live_12345678")
.keepalive(true) // default: true
.keepalive_interval(5) // default: 5 seconds
.build()?;
let client = SlipstreamClient::connect(config).await?;
// Manual ping
let ping = client.ping().await?;
println!("RTT: {}ms, Clock offset: {}ms, Server time: {}",
ping.rtt_ms, ping.clock_offset_ms, ping.server_time);
// Derived measurements (median from sliding window of 10 samples)
if let Some(latency) = client.latency_ms() {
println!("One-way latency: {}ms", latency); // RTT / 2
}
if let Some(offset) = client.clock_offset_ms() {
println!("Clock offset: {}ms", offset); // server - client time difference
}
let server_now = client.server_time(); // local time + offset
```
#### PingResult Fields
| `seq` | `u32` | Sequence number |
| `rtt_ms` | `u64` | Round-trip time in milliseconds |
| `clock_offset_ms` | `i64` | Clock offset: `server_time - (client_send_time + rtt/2)` (can be negative) |
| `server_time` | `u64` | Server timestamp at time of pong (unix millis) |
---
## Token Billing
Token-based billing system. Paid tiers (Standard/Pro/Enterprise) deduct tokens per transaction and stream subscription. Free tier uses a daily counter.
### Billing Costs
| Transaction submission | 1 token (0.00005 SOL) | Per transaction sent to Solana |
| TPU submission | 0.0001 SOL (100,000 lamports) | Direct to validator TPU ports, bypasses senders |
| Bundle submission | 5 tokens (0.00025 SOL) | Per bundle (2-5 transactions, flat rate) |
| Stream subscription | 1 token (0.00005 SOL) | Per stream type; 1-hour reconnect grace period |
| Webhook delivery | 0.00001 SOL (10,000 lamports) | Per successful POST delivery; retries not charged |
| Keep-alive ping | Free | Background ping/pong not billed |
| Discovery | Free | `GET /v1/discovery` has no auth or billing |
| Balance/billing queries | Free | `get_balance()`, `get_usage_history()`, etc. |
| RPC query | 1 token (0.00005 SOL) | Per `rpc()` call; `simulateTransaction`, `getTransaction`, etc. |
| Bundle simulation | 1 token (0.00005 SOL) | Per `simulate_bundle()` call (wraps `simulateTransaction` per TX) |
| Webhook management | Free | `register_webhook()`, `get_webhook()`, `delete_webhook()` not billed |
| Free tier daily limit | 100 operations/day | Transactions + stream subs + webhook deliveries + RPC queries all count |
### Token Economics
| 1 token | 0.00005 SOL = 50,000 lamports |
| Initial balance | 200 tokens (0.01 SOL) per new API key |
| Minimum deposit | 2,000 tokens (0.1 SOL / ~$10 USD) |
| Grace period | -20 tokens (-0.001 SOL) before hard block |
### Check Balance
```rust
let balance = client.get_balance().await?;
println!("SOL: {:.6}", balance.balance_sol);
println!("Tokens: {}", balance.balance_tokens);
println!("Lamports: {}", balance.balance_lamports);
println!("Grace remaining: {} tokens", balance.grace_remaining_tokens);
```
#### Balance Fields
| `balance_sol` | `f64` | Balance in SOL |
| `balance_tokens` | `i64` | Balance in tokens (1 token = 1 query) |
| `balance_lamports` | `i64` | Balance in lamports |
| `grace_remaining_tokens` | `i64` | Grace tokens remaining before hard block |
### Get Deposit Address
```rust
let deposit = client.get_deposit_address().await?;
println!("Send SOL to: {}", deposit.deposit_wallet);
println!("Minimum: {:.4} SOL", deposit.min_amount_sol);
```
### Minimum Deposit
Deposits must reach **$10 USD equivalent** in SOL before tokens are credited. Deposits below this threshold accumulate as pending.
```rust
let min_usd = client.get_minimum_deposit_usd(); // 10.0
let pending = client.get_pending_deposit().await?;
println!("Pending: {:.6} SOL ({} deposits)", pending.pending_sol, pending.pending_count);
```
### Deposit History
```rust
use allenhark_slipstream::types::DepositHistoryOptions;
let deposits = client.get_deposit_history(DepositHistoryOptions {
limit: Some(20),
offset: None,
}).await?;
for d in &deposits {
println!("{:.6} SOL | ${:.2} USD | {}",
d.amount_sol,
d.usd_value.unwrap_or(0.0),
if d.credited { "CREDITED" } else { "PENDING" });
}
```
### Usage History
```rust
use allenhark_slipstream::types::UsageHistoryOptions;
let entries = client.get_usage_history(UsageHistoryOptions {
limit: Some(50),
offset: None,
}).await?;
for entry in &entries {
println!("{}: {} lamports (balance after: {})",
entry.tx_type, entry.amount_lamports, entry.balance_after_lamports);
}
```
### Free Tier Usage
For free-tier API keys, check the daily usage counter:
```rust
let usage = client.get_free_tier_usage().await?;
println!("Used: {}/{}", usage.used, usage.limit); // e.g. 42/100
println!("Remaining: {}", usage.remaining); // e.g. 58
println!("Resets at: {}", usage.resets_at); // UTC midnight ISO string
```
#### FreeTierUsage Fields
| `used` | `u32` | Transactions used today |
| `remaining` | `u32` | Remaining transactions today |
| `limit` | `u32` | Daily transaction limit (100) |
| `resets_at` | `String` | UTC midnight reset time (RFC 3339) |
---
## Webhooks
Server-side HTTP notifications for transaction lifecycle events and billing alerts. One webhook per API key.
### Setup
Register a webhook via config (auto-registers on connect) or manually:
```rust
// Option 1: Via config (auto-registers on connect)
let config = Config::builder()
.api_key("sk_live_12345678")
.webhook_url("https://your-server.com/webhooks/slipstream")
.webhook_events(vec![
"transaction.confirmed".to_string(),
"transaction.failed".to_string(),
"billing.low_balance".to_string(),
])
.webhook_notification_level("final")
.build()?;
let client = SlipstreamClient::connect(config).await?;
// Option 2: Manual registration
let webhook = client.register_webhook(
"https://your-server.com/webhooks/slipstream",
Some(vec!["transaction.confirmed", "billing.low_balance"]),
Some("final"),
).await?;
println!("Webhook ID: {}", webhook.id);
println!("Secret: {}", webhook.secret); // Save this -- shown only once
```
### Manage Webhooks
```rust
// Get current webhook config
if let Some(webhook) = client.get_webhook().await? {
println!("URL: {}", webhook.url);
println!("Events: {:?}", webhook.events);
println!("Active: {}", webhook.is_active);
}
// Remove webhook
client.delete_webhook().await?;
```
### Event Types
| `transaction.sent` | TX accepted and sent to Solana | `signature`, `region`, `sender`, `latency_ms` |
| `transaction.confirmed` | TX confirmed on-chain | `signature`, `confirmed_slot`, `confirmation_time_ms`, full `getTransaction` response |
| `transaction.failed` | TX timed out or errored | `signature`, `error`, `elapsed_ms` |
| `bundle.sent` | Bundle submitted to sender | `bundle_id`, `region`, `sender`, `latency_ms` |
| `bundle.confirmed` | All transactions in bundle confirmed on-chain | `bundle_id`, `signatures`, `confirmed_slot`, `confirmation_time_ms` |
| `bundle.failed` | Bundle timed out with partial confirmations | `bundle_id`, `error`, `elapsed_ms` |
| `billing.low_balance` | Balance below threshold | `balance_tokens`, `threshold_tokens` |
| `billing.depleted` | Balance at zero / grace period | `balance_tokens`, `grace_remaining_tokens` |
| `billing.deposit_received` | SOL deposit credited | `amount_sol`, `tokens_credited`, `new_balance_tokens` |
### Notification Levels (transaction and bundle events)
| `all` | `transaction.sent` + `transaction.confirmed` + `transaction.failed` + `bundle.sent` + `bundle.confirmed` + `bundle.failed` |
| `final` | `transaction.confirmed` + `transaction.failed` + `bundle.confirmed` + `bundle.failed` (terminal states only) |
| `confirmed` | `transaction.confirmed` + `bundle.confirmed` only |
Billing events are always delivered when subscribed (no level filtering).
### Webhook Payload
Each POST includes these headers:
- `X-Slipstream-Signature: sha256=<hex>` -- HMAC-SHA256 of body using webhook secret
- `X-Slipstream-Timestamp: <unix_seconds>` -- for replay protection
- `X-Slipstream-Event: <event_type>` -- event name
- `Content-Type: application/json`
```json
{
"id": "evt_01H...",
"type": "transaction.confirmed",
"created_at": 1707000000,
"api_key_prefix": "sk_live_",
"data": {
"signature": "5K8c...",
"transaction_id": "uuid",
"confirmed_slot": 245678902,
"confirmation_time_ms": 450,
"transaction": { "...full Solana getTransaction response..." }
}
}
```
### Verifying Webhook Signatures
```rust
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
fn verify_webhook(body: &[u8], signature_header: &str, secret: &str) -> bool {
let expected = signature_header.strip_prefix("sha256=").unwrap_or("");
let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).unwrap();
mac.update(body);
let computed = hex::encode(mac.finalize().into_bytes());
computed == expected
}
```
### Billing
Each successful webhook delivery costs **0.00001 SOL (10,000 lamports)**. Failed deliveries (non-2xx or timeout) are not charged. Free tier deliveries count against the daily limit.
### Retry Policy
- Max 3 attempts: immediate, then 30s, then 5 minutes
- Webhook auto-disabled after 10 consecutive failed deliveries
#### WebhookConfig Fields
| `id` | `String` | Webhook ID |
| `url` | `String` | Delivery URL |
| `secret` | `Option<String>` | HMAC signing secret (only shown on registration) |
| `events` | `Vec<String>` | Subscribed event types |
| `notification_level` | `String` | Transaction notification level |
| `is_active` | `bool` | Whether webhook is active |
| `created_at` | `String` | Creation timestamp (RFC 3339) |
---
## Multi-Region Routing
`MultiRegionClient` connects to workers across multiple regions and automatically routes transactions to the region closest to the current Solana leader.
### Auto-Discovery
```rust
use allenhark_slipstream::{Config, MultiRegionClient};
let config = Config::builder()
.api_key("sk_live_12345678")
.build()?;
let multi = MultiRegionClient::connect(config).await?;
// Transactions auto-route to the best region based on leader hints
let result = multi.submit_transaction(&tx_bytes).await?;
// Check current routing decision
if let Some(routing) = multi.get_current_routing() {
println!("Best region: {} (confidence: {}%)", routing.best_region, routing.confidence);
println!("Leader: {}", routing.leader_pubkey);
println!("Expected RTT: {:?}ms", routing.expected_rtt_ms);
println!("Fallbacks: {:?}", routing.fallback_regions);
}
println!("Connected regions: {:?}", multi.connected_regions());
```
### Manual Worker Configuration
```rust
use allenhark_slipstream::{MultiRegionClient, Config, WorkerEndpoint, MultiRegionConfig};
let workers = vec![
WorkerEndpoint::new("w1", "us-east", "10.0.1.1"),
WorkerEndpoint::new("w2", "eu-central", "10.0.2.1"),
WorkerEndpoint::new("w3", "asia-pacific", "10.0.3.1"),
];
let multi = MultiRegionClient::create(
Config::builder().api_key("sk_live_xxx").build()?,
workers,
MultiRegionConfig {
auto_follow_leader: true,
min_switch_confidence: 70,
switch_cooldown_ms: 5000,
broadcast_high_priority: false,
max_broadcast_regions: 3,
},
).await?;
```
#### MultiRegionConfig Fields
| `auto_follow_leader` | `bool` | `true` | Auto-switch region based on leader hints |
| `min_switch_confidence` | `u32` | `60` | Minimum confidence (0-100) to trigger region switch |
| `switch_cooldown_ms` | `u64` | `500` | Cooldown between region switches (ms) |
| `broadcast_high_priority` | `bool` | `false` | Broadcast high-priority transactions to all regions |
| `max_broadcast_regions` | `usize` | `3` | Maximum regions for broadcast mode |
### Routing Recommendation
Ask the server for the current best region:
```rust
let rec = client.get_routing_recommendation().await?;
println!("Best: {} ({}%)", rec.best_region, rec.confidence);
println!("Leader: {}", rec.leader_pubkey);
println!("Fallbacks: {:?} (strategy: {:?})", rec.fallback_regions, rec.fallback_strategy);
println!("Valid for: {}ms", rec.valid_for_ms);
```
#### RoutingRecommendation Fields
| `best_region` | `String` | Recommended region |
| `leader_pubkey` | `String` | Current leader validator pubkey |
| `slot` | `u64` | Current slot |
| `confidence` | `u32` | Confidence score (0-100) |
| `expected_rtt_ms` | `Option<u32>` | Expected RTT to leader from best region |
| `fallback_regions` | `Vec<String>` | Fallback regions in priority order |
| `fallback_strategy` | `FallbackStrategy` | `Sequential`, `Broadcast`, `Retry`, or `None` |
| `valid_for_ms` | `u64` | Time until this recommendation expires |
---
## Solana RPC Proxy
Proxy standard Solana JSON-RPC methods through Slipstream via `POST /v1/rpc`. Each RPC query costs 1 token (0.00005 SOL). Free tier queries count against the 100/day limit.
### Generic RPC Call
```rust
// Any supported Solana RPC method
let response = client.rpc("getBalance", serde_json::json!(["So11111111111111111111111111111111111111112"])).await?;
println!("Balance: {:?}", response.result);
// Get transaction details
let response = client.rpc("getTransaction", serde_json::json!(["5K8c...", {"encoding": "json"}])).await?;
```
### Simulate Transaction
```rust
// Simulate before submitting (does NOT go through Slipstream routing)
let sim = client.simulate_transaction(&tx_bytes).await?;
if let Some(err) = sim.error {
eprintln!("Simulation failed: {}", err.message);
} else {
println!("Simulation OK — logs: {:?}", sim.logs);
// Now submit for real
let result = client.submit_transaction(&tx_bytes).await?;
}
```
### Simulate Bundle
```rust
// Simulate all transactions in a bundle before submitting
let txs = vec![tx1_bytes, tx2_bytes, tx3_bytes];
let results = client.simulate_bundle(&txs).await?;
// Check each simulation result
let all_ok = results.iter().all(|r| r.error.is_none());
if all_ok {
let result = client.submit_bundle(&txs).await?;
println!("Bundle accepted: {}", result.accepted);
} else {
for (i, r) in results.iter().enumerate() {
if let Some(err) = &r.error {
eprintln!("TX {} failed: {}", i, err.message);
}
}
}
```
### Supported RPC Methods
**Network**
| `getHealth` | Node health status |
**Cluster**
| `getSlot` | Get current slot |
| `getBlockHeight` | Get current block height |
| `getEpochInfo` | Get epoch info (epoch, slot index, slots remaining) |
| `getSlotLeaders` | Get scheduled slot leaders |
**Fees**
| `getLatestBlockhash` | Get current blockhash |
| `getFeeForMessage` | Estimate fee for a message |
| `getRecentPrioritizationFees` | Get recent prioritization fees |
**Accounts**
| `getAccountInfo` | Get account data |
| `getMultipleAccounts` | Get multiple accounts in one call |
| `getBalance` | Get SOL balance for a pubkey |
| `getMinimumBalanceForRentExemption` | Get minimum rent-exempt balance |
**Tokens**
| `getTokenAccountBalance` | Get SPL token balance |
| `getTokenSupply` | Get token mint supply |
| `getSupply` | Get total SOL supply |
| `getTokenLargestAccounts` | Get largest token accounts |
**Transactions**
| `sendTransaction` | Submit a signed transaction |
| `simulateTransaction` | Simulate a transaction without submitting |
| `getSignatureStatuses` | Check status of transaction signatures |
| `getTransaction` | Get transaction details by signature |
**Blocks**
| `getBlockCommitment` | Get block commitment level |
| `getFirstAvailableBlock` | Get first available block in ledger |
### SimulationResult Fields
| `error` | `Option<RpcError>` | Simulation error (None if success) |
| `logs` | `Vec<String>` | Program log messages |
| `units_consumed` | `u64` | Compute units consumed |
### RpcResponse Fields
| `result` | `serde_json::Value` | RPC method result |
| `error` | `Option<RpcError>` | JSON-RPC error if failed |
---
## Deduplication
Prevent duplicate submissions with `dedup_id`:
```rust
let options = SubmitOptions {
dedup_id: Some("unique-tx-id-12345".to_string()),
max_retries: 5,
..Default::default()
};
// Same dedup_id across retries = safe from double-spend
let result = client.submit_transaction_with_options(&tx_bytes, &options).await?;
```
---
## Connection Status & Metrics
```rust
// Connection status
let status = client.connection_status().await;
println!("State: {:?}", status.state); // Connected, Disconnected, etc.
println!("Protocol: {:?}", status.protocol); // Quic, Grpc, WebSocket, Http
println!("Region: {:?}", status.region);
println!("Latency: {}ms", status.latency_ms);
// Performance metrics
let metrics = client.metrics();
println!("Submitted: {}", metrics.transactions_submitted);
println!("Confirmed: {}", metrics.transactions_confirmed);
println!("Avg latency: {:.1}ms", metrics.average_latency_ms);
println!("Success rate: {:.1}%", metrics.success_rate * 100.0);
```
---
## Error Handling
```rust
use allenhark_slipstream::SdkError;
match client.submit_transaction(&tx_bytes).await {
Ok(result) => println!("Signature: {:?}", result.signature),
Err(SdkError::Connection(msg)) => println!("Connection error: {}", msg),
Err(SdkError::Auth(msg)) => println!("Auth error: {}", msg),
Err(SdkError::RateLimited) => println!("Rate limited -- back off"),
Err(SdkError::InsufficientTokens) => {
let balance = client.get_balance().await?;
let deposit = client.get_deposit_address().await?;
println!("Low balance: {} tokens", balance.balance_tokens);
println!("Deposit to: {}", deposit.deposit_wallet);
}
Err(SdkError::Timeout) => println!("Request timed out"),
Err(e) => println!("Error: {}", e),
}
```
### Error Variants
| `Config(msg)` | Invalid configuration |
| `Connection(msg)` | Connection failure |
| `Auth(msg)` | Authentication failure (invalid API key) |
| `Protocol(msg)` | Protocol-level error |
| `Transaction(msg)` | Transaction submission error |
| `Timeout` | Operation timed out |
| `RateLimited` | Rate limit exceeded for your tier |
| `NotConnected` | Client not connected |
| `StreamClosed` | Stream closed unexpectedly |
| `InsufficientTokens` | Token balance too low |
| `AllProtocolsFailed` | All connection protocols failed |
| `Internal(msg)` | Internal SDK error |
---
## Examples
| `basic.rs` | Simple connection and disconnection |
| `submit_transaction.rs` | Transaction submission with options |
| `streaming_callbacks.rs` | All streaming subscriptions with `tokio::select!` |
| `priority_fees.rs` | Priority fee configuration and streaming |
| `leader_hints.rs` | Leader region hints for optimal routing |
| `billing.rs` | Token balance, deposits, usage history |
| `deduplication.rs` | Prevent duplicate transaction submissions |
| `advanced_config.rs` | Full configuration options |
| `broadcast_tx.rs` | Fan-out to multiple regions |
| `signer_integration.rs` | Keypair, Ledger, multi-sig, MPC signing |
```bash
SLIPSTREAM_API_KEY=sk_test_xxx cargo run --example streaming_callbacks
```
## Contact
- Website: https://allenhark.com
- Contact: https://allenhark.com/contact
- Discord: https://discord.gg/JpzS72MAKG
## License
Apache-2.0